Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh Callback (+ Extra for AboutToRun) #1884

Merged
merged 4 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
}
}
Expand Down Expand Up @@ -2395,7 +2397,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();
});
}
}
}
Expand Down Expand Up @@ -2555,7 +2559,7 @@ void MainWindow::setWindowEnabled(bool enabled)

void MainWindow::refreshProfile_activated()
{
m_OrganizerCore.profileRefresh();
m_OrganizerCore.refresh();
}

void MainWindow::saveArchiveList()
Expand Down Expand Up @@ -2782,7 +2786,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();
Expand Down
2 changes: 1 addition & 1 deletion src/modlistcontextmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand Down
99 changes: 47 additions & 52 deletions src/organizercore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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!
Expand All @@ -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()
Expand Down Expand Up @@ -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);
Expand All @@ -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());
Al12rs marked this conversation as resolved.
Show resolved Hide resolved

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");
Expand All @@ -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) {
Expand Down Expand Up @@ -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()
Expand All @@ -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();
Expand All @@ -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;
}
Expand Down
54 changes: 44 additions & 10 deletions src/organizercore.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose

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*)>;
Expand Down Expand Up @@ -211,6 +212,31 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose
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);

Expand Down Expand Up @@ -269,8 +295,8 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose

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);
Expand Down Expand Up @@ -356,8 +382,8 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose
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
Expand All @@ -379,6 +405,15 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose
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;
Expand All @@ -388,8 +423,6 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose

public slots:

void profileRefresh();

void syncOverwrite();

void savePluginList();
Expand Down Expand Up @@ -472,7 +505,7 @@ public slots:

private slots:

void directory_refreshed();
void onDirectoryRefreshed();
void downloadRequested(QNetworkReply* reply, QString gameName, int modID,
const QString& fileName);
void removeOrigin(const QString& name);
Expand Down Expand Up @@ -507,11 +540,12 @@ private slots:
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;
Expand All @@ -529,7 +563,7 @@ private slots:

std::thread m_StructureDeleter;

bool m_DirectoryUpdate;
std::atomic<bool> m_DirectoryUpdate;
bool m_ArchivesInit;

MOBase::DelayedFileWriter m_PluginListsWriter;
Expand Down
Loading
Loading