1/*
2 Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "erroroverlay_p.h"
21#include "ui_erroroverlay.h"
22#include "selftestdialog_p.h"
23
24#include <KDebug>
25#include <KIcon>
26#include <KLocalizedString>
27
28#include <QtCore/QEvent>
29#include <QPalette>
30
31using namespace Akonadi;
32
33//@cond PRIVATE
34
35class ErrorOverlayStatic
36{
37public:
38 QVector<QPair<QPointer<QWidget>, QPointer<QWidget> > > baseWidgets;
39};
40
41K_GLOBAL_STATIC(ErrorOverlayStatic, sInstanceOverlay)
42
43static bool isParentOf(QObject *o1, QObject *o2)
44{
45 if (!o1 || !o2) {
46 return false;
47 }
48 if (o1 == o2) {
49 return true;
50 }
51 return isParentOf(o1, o2->parent());
52}
53
54ErrorOverlay::ErrorOverlay(QWidget *baseWidget, QWidget *parent)
55 : QWidget(parent ? parent : baseWidget->window())
56 , mBaseWidget(baseWidget)
57 , mOverlayActive(false)
58 , mBaseWidgetIsParent(false)
59 , ui(new Ui::ErrorOverlay)
60{
61 Q_ASSERT(baseWidget);
62
63 mBaseWidgetIsParent = isParentOf(mBaseWidget, this);
64
65 // check existing overlays to detect cascading
66 for (QVector<QPair< QPointer<QWidget>, QPointer<QWidget> > >::Iterator it = sInstanceOverlay->baseWidgets.begin();
67 it != sInstanceOverlay->baseWidgets.end();) {
68 if ((*it).first == 0 || (*it).second == 0) {
69 // garbage collection
70 it = sInstanceOverlay->baseWidgets.erase(it);
71 continue;
72 }
73 if (isParentOf((*it).first, baseWidget)) {
74 // parent already has an overlay, kill ourselves
75 mBaseWidget = 0;
76 hide();
77 deleteLater();
78 return;
79 }
80 if (isParentOf(baseWidget, (*it).first)) {
81 // child already has overlay, kill that one
82 delete(*it).second;
83 it = sInstanceOverlay->baseWidgets.erase(it);
84 continue;
85 }
86 ++it;
87 }
88 sInstanceOverlay->baseWidgets.append(qMakePair(mBaseWidget, QPointer<QWidget>(this)));
89
90 connect(baseWidget, SIGNAL(destroyed()), SLOT(deleteLater()));
91 mPreviousState = mBaseWidget->isEnabled();
92
93 ui->setupUi(this);
94 ui->notRunningIcon->setPixmap(KIcon(QLatin1String("akonadi")).pixmap(64));
95 ui->brokenIcon->setPixmap(KIcon(QString::fromLatin1("dialog-error")).pixmap(64));
96 ui->progressIcon->setPixmap(KIcon(QLatin1String("akonadi")).pixmap(32));
97 ui->quitButton->setText(KStandardGuiItem::quit().text());
98 ui->detailsQuitButton->setText(KStandardGuiItem::quit().text());
99
100#ifndef KDEPIM_MOBILE_UI
101 ui->quitButton->hide();
102 ui->detailsQuitButton->hide();
103#endif
104
105 connect(ui->startButton, SIGNAL(clicked()), SLOT(startClicked()));
106 connect(ui->quitButton, SIGNAL(clicked()), SLOT(quitClicked()));
107 connect(ui->detailsQuitButton, SIGNAL(clicked()), SLOT(quitClicked()));
108 connect(ui->selfTestButton, SIGNAL(clicked()), SLOT(selfTestClicked()));
109
110 const ServerManager::State state = ServerManager::state();
111 mOverlayActive = (state == ServerManager::Running);
112 serverStateChanged(state);
113
114 connect(ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)),
115 SLOT(serverStateChanged(Akonadi::ServerManager::State)));
116
117 QPalette p = palette();
118 p.setColor(backgroundRole(), QColor(0, 0, 0, 128));
119 p.setColor(foregroundRole(), Qt::white);
120 setPalette(p);
121 setAutoFillBackground(true);
122
123 mBaseWidget->installEventFilter(this);
124
125 reposition();
126}
127
128ErrorOverlay::~ ErrorOverlay()
129{
130 if (mBaseWidget && !mBaseWidgetIsParent) {
131 mBaseWidget->setEnabled(mPreviousState);
132 }
133}
134
135void ErrorOverlay::reposition()
136{
137 if (!mBaseWidget) {
138 return;
139 }
140
141 // reparent to the current top level widget of the base widget if needed
142 // needed eg. in dock widgets
143 if (parentWidget() != mBaseWidget->window()) {
144 setParent(mBaseWidget->window());
145 }
146
147 // follow base widget visibility
148 // needed eg. in tab widgets
149 if (!mBaseWidget->isVisible()) {
150 hide();
151 return;
152 }
153 if (mOverlayActive) {
154 show();
155 }
156
157 // follow position changes
158 const QPoint topLevelPos = mBaseWidget->mapTo(window(), QPoint(0, 0));
159 const QPoint parentPos = parentWidget()->mapFrom(window(), topLevelPos);
160 move(parentPos);
161
162 // follow size changes
163 // TODO: hide/scale icon if we don't have enough space
164 resize(mBaseWidget->size());
165}
166
167bool ErrorOverlay::eventFilter(QObject *object, QEvent *event)
168{
169 if (object == mBaseWidget && mOverlayActive &&
170 (event->type() == QEvent::Move || event->type() == QEvent::Resize ||
171 event->type() == QEvent::Show || event->type() == QEvent::Hide ||
172 event->type() == QEvent::ParentChange)) {
173 reposition();
174 }
175 return QWidget::eventFilter(object, event);
176}
177
178void ErrorOverlay::startClicked()
179{
180 const ServerManager::State state = ServerManager::state();
181 if (state == ServerManager::Running) {
182 serverStateChanged(state);
183 } else {
184 ServerManager::start();
185 }
186}
187
188void ErrorOverlay::quitClicked()
189{
190 qApp->quit();
191}
192
193void ErrorOverlay::selfTestClicked()
194{
195 SelfTestDialog dlg;
196 dlg.exec();
197}
198
199void ErrorOverlay::serverStateChanged(ServerManager::State state)
200{
201 if (!mBaseWidget) {
202 return;
203 }
204
205 if (state == ServerManager::Running && mOverlayActive) {
206 mOverlayActive = false;
207 hide();
208 if (!mBaseWidgetIsParent) {
209 mBaseWidget->setEnabled(mPreviousState);
210 }
211 } else if (!mOverlayActive) {
212 mOverlayActive = true;
213 if (mBaseWidget->isVisible()) {
214 show();
215 }
216
217 if (!mBaseWidgetIsParent) {
218 mPreviousState = mBaseWidget->isEnabled();
219 mBaseWidget->setEnabled(false);
220 }
221
222 reposition();
223 }
224
225 if (mOverlayActive) {
226 switch (state) {
227 case ServerManager::NotRunning:
228 ui->stackWidget->setCurrentWidget(ui->notRunningPage);
229 break;
230 case ServerManager::Broken:
231 ui->stackWidget->setCurrentWidget(ui->brokenPage);
232 break;
233 case ServerManager::Starting:
234 ui->progressPage->setToolTip(i18n("Personal information management service is starting..."));
235 ui->progressDescription->setText(i18n("Personal information management service is starting..."));
236 ui->stackWidget->setCurrentWidget(ui->progressPage);
237 break;
238 case ServerManager::Stopping:
239 ui->progressPage->setToolTip(i18n("Personal information management service is shutting down..."));
240 ui->progressDescription->setText(i18n("Personal information management service is shutting down..."));
241 ui->stackWidget->setCurrentWidget(ui->progressPage);
242 break;
243 case ServerManager::Upgrading:
244 ui->progressPage->setToolTip(i18n("Personal information management service is performing a database upgrade."));
245 ui->progressDescription->setText(i18n("Personal information management service is performing a database upgrade.\n"
246 "This happens after a software update and is necessary to optimize performance.\n"
247 "Depending on the amount of personal information, this might take a few minutes."));
248 ui->stackWidget->setCurrentWidget(ui->progressPage);
249 break;
250 case ServerManager::Running:
251 break;
252 }
253 }
254}
255
256//@endcond
257
258#include "moc_erroroverlay_p.cpp"
259