File indexing completed on 2024-05-26 05:14:41

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "controlgui.h"
0008 #include "akonadiwidgets_debug.h"
0009 #include "erroroverlay_p.h"
0010 #include "selftestdialog.h"
0011 #include "servermanager.h"
0012 #include "ui_controlprogressindicator.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QCoreApplication>
0017 #include <QEventLoop>
0018 #include <QFrame>
0019 #include <QPointer>
0020 #include <QTimer>
0021 
0022 using namespace Akonadi;
0023 using namespace std::chrono_literals;
0024 namespace Akonadi
0025 {
0026 namespace Internal
0027 {
0028 class ControlProgressIndicator : public QFrame
0029 {
0030     Q_OBJECT
0031 public:
0032     explicit ControlProgressIndicator(QWidget *parent = nullptr)
0033         : QFrame(parent)
0034     {
0035         setWindowModality(Qt::ApplicationModal);
0036         resize(400, 100);
0037         setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
0038         ui.setupUi(this);
0039 
0040         setFrameShadow(QFrame::Plain);
0041         setFrameShape(QFrame::Box);
0042     }
0043 
0044     void setMessage(const QString &msg)
0045     {
0046         ui.statusLabel->setText(msg);
0047     }
0048 
0049     Ui::ControlProgressIndicator ui;
0050 };
0051 
0052 class StaticControlGui : public ControlGui
0053 {
0054     Q_OBJECT
0055 };
0056 
0057 } // namespace Internal
0058 
0059 Q_GLOBAL_STATIC(Internal::StaticControlGui, s_instance) // NOLINT(readability-redundant-member-init)
0060 
0061 /**
0062  * @internal
0063  */
0064 class ControlGuiPrivate
0065 {
0066 public:
0067     explicit ControlGuiPrivate(ControlGui *parent)
0068         : mParent(parent)
0069         , mProgressIndicator(nullptr)
0070     {
0071     }
0072 
0073     ~ControlGuiPrivate()
0074     {
0075         delete mProgressIndicator;
0076     }
0077 
0078     void setupProgressIndicator(const QString &msg, QWidget *parent = nullptr)
0079     {
0080         if (!mProgressIndicator) {
0081             mProgressIndicator = new Internal::ControlProgressIndicator(parent);
0082         }
0083 
0084         mProgressIndicator->setMessage(msg);
0085     }
0086 
0087     void createErrorOverlays()
0088     {
0089         for (QWidget *widget : std::as_const(mPendingOverlays)) {
0090             if (widget) {
0091                 new ErrorOverlay(widget);
0092             }
0093         }
0094         mPendingOverlays.clear();
0095     }
0096 
0097     void cleanup()
0098     {
0099         // delete s_instance;
0100     }
0101 
0102     bool exec();
0103     void serverStateChanged(ServerManager::State state);
0104 
0105     QPointer<ControlGui> mParent;
0106     QEventLoop *mEventLoop = nullptr;
0107     QPointer<Internal::ControlProgressIndicator> mProgressIndicator;
0108     QList<QPointer<QWidget>> mPendingOverlays;
0109     bool mSuccess = false;
0110 
0111     bool mStarting = false;
0112     bool mStopping = false;
0113 };
0114 
0115 bool ControlGuiPrivate::exec()
0116 {
0117     if (mProgressIndicator) {
0118         mProgressIndicator->show();
0119     }
0120     qCDebug(AKONADIWIDGETS_LOG) << "Starting/Stopping Akonadi (using an event loop).";
0121     mEventLoop = new QEventLoop(mParent);
0122     mEventLoop->exec();
0123     mEventLoop->deleteLater();
0124     mEventLoop = nullptr;
0125 
0126     if (!mSuccess) {
0127         qCWarning(AKONADIWIDGETS_LOG) << "Could not start/stop Akonadi!";
0128         if (mProgressIndicator && mStarting) {
0129             QPointer<SelfTestDialog> dlg = new SelfTestDialog(mProgressIndicator->parentWidget());
0130             dlg->exec();
0131             delete dlg;
0132             if (!mParent) {
0133                 return false;
0134             }
0135         }
0136     }
0137 
0138     delete mProgressIndicator;
0139     mProgressIndicator = nullptr;
0140     mStarting = false;
0141     mStopping = false;
0142 
0143     const bool rv = mSuccess;
0144     mSuccess = false;
0145     return rv;
0146 }
0147 
0148 void ControlGuiPrivate::serverStateChanged(ServerManager::State state)
0149 {
0150     qCDebug(AKONADIWIDGETS_LOG) << "Server state changed to" << state;
0151     if (mEventLoop && mEventLoop->isRunning()) {
0152         // ignore transient states going into the right direction
0153         if ((mStarting && (state == ServerManager::Starting || state == ServerManager::Upgrading)) || (mStopping && state == ServerManager::Stopping)) {
0154             return;
0155         }
0156         mEventLoop->quit();
0157         mSuccess = (mStarting && state == ServerManager::Running) || (mStopping && state == ServerManager::NotRunning);
0158     }
0159 }
0160 
0161 ControlGui::ControlGui()
0162     : d(new ControlGuiPrivate(this))
0163 {
0164     connect(ServerManager::self(), &ServerManager::stateChanged, this, [this](Akonadi::ServerManager::State state) {
0165         d->serverStateChanged(state);
0166     });
0167     // mProgressIndicator is a widget, so it better be deleted before the QApplication is deleted
0168     // Otherwise we get a crash in QCursor code with Qt-4.5
0169     if (QCoreApplication::instance()) {
0170         connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this]() {
0171             d->cleanup();
0172         });
0173     }
0174 }
0175 
0176 ControlGui::~ControlGui() = default;
0177 
0178 bool ControlGui::start()
0179 {
0180     if (ServerManager::state() == ServerManager::Stopping) {
0181         qCDebug(AKONADIWIDGETS_LOG) << "Server is currently being stopped, won't try to start it now";
0182         return false;
0183     }
0184     if (ServerManager::isRunning() || s_instance->d->mEventLoop) {
0185         qCDebug(AKONADIWIDGETS_LOG) << "Server is already running";
0186         return true;
0187     }
0188     s_instance->d->mStarting = true;
0189     if (!ServerManager::start()) {
0190         qCDebug(AKONADIWIDGETS_LOG) << "ServerManager::start failed -> return false";
0191         return false;
0192     }
0193     return s_instance->d->exec();
0194 }
0195 
0196 bool ControlGui::stop()
0197 {
0198     if (ServerManager::state() == ServerManager::Starting) {
0199         return false;
0200     }
0201     if (!ServerManager::isRunning() || s_instance->d->mEventLoop) {
0202         return true;
0203     }
0204     s_instance->d->mStopping = true;
0205     if (!ServerManager::stop()) {
0206         return false;
0207     }
0208     return s_instance->d->exec();
0209 }
0210 
0211 bool ControlGui::restart()
0212 {
0213     if (ServerManager::isRunning()) {
0214         if (!stop()) {
0215             return false;
0216         }
0217     }
0218     return start();
0219 }
0220 
0221 bool ControlGui::start(QWidget *parent)
0222 {
0223     s_instance->d->setupProgressIndicator(i18n("Starting Akonadi server..."), parent);
0224     return start();
0225 }
0226 
0227 bool ControlGui::stop(QWidget *parent)
0228 {
0229     s_instance->d->setupProgressIndicator(i18n("Stopping Akonadi server..."), parent);
0230     return stop();
0231 }
0232 
0233 bool ControlGui::restart(QWidget *parent)
0234 {
0235     if (ServerManager::isRunning()) {
0236         if (!stop(parent)) {
0237             return false;
0238         }
0239     }
0240     return start(parent);
0241 }
0242 
0243 void ControlGui::widgetNeedsAkonadi(QWidget *widget)
0244 {
0245     s_instance->d->mPendingOverlays.append(widget);
0246     // delay the overlay creation since we rely on widget being reparented
0247     // correctly already
0248     QTimer::singleShot(0s, s_instance, []() {
0249         s_instance->d->createErrorOverlays();
0250     });
0251 }
0252 
0253 } // namespace Akonadi
0254 
0255 #include "controlgui.moc"
0256 
0257 #include "moc_controlgui.cpp"