1
/***************************************************************************
4
* Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
5
* Copyright (C) 2008 Sebastian Sauer *
6
* Copyright (C) 2010 Marco Martin <notmart@gmail.com> *
8
* This program is free software; you can redistribute it and/or modify *
9
* it under the terms of the GNU General Public License as published by *
10
* the Free Software Foundation; either version 2 of the License, or *
11
* (at your option) any later version. *
13
* This program is distributed in the hope that it will be useful, *
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16
* GNU General Public License for more details. *
18
* You should have received a copy of the GNU General Public License *
19
* along with this program; if not, write to the *
20
* Free Software Foundation, Inc., *
21
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
22
***************************************************************************/
24
#include "notifications.h"
26
#include <QtGui/QApplication>
27
#include <QtGui/QGraphicsLayout>
28
#include <QtGui/QGraphicsLinearLayout>
29
#include <QtGui/QVBoxLayout>
30
#include <QtGui/QIcon>
31
#include <QtGui/QLabel>
32
#include <QtGui/QListWidget>
33
#include <QtGui/QTreeWidget>
34
#include <QtGui/QCheckBox>
35
#include <QtGui/QPainter>
36
#include <QtGui/QX11Info>
37
#include <QtCore/QProcess>
40
#include <KConfigDialog>
42
#include <KWindowSystem>
43
#include <KIconLoader>
45
#include <Solid/Device>
47
#include <plasma/extender.h>
48
#include <plasma/extenderitem.h>
49
#include <plasma/extendergroup.h>
50
#include <plasma/framesvg.h>
51
#include <plasma/widgets/label.h>
52
#include <plasma/theme.h>
53
#include <plasma/dataenginemanager.h>
54
#include <plasma/dataengine.h>
55
#include <Plasma/TabBar>
56
#include <Plasma/Animator>
57
#include <Plasma/Animation>
58
#include <Plasma/Containment>
59
#include <Plasma/Corona>
60
#include <Plasma/Dialog>
61
#include <Plasma/WindowEffects>
63
#include "config-notifications.h"
64
#ifdef HAVE_LIBXSS // Idle detection.
66
#include <X11/Xutil.h>
67
#include <X11/extensions/scrnsaver.h>
71
#include "../core/notificationsmanager.h"
72
#include "../core/notification.h"
73
#include "../core/completedjobnotification.h"
74
#include "busywidget.h"
75
#include "jobwidget.h"
76
#include "jobtotalswidget.h"
77
#include "notificationgroup.h"
78
#include "notificationstack.h"
79
#include "stackdialog.h"
82
K_EXPORT_PLASMA_APPLET(notifications, Notifications)
85
Notifications::Notifications(QObject *parent, const QVariantList &arguments)
86
: Plasma::PopupApplet(parent, arguments),
87
m_jobSummaryWidget(0),
88
m_autoHidePopup(true),
89
m_notificationStack(0),
90
m_notificationStackDialog(0),
91
m_standaloneJobSummaryWidget(0),
92
m_standaloneJobSummaryDialog(0),
95
m_manager = new Manager(this);
97
setPopupIcon(QIcon());
98
setPassivePopup(true);
99
setAspectRatioMode(Plasma::IgnoreAspectRatio);
100
setBackgroundHints(NoBackground);
101
setHasConfigurationInterface(true);
102
setMinimumSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
105
Notifications::~Notifications()
107
// stop listening to the manager
108
disconnect(m_manager, 0, this, 0);
109
if (m_notificationStackDialog) {
110
disconnect(m_notificationStackDialog, 0, this, 0);
113
foreach (Notification *notification, m_manager->notifications()) {
114
// we don't want a destroyed managed after the destruction of manager
115
disconnect(notification, 0, this, 0);
118
//has to be deleted before the manager because it will access it
120
delete m_notificationStackDialog;
121
delete m_standaloneJobSummaryDialog;
124
void Notifications::init()
126
extender()->setEmptyExtenderMessage(i18n("No notifications and no jobs"));
128
m_busyWidget = new BusyWidget(this, m_manager);
129
connect(m_busyWidget, SIGNAL(clicked()), this, SLOT(togglePopup()));
130
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(this);
131
setContentsMargins(0, 0, 0, 0);
132
lay->setContentsMargins(0, 0, 0, 0);
133
lay->addItem(m_busyWidget);
136
setStatus(Plasma::ActiveStatus);
139
void Notifications::configChanged()
141
KConfigGroup cg = config();
143
m_autoHidePopup = cg.readEntry("AutoHidePopup", true);
144
if (m_notificationStackDialog) {
145
m_notificationStackDialog->setAutoHide(m_autoHidePopup);
148
if (cg.readEntry("ShowJobs", true)) {
151
m_manager->registerJobProtocol();
152
connect(m_manager, SIGNAL(jobAdded(Job*)),
153
this, SLOT(addJob(Job*)), Qt::UniqueConnection);
154
connect(m_manager, SIGNAL(jobRemoved(Job*)),
155
this, SLOT(finishJob(Job*)), Qt::UniqueConnection);
157
delete extender()->group("jobGroup");
158
m_manager->unregisterJobProtocol();
159
disconnect(m_manager, SIGNAL(jobAdded(Job*)),
160
this, SLOT(addJob(Job*)));
161
disconnect(m_manager, SIGNAL(jobRemoved(Job*)),
162
this, SLOT(finishJob(Job*)));
165
if (cg.readEntry("ShowNotifications", true)) {
166
m_manager->registerNotificationProtocol();
167
connect(m_manager, SIGNAL(notificationAdded(Notification*)),
168
this, SLOT(addNotification(Notification*)), Qt::UniqueConnection);
170
m_manager->unregisterNotificationProtocol();
171
disconnect(m_manager, SIGNAL(notificationAdded(Notification*)),
172
this, SLOT(addNotification(Notification*)));
176
void Notifications::syncNotificationBarNeeded()
182
if (m_manager->notifications().count() > 0) {
183
if (!extender()->item("notifications")) {
184
m_notificationGroup = new NotificationGroup(extender());
186
} else if (extender()->item("notifications")) {
187
//don't let him in the config file
188
extender()->item("notifications")->destroy();
193
Manager *Notifications::manager() const
198
void Notifications::createConfigurationInterface(KConfigDialog *parent)
200
if (!m_notificationInterface) {
201
KConfigGroup cg = config();
202
m_notificationInterface = new QWidget();
204
m_notificationUi.setupUi(m_notificationInterface.data());
206
m_notificationUi.showJobs->setChecked(cg.readEntry("ShowJobs", true));
207
m_notificationUi.showNotifications->setChecked(cg.readEntry("ShowNotifications", true));
209
m_notificationUi.autoHide->setChecked(config().readEntry("AutoHidePopup", true));
211
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
212
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
214
parent->addPage(m_notificationInterface.data(), i18n("Information"),
215
"preferences-desktop-notification",
216
i18n("Choose which information to show"));
218
connect(m_notificationUi.showNotifications, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
219
connect(m_notificationUi.showJobs, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
220
connect(m_notificationUi.autoHide, SIGNAL(stateChanged(int)), parent, SLOT(settingsModified()));
224
void Notifications::configAccepted()
226
//TODO put in a single page
227
//cg.writeEntry("AutoHidePopup", m_autoHideUi.autoHide->isChecked());
229
KConfigGroup cg = config();
230
cg.writeEntry("ShowJobs", m_notificationUi.showJobs->isChecked());
231
cg.writeEntry("ShowNotifications", m_notificationUi.showNotifications->isChecked());
232
cg.writeEntry("AutoHidePopup", m_notificationUi.autoHide->isChecked());
234
emit configNeedsSaving();
237
void Notifications::addNotification(Notification *notification)
239
syncNotificationBarNeeded();
241
//At this point we are sure the pointer is valid
242
m_notificationGroup.data()->addNotification(notification);
245
if (isPopupShowing()) {
249
if (!m_notificationStack) {
250
m_notificationStack = new NotificationStack(this);
251
if (containment() && containment()->corona()) {
252
containment()->corona()->addOffscreenWidget(m_notificationStack);
254
m_notificationStackDialog = new StackDialog;
255
m_notificationStackDialog->setNotificationStack(m_notificationStack);
256
m_notificationStackDialog->setApplet(this);
257
connect(m_notificationStack, SIGNAL(stackEmpty()), m_notificationStackDialog, SLOT(hide()));
258
connect(m_notificationStack, SIGNAL(showRequested()), m_notificationStackDialog, SLOT(show()));
259
m_notificationStackDialog->setAutoHide(m_autoHidePopup);
261
if (m_standaloneJobSummaryDialog) {
262
m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
267
m_notificationStack->addNotification(notification);
268
m_notificationStackDialog->syncToGraphicsWidget();
270
if (containment() && containment()->corona()) {
271
if (!m_notificationStackDialog->isVisible()) {
272
m_notificationStack->setCurrentNotification(notification);
275
KWindowSystem::setOnAllDesktops(m_notificationStackDialog->winId(), true);
276
m_notificationStackDialog->show();
279
Plasma::Animation *pulse = Plasma::Animator::create(Plasma::Animator::PulseAnimation, m_busyWidget);
280
pulse->setTargetWidget(m_busyWidget);
281
pulse->start(QAbstractAnimation::DeleteWhenStopped);
284
void Notifications::addJob(Job *job)
286
Plasma::ExtenderGroup *group = extender()->group("jobGroup");
288
Plasma::ExtenderItem *extenderItem = new Plasma::ExtenderItem(extender());
289
extenderItem->setTransient(true);
290
extenderItem->config().writeEntry("type", "job");
291
extenderItem->setWidget(new JobWidget(job, extenderItem));
293
extenderItem->setGroup(group);
296
group->setCollapsed(group->items().count() < 2);
299
if (isPopupShowing()) {
303
//show the tiny standalone overview
304
if (!m_standaloneJobSummaryWidget) {
305
m_standaloneJobSummaryDialog = new Plasma::Dialog();
306
KWindowSystem::setType(m_standaloneJobSummaryDialog->winId(), NET::Dock);
307
if (m_notificationStackDialog) {
308
m_notificationStackDialog->setWindowToTile(m_standaloneJobSummaryDialog);
311
m_standaloneJobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), this);
312
if (containment() && containment()->corona()) {
313
containment()->corona()->addOffscreenWidget(m_standaloneJobSummaryWidget);
315
m_standaloneJobSummaryDialog->setGraphicsWidget(m_standaloneJobSummaryWidget);
316
//FIXME:sizing hack and layout issues..
317
m_standaloneJobSummaryWidget->resize(m_standaloneJobSummaryWidget->size().width(), 32);
318
m_standaloneJobSummaryWidget->setMaximumHeight(32);
319
m_standaloneJobSummaryWidget->setMinimumHeight(32);
320
m_standaloneJobSummaryWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
323
m_standaloneJobSummaryDialog->syncToGraphicsWidget();
324
KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
326
if (containment() && containment()->corona()) {
327
m_standaloneJobSummaryDialog->move(containment()->corona()->popupPosition(this, m_standaloneJobSummaryDialog->size()));
328
m_standaloneJobSummaryDialog->show();
329
Plasma::WindowEffects::slideWindow(m_standaloneJobSummaryDialog, location());
331
KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
332
KWindowSystem::clearState(m_standaloneJobSummaryDialog->winId(), NET::KeepAbove|NET::StaysOnTop);
334
KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
335
KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
336
KWindowSystem::setOnAllDesktops(m_standaloneJobSummaryDialog->winId(), true);
340
void Notifications::initExtenderItem(Plasma::ExtenderItem *extenderItem)
342
if (extenderItem->name() == "jobGroup") {
343
m_jobSummaryWidget = new JobTotalsWidget(m_manager->jobTotals(), extenderItem);
344
extenderItem->setWidget(m_jobSummaryWidget);
345
Plasma::ExtenderGroup *group = qobject_cast<Plasma::ExtenderGroup*>(extenderItem);
347
extenderItem->setCollapsed(!group->isGroupCollapsed());
352
if (extenderItem->config().readEntry("type", QString()) == "job") {
353
extenderItem->setWidget(new JobWidget(0, extenderItem));
355
//unknown type, this should never happen
356
extenderItem->destroy();
361
void Notifications::popupEvent(bool show)
363
//decide about showing the tiny progressbar or not
364
if (m_standaloneJobSummaryDialog) {
365
if (show || !m_manager->jobs().isEmpty()) {
367
KWindowSystem::raiseWindow(m_standaloneJobSummaryDialog->winId());
368
KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::SkipTaskbar|NET::SkipPager);
369
KWindowSystem::setState(m_standaloneJobSummaryDialog->winId(), NET::KeepBelow);
371
m_standaloneJobSummaryDialog->hide();
376
if (m_notificationStackDialog && show) {
377
m_notificationStackDialog->hide();
380
Plasma::ExtenderGroup * jobGroup = extender()->group("jobGroup");
385
foreach (Plasma::ExtenderItem *item, jobGroup->items()) {
386
JobWidget *job = dynamic_cast<JobWidget *>(item->widget());
393
void Notifications::finishJob(Job *job)
395
//finished all jobs? hide the mini progressbar
396
if (m_standaloneJobSummaryDialog && m_manager->jobs().isEmpty()) {
397
m_standaloneJobSummaryDialog->hide();
400
//create a fake notification
401
CompletedJobNotification *notification = new CompletedJobNotification(this);
402
notification->setJob(job);
403
m_manager->addNotification(notification);
405
Plasma::ExtenderGroup *group = extender()->group("jobGroup");
407
// < 3 because the second still hasn't been removed from the extendergroup
408
group->setCollapsed(!group->isGroupCollapsed() && group->items().count() < 3);
412
void Notifications::open(const QString &url)
414
//kDebug() << "open " << url;
415
QProcess::startDetached("kde-open", QStringList() << url);
418
void Notifications::createJobGroups()
420
if (!extender()->hasItem("jobGroup")) {
421
Plasma::ExtenderGroup *extenderGroup = new Plasma::ExtenderGroup(extender());
422
extenderGroup->setName("jobGroup");
423
initExtenderItem(extenderGroup);
424
extenderGroup->setAutoHide(true);
425
} else if (extender()->group("jobGroup")) {
426
extender()->group("jobGroup")->setAutoHide(true);
431
#include "notifications.moc"