diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2020-12-28 05:28:37 -0500 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-12-28 05:28:37 -0500 |
| commit | a8f6f0d7a0f1c8506e225dc5a817cadc0c0b3662 (patch) | |
| tree | bc95a45dab5acb58ba2b6141362e6cf8fe5e9e29 /src | |
| parent | 462ea08c348b6c524691e435ea7fb911ffd2367e (diff) | |
split saves tab, no changes
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 276 | ||||
| -rw-r--r-- | src/mainwindow.h | 27 | ||||
| -rw-r--r-- | src/savestab.cpp | 278 | ||||
| -rw-r--r-- | src/savestab.h | 60 |
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 |
