Warning, file /plasma/drkonqi/src/statusnotifier.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "statusnotifier.h" 0008 0009 #include <QAction> 0010 #include <QDBusConnectionInterface> 0011 #include <QDBusServiceWatcher> 0012 #include <QMenu> 0013 0014 #include <KLocalizedString> 0015 #include <KNotification> 0016 #include <KStatusNotifierItem> 0017 0018 #include "crashedapplication.h" 0019 #include "drkonqi.h" 0020 #include "statusnotifier_activationclosetimer.h" 0021 0022 StatusNotifier::StatusNotifier(QObject *parent) 0023 : QObject(parent) 0024 , m_sni(new KStatusNotifierItem(this)) 0025 { 0026 CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0027 0028 // this is used for both the SNI tooltip as well as the notification 0029 m_title = i18nc("Placeholder is an application name; it crashed", "%1 Closed Unexpectedly", crashedApp->name()); 0030 } 0031 0032 StatusNotifier::~StatusNotifier() = default; 0033 0034 bool StatusNotifier::activationAllowed() const 0035 { 0036 return m_activationAllowed; 0037 } 0038 0039 void StatusNotifier::setActivationAllowed(bool allowed) 0040 { 0041 m_activationAllowed = allowed; 0042 } 0043 0044 void StatusNotifier::show() 0045 { 0046 CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0047 0048 connect(this, &StatusNotifier::activated, this, &StatusNotifier::deleteLater); 0049 0050 m_sni->setTitle(m_title); 0051 m_sni->setIconByName(QStringLiteral("tools-report-bug")); 0052 m_sni->setStatus(KStatusNotifierItem::Active); 0053 m_sni->setCategory(KStatusNotifierItem::SystemServices); 0054 m_sni->setToolTip(m_sni->iconName(), m_sni->title(), i18n("Please report this error to help improve this software.")); 0055 connect(m_sni, &KStatusNotifierItem::activateRequested, this, &StatusNotifier::activated); 0056 0057 // you cannot turn off that "Do you really want to quit?" message, so we'll add our own below 0058 m_sni->setStandardActionsEnabled(false); 0059 0060 auto *sniMenu = new QMenu(); 0061 auto *action = new QAction(QIcon::fromTheme(QStringLiteral("tools-report-bug")), i18n("Report &Bug"), nullptr); 0062 connect(action, &QAction::triggered, this, &StatusNotifier::activated); 0063 sniMenu->addAction(action); 0064 sniMenu->setDefaultAction(action); 0065 0066 if (canBeRestarted(crashedApp)) { 0067 action = new QAction(QIcon::fromTheme(QStringLiteral("system-reboot")), i18n("&Restart Application"), nullptr); 0068 connect(action, &QAction::triggered, crashedApp, &CrashedApplication::restart); 0069 // once restarted successfully, disable restart option 0070 connect(crashedApp, &CrashedApplication::restarted, action, [action](bool success) { 0071 action->setEnabled(!success); 0072 }); 0073 sniMenu->addAction(action); 0074 } 0075 0076 sniMenu->addSeparator(); 0077 0078 action = new QAction(QIcon::fromTheme(QStringLiteral("application-exit")), i18nc("Allows the user to hide this notifier item", "Hide"), nullptr); 0079 connect(action, &QAction::triggered, this, &StatusNotifier::expired); 0080 sniMenu->addAction(action); 0081 0082 m_sni->setContextMenu(sniMenu); 0083 0084 // Depending on the environmental state we may auto-activate or auto-close the SNI. 0085 auto timer = new ActivationCloseTimer(this); 0086 connect(timer, &ActivationCloseTimer::autoActivate, this, &StatusNotifier::activated); 0087 connect(timer, &ActivationCloseTimer::autoClose, this, &StatusNotifier::expired); 0088 auto dbusWatcher = new DBusServiceWatcher(timer); 0089 auto idleWatcher = new IdleWatcher(timer); 0090 timer->start(dbusWatcher, idleWatcher); 0091 } 0092 0093 void StatusNotifier::notify() 0094 { 0095 CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0096 0097 const QString title = m_activationAllowed ? m_title : crashedApp->name(); 0098 const QString message = m_activationAllowed ? i18nc("Notification text", "Please report this error to help improve this software.") 0099 : i18nc("Notification text", "The application closed unexpectedly."); 0100 0101 KNotification *notification = KNotification::event(QStringLiteral("applicationcrash"), 0102 title, 0103 message, 0104 QStringLiteral("tools-report-bug"), 0105 nullptr, 0106 KNotification::DefaultEvent | KNotification::SkipGrouping); 0107 0108 QStringList actions; 0109 if (m_activationAllowed) { 0110 actions << i18nc("Notification action button, keep short", "Report Bug"); 0111 } 0112 if (canBeRestarted(crashedApp)) { 0113 actions << i18nc("Notification action button, keep short", "Restart App"); 0114 } 0115 0116 notification->setActions(actions); 0117 0118 connect(notification, static_cast<void (KNotification::*)(unsigned int)>(&KNotification::activated), this, [this, crashedApp](int actionIndex) { 0119 if (actionIndex == 1 && m_activationAllowed) { 0120 Q_EMIT activated(); 0121 } else if (canBeRestarted(crashedApp)) { 0122 crashedApp->restart(); 0123 } 0124 }); 0125 0126 // when the SNI disappears you won't be able to interact with the notification anymore anyway, so close it 0127 if (m_activationAllowed) { 0128 connect(this, &StatusNotifier::activated, notification, &KNotification::close); 0129 connect(this, &StatusNotifier::expired, notification, &KNotification::close); 0130 } else { 0131 // No SNI means we should quit when the notification is gone 0132 connect(notification, &KNotification::closed, this, &StatusNotifier::expired); 0133 } 0134 } 0135 0136 bool StatusNotifier::notificationServiceRegistered() 0137 { 0138 return QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.Notifications")); 0139 } 0140 0141 bool StatusNotifier::canBeRestarted(CrashedApplication *app) 0142 { 0143 return !app->hasBeenRestarted() && app->fakeExecutableBaseName() != QLatin1String("drkonqi"); 0144 }