diff --git a/src/knotification.cpp b/src/knotification.cpp index 73bcb19..9c75d1c 100644 --- a/src/knotification.cpp +++ b/src/knotification.cpp @@ -1,516 +1,516 @@ /* This file is part of the KDE libraries Copyright (C) 2005-2006 Olivier Goffart Copyright (C) 2013-2014 Martin Klapetek code from KNotify/KNotifyClient Copyright (c) 1997 Christian Esken (esken@kde.org) 2000 Charles Samuels (charles@kde.org) 2000 Stefan Schimanski (1Stein@gmx.de) 2000 Matthias Ettrich (ettrich@kde.org) 2000 Waldo Bastian 2000-2003 Carsten Pfeiffer 2005 Allan Sandfeld Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "knotification.h" #include "knotificationmanager_p.h" #include #include #include #include #include #include #include #include #include #include #include -struct KNotification::Private { +struct Q_DECL_HIDDEN KNotification::Private { QString eventId; int id; int ref; QWidget *widget; QString title; QString text; QString iconName; QString defaultAction; QStringList actions; QPixmap pixmap; ContextList contexts; NotificationFlags flags; QString componentName; QList urls; QTimer updateTimer; bool needUpdate; Private() : id(-1), ref(0), widget(nullptr), needUpdate(false) {} /** * recursive function that raise the widget. @p w * * @see raiseWidget() */ static void raiseWidget(QWidget *w); }; KNotification::KNotification(const QString &eventId, QWidget *parent, const NotificationFlags &flags) : QObject(parent), d(new Private) { d->eventId = eventId; d->flags = flags; setWidget(parent); connect(&d->updateTimer, SIGNAL(timeout()), this, SLOT(update())); d->updateTimer.setSingleShot(true); d->updateTimer.setInterval(100); } KNotification::KNotification( const QString &eventId, const NotificationFlags &flags, QObject *parent) : QObject(parent), d(new Private) { d->eventId = eventId; d->flags = flags; connect(&d->updateTimer, SIGNAL(timeout()), this, SLOT(update())); d->updateTimer.setSingleShot(true); d->updateTimer.setInterval(100); d->widget = nullptr; } KNotification::~KNotification() { if (d->id >= 0) { KNotificationManager::self()->close(d->id); } delete d; } QString KNotification::eventId() const { return d->eventId; } QString KNotification::title() const { return d->title; } QString KNotification::text() const { return d->text; } QWidget *KNotification::widget() const { return d->widget; } void KNotification::setWidget(QWidget *wid) { d->widget = wid; // setParent(wid); if (wid && d->flags & CloseWhenWidgetActivated) { wid->installEventFilter(this); } } void KNotification::setTitle(const QString &title) { if (title == d->title) { return; } d->needUpdate = true; d->title = title; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setText(const QString &text) { if (text == d->text) { return; } d->needUpdate = true; d->text = text; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setIconName(const QString &icon) { if (icon == d->iconName) { return; } d->needUpdate = true; d->iconName = icon; if (d->id >= 0) { d->updateTimer.start(); } } QString KNotification::iconName() const { return d->iconName; } QPixmap KNotification::pixmap() const { return d->pixmap; } void KNotification::setPixmap(const QPixmap &pix) { d->needUpdate = true; d->pixmap = pix; if (d->id >= 0) { d->updateTimer.start(); } } QStringList KNotification::actions() const { return d->actions; } void KNotification::setActions(const QStringList &as) { if (as == d->actions) { return; } d->needUpdate = true; d->actions = as; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setDefaultAction(const QString &defaultAction) { if (defaultAction == d->defaultAction) { return; } d->needUpdate = true; d->defaultAction = defaultAction; if (d->id >= 0) { d->updateTimer.start(); } } QString KNotification::defaultAction() const { return d->defaultAction; } KNotification::ContextList KNotification::contexts() const { return d->contexts; } void KNotification::setContexts(const KNotification::ContextList &contexts) { d->contexts = contexts; } void KNotification::addContext(const KNotification::Context &context) { d->contexts << context; } void KNotification::addContext(const QString &context_key, const QString &context_value) { d->contexts << qMakePair(context_key, context_value); } KNotification::NotificationFlags KNotification::flags() const { return d->flags; } void KNotification::setFlags(const NotificationFlags &flags) { d->flags = flags; } void KNotification::setComponentName(const QString &c) { d->componentName = c; } QList KNotification::urls() const { return d->urls; } void KNotification::setUrls(const QList &urls) { d->urls = urls; } void KNotification::activate(unsigned int action) { switch (action) { case 0: emit activated(); break; case 1: emit action1Activated(); break; case 2: emit action2Activated(); break; case 3: emit action3Activated(); break; } // emitting activated() makes the Manager close all the active plugins // which will deref() the KNotification object, which will result // in closing the notification emit activated(action); } void KNotification::close() { if (d->id >= 0) { KNotificationManager::self()->close(d->id); } if (d->id == -1) { d->id = -2; emit closed(); deleteLater(); } } void KNotification::raiseWidget() { if (!d->widget) { return; } Private::raiseWidget(d->widget); } void KNotification::Private::raiseWidget(QWidget *w) { //TODO this function is far from finished. if (w->isTopLevel()) { w->raise(); KWindowSystem::activateWindow(w->winId()); #if defined(Q_OS_MACOS) w->activateWindow(); #endif } else { QWidget *pw = w->parentWidget(); raiseWidget(pw); if (QTabWidget *tab_widget = qobject_cast(pw)) { tab_widget->setCurrentIndex(tab_widget->indexOf(w)); } } } KNotification *KNotification::event(const QString &eventid, const QString &title, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { KNotification *notify = new KNotification(eventid, widget, flags); notify->setTitle(title); notify->setText(text); notify->setPixmap(pixmap); notify->setComponentName(flags & DefaultEvent ? QStringLiteral("plasma_workspace") : componentName); QTimer::singleShot(0, notify, SLOT(sendEvent())); return notify; } KNotification *KNotification::event(const QString &eventid, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { return event(eventid, QString(), text, pixmap, widget, flags, componentName); } KNotification *KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, pixmap, widget, flags | DefaultEvent); } KNotification *KNotification::event(StandardEvent eventid, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags) { return event(eventid, QString(), text, pixmap, widget, flags); } KNotification *KNotification::event(const QString &eventid, const QString &title, const QString &text, const QString &iconName, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { KNotification *notify = new KNotification(eventid, widget, flags); notify->setTitle(title); notify->setText(text); notify->setIconName(iconName); notify->setComponentName(flags & DefaultEvent ? QStringLiteral("plasma_workspace") : componentName); QTimer::singleShot(0, notify, SLOT(sendEvent())); return notify; } KNotification *KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QString &iconName, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, iconName, widget, flags | DefaultEvent); } KNotification* KNotification::event(StandardEvent eventid, const QString &title, const QString &text, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, standardEventToIconName(eventid), widget, flags | DefaultEvent); } void KNotification::ref() { d->ref++; } void KNotification::deref() { Q_ASSERT(d->ref > 0); d->ref--; if (d->ref == 0) { d->id = -1; close(); } } void KNotification::beep(const QString &reason, QWidget *widget) { event(QStringLiteral("beep"), reason, QPixmap(), widget, CloseOnTimeout | DefaultEvent); } void KNotification::sendEvent() { d->needUpdate = false; if (d->id == -1) { d->id = KNotificationManager::self()->notify(this); } else if (d->id >= 0) { KNotificationManager::self()->reemit(this); } } int KNotification::id() { if (!d) { return -1; } return d->id; } QString KNotification::appName() const { QString appname; if (d->flags & DefaultEvent) { appname = QStringLiteral("plasma_workspace"); } else if (!d->componentName.isEmpty()) { appname = d->componentName; } else { appname = QCoreApplication::applicationName(); } return appname; } void KNotification::update() { if (d->needUpdate) { KNotificationManager::self()->update(this); } } bool KNotification::eventFilter(QObject *watched, QEvent *event) { if (watched == d->widget) { if (event->type() == QEvent::WindowActivate) { if (d->flags & CloseWhenWidgetActivated) { QTimer::singleShot(500, this, SLOT(close())); } } } return false; } QString KNotification::standardEventToEventId(KNotification::StandardEvent event) { QString eventId; switch (event) { case Warning: eventId = QStringLiteral("warning"); break; case Error: eventId = QStringLiteral("fatalerror"); break; case Catastrophe: eventId = QStringLiteral("catastrophe"); break; case Notification: // fall through default: eventId = QStringLiteral("notification"); break; } return eventId; } QString KNotification::standardEventToIconName(KNotification::StandardEvent event) { QString iconName; switch (event) { case Warning: iconName = QStringLiteral("dialog-warning"); break; case Error: iconName = QStringLiteral("dialog-error"); break; case Catastrophe: iconName = QStringLiteral("dialog-error"); break; case Notification: // fall through default: iconName = QStringLiteral("dialog-information"); break; } return iconName; } diff --git a/src/knotificationmanager.cpp b/src/knotificationmanager.cpp index 7764ed4..cecd250 100644 --- a/src/knotificationmanager.cpp +++ b/src/knotificationmanager.cpp @@ -1,270 +1,270 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Olivier Goffart Copyright (C) 2013-2015 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "knotificationmanager_p.h" #include "knotification.h" #include #include #include #include #include #include #include #include #include "knotifyconfig.h" #include "knotificationplugin.h" #include "notifybypopup.h" #include "notifybylogfile.h" #include "notifybytaskbar.h" #include "notifybyexecute.h" #include "notifybyflatpak.h" #include "debug_p.h" #ifdef HAVE_PHONON4QT5 #include "notifybyaudio.h" #endif #ifdef HAVE_SPEECH #include "notifybytts.h" #endif typedef QHash Dict; -struct KNotificationManager::Private { +struct Q_DECL_HIDDEN KNotificationManager::Private { QHash notifications; QHash notifyPlugins; // incremental ids for notifications int notifyIdCounter; QStringList dirtyConfigCache; }; class KNotificationManagerSingleton { public: KNotificationManager instance; }; Q_GLOBAL_STATIC(KNotificationManagerSingleton, s_self) KNotificationManager *KNotificationManager::self() { return &s_self()->instance; } KNotificationManager::KNotificationManager() : d(new Private) { d->notifyIdCounter = 0; qDeleteAll(d->notifyPlugins); d->notifyPlugins.clear(); bool inSandbox = false; bool portalDBusServiceExists = false; if (!qEnvironmentVariableIsEmpty("XDG_RUNTIME_DIR")) { const QByteArray runtimeDir = qgetenv("XDG_RUNTIME_DIR"); if (!runtimeDir.isEmpty()) { inSandbox = QFileInfo::exists(QFile::encodeName(runtimeDir) + QLatin1String("/flatpak-info")); } } if (inSandbox) { QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); portalDBusServiceExists = interface->isServiceRegistered(QStringLiteral("org.freedesktop.portal.Desktop")); } // If we are running in sandbox and flatpak portal dbus service is available send popup notifications // through the portal if (inSandbox && portalDBusServiceExists) { addPlugin(new NotifyByFlatpak(this)); } else { addPlugin(new NotifyByPopup(this)); } addPlugin(new NotifyByExecute(this)); addPlugin(new NotifyByLogfile(this)); #ifdef HAVE_PHONON4QT5 addPlugin(new NotifyByAudio(this)); #endif addPlugin(new NotifyByTaskbar(this)); #ifdef HAVE_SPEECH addPlugin(new NotifyByTTS(this)); #endif QList plugins = KPluginLoader::instantiatePlugins(QStringLiteral("knotification/notifyplugins"), std::function(), this); Q_FOREACH (QObject *plugin, plugins) { KNotificationPlugin *notifyPlugin = qobject_cast(plugin); if (notifyPlugin) { addPlugin(notifyPlugin); } else { // not our/valid plugin, so delete the created object plugin->deleteLater(); } } QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/Config"), QStringLiteral("org.kde.knotification"), QStringLiteral("reparseConfiguration"), this, SLOT(reparseConfiguration(QString))); } KNotificationManager::~KNotificationManager() { delete d; } void KNotificationManager::addPlugin(KNotificationPlugin *notifyPlugin) { d->notifyPlugins[notifyPlugin->optionName()] = notifyPlugin; connect(notifyPlugin, SIGNAL(finished(KNotification*)), this, SLOT(notifyPluginFinished(KNotification*))); connect(notifyPlugin, SIGNAL(actionInvoked(int, int)), this, SLOT(notificationActivated(int, int))); } void KNotificationManager::notifyPluginFinished(KNotification *notification) { if (!notification || !d->notifications.contains(notification->id())) { return; } notification->deref(); } void KNotificationManager::notificationActivated(int id, int action) { if (d->notifications.contains(id)) { qCDebug(LOG_KNOTIFICATIONS) << id << " " << action; KNotification *n = d->notifications[id]; n->activate(action); close(id); } } void KNotificationManager::notificationClosed() { KNotification *notification = qobject_cast(sender()); if (!notification) { return; } // We cannot do d->notifications.find(notification->id()); here because the // notification->id() is -1 or -2 at this point, so we need to look for value for (auto iter = d->notifications.begin(); iter != d->notifications.end(); ++iter) { if (iter.value() == notification) { d->notifications.erase(iter); break; } } } void KNotificationManager::close(int id, bool force) { if (force || d->notifications.contains(id)) { KNotification *n = d->notifications.value(id); qCDebug(LOG_KNOTIFICATIONS) << "Closing notification" << id; // Find plugins that are actually acting on this notification // call close() only on those, otherwise each KNotificationPlugin::close() // will call finish() which may close-and-delete the KNotification object // before it finishes calling close on all the other plugins. // For example: Action=Popup is a single actions but there is 5 loaded // plugins, calling close() on the second would already close-and-delete // the notification KNotifyConfig notifyConfig(n->appName(), n->contexts(), n->eventId()); QString notifyActions = notifyConfig.readEntry(QStringLiteral("Action")); Q_FOREACH (const QString &action, notifyActions.split('|')) { if (!d->notifyPlugins.contains(action)) { qCDebug(LOG_KNOTIFICATIONS) << "No plugin for action" << action; continue; } d->notifyPlugins[action]->close(n); } } } int KNotificationManager::notify(KNotification *n) { KNotifyConfig notifyConfig(n->appName(), n->contexts(), n->eventId()); if (d->dirtyConfigCache.contains(n->appName())) { notifyConfig.reparseSingleConfiguration(n->appName()); d->dirtyConfigCache.removeOne(n->appName()); } QString notifyActions = notifyConfig.readEntry(QStringLiteral("Action")); if (notifyActions.isEmpty() || notifyActions == QLatin1String("None")) { // this will cause KNotification closing itself fast n->ref(); n->deref(); return -1; } d->notifications.insert(d->notifyIdCounter, n); Q_FOREACH (const QString &action, notifyActions.split('|')) { if (!d->notifyPlugins.contains(action)) { qCDebug(LOG_KNOTIFICATIONS) << "No plugin for action" << action; continue; } KNotificationPlugin *notifyPlugin = d->notifyPlugins[action]; n->ref(); qCDebug(LOG_KNOTIFICATIONS) << "Calling notify on" << notifyPlugin->optionName(); notifyPlugin->notify(n, ¬ifyConfig); } connect(n, &KNotification::closed, this, &KNotificationManager::notificationClosed); return d->notifyIdCounter++; } void KNotificationManager::update(KNotification *n) { KNotifyConfig notifyConfig(n->appName(), n->contexts(), n->eventId()); Q_FOREACH (KNotificationPlugin *p, d->notifyPlugins) { p->update(n, ¬ifyConfig); } } void KNotificationManager::reemit(KNotification *n) { notify(n); } void KNotificationManager::reparseConfiguration(const QString &app) { if (!d->dirtyConfigCache.contains(app)) { d->dirtyConfigCache << app; } } #include "moc_knotificationmanager_p.cpp" diff --git a/src/knotificationrestrictions.cpp b/src/knotificationrestrictions.cpp index e24ba34..2af8566 100644 --- a/src/knotificationrestrictions.cpp +++ b/src/knotificationrestrictions.cpp @@ -1,197 +1,197 @@ /* This file is part of the KDE libraries Copyright (C) 2006 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "knotificationrestrictions.h" #include #include #include #include #include #include "debug_p.h" #if HAVE_XTEST #include #include #include #include #endif // HAVE_XTEST -class KNotificationRestrictions::Private +class Q_DECL_HIDDEN KNotificationRestrictions::Private { public: Private(KNotificationRestrictions *qq, Services c, const QString &r) : q(qq), control(c) , screenSaverDbusCookie(-1) , reason(r) #if HAVE_XTEST , screensaverTimer(nullptr), haveXTest(0), XTestKeyCode(0), isX11(QX11Info::isPlatformX11()) #endif // HAVE_XTEST { } void screensaverFakeKeyEvent(); void startScreenSaverPrevention(); void stopScreenSaverPrevention(); static QString determineProgramName(); KNotificationRestrictions *q; Services control; int screenSaverDbusCookie; QString reason; #if HAVE_XTEST QTimer *screensaverTimer; int haveXTest; int XTestKeyCode; bool isX11; #endif // HAVE_XTEST }; KNotificationRestrictions::KNotificationRestrictions(Services control, QObject *parent) : KNotificationRestrictions(control, QStringLiteral("no_reason_specified"), parent) { } KNotificationRestrictions::KNotificationRestrictions(Services control, const QString &reason, QObject *parent) : QObject(parent), d(new Private(this, control, reason)) { if (d->control & ScreenSaver) { d->startScreenSaverPrevention(); } } KNotificationRestrictions::~KNotificationRestrictions() { if (d->control & ScreenSaver) { d->stopScreenSaverPrevention(); } delete d; } void KNotificationRestrictions::Private::screensaverFakeKeyEvent() { qCDebug(LOG_KNOTIFICATIONS); #if HAVE_XTEST if (!isX11) { return; } qCDebug(LOG_KNOTIFICATIONS) << "---- using XTestFakeKeyEvent"; Display *display = QX11Info::display(); XTestFakeKeyEvent(display, XTestKeyCode, true, CurrentTime); XTestFakeKeyEvent(display, XTestKeyCode, false, CurrentTime); XSync(display, false); #endif // HAVE_XTEST } void KNotificationRestrictions::Private::startScreenSaverPrevention() { qCDebug(LOG_KNOTIFICATIONS); QDBusMessage message = QDBusMessage::createMethodCall( QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("Inhibit")); message << determineProgramName(); message << reason; QDBusReply reply = QDBusConnection::sessionBus().call(message); if (reply.isValid()) { screenSaverDbusCookie = reply.value(); return; } #if HAVE_XTEST if (!isX11) { return; } if (!haveXTest) { int a, b, c, e; haveXTest = XTestQueryExtension(QX11Info::display(), &a, &b, &c, &e); if (!haveXTest) { qCDebug(LOG_KNOTIFICATIONS) << "--- No XTEST!"; return; } } if (!XTestKeyCode) { XTestKeyCode = XKeysymToKeycode(QX11Info::display(), XK_Shift_L); if (!XTestKeyCode) { qCDebug(LOG_KNOTIFICATIONS) << "--- No XKeyCode for XK_Shift_L!"; return; } } if (!screensaverTimer) { screensaverTimer = new QTimer(q); connect(screensaverTimer, SIGNAL(timeout()), q, SLOT(screensaverFakeKeyEvent())); } qCDebug(LOG_KNOTIFICATIONS) << "---- using XTest"; // send a fake event right away in case this got started after a period of // innactivity leading to the screensaver set to activate in <55s screensaverFakeKeyEvent(); screensaverTimer->start(55000); #endif // HAVE_XTEST } void KNotificationRestrictions::Private::stopScreenSaverPrevention() { if (screenSaverDbusCookie != -1) { QDBusMessage message = QDBusMessage::createMethodCall( QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("UnInhibit")); message << static_cast(screenSaverDbusCookie); screenSaverDbusCookie = -1; if (QDBusConnection::sessionBus().send(message)) { return; } } #if HAVE_XTEST if (!isX11) { return; } delete screensaverTimer; screensaverTimer = nullptr; #endif // HAVE_XTEST } QString KNotificationRestrictions::Private::determineProgramName() { QString appName = QGuiApplication::applicationDisplayName(); if (appName.isEmpty()) { appName = QCoreApplication::applicationName(); } if (appName.isEmpty()) { appName = tr("Unknown Application"); } return appName; } #include "moc_knotificationrestrictions.cpp" diff --git a/src/kpassivepopup.cpp b/src/kpassivepopup.cpp index 120caa3..e13d41f 100644 --- a/src/kpassivepopup.cpp +++ b/src/kpassivepopup.cpp @@ -1,640 +1,640 @@ /* * Copyright (C) 2001-2006 by Richard Moore * Copyright (C) 2004-2005 by Sascha Cunz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, */ #include "kpassivepopup.h" #include #include "debug_p.h" // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #include #endif #include static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed; static const int DEFAULT_POPUP_TIME = 6 * 1000; static const Qt::WindowFlags POPUP_FLAGS = Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint; -class KPassivePopup::Private +class Q_DECL_HIDDEN KPassivePopup::Private { public: Private(KPassivePopup *q, WId winId) : q(q), popupStyle(DEFAULT_POPUP_TYPE), window(winId), msgView(nullptr), topLayout(nullptr), hideDelay(DEFAULT_POPUP_TIME), hideTimer(new QTimer(q)), autoDelete(false) { #if HAVE_X11 if (QX11Info::isPlatformX11()) { q->setWindowFlags(POPUP_FLAGS | Qt::X11BypassWindowManagerHint); } else #else q->setWindowFlags(POPUP_FLAGS); #endif q->setFrameStyle(QFrame::Box | QFrame::Plain); q->setLineWidth(2); if (popupStyle == Boxed) { q->setFrameStyle(QFrame::Box | QFrame::Plain); q->setLineWidth(2); } else if (popupStyle == Balloon) { q->setPalette(QToolTip::palette()); } connect(hideTimer, SIGNAL(timeout()), q, SLOT(hide())); connect(q, SIGNAL(clicked()), q, SLOT(hide())); } KPassivePopup *q; int popupStyle; QPolygon surround; QPoint anchor; QPoint fixedPosition; WId window; QWidget *msgView; QBoxLayout *topLayout; int hideDelay; QTimer *hideTimer; QLabel *ttlIcon; QLabel *ttl; QLabel *msg; bool autoDelete; /** * Updates the transparency mask. Unused if PopupStyle == Boxed */ void updateMask() { // get screen-geometry for screen our anchor is on // (geometry can differ from screen to screen! QRect deskRect = desktopRectForPoint(anchor); const int width = q->width(); const int height = q->height(); int xh = 70, xl = 40; if (width < 80) { xh = xl = 40; } else if (width < 110) { xh = width - 40; } bool bottom = (anchor.y() + height) > ((deskRect.y() + deskRect.height() - 48)); bool right = (anchor.x() + width) > ((deskRect.x() + deskRect.width() - 48)); QPoint corners[4] = { QPoint(width - 50, 10), QPoint(10, 10), QPoint(10, height - 50), QPoint(width - 50, height - 50) }; QBitmap mask(width, height); mask.clear(); QPainter p(&mask); QBrush brush(Qt::color1, Qt::SolidPattern); p.setBrush(brush); int i = 0, z = 0; for (; i < 4; ++i) { QPainterPath path; path.moveTo(corners[i].x(), corners[i].y()); path.arcTo(corners[i].x(), corners[i].y(), 40, 40, i * 90, 90); QPolygon corner = path.toFillPolygon().toPolygon(); surround.resize(z + corner.count() - 1); for (int s = 1; s < corner.count() - 1; s++, z++) { surround.setPoint(z, corner[s]); } if (bottom && i == 2) { if (right) { surround.resize(z + 3); surround.setPoint(z++, QPoint(width - xh, height - 10)); surround.setPoint(z++, QPoint(width - 20, height)); surround.setPoint(z++, QPoint(width - xl, height - 10)); } else { surround.resize(z + 3); surround.setPoint(z++, QPoint(xl, height - 10)); surround.setPoint(z++, QPoint(20, height)); surround.setPoint(z++, QPoint(xh, height - 10)); } } else if (!bottom && i == 0) { if (right) { surround.resize(z + 3); surround.setPoint(z++, QPoint(width - xl, 10)); surround.setPoint(z++, QPoint(width - 20, 0)); surround.setPoint(z++, QPoint(width - xh, 10)); } else { surround.resize(z + 3); surround.setPoint(z++, QPoint(xh, 10)); surround.setPoint(z++, QPoint(20, 0)); surround.setPoint(z++, QPoint(xl, 10)); } } } surround.resize(z + 1); surround.setPoint(z, surround[0]); p.drawPolygon(surround); q->setMask(mask); q->move(right ? anchor.x() - width + 20 : (anchor.x() < 11 ? 11 : anchor.x() - 20), bottom ? anchor.y() - height : (anchor.y() < 11 ? 11 : anchor.y())); q->update(); } /** * Calculates the position to place the popup near the specified rectangle. */ QPoint calculateNearbyPoint(const QRect &target) { QPoint pos = target.topLeft(); int x = pos.x(); int y = pos.y(); int w = q->minimumSizeHint().width(); int h = q->minimumSizeHint().height(); QRect r = desktopRectForPoint(QPoint(x + w / 2, y + h / 2)); if (popupStyle == Balloon) { // find a point to anchor to if (x + w > r.width()) { x = x + target.width(); } if (y + h > r.height()) { y = y + target.height(); } } else { if (x < r.center().x()) { x = x + target.width(); } else { x = x - w; } // It's apparently trying to go off screen, so display it ALL at the bottom. if ((y + h) > r.bottom()) { y = r.bottom() - h; } if ((x + w) > r.right()) { x = r.right() - w; } } if (y < r.top()) { y = r.top(); } if (x < r.left()) { x = r.left(); } return QPoint(x, y); } QRect desktopRectForPoint(const QPoint &point) { QList screens = QGuiApplication::screens(); Q_FOREACH(const QScreen *screen, screens) { if (screen->geometry().contains(point)) { return screen->geometry(); } } // If no screen was found, return the primary screen's geometry return QGuiApplication::primaryScreen()->geometry(); } }; KPassivePopup::KPassivePopup(QWidget *parent, Qt::WindowFlags f) : QFrame(nullptr, f ? f : POPUP_FLAGS), d(new Private(this, parent ? parent->effectiveWinId() : 0L)) { } KPassivePopup::KPassivePopup(WId win) : QFrame(nullptr), d(new Private(this, win)) { } KPassivePopup::~KPassivePopup() { delete d; } void KPassivePopup::setPopupStyle(int popupstyle) { if (d->popupStyle == popupstyle) { return; } d->popupStyle = popupstyle; if (d->popupStyle == Boxed) { setFrameStyle(QFrame::Box | QFrame::Plain); setLineWidth(2); } else if (d->popupStyle == Balloon) { setPalette(QToolTip::palette()); } } void KPassivePopup::setView(QWidget *child) { delete d->msgView; d->msgView = child; delete d->topLayout; d->topLayout = new QVBoxLayout(this); if (d->popupStyle == Balloon) { const int marginHint = style()->pixelMetric(QStyle::PM_DefaultChildMargin); d->topLayout->setMargin(2 * marginHint); } d->topLayout->addWidget(d->msgView); d->topLayout->activate(); } void KPassivePopup::setView(const QString &caption, const QString &text, const QPixmap &icon) { // qCDebug(LOG_KNOTIFICATIONS) << "KPassivePopup::setView " << caption << ", " << text; setView(standardView(caption, text, icon, this)); } QWidget *KPassivePopup::standardView(const QString &caption, const QString &text, const QPixmap &icon, QWidget *parent) { QWidget *top = new QWidget(parent ? parent : this); QVBoxLayout *vb = new QVBoxLayout(top); vb->setMargin(0); top->setLayout(vb); QHBoxLayout *hb = nullptr; if (!icon.isNull()) { hb = new QHBoxLayout; hb->setMargin(0); vb->addLayout(hb); d->ttlIcon = new QLabel(top); d->ttlIcon->setPixmap(icon); d->ttlIcon->setAlignment(Qt::AlignLeft); hb->addWidget(d->ttlIcon); } if (!caption.isEmpty()) { d->ttl = new QLabel(caption, top); QFont fnt = d->ttl->font(); fnt.setBold(true); d->ttl->setFont(fnt); d->ttl->setAlignment(Qt::AlignHCenter); if (hb) { hb->addWidget(d->ttl); hb->setStretchFactor(d->ttl, 10); // enforce centering } else { vb->addWidget(d->ttl); } } if (!text.isEmpty()) { d->msg = new QLabel(text, top); d->msg->setAlignment(Qt::AlignLeft); d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse); d->msg->setOpenExternalLinks(true); vb->addWidget(d->msg); } return top; } void KPassivePopup::setView(const QString &caption, const QString &text) { setView(caption, text, QPixmap()); } QWidget *KPassivePopup::view() const { return d->msgView; } int KPassivePopup::timeout() const { return d->hideDelay; } void KPassivePopup::setTimeout(int delay) { d->hideDelay = delay < 0 ? DEFAULT_POPUP_TIME : delay; if (d->hideTimer->isActive()) { if (delay) { d->hideTimer->start(delay); } else { d->hideTimer->stop(); } } } bool KPassivePopup::autoDelete() const { return d->autoDelete; } void KPassivePopup::setAutoDelete(bool autoDelete) { d->autoDelete = autoDelete; } void KPassivePopup::mouseReleaseEvent(QMouseEvent *e) { emit clicked(); emit clicked(e->pos()); } // // Main Implementation // void KPassivePopup::setVisible(bool visible) { if (! visible) { QFrame::setVisible(visible); return; } if (size() != sizeHint()) { resize(sizeHint()); } if (d->fixedPosition.isNull()) { positionSelf(); } else { if (d->popupStyle == Balloon) { setAnchor(d->fixedPosition); } else { move(d->fixedPosition); } } QFrame::setVisible(/*visible=*/ true); int delay = d->hideDelay; if (delay < 0) { delay = DEFAULT_POPUP_TIME; } if (delay > 0) { d->hideTimer->start(delay); } } void KPassivePopup::show(const QPoint &p) { d->fixedPosition = p; show(); } void KPassivePopup::hideEvent(QHideEvent *) { d->hideTimer->stop(); if (d->autoDelete) { deleteLater(); } } QPoint KPassivePopup::defaultLocation() const { const QRect r = QGuiApplication::primaryScreen()->availableGeometry(); return QPoint(r.left(), r.top()); } void KPassivePopup::positionSelf() { QRect target; if (d->window) { #if HAVE_X11 if (QX11Info::isPlatformX11()) { NETWinInfo ni(QX11Info::connection(), d->window, QX11Info::appRootWindow(), NET::WMIconGeometry | NET::WMState, NET::Properties2()); // Try to put the popup by the taskbar entry if (!(ni.state() & NET::SkipTaskbar)) { NETRect r = ni.iconGeometry(); target.setRect(r.pos.x, r.pos.y, r.size.width, r.size.height); } } #endif // If that failed, put it by the window itself if (target.isNull()) { // Avoid making calls to the window system if we can QWidget *widget = QWidget::find(d->window); if (widget) { target = widget->geometry(); } } if (target.isNull()) { KWindowInfo info(d->window, NET::WMGeometry); if (info.valid()) { target = info.geometry(); } } } if (target.isNull()) { target = QRect(defaultLocation(), QSize(0, 0)); } moveNear(target); } void KPassivePopup::moveNear(const QRect &target) { QPoint pos = d->calculateNearbyPoint(target); if (d->popupStyle == Balloon) { setAnchor(pos); } else { move(pos.x(), pos.y()); } } QPoint KPassivePopup::anchor() const { return d->anchor; } void KPassivePopup::setAnchor(const QPoint &anchor) { d->anchor = anchor; d->updateMask(); } void KPassivePopup::paintEvent(QPaintEvent *pe) { if (d->popupStyle == Balloon) { QPainter p; p.begin(this); p.drawPolygon(d->surround); } else { QFrame::paintEvent(pe); } } // // Convenience Methods // KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text, const QPixmap &icon, QWidget *parent, int timeout, const QPoint &p) { return message(DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout, p); } KPassivePopup *KPassivePopup::message(const QString &text, QWidget *parent, const QPoint &p) { return message(DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent, -1, p); } KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text, QWidget *parent, const QPoint &p) { return message(DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent, -1, p); } KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text, const QPixmap &icon, WId parent, int timeout, const QPoint &p) { return message(DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout, p); } KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text, const QPixmap &icon, QSystemTrayIcon *parent, int timeout) { return message(DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout); } KPassivePopup *KPassivePopup::message(const QString &text, QSystemTrayIcon *parent) { return message(DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent); } KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text, QSystemTrayIcon *parent) { return message(DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent); } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text, const QPixmap &icon, QWidget *parent, int timeout, const QPoint &p) { KPassivePopup *pop = new KPassivePopup(parent); pop->setPopupStyle(popupStyle); pop->setAutoDelete(true); pop->setView(caption, text, icon); pop->d->hideDelay = timeout < 0 ? DEFAULT_POPUP_TIME : timeout; if (p.isNull()) { pop->show(); } else { pop->show(p); } return pop; } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &text, QWidget *parent, const QPoint &p) { return message(popupStyle, QString(), text, QPixmap(), parent, -1, p); } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text, QWidget *parent, const QPoint &p) { return message(popupStyle, caption, text, QPixmap(), parent, -1, p); } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text, const QPixmap &icon, WId parent, int timeout, const QPoint &p) { KPassivePopup *pop = new KPassivePopup(parent); pop->setPopupStyle(popupStyle); pop->setAutoDelete(true); pop->setView(caption, text, icon); pop->d->hideDelay = timeout < 0 ? DEFAULT_POPUP_TIME : timeout; if (p.isNull()) { pop->show(); } else { pop->show(p); } return pop; } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text, const QPixmap &icon, QSystemTrayIcon *parent, int timeout) { KPassivePopup *pop = new KPassivePopup(); pop->setPopupStyle(popupStyle); pop->setAutoDelete(true); pop->setView(caption, text, icon); pop->d->hideDelay = timeout < 0 ? DEFAULT_POPUP_TIME : timeout; QPoint pos = pop->d->calculateNearbyPoint(parent->geometry()); pop->show(pos); pop->moveNear(parent->geometry()); return pop; } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &text, QSystemTrayIcon *parent) { return message(popupStyle, QString(), text, QPixmap(), parent); } KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text, QSystemTrayIcon *parent) { return message(popupStyle, caption, text, QPixmap(), parent); } // Local Variables: // End: