summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mainwindow.cpp12
-rw-r--r--src/modlistcontextmenu.cpp2
-rw-r--r--src/organizercore.cpp99
-rw-r--r--src/organizercore.h54
-rw-r--r--src/organizerproxy.cpp40
-rw-r--r--src/organizerproxy.h4
-rw-r--r--src/processrunner.cpp4
7 files changed, 138 insertions, 77 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 12586d3b..8610f5e4 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1803,7 +1803,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
if (invalidation != nullptr) {
if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) {
- QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ QTimer::singleShot(5, [this] {
+ m_OrganizerCore.refresh();
+ });
}
}
}
@@ -2389,7 +2391,9 @@ void MainWindow::on_actionAdd_Profile_triggered()
m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
if (invalidation != nullptr) {
if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) {
- QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ QTimer::singleShot(5, [this] {
+ m_OrganizerCore.refresh();
+ });
}
}
}
@@ -2549,7 +2553,7 @@ void MainWindow::setWindowEnabled(bool enabled)
void MainWindow::refreshProfile_activated()
{
- m_OrganizerCore.profileRefresh();
+ m_OrganizerCore.refresh();
}
void MainWindow::saveArchiveList()
@@ -2776,7 +2780,7 @@ void MainWindow::on_actionSettings_triggered()
if ((settings.paths().mods() != oldModDirectory) ||
(settings.interface().displayForeign() != oldDisplayForeign)) {
- m_OrganizerCore.profileRefresh();
+ m_OrganizerCore.refresh();
}
const auto state = settings.archiveParsing();
diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp
index 06cd19d6..d4630ec3 100644
--- a/src/modlistcontextmenu.cpp
+++ b/src/modlistcontextmenu.cpp
@@ -97,7 +97,7 @@ void ModListGlobalContextMenu::populate(OrganizerCore& core, ModListView* view,
addAction(tr("Auto assign categories"), [=]() {
view->actions().assignCategories();
});
- addAction(tr("Refresh"), &core, &OrganizerCore::profileRefresh);
+ addAction(tr("Refresh"), &core, &OrganizerCore::refresh);
addAction(tr("Export to csv..."), [=]() {
view->actions().exportModListCSV();
});
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index b4e758d1..769c6018 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -111,9 +111,9 @@ OrganizerCore::OrganizerCore(Settings& settings)
connect(&m_DownloadManager, SIGNAL(downloadSpeed(QString, int)), this,
SLOT(downloadSpeed(QString, int)));
- connect(m_DirectoryRefresher.get(), SIGNAL(refreshed()), this,
- SLOT(directory_refreshed()));
-
+ connect(m_DirectoryRefresher.get(), &DirectoryRefresher::refreshed, [this]() {
+ onDirectoryRefreshed();
+ });
connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString)));
connect(&m_ModList, &ModList::modStatesChanged, [=] {
currentProfile()->writeModlist();
@@ -1134,8 +1134,8 @@ bool OrganizerCore::previewFile(QWidget* parent, const QString& originName,
return true;
}
-boost::signals2::connection
-OrganizerCore::onAboutToRun(const std::function<bool(const QString&)>& func)
+boost::signals2::connection OrganizerCore::onAboutToRun(
+ const std::function<bool(const QString&, const QDir&, const QString&)>& func)
{
return m_AboutToRun.connect(func);
}
@@ -1195,6 +1195,18 @@ OrganizerCore::onPluginDisabled(std::function<void(const IPlugin*)> const& func)
return m_PluginDisabled.connect(func);
}
+boost::signals2::connection
+OrganizerCore::onNextRefresh(std::function<void()> const& func,
+ RefreshCallbackGroup group, RefreshCallbackMode mode)
+{
+ if (m_DirectoryUpdate || mode == RefreshCallbackMode::FORCE_WAIT_FOR_REFRESH) {
+ return m_OnNextRefreshCallbacks.connect(static_cast<int>(group), func);
+ } else {
+ func();
+ return {};
+ }
+}
+
void OrganizerCore::refresh(bool saveChanges)
{
// don't lose changes!
@@ -1212,25 +1224,21 @@ void OrganizerCore::refresh(bool saveChanges)
void OrganizerCore::refreshESPList(bool force)
{
- TimeThis tt("OrganizerCore::refreshESPList()");
+ onNextRefresh(
+ [this, force] {
+ TimeThis tt("OrganizerCore::refreshESPList()");
- if (m_DirectoryUpdate) {
- // don't mess up the esp list if we're currently updating the directory
- // structure
- m_PostRefreshTasks.append([=]() {
- this->refreshESPList(force);
- });
- return;
- }
- m_CurrentProfile->writeModlist();
+ m_CurrentProfile->writeModlist();
- // clear list
- try {
- m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure,
- m_CurrentProfile->getLockedOrderFileName(), force);
- } catch (const std::exception& e) {
- reportError(tr("Failed to refresh list of esps: %1").arg(e.what()));
- }
+ // clear list
+ try {
+ m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure,
+ m_CurrentProfile->getLockedOrderFileName(), force);
+ } catch (const std::exception& e) {
+ reportError(tr("Failed to refresh list of esps: %1").arg(e.what()));
+ }
+ },
+ RefreshCallbackGroup::CORE, RefreshCallbackMode::RUN_NOW_IF_POSSIBLE);
}
void OrganizerCore::refreshBSAList()
@@ -1513,13 +1521,13 @@ void OrganizerCore::refreshDirectoryStructure()
std::set<QString>(archives.begin(), archives.end()));
// runs refresh() in a thread
- QTimer::singleShot(0, m_DirectoryRefresher.get(), SLOT(refresh()));
+ QTimer::singleShot(0, m_DirectoryRefresher.get(), &DirectoryRefresher::refresh);
}
-void OrganizerCore::directory_refreshed()
+void OrganizerCore::onDirectoryRefreshed()
{
log::debug("directory refreshed, finishing up");
- TimeThis tt("OrganizerCore::directory_refreshed()");
+ TimeThis tt("OrganizerCore::onDirectoryRefreshed()");
DirectoryEntry* newStructure = m_DirectoryRefresher->stealDirectoryStructure();
Q_ASSERT(newStructure != m_DirectoryStructure);
@@ -1543,23 +1551,18 @@ void OrganizerCore::directory_refreshed()
log::debug("structure deleter thread done");
});
- m_DirectoryUpdate = false;
-
log::debug("clearing caches");
for (int i = 0; i < m_ModList.rowCount(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->clearCaches();
}
- if (!m_PostRefreshTasks.empty()) {
- log::debug("running {} post refresh tasks", m_PostRefreshTasks.size());
-
- for (auto task : m_PostRefreshTasks) {
- task();
- }
+ // needs to be done before post refresh tasks
+ m_DirectoryUpdate = false;
- m_PostRefreshTasks.clear();
- }
+ log::debug("running {} post refresh tasks");
+ m_OnNextRefreshCallbacks();
+ m_OnNextRefreshCallbacks.disconnect_all_slots();
if (m_CurrentProfile != nullptr) {
log::debug("refreshing lists");
@@ -1571,11 +1574,6 @@ void OrganizerCore::directory_refreshed()
log::debug("refresh done");
}
-void OrganizerCore::profileRefresh()
-{
- refresh();
-}
-
void OrganizerCore::clearCaches(std::vector<unsigned int> const& indices) const
{
const auto insert = [](auto& dest, const auto& from) {
@@ -1891,15 +1889,12 @@ bool OrganizerCore::saveCurrentLists()
void OrganizerCore::savePluginList()
{
- if (m_DirectoryUpdate) {
- // delay save till after directory update
- m_PostRefreshTasks.append([this]() {
- this->savePluginList();
- });
- return;
- }
- m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName());
- m_PluginList.saveLoadOrder(*m_DirectoryStructure);
+ onNextRefresh(
+ [this]() {
+ m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName());
+ m_PluginList.saveLoadOrder(*m_DirectoryStructure);
+ },
+ RefreshCallbackGroup::CORE, RefreshCallbackMode::RUN_NOW_IF_POSSIBLE);
}
void OrganizerCore::saveCurrentProfile()
@@ -1920,7 +1915,8 @@ ProcessRunner OrganizerCore::processRunner()
}
bool OrganizerCore::beforeRun(
- const QFileInfo& binary, const QString& profileName, const QString& customOverwrite,
+ const QFileInfo& binary, const QDir& cwd, const QString& arguments,
+ const QString& profileName, const QString& customOverwrite,
const QList<MOBase::ExecutableForcedLoadSetting>& forcedLibraries)
{
saveCurrentProfile();
@@ -1938,8 +1934,7 @@ bool OrganizerCore::beforeRun(
m_CurrentProfile->writeModlistNow(true);
}
- // TODO: should also pass arguments
- if (!m_AboutToRun(binary.absoluteFilePath())) {
+ if (!m_AboutToRun(binary.absoluteFilePath(), cwd, arguments)) {
log::debug("start of \"{}\" cancelled by plugin", binary.absoluteFilePath());
return false;
}
diff --git a/src/organizercore.h b/src/organizercore.h
index daeb7a7f..7238466a 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -83,7 +83,8 @@ private:
private:
using SignalAboutToRunApplication =
- boost::signals2::signal<bool(const QString&), SignalCombinerAnd>;
+ boost::signals2::signal<bool(const QString&, const QDir&, const QString&),
+ SignalCombinerAnd>;
using SignalFinishedRunApplication =
boost::signals2::signal<void(const QString&, unsigned int)>;
using SignalUserInterfaceInitialized = boost::signals2::signal<void(QMainWindow*)>;
@@ -211,6 +212,31 @@ public:
friend class OrganizerCore;
};
+ // enumeration for the mode when adding refresh callbacks
+ //
+ enum class RefreshCallbackMode : int
+ {
+ // run the callbacks immediately if no refresh is running
+ RUN_NOW_IF_POSSIBLE = 0,
+
+ // wait for the next refresh if none is running
+ FORCE_WAIT_FOR_REFRESH = 1
+ };
+
+ // enumeration for the groups where refresh callbacks can be put
+ //
+ enum class RefreshCallbackGroup : int
+ {
+ // for callbacks by the core itself, highest priority
+ CORE = 0,
+
+ // internal MO2 callbacks
+ INTERNAL = 1,
+
+ // external callbacks, typically MO2 plugins
+ EXTERNAL = 2
+ };
+
public:
OrganizerCore(Settings& settings);
@@ -269,8 +295,8 @@ public:
ProcessRunner processRunner();
- bool beforeRun(const QFileInfo& binary, const QString& profileName,
- const QString& customOverwrite,
+ bool beforeRun(const QFileInfo& binary, const QDir& cwd, const QString& arguments,
+ const QString& profileName, const QString& customOverwrite,
const QList<MOBase::ExecutableForcedLoadSetting>& forcedLibraries);
void afterRun(const QFileInfo& binary, DWORD exitCode);
@@ -356,8 +382,8 @@ public:
ModList* modList();
void refresh(bool saveChanges = true);
- boost::signals2::connection
- onAboutToRun(const std::function<bool(const QString&)>& func);
+ boost::signals2::connection onAboutToRun(
+ const std::function<bool(const QString&, const QDir&, const QString&)>& func);
boost::signals2::connection
onFinishedRun(const std::function<void(const QString&, unsigned int)>& func);
boost::signals2::connection
@@ -379,6 +405,15 @@ public:
boost::signals2::connection
onPluginDisabled(std::function<void(const MOBase::IPlugin*)> const& func);
+ // add a function to be called after the next refresh is done
+ //
+ // - group to add the function to
+ // - if immediateIfReady is true, the function will be called immediately if no
+ // directory update is running
+ boost::signals2::connection onNextRefresh(std::function<void()> const& func,
+ RefreshCallbackGroup group,
+ RefreshCallbackMode mode);
+
public: // IPluginDiagnose interface
virtual std::vector<unsigned int> activeProblems() const;
virtual QString shortDescription(unsigned int key) const;
@@ -388,8 +423,6 @@ public: // IPluginDiagnose interface
public slots:
- void profileRefresh();
-
void syncOverwrite();
void savePluginList();
@@ -472,7 +505,7 @@ private:
private slots:
- void directory_refreshed();
+ void onDirectoryRefreshed();
void downloadRequested(QNetworkReply* reply, QString gameName, int modID,
const QString& fileName);
void removeOrigin(const QString& name);
@@ -507,11 +540,12 @@ private:
SignalPluginEnabled m_PluginEnabled;
SignalPluginEnabled m_PluginDisabled;
+ boost::signals2::signal<void()> m_OnNextRefreshCallbacks;
+
ModList m_ModList;
PluginList m_PluginList;
QList<std::function<void()>> m_PostLoginTasks;
- QList<std::function<void()>> m_PostRefreshTasks;
ExecutablesList m_ExecutablesList;
QStringList m_PendingDownloads;
@@ -529,7 +563,7 @@ private:
std::thread m_StructureDeleter;
- bool m_DirectoryUpdate;
+ std::atomic<bool> m_DirectoryUpdate;
bool m_ArchivesInit;
MOBase::DelayedFileWriter m_PluginListsWriter;
diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp
index a47e8798..0c1fb409 100644
--- a/src/organizerproxy.cpp
+++ b/src/organizerproxy.cpp
@@ -324,6 +324,19 @@ MOBase::IPluginGame const* OrganizerProxy::managedGame() const
bool OrganizerProxy::onAboutToRun(const std::function<bool(const QString&)>& func)
{
+ return m_Proxied
+ ->onAboutToRun(MOShared::callIfPluginActive(
+ this,
+ [func](const QString& binary, const QDir&, const QString&) {
+ return func(binary);
+ },
+ true))
+ .connected();
+}
+
+bool OrganizerProxy::onAboutToRun(
+ const std::function<bool(const QString&, const QDir&, const QString&)>& func)
+{
return m_Proxied->onAboutToRun(MOShared::callIfPluginActive(this, func, true))
.connected();
}
@@ -334,6 +347,25 @@ bool OrganizerProxy::onFinishedRun(
return m_Proxied->onFinishedRun(MOShared::callIfPluginActive(this, func)).connected();
}
+bool OrganizerProxy::onUserInterfaceInitialized(
+ std::function<void(QMainWindow*)> const& func)
+{
+ // Always call this one to allow plugin to initialize themselves even when not active:
+ return m_UserInterfaceInitialized.connect(func).connected();
+}
+
+bool OrganizerProxy::onNextRefresh(const std::function<void()>& func,
+ bool immediateIfPossible)
+{
+ using enum OrganizerCore::RefreshCallbackMode;
+ return m_Proxied
+ ->onNextRefresh(MOShared::callIfPluginActive(this, func),
+ OrganizerCore::RefreshCallbackGroup::EXTERNAL,
+ immediateIfPossible ? RUN_NOW_IF_POSSIBLE
+ : FORCE_WAIT_FOR_REFRESH)
+ .connected();
+}
+
bool OrganizerProxy::onProfileCreated(std::function<void(IProfile*)> const& func)
{
return m_ProfileCreated.connect(func).connected();
@@ -355,14 +387,6 @@ bool OrganizerProxy::onProfileChanged(
{
return m_ProfileChanged.connect(func).connected();
}
-
-bool OrganizerProxy::onUserInterfaceInitialized(
- std::function<void(QMainWindow*)> const& func)
-{
- // Always call this one to allow plugin to initialize themselves even when not active:
- return m_UserInterfaceInitialized.connect(func).connected();
-}
-
// Always call these one, otherwise plugin cannot detect they are being enabled /
// disabled:
bool OrganizerProxy::onPluginSettingChanged(
diff --git a/src/organizerproxy.h b/src/organizerproxy.h
index 771d0981..75146432 100644
--- a/src/organizerproxy.h
+++ b/src/organizerproxy.h
@@ -73,10 +73,14 @@ public: // IOrganizer interface
virtual void refresh(bool saveChanges);
virtual bool onAboutToRun(const std::function<bool(const QString&)>& func) override;
+ virtual bool onAboutToRun(const std::function<bool(const QString&, const QDir&,
+ const QString&)>& func) override;
virtual bool
onFinishedRun(const std::function<void(const QString&, unsigned int)>& func) override;
virtual bool
onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func) override;
+ virtual bool onNextRefresh(const std::function<void()>& func,
+ bool immediateIfPossible) override;
virtual bool
onProfileCreated(std::function<void(MOBase::IProfile*)> const& func) override;
virtual bool onProfileRenamed(
diff --git a/src/processrunner.cpp b/src/processrunner.cpp
index f748f490..65e44598 100644
--- a/src/processrunner.cpp
+++ b/src/processrunner.cpp
@@ -768,8 +768,8 @@ std::optional<ProcessRunner::Results> ProcessRunner::runBinary()
// saves profile, sets up usvfs, notifies plugins, etc.; can return false if
// a plugin doesn't want the program to run (such as when checkFNIS fails to
// run FNIS and the user clicks cancel)
- if (!m_core.beforeRun(m_sp.binary, m_profileName, m_customOverwrite,
- m_forcedLibraries)) {
+ if (!m_core.beforeRun(m_sp.binary, m_sp.currentDirectory, m_sp.arguments,
+ m_profileName, m_customOverwrite, m_forcedLibraries)) {
return Error;
}