summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-12-28 05:28:37 -0500
committerisanae <14251494+isanae@users.noreply.github.com>2020-12-28 05:28:37 -0500
commita8f6f0d7a0f1c8506e225dc5a817cadc0c0b3662 (patch)
treebc95a45dab5acb58ba2b6141362e6cf8fe5e9e29
parent462ea08c348b6c524691e435ea7fb911ffd2367e (diff)
split saves tab, no changes
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/mainwindow.cpp276
-rw-r--r--src/mainwindow.h27
-rw-r--r--src/savestab.cpp278
-rw-r--r--src/savestab.h60
5 files changed, 359 insertions, 283 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ce58f7c2..98bbe05f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -105,6 +105,7 @@ add_filter(NAME src/mainwindow GROUPS
filetreemodel
filterlist
mainwindow
+ savestab
statusbar
)
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 37e0027d..e7d99f23 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -49,7 +49,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "categoriesdialog.h"
#include "modinfodialog.h"
#include "overwriteinfodialog.h"
-#include "activatemodsdialog.h"
#include "downloadlist.h"
#include "downloadlistwidget.h"
#include "messagedialog.h"
@@ -76,6 +75,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "filterlist.h"
#include "datatab.h"
#include "downloadstab.h"
+#include "savestab.h"
#include "instancemanagerdialog.h"
#include <utility.h>
#include <dataarchives.h>
@@ -260,7 +260,6 @@ MainWindow::MainWindow(Settings &settings
, m_ContextItem(nullptr)
, m_ContextAction(nullptr)
, m_ContextRow(-1)
- , m_CurrentSaveView(nullptr)
, m_OrganizerCore(organizerCore)
, m_PluginContainer(pluginContainer)
, m_DidUpdateMasterList(false)
@@ -373,6 +372,8 @@ MainWindow::MainWindow(Settings &settings
// downloads tab
m_DownloadsTab.reset(new DownloadsTab(m_OrganizerCore, ui));
+ // saves tab
+ m_SavesTab.reset(new SavesTab(this, m_OrganizerCore, ui));
// Hide stuff we do not need:
IPluginGame const* game = m_OrganizerCore.managedGame();
@@ -403,9 +404,6 @@ MainWindow::MainWindow(Settings &settings
ui->openFolderMenu->setMenu(openFolderMenu());
- ui->savegameList->installEventFilter(this);
- ui->savegameList->setMouseTracking(true);
-
// don't allow mouse wheel to switch grouping, too many people accidentally
// turn on grouping and then don't understand what happened
EventFilter *noWheel
@@ -423,8 +421,6 @@ MainWindow::MainWindow(Settings &settings
connect(&m_PluginContainer, SIGNAL(diagnosisUpdate()), this, SLOT(scheduleCheckForProblems()));
- connect(ui->savegameList, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(saveSelectionChanged(QListWidgetItem*)));
-
connect(m_ModListSortProxy, SIGNAL(filterActive(bool)), this, SLOT(modFilterActive(bool)));
connect(m_ModListSortProxy, SIGNAL(layoutChanged()), this, SLOT(updateModCount()));
connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString)));
@@ -439,11 +435,6 @@ MainWindow::MainWindow(Settings &settings
this, &MainWindow::refresherProgress);
connect(m_OrganizerCore.directoryRefresher(), SIGNAL(error(QString)), this, SLOT(showError(QString)));
- m_SavesWatcherTimer.setSingleShot(true);
- m_SavesWatcherTimer.setInterval(500);
- connect(&m_SavesWatcher, &QFileSystemWatcher::directoryChanged, [this]() { m_SavesWatcherTimer.start(); });
- connect(&m_SavesWatcherTimer, &QTimer::timeout, this, &MainWindow::refreshSavesIfOpen);
-
connect(&m_OrganizerCore.settings(), SIGNAL(languageChanged(QString)), this, SLOT(languageChange(QString)));
connect(&m_OrganizerCore.settings(), SIGNAL(styleChanged(QString)), this, SIGNAL(styleChanged(QString)));
@@ -496,7 +487,6 @@ MainWindow::MainWindow(Settings &settings
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()));
setFilterShortcuts(ui->modList, ui->modFilterEdit);
@@ -727,7 +717,6 @@ MainWindow::~MainWindow()
}
}
-
void MainWindow::updateWindowTitle(const APIUserAccount& user)
{
//"\xe2\x80\x93" is an "em dash", a longer "-"
@@ -1512,78 +1501,11 @@ void MainWindow::cleanup()
m_MetaSave.waitForFinished();
}
-void MainWindow::displaySaveGameInfo(QListWidgetItem *newItem)
-{
- // don't display the widget if the main window doesn't have focus
- //
- // this goes against the standard behaviour for tooltips, which are displayed
- // on hover regardless of focus, but this widget is so large and busy that
- // it's probably better this way
- if (!isActiveWindow()){
- return;
- }
-
- if (m_CurrentSaveView == nullptr) {
- IPluginGame const *game = m_OrganizerCore.managedGame();
- SaveGameInfo const *info = game->feature<SaveGameInfo>();
- if (info != nullptr) {
- m_CurrentSaveView = info->getSaveGameWidget(this);
- }
- if (m_CurrentSaveView == nullptr) {
- return;
- }
- }
- m_CurrentSaveView->setSave(*m_SaveGames[ui->savegameList->row(newItem)]);
-
- QWindow *window = m_CurrentSaveView->window()->windowHandle();
- QRect screenRect;
- if (window == nullptr)
- screenRect = QGuiApplication::primaryScreen()->geometry();
- else
- screenRect = window->screen()->geometry();
-
- QPoint pos = QCursor::pos();
- if (pos.x() + m_CurrentSaveView->width() > screenRect.right()) {
- pos.rx() -= (m_CurrentSaveView->width() + 2);
- } else {
- pos.rx() += 5;
- }
-
- if (pos.y() + m_CurrentSaveView->height() > screenRect.bottom()) {
- pos.ry() -= (m_CurrentSaveView->height() + 10);
- } else {
- pos.ry() += 20;
- }
- m_CurrentSaveView->move(pos);
-
- m_CurrentSaveView->show();
- m_CurrentSaveView->setProperty("displayItem", QVariant::fromValue(static_cast<void *>(newItem)));
-}
-
-
-void MainWindow::saveSelectionChanged(QListWidgetItem *newItem)
-{
- if (newItem == nullptr) {
- hideSaveGameInfo();
- } else if (m_CurrentSaveView == nullptr || newItem != m_CurrentSaveView->property("displayItem").value<void*>()) {
- displaySaveGameInfo(newItem);
- }
-}
-
-
-void MainWindow::hideSaveGameInfo()
-{
- if (m_CurrentSaveView != nullptr) {
- m_CurrentSaveView->deleteLater();
- m_CurrentSaveView = nullptr;
- }
-}
-
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if ((object == ui->savegameList) &&
((event->type() == QEvent::Leave) || (event->type() == QEvent::WindowDeactivate))) {
- hideSaveGameInfo();
+ m_SavesTab->hideSaveGameInfo();
} else if (event->type() == QEvent::StatusTip && object != this) {
QMainWindow::event(event);
return true;
@@ -1759,7 +1681,7 @@ void MainWindow::activateSelectedProfile()
m_ModListSortProxy->setProfile(m_OrganizerCore.currentProfile());
- refreshSaveList();
+ m_SavesTab->refreshSaveList();
m_OrganizerCore.refresh();
updateModCount();
updatePluginCount();
@@ -1805,14 +1727,16 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature<LocalSavegames>();
if (saveGames != nullptr) {
- if (saveGames->prepareProfile(m_OrganizerCore.currentProfile()))
- refreshSaveList();
+ if (saveGames->prepareProfile(m_OrganizerCore.currentProfile())) {
+ m_SavesTab->refreshSaveList();
+ }
}
BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
if (invalidation != nullptr) {
- if (invalidation->prepareProfile(m_OrganizerCore.currentProfile()))
+ if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) {
QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ }
}
}
}
@@ -1898,78 +1822,6 @@ void MainWindow::refreshExecutablesList()
ui->executablesListBox->setEnabled(true);
}
-void MainWindow::refreshSavesIfOpen()
-{
- if (ui->tabWidget->currentWidget() == ui->savesTab) {
- refreshSaveList();
- }
-}
-
-QDir MainWindow::currentSavesDir() const
-{
- QDir savesDir;
- if (m_OrganizerCore.currentProfile()->localSavesEnabled()) {
- savesDir.setPath(m_OrganizerCore.currentProfile()->savePath());
- } else {
- auto iniFiles = m_OrganizerCore.managedGame()->iniFiles();
-
- if (iniFiles.isEmpty()) {
- return m_OrganizerCore.managedGame()->savesDirectory();
- }
-
- QString iniPath = m_OrganizerCore.currentProfile()->absoluteIniFilePath(iniFiles[0]);
-
- wchar_t path[MAX_PATH];
- if (::GetPrivateProfileStringW(
- L"General", L"SLocalSavePath", L"",
- path, MAX_PATH,
- iniPath.toStdWString().c_str()
- )) {
- savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path)));
- }
- else {
- savesDir = m_OrganizerCore.managedGame()->savesDirectory();
- }
- }
-
- return savesDir;
-}
-
-void MainWindow::startMonitorSaves()
-{
- stopMonitorSaves();
-
- QDir savesDir = currentSavesDir();
-
- m_SavesWatcher.addPath(savesDir.absolutePath());
-}
-
-void MainWindow::stopMonitorSaves()
-{
- if (m_SavesWatcher.directories().length() > 0) {
- m_SavesWatcher.removePaths(m_SavesWatcher.directories());
- }
-}
-
-void MainWindow::refreshSaveList()
-{
- TimeThis tt("MainWindow::refreshSaveList()");
-
- startMonitorSaves(); // re-starts monitoring
-
- QDir savesDir = currentSavesDir();
- MOBase::log::debug("reading save games from {}", savesDir.absolutePath());
- m_SaveGames = m_OrganizerCore.managedGame()->listSaves(savesDir);
- std::sort(m_SaveGames.begin(), m_SaveGames.end(), [](auto const& lhs, auto const& rhs) {
- return lhs->getCreationTime() > rhs->getCreationTime();
- });
-
- ui->savegameList->clear();
- for (auto& save: m_SaveGames) {
- ui->savegameList->addItem(savesDir.relativeFilePath(save->getFilepath()));
- }
-}
-
static bool BySortValue(const std::pair<UINT32, QTreeWidgetItem*> &LHS, const std::pair<UINT32, QTreeWidgetItem*> &RHS)
{
return LHS.first < RHS.first;
@@ -2320,7 +2172,7 @@ void MainWindow::on_tabWidget_currentChanged(int index)
} else if (currentWidget == ui->dataTab) {
m_DataTab->activated();
} else if (currentWidget == ui->savesTab) {
- refreshSaveList();
+ m_SavesTab->refreshSaveList();
}
}
@@ -2452,9 +2304,9 @@ void MainWindow::on_actionAdd_Profile_triggered()
// workaround: need to disable monitoring of the saves directory, otherwise the active
// profile directory is locked
- stopMonitorSaves();
+ m_SavesTab->stopMonitorSaves();
profilesDialog.exec();
- refreshSaveList(); // since the save list may now be outdated we have to refresh it completely
+ m_SavesTab->refreshSaveList(); // since the save list may now be outdated we have to refresh it completely
if (refreshProfiles() && !profilesDialog.failed()) {
break;
@@ -2463,14 +2315,16 @@ void MainWindow::on_actionAdd_Profile_triggered()
LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature<LocalSavegames>();
if (saveGames != nullptr) {
- if (saveGames->prepareProfile(m_OrganizerCore.currentProfile()))
- refreshSaveList();
+ if (saveGames->prepareProfile(m_OrganizerCore.currentProfile())) {
+ m_SavesTab->refreshSaveList();
+ }
}
BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
if (invalidation != nullptr) {
- if (invalidation->prepareProfile(m_OrganizerCore.currentProfile()))
+ if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) {
QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ }
}
}
@@ -4923,100 +4777,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
}
}
-
-void MainWindow::deleteSavegame_clicked()
-{
- SaveGameInfo const *info = m_OrganizerCore.managedGame()->feature<SaveGameInfo>();
-
- QString savesMsgLabel;
- QStringList deleteFiles;
-
- int count = 0;
-
- for (const QModelIndex &idx : ui->savegameList->selectionModel()->selectedIndexes()) {
-
- auto& saveGame = m_SaveGames[idx.row()];
-
- if (count < 10) {
- savesMsgLabel += "<li>" + QFileInfo(saveGame->getFilepath()).completeBaseName() + "</li>";
- }
- ++count;
-
- deleteFiles += saveGame->allFiles();
- }
-
- if (count > 10) {
- savesMsgLabel += "<li><i>... " + tr("%1 more").arg(count - 10) + "</i></li>";
- }
-
- if (QMessageBox::question(this, tr("Confirm"),
- tr("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.", "", count)
- .arg(savesMsgLabel),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- shellDelete(deleteFiles, true); // recycle bin delete.
- refreshSaveList();
- }
-}
-
-
-void MainWindow::fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets)
-{
- ActivateModsDialog dialog(missingAssets, this);
- if (dialog.exec() == QDialog::Accepted) {
- // activate the required mods, then enable all esps
- std::set<QString> modsToActivate = dialog.getModsToActivate();
- for (std::set<QString>::iterator iter = modsToActivate.begin(); iter != modsToActivate.end(); ++iter) {
- if ((*iter != "<data>") && (*iter != "<overwrite>")) {
- unsigned int modIndex = ModInfo::getIndex(*iter);
- m_OrganizerCore.currentProfile()->setModEnabled(modIndex, true);
- }
- }
-
- m_OrganizerCore.currentProfile()->writeModlist();
- m_OrganizerCore.refreshLists();
-
- std::set<QString> espsToActivate = dialog.getESPsToActivate();
- for (std::set<QString>::iterator iter = espsToActivate.begin(); iter != espsToActivate.end(); ++iter) {
- m_OrganizerCore.pluginList()->enableESP(*iter);
- }
- m_OrganizerCore.saveCurrentLists();
- }
-}
-
-
-void MainWindow::on_savegameList_customContextMenuRequested(const QPoint& pos)
-{
- QItemSelectionModel* selection = ui->savegameList->selectionModel();
-
- if (!selection->hasSelection()) {
- return;
- }
-
- QMenu menu;
-
- SaveGameInfo const* info = this->m_OrganizerCore.managedGame()->feature<SaveGameInfo>();
- if (info != nullptr) {
- QAction* action = menu.addAction(tr("Enable Mods..."));
- action->setEnabled(false);
- if (selection->selectedIndexes().count() == 1) {
- auto& save = m_SaveGames[selection->selectedIndexes()[0].row()];
- SaveGameInfo::MissingAssets missing = info->getMissingAssets(*save);
- if (missing.size() != 0) {
- connect(action, &QAction::triggered, this, [this, missing] { fixMods_clicked(missing); });
- action->setEnabled(true);
- }
- }
- }
-
- QString deleteMenuLabel = tr("Delete %n save(s)", "", selection->selectedIndexes().count());
-
- menu.addAction(deleteMenuLabel, this, SLOT(deleteSavegame_clicked()));
-
- menu.exec(ui->savegameList->viewport()->mapToGlobal(pos));
-}
-
void MainWindow::linkToolbar()
{
Executable* exe = getSelectedExecutable();
diff --git a/src/mainwindow.h b/src/mainwindow.h
index e4c7b851..eae150d6 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -27,7 +27,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "iuserinterface.h"
#include "modinfo.h"
#include "modlistsortproxy.h"
-#include "savegameinfo.h"
#include "tutorialcontrol.h"
#include "plugincontainer.h" //class PluginContainer;
#include "iplugingame.h" //namespace MOBase { class IPluginGame; }
@@ -40,6 +39,7 @@ class OrganizerCore;
class FilterList;
class DataTab;
class DownloadsTab;
+class SavesTab;
class BrowserDialog;
class PluginListSortProxy;
@@ -47,7 +47,6 @@ namespace BSA { class Archive; }
namespace MOBase { class IPluginModPage; }
namespace MOBase { class IPluginTool; }
-namespace MOBase { class ISaveGame; }
namespace MOShared { class DirectoryEntry; }
@@ -123,7 +122,6 @@ public:
bool addProfile();
void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives);
- void refreshSaveList();
void setModListSorting(int index);
void setESPListSorting(int index);
@@ -245,8 +243,6 @@ private:
void setCategoryListVisible(bool visible);
- void displaySaveGameInfo(QListWidgetItem *newItem);
-
bool errorReported(QString &logFile);
void updateESPLock(bool locked);
@@ -263,11 +259,6 @@ private:
QMenu *openFolderMenu();
- QDir currentSavesDir() const;
-
- void startMonitorSaves();
- void stopMonitorSaves();
-
void dropLocalFile(const QUrl &url, const QString &outputDir, bool move);
void sendSelectedModsToPriority(int newPriority);
@@ -297,6 +288,7 @@ private:
std::unique_ptr<FilterList> m_Filters;
std::unique_ptr<DataTab> m_DataTab;
std::unique_ptr<DownloadsTab> m_DownloadsTab;
+ std::unique_ptr<SavesTab> m_SavesTab;
int m_OldProfileIndex;
@@ -324,9 +316,6 @@ private:
QFuture<void> m_MetaSave;
QTime m_StartTime;
- //SaveGameInfoWidget *m_CurrentSaveView;
- std::vector<std::shared_ptr<const MOBase::ISaveGame>> m_SaveGames;
- MOBase::ISaveGameInfoWidget *m_CurrentSaveView;
OrganizerCore &m_OrganizerCore;
PluginContainer &m_PluginContainer;
@@ -336,9 +325,6 @@ private:
std::unique_ptr<BrowserDialog> m_IntegratedBrowser;
- QTimer m_SavesWatcherTimer;
- QFileSystemWatcher m_SavesWatcher;
-
QByteArray m_ArchiveListHash;
bool m_DidUpdateMasterList;
@@ -360,7 +346,6 @@ private:
Executable* getSelectedExecutable();
private slots:
-
void updateWindowTitle(const APIUserAccount& user);
void showMessage(const QString &message);
void showError(const QString &message);
@@ -410,9 +395,6 @@ private slots:
void sendSelectedModsToBottom_clicked();
void sendSelectedModsToPriority_clicked();
void sendSelectedModsToSeparator_clicked();
- // savegame context menu
- void deleteSavegame_clicked();
- void fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets);
// data-tree context menu
// pluginlist context menu
@@ -427,7 +409,6 @@ private slots:
void linkMenu();
void languageChange(const QString &newLanguage);
- void saveSelectionChanged(QListWidgetItem *newItem);
void windowTutorialFinished(const QString &windowName);
@@ -492,8 +473,6 @@ private slots:
void modRenamed(const QString &oldName, const QString &newName);
void modRemoved(const QString &fileName);
- void hideSaveGameInfo();
-
void hookUpWindowTutorials();
bool shouldStartTutorial() const;
@@ -562,7 +541,6 @@ private slots:
void ignoreUpdate();
void unignoreUpdate();
- void refreshSavesIfOpen();
void about();
void modListSortIndicatorChanged(int column, Qt::SortOrder order);
@@ -605,7 +583,6 @@ private slots: // ui slots
void on_listOptionsBtn_pressed();
void on_espList_doubleClicked(const QModelIndex &index);
void on_profileBox_currentIndexChanged(int index);
- void on_savegameList_customContextMenuRequested(const QPoint &pos);
void on_startButton_clicked();
void on_tabWidget_currentChanged(int index);
diff --git a/src/savestab.cpp b/src/savestab.cpp
new file mode 100644
index 00000000..a14321d3
--- /dev/null
+++ b/src/savestab.cpp
@@ -0,0 +1,278 @@
+#include "savestab.h"
+#include "ui_mainwindow.h"
+#include "organizercore.h"
+#include "activatemodsdialog.h"
+#include <iplugingame.h>
+#include <isavegameinfowidget.h>
+
+using namespace MOBase;
+
+SavesTab::SavesTab(QWidget* window, OrganizerCore& core, Ui::MainWindow* mwui)
+ : m_window(window), m_core(core), m_CurrentSaveView(nullptr), ui{
+ mwui->tabWidget, mwui->savesTab, mwui->savegameList}
+{
+ m_SavesWatcherTimer.setSingleShot(true);
+ m_SavesWatcherTimer.setInterval(500);
+
+ ui.list->installEventFilter(this);
+ ui.list->setMouseTracking(true);
+
+ connect(
+ &m_SavesWatcher, &QFileSystemWatcher::directoryChanged,
+ [&]{ m_SavesWatcherTimer.start(); });
+
+ connect(
+ &m_SavesWatcherTimer, &QTimer::timeout,
+ [&]{ refreshSavesIfOpen(); });
+
+ connect(
+ ui.list, &QWidget::customContextMenuRequested,
+ [&](auto pos){ onContextMenu(pos); });
+
+ connect(
+ ui.list, &QListWidget::itemEntered,
+ [&](auto* item){ saveSelectionChanged(item); });
+}
+
+void SavesTab::displaySaveGameInfo(QListWidgetItem *newItem)
+{
+ // don't display the widget if the main window doesn't have focus
+ //
+ // this goes against the standard behaviour for tooltips, which are displayed
+ // on hover regardless of focus, but this widget is so large and busy that
+ // it's probably better this way
+ if (!m_window->isActiveWindow()){
+ return;
+ }
+
+ if (m_CurrentSaveView == nullptr) {
+ const IPluginGame* game = m_core.managedGame();
+ const SaveGameInfo* info = game->feature<SaveGameInfo>();
+
+ if (info != nullptr) {
+ m_CurrentSaveView = info->getSaveGameWidget(m_window);
+ }
+
+ if (m_CurrentSaveView == nullptr) {
+ return;
+ }
+ }
+
+ m_CurrentSaveView->setSave(*m_SaveGames[ui.list->row(newItem)]);
+
+ QWindow *window = m_CurrentSaveView->window()->windowHandle();
+ QRect screenRect;
+ if (window == nullptr)
+ screenRect = QGuiApplication::primaryScreen()->geometry();
+ else
+ screenRect = window->screen()->geometry();
+
+ QPoint pos = QCursor::pos();
+ if (pos.x() + m_CurrentSaveView->width() > screenRect.right()) {
+ pos.rx() -= (m_CurrentSaveView->width() + 2);
+ } else {
+ pos.rx() += 5;
+ }
+
+ if (pos.y() + m_CurrentSaveView->height() > screenRect.bottom()) {
+ pos.ry() -= (m_CurrentSaveView->height() + 10);
+ } else {
+ pos.ry() += 20;
+ }
+ m_CurrentSaveView->move(pos);
+
+ m_CurrentSaveView->show();
+ m_CurrentSaveView->setProperty("displayItem", QVariant::fromValue(static_cast<void *>(newItem)));
+}
+
+
+void SavesTab::saveSelectionChanged(QListWidgetItem *newItem)
+{
+ if (newItem == nullptr) {
+ hideSaveGameInfo();
+ } else if (m_CurrentSaveView == nullptr || newItem != m_CurrentSaveView->property("displayItem").value<void*>()) {
+ displaySaveGameInfo(newItem);
+ }
+}
+
+
+void SavesTab::hideSaveGameInfo()
+{
+ if (m_CurrentSaveView != nullptr) {
+ m_CurrentSaveView->deleteLater();
+ m_CurrentSaveView = nullptr;
+ }
+}
+
+void SavesTab::refreshSavesIfOpen()
+{
+ if (ui.mainTabs->currentWidget() == ui.tab) {
+ refreshSaveList();
+ }
+}
+
+QDir SavesTab::currentSavesDir() const
+{
+ QDir savesDir;
+ if (m_core.currentProfile()->localSavesEnabled()) {
+ savesDir.setPath(m_core.currentProfile()->savePath());
+ } else {
+ auto iniFiles = m_core.managedGame()->iniFiles();
+
+ if (iniFiles.isEmpty()) {
+ return m_core.managedGame()->savesDirectory();
+ }
+
+ QString iniPath = m_core.currentProfile()->absoluteIniFilePath(iniFiles[0]);
+
+ wchar_t path[MAX_PATH];
+ if (::GetPrivateProfileStringW(
+ L"General", L"SLocalSavePath", L"",
+ path, MAX_PATH,
+ iniPath.toStdWString().c_str()
+ )) {
+ savesDir.setPath(m_core.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path)));
+ }
+ else {
+ savesDir = m_core.managedGame()->savesDirectory();
+ }
+ }
+
+ return savesDir;
+}
+
+void SavesTab::startMonitorSaves()
+{
+ stopMonitorSaves();
+
+ QDir savesDir = currentSavesDir();
+
+ m_SavesWatcher.addPath(savesDir.absolutePath());
+}
+
+void SavesTab::stopMonitorSaves()
+{
+ if (m_SavesWatcher.directories().length() > 0) {
+ m_SavesWatcher.removePaths(m_SavesWatcher.directories());
+ }
+}
+
+void SavesTab::refreshSaveList()
+{
+ TimeThis tt("MainWindow::refreshSaveList()");
+
+ startMonitorSaves(); // re-starts monitoring
+
+ try
+ {
+ QDir savesDir = currentSavesDir();
+ MOBase::log::debug("reading save games from {}", savesDir.absolutePath());
+ m_SaveGames = m_core.managedGame()->listSaves(savesDir);
+ std::sort(m_SaveGames.begin(), m_SaveGames.end(), [](auto const& lhs, auto const& rhs) {
+ return lhs->getCreationTime() > rhs->getCreationTime();
+ });
+
+ ui.list->clear();
+ for (auto& save: m_SaveGames) {
+ ui.list->addItem(savesDir.relativeFilePath(save->getFilepath()));
+ }
+ }
+ catch(std::exception& e)
+ {
+ // listSaves() can throw
+ log::error("{}", e.what());
+ }
+}
+
+void SavesTab::deleteSavegame()
+{
+ SaveGameInfo const *info = m_core.managedGame()->feature<SaveGameInfo>();
+
+ QString savesMsgLabel;
+ QStringList deleteFiles;
+
+ int count = 0;
+
+ for (const QModelIndex &idx : ui.list->selectionModel()->selectedIndexes()) {
+
+ auto& saveGame = m_SaveGames[idx.row()];
+
+ if (count < 10) {
+ savesMsgLabel += "<li>" + QFileInfo(saveGame->getFilepath()).completeBaseName() + "</li>";
+ }
+ ++count;
+
+ deleteFiles += saveGame->allFiles();
+ }
+
+ if (count > 10) {
+ savesMsgLabel += "<li><i>... " + tr("%1 more").arg(count - 10) + "</i></li>";
+ }
+
+ if (QMessageBox::question(m_window, tr("Confirm"),
+ tr("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.", "", count)
+ .arg(savesMsgLabel),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ shellDelete(deleteFiles, true); // recycle bin delete.
+ refreshSaveList();
+ }
+}
+
+
+void SavesTab::onContextMenu(const QPoint& pos)
+{
+ QItemSelectionModel* selection = ui.list->selectionModel();
+
+ if (!selection->hasSelection()) {
+ return;
+ }
+
+ QMenu menu;
+
+ SaveGameInfo const* info = this->m_core.managedGame()->feature<SaveGameInfo>();
+ if (info != nullptr) {
+ QAction* action = menu.addAction(tr("Fix enabled mods..."));
+ action->setEnabled(false);
+ if (selection->selectedIndexes().count() == 1) {
+ auto& save = m_SaveGames[selection->selectedIndexes()[0].row()];
+ SaveGameInfo::MissingAssets missing = info->getMissingAssets(*save);
+ if (missing.size() != 0) {
+ connect(action, &QAction::triggered, this, [this, missing] { fixMods(missing); });
+ action->setEnabled(true);
+ }
+ }
+ }
+
+ QString deleteMenuLabel = tr("Delete %n save(s)", "", selection->selectedIndexes().count());
+
+ menu.addAction(deleteMenuLabel, [&]{ deleteSavegame(); });
+
+ menu.exec(ui.list->viewport()->mapToGlobal(pos));
+}
+
+void SavesTab::fixMods(SaveGameInfo::MissingAssets const &missingAssets)
+{
+ ActivateModsDialog dialog(missingAssets, m_window);
+ if (dialog.exec() == QDialog::Accepted) {
+ // activate the required mods, then enable all esps
+ std::set<QString> modsToActivate = dialog.getModsToActivate();
+ for (std::set<QString>::iterator iter = modsToActivate.begin(); iter != modsToActivate.end(); ++iter) {
+ if ((*iter != "<data>") && (*iter != "<overwrite>")) {
+ unsigned int modIndex = ModInfo::getIndex(*iter);
+ m_core.currentProfile()->setModEnabled(modIndex, true);
+ }
+ }
+
+ m_core.currentProfile()->writeModlist();
+ m_core.refreshLists();
+
+ std::set<QString> espsToActivate = dialog.getESPsToActivate();
+ for (std::set<QString>::iterator iter = espsToActivate.begin(); iter != espsToActivate.end(); ++iter) {
+ m_core.pluginList()->enableESP(*iter);
+ }
+
+ m_core.saveCurrentLists();
+ }
+}
diff --git a/src/savestab.h b/src/savestab.h
new file mode 100644
index 00000000..d3eb7e63
--- /dev/null
+++ b/src/savestab.h
@@ -0,0 +1,60 @@
+#ifndef MODORGANIZER_SAVESTAB_INCLUDED
+#define MODORGANIZER_SAVESTAB_INCLUDED
+
+#include "savegameinfo.h"
+#include <filterwidget.h>
+
+namespace Ui { class MainWindow; }
+
+namespace MOBase
+{
+ class ISaveGame;
+ class ISaveGameInfoWidget;
+}
+
+class MainWindow;
+class OrganizerCore;
+
+
+class SavesTab : public QObject
+{
+ Q_OBJECT;
+
+public:
+ SavesTab(QWidget* window, OrganizerCore& core, Ui::MainWindow* ui);
+
+ void refreshSaveList();
+ void displaySaveGameInfo(QListWidgetItem *newItem);
+
+ QDir currentSavesDir() const;
+
+ void startMonitorSaves();
+ void stopMonitorSaves();
+ void hideSaveGameInfo();
+
+private:
+ struct SavesTabUi
+ {
+ QTabWidget* mainTabs;
+ QWidget* tab;
+ QListWidget* list;
+ };
+
+ QWidget* m_window;
+ OrganizerCore& m_core;
+ SavesTabUi ui;
+ MOBase::FilterWidget m_filter;
+ std::vector<std::shared_ptr<const MOBase::ISaveGame>> m_SaveGames;
+ MOBase::ISaveGameInfoWidget *m_CurrentSaveView;
+
+ QTimer m_SavesWatcherTimer;
+ QFileSystemWatcher m_SavesWatcher;
+
+ void onContextMenu(const QPoint &pos);
+ void deleteSavegame();
+ void saveSelectionChanged(QListWidgetItem *newItem);
+ void fixMods(SaveGameInfo::MissingAssets const &missingAssets);
+ void refreshSavesIfOpen();
+};
+
+#endif // MODORGANIZER_SAVESTAB_INCLUDED