File indexing completed on 2025-01-05 04:47:09

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "erroroverlay_p.h"
0008 #include "ui_erroroverlay.h"
0009 #if 0
0010 #include "selftestdialog_p.h"
0011 #endif
0012 
0013 #include <KLocalizedString>
0014 #include <KStandardGuiItem>
0015 #include <QIcon>
0016 
0017 #include <QEvent>
0018 #include <QPalette>
0019 
0020 using namespace Akonadi;
0021 
0022 /// @cond PRIVATE
0023 
0024 class ErrorOverlayStatic
0025 {
0026 public:
0027     QList<QPair<QPointer<QWidget>, QPointer<QWidget>>> baseWidgets;
0028 };
0029 
0030 Q_GLOBAL_STATIC(ErrorOverlayStatic, sInstanceOverlay) // NOLINT(readability-redundant-member-init)
0031 
0032 // return true if o1 is a parent of o2
0033 static bool isParentOf(QWidget *o1, QWidget *o2)
0034 {
0035     if (!o1 || !o2) {
0036         return false;
0037     }
0038     if (o1 == o2) {
0039         return true;
0040     }
0041     if (o2->isWindow()) {
0042         return false;
0043     }
0044     return isParentOf(o1, o2->parentWidget());
0045 }
0046 
0047 ErrorOverlay::ErrorOverlay(QWidget *baseWidget, QWidget *parent)
0048     : QWidget(parent ? parent : baseWidget->window())
0049     , mBaseWidget(baseWidget)
0050     , ui(new Ui::ErrorOverlay)
0051 {
0052     Q_ASSERT(baseWidget);
0053 
0054     mBaseWidgetIsParent = isParentOf(mBaseWidget, this);
0055 
0056     // check existing overlays to detect cascading
0057     for (QList<QPair<QPointer<QWidget>, QPointer<QWidget>>>::Iterator it = sInstanceOverlay->baseWidgets.begin(); it != sInstanceOverlay->baseWidgets.end();) {
0058         if ((*it).first == nullptr || (*it).second == nullptr) {
0059             // garbage collection
0060             it = sInstanceOverlay->baseWidgets.erase(it);
0061             continue;
0062         }
0063         if (isParentOf((*it).first, baseWidget)) {
0064             // parent already has an overlay, kill ourselves
0065             mBaseWidget = nullptr;
0066             hide();
0067             deleteLater();
0068             return;
0069         }
0070         if (isParentOf(baseWidget, (*it).first)) {
0071             // child already has overlay, kill that one
0072             delete (*it).second;
0073             it = sInstanceOverlay->baseWidgets.erase(it);
0074             continue;
0075         }
0076         ++it;
0077     }
0078     sInstanceOverlay->baseWidgets.append(qMakePair(mBaseWidget, QPointer<QWidget>(this)));
0079 
0080     connect(baseWidget, &QObject::destroyed, this, &QObject::deleteLater);
0081     mPreviousState = !mBaseWidget->testAttribute(Qt::WA_ForceDisabled);
0082 
0083     ui->setupUi(this);
0084     ui->notRunningIcon->setPixmap(QIcon::fromTheme(QStringLiteral("akonadi")).pixmap(64));
0085     ui->brokenIcon->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(64));
0086     ui->progressIcon->setPixmap(QIcon::fromTheme(QStringLiteral("akonadi")).pixmap(32));
0087     ui->quitButton->setText(KStandardGuiItem::quit().text());
0088     ui->detailsQuitButton->setText(KStandardGuiItem::quit().text());
0089 
0090     ui->quitButton->hide();
0091     ui->detailsQuitButton->hide();
0092 
0093     connect(ui->startButton, &QAbstractButton::clicked, this, &ErrorOverlay::startClicked);
0094     connect(ui->quitButton, &QAbstractButton::clicked, this, &ErrorOverlay::quitClicked);
0095     connect(ui->detailsQuitButton, &QAbstractButton::clicked, this, &ErrorOverlay::quitClicked);
0096     connect(ui->selfTestButton, &QAbstractButton::clicked, this, &ErrorOverlay::selfTestClicked);
0097 
0098     const ServerManager::State state = ServerManager::state();
0099     mOverlayActive = (state == ServerManager::Running);
0100     serverStateChanged(state);
0101 
0102     connect(ServerManager::self(), &ServerManager::stateChanged, this, &ErrorOverlay::serverStateChanged);
0103 
0104     QPalette p = palette();
0105     p.setColor(backgroundRole(), QColor(0, 0, 0, 128));
0106     p.setColor(foregroundRole(), Qt::white);
0107     setPalette(p);
0108     setAutoFillBackground(true);
0109 
0110     mBaseWidget->installEventFilter(this);
0111 
0112     reposition();
0113 }
0114 
0115 ErrorOverlay::~ErrorOverlay()
0116 {
0117     if (mBaseWidget && !mBaseWidgetIsParent) {
0118         mBaseWidget->setEnabled(mPreviousState);
0119     }
0120 }
0121 
0122 void ErrorOverlay::reposition()
0123 {
0124     if (!mBaseWidget) {
0125         return;
0126     }
0127 
0128     // reparent to the current top level widget of the base widget if needed
0129     // needed eg. in dock widgets
0130     if (parentWidget() != mBaseWidget->window()) {
0131         setParent(mBaseWidget->window());
0132     }
0133 
0134     // follow base widget visibility
0135     // needed eg. in tab widgets
0136     if (!mBaseWidget->isVisible()) {
0137         hide();
0138         return;
0139     }
0140     if (mOverlayActive) {
0141         show();
0142     }
0143 
0144     // follow position changes
0145     const QPoint topLevelPos = mBaseWidget->mapTo(window(), QPoint(0, 0));
0146     const QPoint parentPos = parentWidget()->mapFrom(window(), topLevelPos);
0147     move(parentPos);
0148 
0149     // follow size changes
0150     // TODO: hide/scale icon if we don't have enough space
0151     resize(mBaseWidget->size());
0152 }
0153 
0154 bool ErrorOverlay::eventFilter(QObject *object, QEvent *event)
0155 {
0156     if (object == mBaseWidget && mOverlayActive
0157         && (event->type() == QEvent::Move || event->type() == QEvent::Resize || event->type() == QEvent::Show || event->type() == QEvent::Hide
0158             || event->type() == QEvent::ParentChange)) {
0159         reposition();
0160     }
0161     return QWidget::eventFilter(object, event);
0162 }
0163 
0164 void ErrorOverlay::startClicked()
0165 {
0166     const ServerManager::State state = ServerManager::state();
0167     if (state == ServerManager::Running) {
0168         serverStateChanged(state);
0169     } else {
0170         ServerManager::start();
0171     }
0172 }
0173 
0174 void ErrorOverlay::quitClicked()
0175 {
0176     qApp->quit();
0177 }
0178 
0179 void ErrorOverlay::selfTestClicked()
0180 {
0181 #if 0
0182     SelfTestDialog dlg;
0183     dlg.exec();
0184 #endif
0185 }
0186 
0187 void ErrorOverlay::serverStateChanged(ServerManager::State state)
0188 {
0189     if (!mBaseWidget) {
0190         return;
0191     }
0192 
0193     if (state == ServerManager::Running) {
0194         if (mOverlayActive) {
0195             mOverlayActive = false;
0196             hide();
0197             if (!mBaseWidgetIsParent) {
0198                 mBaseWidget->setEnabled(mPreviousState);
0199             }
0200         }
0201     } else if (!mOverlayActive) {
0202         mOverlayActive = true;
0203         if (mBaseWidget->isVisible()) {
0204             show();
0205         }
0206 
0207         if (!mBaseWidgetIsParent) {
0208             mPreviousState = !mBaseWidget->testAttribute(Qt::WA_ForceDisabled);
0209             mBaseWidget->setEnabled(false);
0210         }
0211 
0212         reposition();
0213     }
0214 
0215     if (mOverlayActive) {
0216         switch (state) {
0217         case ServerManager::NotRunning:
0218             ui->stackWidget->setCurrentWidget(ui->notRunningPage);
0219             break;
0220         case ServerManager::Broken:
0221             ui->stackWidget->setCurrentWidget(ui->brokenPage);
0222             if (!ServerManager::brokenReason().isEmpty()) {
0223                 ui->brokenDescription->setText(
0224                     i18nc("%1 is a reason why", "Cannot connect to the Personal information management service.\n\n%1", ServerManager::brokenReason()));
0225             }
0226             break;
0227         case ServerManager::Starting:
0228             ui->progressPage->setToolTip(i18n("Personal information management service is starting..."));
0229             ui->progressDescription->setText(i18n("Personal information management service is starting..."));
0230             ui->stackWidget->setCurrentWidget(ui->progressPage);
0231             break;
0232         case ServerManager::Stopping:
0233             ui->progressPage->setToolTip(i18n("Personal information management service is shutting down..."));
0234             ui->progressDescription->setText(i18n("Personal information management service is shutting down..."));
0235             ui->stackWidget->setCurrentWidget(ui->progressPage);
0236             break;
0237         case ServerManager::Upgrading:
0238             ui->progressPage->setToolTip(i18n("Personal information management service is performing a database upgrade."));
0239             ui->progressDescription->setText(
0240                 i18n("Personal information management service is performing a database upgrade.\n"
0241                      "This happens after a software update and is necessary to optimize performance.\n"
0242                      "Depending on the amount of personal information, this might take a few minutes."));
0243             ui->stackWidget->setCurrentWidget(ui->progressPage);
0244             break;
0245         case ServerManager::Running:
0246             break;
0247         }
0248     }
0249 }
0250 
0251 /// @endcond
0252 
0253 #include "moc_erroroverlay_p.cpp"