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"