Changeset View
Changeset View
Standalone View
Standalone View
src/notifybypopup.cpp
Show All 18 Lines | 1 | /* | |||
---|---|---|---|---|---|
19 | You should have received a copy of the GNU Lesser General Public | 19 | You should have received a copy of the GNU Lesser General Public | ||
20 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | 20 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
21 | 21 | | |||
22 | */ | 22 | */ | ||
23 | 23 | | |||
24 | #include "notifybypopup.h" | 24 | #include "notifybypopup.h" | ||
25 | #include "imageconverter.h" | 25 | #include "imageconverter.h" | ||
26 | 26 | | |||
27 | #include "kpassivepopup.h" | | |||
28 | #include "knotifyconfig.h" | 27 | #include "knotifyconfig.h" | ||
29 | #include "knotification.h" | 28 | #include "knotification.h" | ||
30 | #include "debug_p.h" | 29 | #include "debug_p.h" | ||
31 | 30 | | |||
32 | #include <QApplication> | | |||
33 | #include <QBuffer> | 31 | #include <QBuffer> | ||
34 | #include <QImage> | 32 | #include <QImage> | ||
35 | #include <QLabel> | 33 | #include <QLabel> | ||
36 | #include <QGuiApplication> | 34 | #include <QGuiApplication> | ||
37 | #include <QLayout> | | |||
38 | #include <QDBusConnection> | 35 | #include <QDBusConnection> | ||
39 | #include <QDBusConnectionInterface> | 36 | #include <QDBusConnectionInterface> | ||
40 | #include <QDBusServiceWatcher> | 37 | #include <QDBusServiceWatcher> | ||
41 | #include <QDBusMessage> | 38 | #include <QDBusMessage> | ||
42 | #include <QXmlStreamReader> | 39 | #include <QXmlStreamReader> | ||
43 | #include <QMap> | 40 | #include <QMap> | ||
44 | #include <QHash> | 41 | #include <QHash> | ||
45 | #include <QXmlStreamEntityResolver> | 42 | #include <QXmlStreamEntityResolver> | ||
46 | #include <QPointer> | 43 | #include <QPointer> | ||
47 | #include <QMutableListIterator> | 44 | #include <QMutableListIterator> | ||
48 | #include <QThread> | 45 | #include <QThread> | ||
49 | #include <QFontMetrics> | | |||
50 | #include <QIcon> | 46 | #include <QIcon> | ||
51 | #include <QUrl> | 47 | #include <QUrl> | ||
52 | #include <QScreen> | | |||
53 | 48 | | |||
54 | #include <kconfiggroup.h> | 49 | #include <kconfiggroup.h> | ||
55 | #include <KCodecs/KCharsets> | 50 | #include <KCodecs/KCharsets> | ||
56 | 51 | | |||
57 | static const char dbusServiceName[] = "org.freedesktop.Notifications"; | 52 | static const char dbusServiceName[] = "org.freedesktop.Notifications"; | ||
58 | static const char dbusInterfaceName[] = "org.freedesktop.Notifications"; | 53 | static const char dbusInterfaceName[] = "org.freedesktop.Notifications"; | ||
59 | static const char dbusPath[] = "/org/freedesktop/Notifications"; | 54 | static const char dbusPath[] = "/org/freedesktop/Notifications"; | ||
60 | 55 | | |||
61 | class NotifyByPopupPrivate { | 56 | class NotifyByPopupPrivate { | ||
62 | public: | 57 | public: | ||
63 | NotifyByPopupPrivate(NotifyByPopup *parent) : q(parent) {} | 58 | NotifyByPopupPrivate(NotifyByPopup *parent) : q(parent) {} | ||
64 | /** | 59 | /** | ||
65 | * @internal | | |||
66 | * Fills the KPassivePopup with data | | |||
67 | */ | | |||
68 | void fillPopup(KPassivePopup *popup, KNotification *notification, const KNotifyConfig &config); | | |||
69 | /** | | |||
70 | * Removes HTML from a given string. Replaces line breaks with \n and | 60 | * Removes HTML from a given string. Replaces line breaks with \n and | ||
71 | * HTML entities by their 'normal forms'. | 61 | * HTML entities by their 'normal forms'. | ||
72 | * @param string the HTML to remove. | 62 | * @param string the HTML to remove. | ||
73 | * @return the cleaned string. | 63 | * @return the cleaned string. | ||
74 | */ | 64 | */ | ||
75 | QString stripHtml(const QString &text); | 65 | QString stripHtml(const QString &text); | ||
76 | /** | 66 | /** | ||
77 | * Sends notification to DBus "org.freedesktop.notifications" interface. | 67 | * Sends notification to DBus "org.freedesktop.notifications" interface. | ||
Show All 11 Lines | |||||
89 | */ | 79 | */ | ||
90 | void closeGalagoNotification(KNotification *notification); | 80 | void closeGalagoNotification(KNotification *notification); | ||
91 | /** | 81 | /** | ||
92 | * Find the caption and the icon name of the application | 82 | * Find the caption and the icon name of the application | ||
93 | */ | 83 | */ | ||
94 | void getAppCaptionAndIconName(const KNotifyConfig &config, QString *appCaption, QString *iconName); | 84 | void getAppCaptionAndIconName(const KNotifyConfig &config, QString *appCaption, QString *iconName); | ||
95 | /* | 85 | /* | ||
96 | * Query the dbus server for notification capabilities | 86 | * Query the dbus server for notification capabilities | ||
97 | * If no DBus server is present, use fallback capabilities for KPassivePopup | | |||
98 | */ | 87 | */ | ||
99 | void queryPopupServerCapabilities(); | 88 | void queryPopupServerCapabilities(); | ||
100 | 89 | | |||
101 | void checkServiceExists(); | 90 | void checkServiceExists(); | ||
102 | 91 | | |||
103 | // the y coordinate of the next position popup should appears | 92 | // the y coordinate of the next position popup should appears | ||
104 | int nextPosition; | 93 | int nextPosition; | ||
105 | int animationTimer; | 94 | int animationTimer; | ||
Show All 16 Lines | |||||
122 | * to return, then process them from this queue | 111 | * to return, then process them from this queue | ||
123 | */ | 112 | */ | ||
124 | QList<QPair<KNotification*, KNotifyConfig> > notificationQueue; | 113 | QList<QPair<KNotification*, KNotifyConfig> > notificationQueue; | ||
125 | /** | 114 | /** | ||
126 | * Whether the DBus notification daemon capability cache is up-to-date. | 115 | * Whether the DBus notification daemon capability cache is up-to-date. | ||
127 | */ | 116 | */ | ||
128 | bool dbusServiceCapCacheDirty; | 117 | bool dbusServiceCapCacheDirty; | ||
129 | 118 | | |||
130 | /** | | |||
131 | * Keeps the map of notifications done in KPassivePopup | | |||
132 | */ | | |||
133 | QMap<KNotification*, KPassivePopup *> passivePopups; | | |||
134 | | ||||
135 | /* | 119 | /* | ||
136 | * As we communicate with the notification server over dbus | 120 | * As we communicate with the notification server over dbus | ||
137 | * we use only ids, this is for fast KNotifications lookup | 121 | * we use only ids, this is for fast KNotifications lookup | ||
138 | */ | 122 | */ | ||
139 | QHash<uint, QPointer<KNotification>> galagoNotifications; | 123 | QHash<uint, QPointer<KNotification>> galagoNotifications; | ||
140 | 124 | | |||
141 | 125 | | |||
142 | NotifyByPopup * const q; | 126 | NotifyByPopup * const q; | ||
Show All 10 Lines | |||||
153 | }; | 137 | }; | ||
154 | 138 | | |||
155 | //--------------------------------------------------------------------------------------- | 139 | //--------------------------------------------------------------------------------------- | ||
156 | 140 | | |||
157 | NotifyByPopup::NotifyByPopup(QObject *parent) | 141 | NotifyByPopup::NotifyByPopup(QObject *parent) | ||
158 | : KNotificationPlugin(parent), | 142 | : KNotificationPlugin(parent), | ||
159 | d(new NotifyByPopupPrivate(this)) | 143 | d(new NotifyByPopupPrivate(this)) | ||
160 | { | 144 | { | ||
161 | d->animationTimer = 0; | | |||
162 | d->dbusServiceExists = false; | 145 | d->dbusServiceExists = false; | ||
163 | d->dbusServiceCapCacheDirty = true; | 146 | d->dbusServiceCapCacheDirty = true; | ||
164 | d->nextPosition = -1; | 147 | d->nextPosition = -1; | ||
165 | 148 | | |||
166 | // check if service already exists on plugin instantiation | 149 | // check if service already exists on plugin instantiation | ||
167 | d->checkServiceExists(); | 150 | d->checkServiceExists(); | ||
168 | 151 | | |||
169 | if (d->dbusServiceExists) { | 152 | if (d->dbusServiceExists) { | ||
170 | onServiceOwnerChanged(QString::fromLatin1(dbusServiceName), QString(), QStringLiteral("_")); //connect signals | 153 | onServiceOwnerChanged(QString::fromLatin1(dbusServiceName), QString(), QStringLiteral("_")); //connect signals | ||
171 | } | 154 | } | ||
172 | 155 | | |||
173 | // to catch register/unregister events from service in runtime | 156 | // to catch register/unregister events from service in runtime | ||
174 | QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this); | 157 | QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this); | ||
175 | watcher->setConnection(QDBusConnection::sessionBus()); | 158 | watcher->setConnection(QDBusConnection::sessionBus()); | ||
176 | watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); | 159 | watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); | ||
177 | watcher->addWatchedService(QString::fromLatin1(dbusServiceName)); | 160 | watcher->addWatchedService(QString::fromLatin1(dbusServiceName)); | ||
178 | connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, | 161 | connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, | ||
179 | this, &NotifyByPopup::onServiceOwnerChanged); | 162 | this, &NotifyByPopup::onServiceOwnerChanged); | ||
180 | } | 163 | } | ||
181 | 164 | | |||
182 | 165 | | |||
183 | NotifyByPopup::~NotifyByPopup() | 166 | NotifyByPopup::~NotifyByPopup() | ||
184 | { | 167 | { | ||
185 | for (KPassivePopup *p : qAsConst(d->passivePopups)) { | | |||
186 | p->deleteLater(); | | |||
187 | } | | |||
188 | | ||||
189 | delete d; | 168 | delete d; | ||
190 | } | 169 | } | ||
191 | 170 | | |||
192 | void NotifyByPopup::notify(KNotification *notification, KNotifyConfig *notifyConfig) | 171 | void NotifyByPopup::notify(KNotification *notification, KNotifyConfig *notifyConfig) | ||
193 | { | 172 | { | ||
194 | notify(notification, *notifyConfig); | 173 | notify(notification, *notifyConfig); | ||
195 | } | 174 | } | ||
196 | 175 | | |||
197 | void NotifyByPopup::notify(KNotification *notification, const KNotifyConfig ¬ifyConfig) | 176 | void NotifyByPopup::notify(KNotification *notification, const KNotifyConfig ¬ifyConfig) | ||
198 | { | 177 | { | ||
199 | if (d->passivePopups.contains(notification) || d->galagoNotifications.contains(notification->id())) { | 178 | if (d->galagoNotifications.contains(notification->id())) { | ||
200 | // notification is already on the screen, do nothing | 179 | // notification is already on the screen, do nothing | ||
201 | finish(notification); | 180 | finish(notification); | ||
202 | return; | 181 | return; | ||
203 | } | 182 | } | ||
204 | 183 | | |||
205 | // check if Notifications DBus service exists on bus, use it if it does | 184 | // check if Notifications DBus service exists on bus, use it if it does | ||
206 | if (d->dbusServiceExists) { | 185 | if (d->dbusServiceExists) { | ||
207 | if (d->dbusServiceCapCacheDirty) { | 186 | if (d->dbusServiceCapCacheDirty) { | ||
208 | // if we don't have the server capabilities yet, we need to query for them first; | 187 | // if we don't have the server capabilities yet, we need to query for them first; | ||
209 | // as that is an async dbus operation, we enqueue the notification and process them | 188 | // as that is an async dbus operation, we enqueue the notification and process them | ||
210 | // when we receive dbus reply with the server capabilities | 189 | // when we receive dbus reply with the server capabilities | ||
211 | d->notificationQueue.append(qMakePair(notification, notifyConfig)); | 190 | d->notificationQueue.append(qMakePair(notification, notifyConfig)); | ||
212 | d->queryPopupServerCapabilities(); | 191 | d->queryPopupServerCapabilities(); | ||
213 | } else { | 192 | } else { | ||
214 | if (!d->sendNotificationToGalagoServer(notification, notifyConfig)) { | 193 | if (!d->sendNotificationToGalagoServer(notification, notifyConfig)) { | ||
215 | finish(notification); //an error occurred. | 194 | finish(notification); //an error occurred. | ||
216 | } | 195 | } | ||
217 | } | 196 | } | ||
218 | return; | 197 | return; | ||
219 | } | 198 | } | ||
220 | | ||||
221 | // Persistent => 0 == infinite timeout | | |||
222 | // CloseOnTimeout => -1 == let the server decide | | |||
223 | int timeout = (notification->flags() & KNotification::Persistent) ? 0 : -1; | | |||
224 | | ||||
225 | // Check if this object lives in the GUI thread and return if it doesn't | | |||
226 | // as Qt cannot create/handle widgets in non-GUI threads | | |||
227 | if (QThread::currentThread() != qApp->thread()) { | | |||
228 | qCWarning(LOG_KNOTIFICATIONS) << "KNotification did not detect any running org.freedesktop.Notifications server and fallback notifications cannot be used from non-GUI thread!"; | | |||
229 | return; | | |||
230 | } | | |||
231 | | ||||
232 | if (!qobject_cast<QApplication *>(QCoreApplication::instance())) { | | |||
233 | qCWarning(LOG_KNOTIFICATIONS) << "KNotification did not detect any running org.freedesktop.Notifications server and fallback notifications cannot be used without a QApplication!"; | | |||
234 | return; | | |||
235 | } | | |||
236 | | ||||
237 | // last fallback - display the popup using KPassivePopup | | |||
238 | KPassivePopup *pop = new KPassivePopup(notification->widget()); | | |||
239 | d->passivePopups.insert(notification, pop); | | |||
240 | d->fillPopup(pop, notification, notifyConfig); | | |||
241 | | ||||
242 | QRect screen = QGuiApplication::primaryScreen()->availableGeometry(); | | |||
243 | if (d->nextPosition == -1) { | | |||
244 | d->nextPosition = screen.top(); | | |||
245 | } | | |||
246 | pop->setAutoDelete(true); | | |||
247 | connect(pop, &QObject::destroyed, this, &NotifyByPopup::onPassivePopupDestroyed); | | |||
248 | | ||||
249 | pop->setTimeout(timeout); | | |||
250 | pop->adjustSize(); | | |||
251 | pop->show(QPoint(screen.left() + screen.width()/2 - pop->width()/2 , d->nextPosition)); | | |||
252 | d->nextPosition += pop->height(); | | |||
253 | } | | |||
254 | | ||||
255 | void NotifyByPopup::onPassivePopupDestroyed() | | |||
256 | { | | |||
257 | const QObject *destroyedPopup = sender(); | | |||
258 | | ||||
259 | if (!destroyedPopup) { | | |||
260 | return; | | |||
261 | } | | |||
262 | | ||||
263 | for (QMap<KNotification*, KPassivePopup*>::iterator it = d->passivePopups.begin(); it != d->passivePopups.end(); ++it) { | | |||
264 | QObject *popup = it.value(); | | |||
265 | if (popup && popup == destroyedPopup) { | | |||
266 | finish(it.key()); | | |||
267 | d->passivePopups.remove(it.key()); | | |||
268 | break; | | |||
269 | } | | |||
270 | } | | |||
271 | | ||||
272 | //relocate popup | | |||
273 | if (!d->animationTimer) { | | |||
274 | d->animationTimer = startTimer(10); | | |||
275 | } | | |||
276 | } | | |||
277 | | ||||
278 | void NotifyByPopup::timerEvent(QTimerEvent *event) | | |||
279 | { | | |||
280 | if (event->timerId() != d->animationTimer) { | | |||
281 | KNotificationPlugin::timerEvent(event); | | |||
282 | return; | | |||
283 | } | | |||
284 | | ||||
285 | bool cont = false; | | |||
286 | QRect screen = QGuiApplication::primaryScreen()->availableGeometry(); | | |||
287 | d->nextPosition = screen.top(); | | |||
288 | | ||||
289 | for (KPassivePopup *popup : qAsConst(d->passivePopups)) | | |||
290 | { | | |||
291 | int y = popup->pos().y(); | | |||
292 | if (y > d->nextPosition) { | | |||
293 | y = qMax(y - 5, d->nextPosition); | | |||
294 | d->nextPosition = y + popup->height(); | | |||
295 | cont = cont || y != d->nextPosition; | | |||
296 | popup->move(popup->pos().x(), y); | | |||
297 | } else { | | |||
298 | d->nextPosition += popup->height(); | | |||
299 | } | | |||
300 | } | | |||
301 | | ||||
302 | if (!cont) { | | |||
303 | killTimer(d->animationTimer); | | |||
304 | d->animationTimer = 0; | | |||
305 | } | | |||
306 | } | | |||
307 | | ||||
308 | void NotifyByPopup::onPassivePopupLinkClicked(const QString &link) | | |||
309 | { | | |||
310 | unsigned int id = link.section(QLatin1Char('/') , 0 , 0).toUInt(); | | |||
311 | unsigned int action = link.section(QLatin1Char('/') , 1 , 1).toUInt(); | | |||
312 | | ||||
313 | if (id == 0 || action == 0) { | | |||
314 | return; | | |||
315 | } | | |||
316 | | ||||
317 | emit actionInvoked(id, action); | | |||
318 | } | 199 | } | ||
319 | 200 | | |||
320 | void NotifyByPopup::close(KNotification *notification) | 201 | void NotifyByPopup::close(KNotification *notification) | ||
321 | { | 202 | { | ||
322 | if (d->dbusServiceExists) { | 203 | if (d->dbusServiceExists) { | ||
323 | d->closeGalagoNotification(notification); | 204 | d->closeGalagoNotification(notification); | ||
324 | } | 205 | } | ||
325 | 206 | | |||
326 | if (d->passivePopups.contains(notification)) { | | |||
327 | // this will call onPassivePopupDestroyed() | | |||
328 | // which will call finish() on the notification | | |||
329 | d->passivePopups[notification]->deleteLater(); | | |||
330 | } | | |||
331 | | ||||
332 | QMutableListIterator<QPair<KNotification*, KNotifyConfig> > iter(d->notificationQueue); | 207 | QMutableListIterator<QPair<KNotification*, KNotifyConfig> > iter(d->notificationQueue); | ||
333 | while (iter.hasNext()) { | 208 | while (iter.hasNext()) { | ||
334 | auto &item = iter.next(); | 209 | auto &item = iter.next(); | ||
335 | if (item.first == notification) { | 210 | if (item.first == notification) { | ||
336 | iter.remove(); | 211 | iter.remove(); | ||
337 | } | 212 | } | ||
338 | } | 213 | } | ||
339 | } | 214 | } | ||
340 | 215 | | |||
341 | void NotifyByPopup::update(KNotification *notification, KNotifyConfig *notifyConfig) | 216 | void NotifyByPopup::update(KNotification *notification, KNotifyConfig *notifyConfig) | ||
342 | { | 217 | { | ||
343 | update(notification, *notifyConfig); | 218 | update(notification, *notifyConfig); | ||
344 | } | 219 | } | ||
345 | 220 | | |||
346 | void NotifyByPopup::update(KNotification *notification, const KNotifyConfig ¬ifyConfig) | 221 | void NotifyByPopup::update(KNotification *notification, const KNotifyConfig ¬ifyConfig) | ||
347 | { | 222 | { | ||
348 | if (d->passivePopups.contains(notification)) { | | |||
349 | KPassivePopup *p = d->passivePopups[notification]; | | |||
350 | d->fillPopup(p, notification, notifyConfig); | | |||
351 | return; | | |||
352 | } | | |||
353 | | ||||
354 | // if Notifications DBus service exists on bus, | 223 | // if Notifications DBus service exists on bus, | ||
355 | // it'll be used instead | 224 | // it'll be used instead | ||
356 | if (d->dbusServiceExists) { | 225 | if (d->dbusServiceExists) { | ||
357 | d->sendNotificationToGalagoServer(notification, notifyConfig, true); | 226 | d->sendNotificationToGalagoServer(notification, notifyConfig, true); | ||
358 | return; | 227 | return; | ||
359 | } | 228 | } | ||
360 | } | 229 | } | ||
361 | 230 | | |||
362 | void NotifyByPopup::onServiceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) | 231 | void NotifyByPopup::onServiceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) | ||
363 | { | 232 | { | ||
364 | Q_UNUSED(serviceName); | 233 | Q_UNUSED(serviceName); | ||
365 | Q_UNUSED(oldOwner); | 234 | Q_UNUSED(oldOwner); | ||
366 | Q_UNUSED(newOwner); | 235 | Q_UNUSED(newOwner); | ||
367 | 236 | | |||
368 | // close all notifications we currently hold reference to | 237 | // close all notifications we currently hold reference to | ||
369 | for (KNotification *n : qAsConst(d->galagoNotifications)) { | 238 | for (KNotification *n : qAsConst(d->galagoNotifications)) { | ||
370 | if (n) { | 239 | if (n) { | ||
371 | emit finished(n); | 240 | emit finished(n); | ||
372 | } | 241 | } | ||
373 | } | 242 | } | ||
374 | QMap<KNotification*, KPassivePopup *>::const_iterator i = d->passivePopups.constBegin(); | | |||
375 | while (i != d->passivePopups.constEnd()) { | | |||
376 | emit finished(i.key()); | | |||
377 | ++i; | | |||
378 | } | | |||
379 | d->galagoNotifications.clear(); | 243 | d->galagoNotifications.clear(); | ||
380 | d->passivePopups.clear(); | | |||
381 | 244 | | |||
382 | d->dbusServiceCapCacheDirty = true; | 245 | d->dbusServiceCapCacheDirty = true; | ||
383 | d->popupServerCapabilities.clear(); | 246 | d->popupServerCapabilities.clear(); | ||
384 | 247 | | |||
385 | d->checkServiceExists(); | 248 | d->checkServiceExists(); | ||
386 | 249 | | |||
387 | if (d->dbusServiceExists) { | 250 | if (d->dbusServiceExists) { | ||
388 | // connect to action invocation signals | 251 | // connect to action invocation signals | ||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Line(s) | 344 | { | |||
485 | KConfigGroup eventGroup(&(*notifyConfig.eventsfile), QStringLiteral("Event/%1").arg(notifyConfig.eventid)); | 348 | KConfigGroup eventGroup(&(*notifyConfig.eventsfile), QStringLiteral("Event/%1").arg(notifyConfig.eventid)); | ||
486 | if (eventGroup.hasKey("IconName")) { | 349 | if (eventGroup.hasKey("IconName")) { | ||
487 | *iconName = eventGroup.readEntry("IconName", notifyConfig.appname); | 350 | *iconName = eventGroup.readEntry("IconName", notifyConfig.appname); | ||
488 | } else { | 351 | } else { | ||
489 | *iconName = globalgroup.readEntry("IconName", notifyConfig.appname); | 352 | *iconName = globalgroup.readEntry("IconName", notifyConfig.appname); | ||
490 | } | 353 | } | ||
491 | } | 354 | } | ||
492 | 355 | | |||
493 | void NotifyByPopupPrivate::fillPopup(KPassivePopup *popup, KNotification *notification, const KNotifyConfig ¬ifyConfig) | | |||
494 | { | | |||
495 | QString appCaption; | | |||
496 | QString iconName; | | |||
497 | getAppCaptionAndIconName(notifyConfig, &appCaption, &iconName); | | |||
498 | | ||||
499 | // If we're at this place, it means there's no D-Bus service for notifications | | |||
500 | // so we don't need to do D-Bus query for the capabilities. | | |||
501 | // If queryPopupServerCapabilities() finds no service, it sets the KPassivePopup | | |||
502 | // capabilities immediately, so we don't need to wait for callback as in the case | | |||
503 | // of galago notifications | | |||
504 | queryPopupServerCapabilities(); | | |||
505 | | ||||
506 | int iconDimension = QFontMetrics(QFont()).height(); | | |||
507 | QPixmap appIcon = QIcon::fromTheme(iconName).pixmap(iconDimension, iconDimension); | | |||
508 | | ||||
509 | QWidget *vb = popup->standardView(notification->title().isEmpty() ? appCaption : notification->title(), | | |||
510 | notification->pixmap().isNull() ? notification->text() : QString(), | | |||
511 | appIcon); | | |||
512 | | ||||
513 | if (!notification->pixmap().isNull()) { | | |||
514 | const QPixmap pix = notification->pixmap(); | | |||
515 | QHBoxLayout *hbox = new QHBoxLayout(vb); | | |||
516 | | ||||
517 | QLabel *pil = new QLabel(); | | |||
518 | pil->setPixmap(pix); | | |||
519 | pil->setScaledContents(true); | | |||
520 | | ||||
521 | if (pix.height() > 80 && pix.height() > pix.width()) { | | |||
522 | pil->setMaximumHeight(80); | | |||
523 | pil->setMaximumWidth(80 * pix.width() / pix.height()); | | |||
524 | } else if(pix.width() > 80 && pix.height() <= pix.width()) { | | |||
525 | pil->setMaximumWidth(80); | | |||
526 | pil->setMaximumHeight(80*pix.height()/pix.width()); | | |||
527 | } | | |||
528 | | ||||
529 | hbox->addWidget(pil); | | |||
530 | | ||||
531 | QVBoxLayout *vb2 = new QVBoxLayout(vb); | | |||
532 | QLabel *msg = new QLabel(notification->text()); | | |||
533 | msg->setAlignment(Qt::AlignLeft); | | |||
534 | | ||||
535 | vb2->addWidget(msg); | | |||
536 | | ||||
537 | hbox->addLayout(vb2); | | |||
538 | | ||||
539 | vb->layout()->addItem(hbox); | | |||
540 | } | | |||
541 | | ||||
542 | | ||||
543 | if (!notification->actions().isEmpty()) { | | |||
544 | QString linkCode = QStringLiteral("<p align=\"right\">"); | | |||
545 | int i = 0; | | |||
546 | const auto actionList = notification->actions(); | | |||
547 | for (const QString &it : actionList) { | | |||
548 | i++; | | |||
549 | linkCode += QStringLiteral(" <a href=\"%1/%2\">%3</a>").arg(QString::number(notification->id()), QString::number(i), it.toHtmlEscaped()); | | |||
550 | } | | |||
551 | | ||||
552 | linkCode += QLatin1String("</p>"); | | |||
553 | QLabel *link = new QLabel(linkCode , vb ); | | |||
554 | link->setTextInteractionFlags(Qt::LinksAccessibleByMouse); | | |||
555 | link->setOpenExternalLinks(false); | | |||
556 | //link->setAlignment( AlignRight ); | | |||
557 | QObject::connect(link, &QLabel::linkActivated, | | |||
558 | q, &NotifyByPopup::onPassivePopupLinkClicked); | | |||
559 | QObject::connect(link, &QLabel::linkActivated, | | |||
560 | popup, &QWidget::hide); | | |||
561 | } | | |||
562 | | ||||
563 | popup->setView( vb ); | | |||
564 | } | | |||
565 | | ||||
566 | bool NotifyByPopupPrivate::sendNotificationToGalagoServer(KNotification *notification, const KNotifyConfig ¬ifyConfig_nocheck, bool update) | 356 | bool NotifyByPopupPrivate::sendNotificationToGalagoServer(KNotification *notification, const KNotifyConfig ¬ifyConfig_nocheck, bool update) | ||
567 | { | 357 | { | ||
568 | uint updateId = galagoNotifications.key(notification, 0); | 358 | uint updateId = galagoNotifications.key(notification, 0); | ||
569 | 359 | | |||
570 | if (update) { | 360 | if (update) { | ||
571 | if (updateId == 0) { | 361 | if (updateId == 0) { | ||
572 | // we have nothing to update; the notification we're trying to update | 362 | // we have nothing to update; the notification we're trying to update | ||
573 | // has been already closed | 363 | // has been already closed | ||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Line(s) | 518 | { | |||
744 | 534 | | |||
745 | if (!queued) { | 535 | if (!queued) { | ||
746 | qCWarning(LOG_KNOTIFICATIONS) << "Failed to queue dbus message for closing a notification"; | 536 | qCWarning(LOG_KNOTIFICATIONS) << "Failed to queue dbus message for closing a notification"; | ||
747 | } | 537 | } | ||
748 | } | 538 | } | ||
749 | 539 | | |||
750 | void NotifyByPopupPrivate::queryPopupServerCapabilities() | 540 | void NotifyByPopupPrivate::queryPopupServerCapabilities() | ||
751 | { | 541 | { | ||
752 | if (!dbusServiceExists) { | | |||
753 | // Return capabilities of the KPassivePopup implementation | | |||
754 | popupServerCapabilities = QStringList() << QStringLiteral("actions") << QStringLiteral("body") << QStringLiteral("body-hyperlinks") | | |||
755 | << QStringLiteral("body-markup") << QStringLiteral("icon-static"); | | |||
756 | } | | |||
757 | | ||||
758 | if (dbusServiceCapCacheDirty) { | 542 | if (dbusServiceCapCacheDirty) { | ||
759 | QDBusMessage m = QDBusMessage::createMethodCall(QString::fromLatin1(dbusServiceName), | 543 | QDBusMessage m = QDBusMessage::createMethodCall(QString::fromLatin1(dbusServiceName), | ||
760 | QString::fromLatin1(dbusPath), | 544 | QString::fromLatin1(dbusPath), | ||
761 | QString::fromLatin1(dbusInterfaceName), | 545 | QString::fromLatin1(dbusInterfaceName), | ||
762 | QStringLiteral("GetCapabilities")); | 546 | QStringLiteral("GetCapabilities")); | ||
763 | 547 | | |||
764 | QDBusConnection::sessionBus().callWithCallback(m, | 548 | QDBusConnection::sessionBus().callWithCallback(m, | ||
765 | q, | 549 | q, | ||
▲ Show 20 Lines • Show All 73 Lines • Show Last 20 Lines |