summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lockwidget.cpp490
-rw-r--r--src/lockwidget.h65
-rw-r--r--src/organizercore.cpp1
-rw-r--r--src/organizercore.h3
-rw-r--r--src/processrunner.cpp33
-rw-r--r--src/processrunner.h2
6 files changed, 414 insertions, 180 deletions
diff --git a/src/lockwidget.cpp b/src/lockwidget.cpp
index 35d51dab..68eb9c2d 100644
--- a/src/lockwidget.cpp
+++ b/src/lockwidget.cpp
@@ -4,203 +4,431 @@
#include <QMenuBar>
#include <QStatusBar>
-QWidget* createTransparentWidget(QWidget* parent=nullptr)
+class LockInterface
{
- auto* w = new QWidget(parent);
+public:
+ LockInterface() :
+ m_hasMainUI(false), m_message(nullptr), m_info(nullptr), m_buttons(nullptr)
+ {
+ }
- w->setWindowOpacity(0);
- w->setAttribute(Qt::WA_NoSystemBackground);
- w->setAttribute(Qt::WA_TranslucentBackground);
+ ~LockInterface()
+ {
+ }
- return w;
-}
+ void set(QWidget* target)
+ {
+ QFrame* center = nullptr;
+ if (target) {
+ center = createOverlay(target);
+ } else {
+ center = createDialog();
+ }
-LockWidget::LockWidget(QWidget* parent, Reasons reason) :
- m_parent(parent), m_overlay(nullptr), m_info(nullptr), m_result(NoResult),
- m_filter(nullptr)
-{
- if (reason != NoReason) {
- lock(reason);
+ createMessageLabel();
+ createInfoLabel();
+ createButtonsPanel();
+
+ center->layout()->addWidget(m_message);
+ center->layout()->addWidget(m_info);
+ center->layout()->addWidget(m_buttons);
+
+ m_topLevel->setFocusPolicy(Qt::TabFocus);
+ m_topLevel->setFocus();
+ m_topLevel->show();
+ m_topLevel->setEnabled(true);
+
+ m_topLevel->raise();
+ m_topLevel->activateWindow();
}
-}
-LockWidget::~LockWidget()
-{
- unlock();
-}
+ void update(LockWidget::Reasons reason)
+ {
+ updateMessage(reason);
+ updateButtons(reason);
+ }
-void LockWidget::lock(Reasons reason)
-{
- m_result = StillLocked;
- createUi(reason);
-}
+ void setInfo(const QString& s)
+ {
+ m_info->setText(s);
+ }
+
+ QWidget* topLevel()
+ {
+ return m_topLevel.get();
+ }
+
+private:
+ class Filter : public QObject
+ {
+ public:
+ std::function<void ()> resized;
+ std::function<void ()> closed;
+
+ protected:
+ bool eventFilter(QObject* o, QEvent* e) override
+ {
+ if (e->type() == QEvent::Resize) {
+ if (resized) {
+ resized();
+ }
+ } else if (e->type() == QEvent::Close) {
+ if (closed) {
+ closed();
+ }
+ }
+
+ return QObject::eventFilter(o, e);
+ }
+ };
-void LockWidget::unlock()
-{
- m_overlay.reset();
- if (m_filter && m_parent) {
- m_parent->removeEventFilter(m_filter.get());
+ bool m_hasMainUI;
+ std::unique_ptr<QWidget> m_topLevel;
+ QLabel* m_message;
+ QLabel* m_info;
+ QWidget* m_buttons;
+ std::unique_ptr<Filter> m_filter;
+
+ QWidget* createTransparentWidget(QWidget* parent=nullptr)
+ {
+ auto* w = new QWidget(parent);
+
+ w->setWindowOpacity(0);
+ w->setAttribute(Qt::WA_NoSystemBackground);
+ w->setAttribute(Qt::WA_TranslucentBackground);
+
+ return w;
}
- enableAll();
-}
+ QFrame* createOverlay(QWidget* mainUI)
+ {
+ m_hasMainUI = true;
-void LockWidget::setInfo(DWORD pid, const QString& name)
-{
- m_info->setText(QString("%1 (%2)").arg(name).arg(pid));
-}
+ m_topLevel.reset(createTransparentWidget(mainUI));
+ m_topLevel->setWindowFlags(m_topLevel->windowFlags() & Qt::FramelessWindowHint);
+ m_topLevel->setGeometry(mainUI->rect());
-LockWidget::Results LockWidget::result() const
-{
- return m_result;
-}
+ m_filter.reset(new Filter);
+ m_filter->resized = [=]{ m_topLevel->setGeometry(mainUI->rect()); };
+ m_filter->closed = [=]{ LockWidget::instance().onForceUnlock(); };
-void LockWidget::createUi(Reasons reason)
-{
- QWidget* overlayTarget = m_parent;
- if (auto* w = qApp->activeWindow()) {
- overlayTarget = w;
+ mainUI->installEventFilter(m_filter.get());
+
+ return createFrame();
}
- if (overlayTarget) {
- m_overlay.reset(createTransparentWidget(overlayTarget));
- m_overlay->setWindowFlags(m_overlay->windowFlags() & Qt::FramelessWindowHint);
- m_overlay->setGeometry(overlayTarget->rect());
- } else {
- m_overlay.reset(new QDialog);
+ QFrame* createDialog()
+ {
+ m_hasMainUI = false;
+ m_topLevel.reset(new QDialog);
+
+ return createFrame();
}
- auto* center = new QFrame;
+ QFrame* createFrame()
+ {
+ auto* frame = new QFrame;
+ auto* ly = new QVBoxLayout(frame);
- if (overlayTarget) {
- center->setFrameStyle(QFrame::StyledPanel);
- center->setLineWidth(1);
- center->setAutoFillBackground(true);
+ if (m_hasMainUI) {
+ frame->setFrameStyle(QFrame::StyledPanel);
+ frame->setLineWidth(1);
+ frame->setAutoFillBackground(true);
- auto* shadow = new QGraphicsDropShadowEffect;
- shadow->setBlurRadius(50);
- shadow->setOffset(0);
- shadow->setColor(QColor(0, 0, 0, 100));
- center->setGraphicsEffect(shadow);
- }
+ auto* shadow = new QGraphicsDropShadowEffect;
+ shadow->setBlurRadius(50);
+ shadow->setOffset(0);
+ shadow->setColor(QColor(0, 0, 0, 100));
+ frame->setGraphicsEffect(shadow);
+ } else {
+ ly->setContentsMargins(0, 0, 0, 0);
+ }
+
+ auto* grid = new QGridLayout(m_topLevel.get());
+ grid->addWidget(createTransparentWidget(), 0, 1);
+ grid->addWidget(createTransparentWidget(), 2, 1);
+ grid->addWidget(createTransparentWidget(), 1, 0);
+ grid->addWidget(createTransparentWidget(), 1, 2);
+ grid->addWidget(frame, 1, 1);
+
+ if (!m_hasMainUI) {
+ grid->setContentsMargins(0, 0, 0, 0);
+ }
+
+ grid->setRowStretch(0, 1);
+ grid->setRowStretch(2, 1);
+ grid->setColumnStretch(0, 1);
+ grid->setColumnStretch(2, 1);
- m_info = new QLabel(" ");
- m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter);
+ return frame;
+ }
- auto* ly = new QVBoxLayout(center);
+ void createMessageLabel()
+ {
+ m_message = new QLabel;
+ }
- if (!overlayTarget) {
- ly->setContentsMargins(0, 0, 0, 0);
+ void createInfoLabel()
+ {
+ m_info = new QLabel(" ");
+ m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter);
}
- auto* message = new QLabel;
- ly->addWidget(message);
- ly->addWidget(m_info);
+ void createButtonsPanel()
+ {
+ m_buttons = new QWidget;
+ m_buttons->setLayout(new QHBoxLayout);
+ }
- auto* buttons = new QWidget;
- auto* buttonsLayout = new QHBoxLayout(buttons);
- ly->addWidget(buttons);
- switch (reason)
+ void updateMessage(LockWidget::Reasons reason)
{
- case LockUI:
+ switch (reason)
{
- QString s;
+ case LockWidget::LockUI:
+ {
+ QString s;
+
+ if (m_hasMainUI) {
+ s = QObject::tr(
+ "Mod Organizer is locked while the application is running.");
+ } else {
+ s = QObject::tr("Mod Organizer is currently running an application.");
+ }
- if (!m_parent) {
- s = QObject::tr("Mod Organizer is currently running an application.");
- } else {
- s = QObject::tr(
- "Mod Organizer is locked while the application is running.");
+ m_message->setText(s);
+
+ break;
}
- message->setText(s);
+ case LockWidget::OutputRequired:
+ {
+ m_message->setText(QObject::tr(
+ "The application must run to completion because its output is "
+ "required."));
- auto* unlockButton = new QPushButton(QObject::tr("Unlock"));
- QObject::connect(unlockButton, &QPushButton::clicked, [&]{ onForceUnlock(); });
- buttonsLayout->addWidget(unlockButton);
+ break;
+ }
- break;
+ case LockWidget::PreventExit:
+ {
+ m_message->setText(QObject::tr(
+ "Mod Organizer is waiting on application to close before exiting."));
+
+ break;
+ }
}
+ }
+
+ void updateButtons(LockWidget::Reasons reason)
+ {
+ MOBase::deleteChildWidgets(m_buttons);
+ auto* ly = m_buttons->layout();
- case OutputRequired:
+ switch (reason)
{
- message->setText(QObject::tr(
- "The application must run to completion because its output is "
- "required."));
+ case LockWidget::LockUI: // fall-through
+ case LockWidget::OutputRequired:
+ {
+ auto* unlock = new QPushButton(QObject::tr("Unlock"));
- auto* unlockButton = new QPushButton(QObject::tr("Unlock"));
- QObject::connect(unlockButton, &QPushButton::clicked, [&]{ onForceUnlock(); });
- buttonsLayout->addWidget(unlockButton);
+ QObject::connect(unlock, &QPushButton::clicked, [&]{
+ LockWidget::instance().onForceUnlock();
+ });
- break;
+ ly->addWidget(unlock);
+
+ break;
+ }
+
+ case LockWidget::PreventExit:
+ {
+ auto* exit = new QPushButton(QObject::tr("Exit Now"));
+ QObject::connect(exit, &QPushButton::clicked, [&]{
+ LockWidget::instance().onForceUnlock();
+ });
+
+ ly->addWidget(exit);
+
+ auto* cancel = new QPushButton(QObject::tr("Cancel"));
+ QObject::connect(cancel, &QPushButton::clicked, [&]{
+ LockWidget::instance().onCancel();
+ });
+
+ ly->addWidget(cancel);
+
+ break;
+ }
}
+ }
+};
- case PreventExit:
- {
- message->setText(QObject::tr(
- "Mod Organizer is waiting on application to close before exiting."));
+LockWidget::Session::~Session()
+{
+ LockWidget::instance().unlock(this);
+}
+
+void LockWidget::Session::setInfo(DWORD pid, const QString& name)
+{
+ m_pid = pid;
+ m_name = name;
+
+ LockWidget::instance().updateLabel();
+}
+
+DWORD LockWidget::Session::pid() const
+{
+ return m_pid;
+}
+
+const QString& LockWidget::Session::name() const
+{
+ return m_name;
+}
+
+LockWidget::Results LockWidget::Session::result() const
+{
+ return LockWidget::instance().result();
+}
+
+
+static LockWidget* g_instance = nullptr;
+
+
+LockWidget::LockWidget()
+ : m_parent(nullptr), m_result(NoResult)
+{
+ Q_ASSERT(!g_instance);
+ g_instance = this;
+}
+
+LockWidget::~LockWidget()
+{
+ const auto v = m_sessions;
+
+ for (auto& wp : v) {
+ if (auto s=wp.lock()) {
+ unlock(s.get());
+ }
+ }
+}
+
+LockWidget& LockWidget::instance()
+{
+ Q_ASSERT(g_instance);
+ return *g_instance;
+}
- auto* exit = new QPushButton(QObject::tr("Exit Now"));
- QObject::connect(exit, &QPushButton::clicked, [&]{ onForceUnlock(); });
- buttonsLayout->addWidget(exit);
+void LockWidget::setUserInterface(QWidget* parent)
+{
+ m_parent = parent;
+}
+
+std::shared_ptr<LockWidget::Session> LockWidget::lock(Reasons reason)
+{
+ m_result = StillLocked;
+ createUi(reason);
+
+ auto ls = std::make_shared<Session>();
+ m_sessions.push_back(ls);
- auto* cancel = new QPushButton(QObject::tr("Cancel"));
- QObject::connect(cancel, &QPushButton::clicked, [&]{ onCancel(); });
- buttonsLayout->addWidget(cancel);
+ updateLabel();
+ return ls;
+}
+
+void LockWidget::unlock(Session* s)
+{
+ auto itor = m_sessions.begin();
+ for (;;) {
+ if (itor == m_sessions.end()) {
break;
}
+
+ if (auto ss=itor->lock()) {
+ if (ss.get() == s) {
+ itor = m_sessions.erase(itor);
+ continue;
+ }
+ } else {
+ itor = m_sessions.erase(itor);
+ continue;
+ }
+
+ ++itor;
}
- auto* grid = new QGridLayout(m_overlay.get());
- grid->addWidget(createTransparentWidget(), 0, 1);
- grid->addWidget(createTransparentWidget(), 2, 1);
- grid->addWidget(createTransparentWidget(), 1, 0);
- grid->addWidget(createTransparentWidget(), 1, 2);
- grid->addWidget(center, 1, 1);
+ if (m_sessions.empty()) {
+ m_ui.reset();
+ enableAll();
+ } else {
+ updateLabel();
+ }
+}
- if (!overlayTarget) {
- grid->setContentsMargins(0, 0, 0, 0);
+void LockWidget::unlockCurrent()
+{
+ if (m_sessions.empty()) {
+ return;
}
- grid->setRowStretch(0, 1);
- grid->setRowStretch(2, 1);
- grid->setColumnStretch(0, 1);
- grid->setColumnStretch(2, 1);
+ auto s = m_sessions.back().lock();
+ if (!s) {
+ m_sessions.pop_back();
+ return;
+ }
- disableAll();
+ unlock(s.get());
+}
- if (overlayTarget) {
- m_filter.reset(new Filter);
- m_filter->resized = [=]{ m_overlay->setGeometry(overlayTarget->rect()); };
- m_filter->closed = [=]{ onForceUnlock(); };
- overlayTarget->installEventFilter(m_filter.get());
+void LockWidget::updateLabel()
+{
+ QString label;
+
+ for (auto itor=m_sessions.rbegin(); itor!=m_sessions.rend(); ++itor) {
+ if (auto ss=itor->lock()) {
+ label += QString("%1 (%2)").arg(ss->name()).arg(ss->pid());
+ break;
+ }
}
- m_overlay->setFocusPolicy(Qt::TabFocus);
- m_overlay->setFocus();
- m_overlay->show();
- m_overlay->setEnabled(true);
+ m_ui->setInfo(label);
+}
+
+LockWidget::Results LockWidget::result() const
+{
+ return m_result;
+}
+
+void LockWidget::createUi(Reasons reason)
+{
+ QWidget* target = m_parent;
+ if (auto* w = qApp->activeWindow()) {
+ target = w;
+ }
- if (!overlayTarget) {
- m_overlay->raise();
- m_overlay->activateWindow();
+ if (!m_ui) {
+ m_ui.reset(new LockInterface);
}
+
+ m_ui->set(target);
+ m_ui->update(reason);
+
+ disableAll();
}
void LockWidget::onForceUnlock()
{
m_result = ForceUnlocked;
- unlock();
+ unlockCurrent();
}
void LockWidget::onCancel()
{
m_result = Cancelled;
- unlock();
+ unlockCurrent();
}
template <class T>
@@ -231,10 +459,10 @@ void LockWidget::disableAll()
if (auto* d=dynamic_cast<QDialog*>(w)) {
// don't disable stuff if this dialog is the overlay, which happens when
// there's no ui
- if (d != m_overlay.get()) {
+ if (d != m_ui->topLevel()) {
// no central widget, just disable the children, except for the overlay
for (auto* child : findChildrenImmediate<QWidget*>(d)) {
- if (child != m_overlay.get()) {
+ if (child != m_ui->topLevel()) {
disable(child);
}
}
diff --git a/src/lockwidget.h b/src/lockwidget.h
index d3ff2f7d..8062c478 100644
--- a/src/lockwidget.h
+++ b/src/lockwidget.h
@@ -2,8 +2,12 @@
#include <QMainWindow>
+class LockInterface;
+
class LockWidget
{
+ friend class LockInterface;
+
public:
// reason to show the widget
//
@@ -37,51 +41,50 @@ public:
Cancelled
};
- // if `reason` is not NoReason, lock() is called with it
- //
- LockWidget(QWidget* parent, Reasons reason=NoReason);
- ~LockWidget();
-
- void lock(Reasons reason);
- void unlock();
- void setInfo(DWORD pid, const QString& name);
- Results result() const;
-
-private:
- class Filter : public QObject
+ class Session
{
public:
- std::function<void ()> resized;
- std::function<void ()> closed;
+ ~Session();
- protected:
- bool eventFilter(QObject* o, QEvent* e) override
- {
- if (e->type() == QEvent::Resize) {
- if (resized) {
- resized();
- }
- } else if (e->type() == QEvent::Close) {
- if (closed) {
- closed();
- }
- }
+ void setInfo(DWORD pid, const QString& name);
+ Results result() const;
- return QObject::eventFilter(o, e);
- }
+ DWORD pid() const;
+ const QString& name() const;
+
+ private:
+ DWORD m_pid;
+ QString m_name;
};
+ // if `reason` is not NoReason, lock() is called with it
+ //
+ LockWidget();
+ ~LockWidget();
+
+ static LockWidget& instance();
+
+ void setUserInterface(QWidget* parent);
+
+ std::shared_ptr<Session> lock(Reasons reason);
+
+ Results result() const;
+
+private:
QWidget* m_parent;
- std::unique_ptr<QWidget> m_overlay;
- QLabel* m_info;
+ std::unique_ptr<LockInterface> m_ui;
+ std::vector<std::weak_ptr<Session>> m_sessions;
Results m_result;
- std::unique_ptr<Filter> m_filter;
std::vector<QPointer<QWidget>> m_disabled;
void createUi(Reasons reason);
+ void unlockCurrent();
+ void unlock(Session* s);
+ void updateLabel();
+
void onForceUnlock();
void onCancel();
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index dda60f76..3d4d8e4b 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -250,6 +250,7 @@ void OrganizerCore::setUserInterface(IUserInterface* ui)
m_InstallationManager.setParentWidget(w);
m_Updater.setUserInterface(w);
+ m_LockWidget.setUserInterface(w);
checkForUpdates();
}
diff --git a/src/organizercore.h b/src/organizercore.h
index 0ba52284..7e0e4b7f 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -14,6 +14,7 @@
#include "usvfsconnector.h"
#include "moshortcut.h"
#include "processrunner.h"
+#include "lockwidget.h"
#include <directoryentry.h>
#include <imoinfo.h>
#include <iplugindiagnose.h>
@@ -343,6 +344,8 @@ private:
MOBase::DelayedFileWriter m_PluginListsWriter;
UsvfsConnector m_USVFS;
+ LockWidget m_LockWidget;
+
static CrashDumpsType m_globalCrashDumpsType;
};
diff --git a/src/processrunner.cpp b/src/processrunner.cpp
index 220ffe38..d97c00ef 100644
--- a/src/processrunner.cpp
+++ b/src/processrunner.cpp
@@ -195,7 +195,8 @@ const std::chrono::milliseconds Infinite(-1);
// waits for completion, times out after `wait` if not Infinite
//
std::optional<ProcessRunner::Results> timedWait(
- HANDLE handle, DWORD pid, LockWidget& lock, std::chrono::milliseconds wait)
+ HANDLE handle, DWORD pid, LockWidget::Session& ls,
+ std::chrono::milliseconds wait)
{
using namespace std::chrono;
@@ -220,7 +221,7 @@ std::optional<ProcessRunner::Results> timedWait(
QCoreApplication::processEvents();
// check the lock widget
- switch (lock.result())
+ switch (ls.result())
{
case LockWidget::StillLocked:
{
@@ -245,7 +246,7 @@ std::optional<ProcessRunner::Results> timedWait(
// shouldn't happen
log::debug(
"unexpected result {} while waiting for {}",
- static_cast<int>(lock.result()), pid);
+ static_cast<int>(ls.result()), pid);
return ProcessRunner::Error;
}
@@ -263,7 +264,7 @@ std::optional<ProcessRunner::Results> timedWait(
}
ProcessRunner::Results waitForProcesses(
- const std::vector<HANDLE>& initialProcesses, LockWidget& lock)
+ const std::vector<HANDLE>& initialProcesses, LockWidget::Session& ls)
{
using namespace std::chrono;
@@ -287,7 +288,7 @@ ProcessRunner::Results waitForProcesses(
}
// update the lock widget
- lock.setInfo(p.pid(), p.name());
+ ls.setInfo(p.pid(), p.name());
// open the process
auto interestingHandle = p.openHandleForWait();
@@ -309,7 +310,7 @@ ProcessRunner::Results waitForProcesses(
wait = Infinite;
}
- const auto r = timedWait(interestingHandle.get(), p.pid(), lock, wait);
+ const auto r = timedWait(interestingHandle.get(), p.pid(), ls, wait);
if (r) {
// the process has completed or returned an error
return *r;
@@ -322,11 +323,11 @@ ProcessRunner::Results waitForProcesses(
}
ProcessRunner::Results waitForProcess(
- HANDLE initialProcess, LPDWORD exitCode, LockWidget& lock)
+ HANDLE initialProcess, LPDWORD exitCode, LockWidget::Session& ls)
{
std::vector<HANDLE> processes = {initialProcess};
- const auto r = waitForProcesses(processes, lock);
+ const auto r = waitForProcesses(processes, ls);
// as long as it's not running anymore, try to get the exit code
if (exitCode && r != ProcessRunner::Running) {
@@ -682,8 +683,8 @@ ProcessRunner::Results ProcessRunner::postRun()
auto r = Error;
- withLock([&](auto& lock) {
- r = waitForProcess(m_handle.get(), &m_exitCode, lock);
+ withLock([&](auto& ls) {
+ r = waitForProcess(m_handle.get(), &m_exitCode, ls);
});
if (r == Completed && (m_waitFlags & Refresh)) {
@@ -740,14 +741,14 @@ ProcessRunner::Results ProcessRunner::waitForAllUSVFSProcessesWithLock(
auto r = Error;
- withLock([&](auto& lock) {
+ withLock([&](auto& ls) {
for (;;) {
const auto processes = getRunningUSVFSProcesses();
if (processes.empty()) {
break;
}
- r = waitForProcesses(processes, lock);
+ r = waitForProcesses(processes, ls);
if (r != Completed) {
// error, cancelled, or unlocked
@@ -763,10 +764,8 @@ ProcessRunner::Results ProcessRunner::waitForAllUSVFSProcessesWithLock(
return r;
}
-void ProcessRunner::withLock(std::function<void (LockWidget&)> f)
+void ProcessRunner::withLock(std::function<void (LockWidget::Session&)> f)
{
- auto lk = std::make_unique<LockWidget>(
- m_ui ? m_ui->qtWidget() : nullptr, m_lockReason);
-
- f(*lk);
+ auto ls = LockWidget::instance().lock(m_lockReason);
+ f(*ls);
}
diff --git a/src/processrunner.h b/src/processrunner.h
index fd451e38..276c46b1 100644
--- a/src/processrunner.h
+++ b/src/processrunner.h
@@ -164,7 +164,7 @@ private:
// creates the lock widget and calls f()
//
- void withLock(std::function<void (LockWidget&)> f);
+ void withLock(std::function<void (LockWidget::Session&)> f);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(ProcessRunner::WaitFlags);