You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
6.6 KiB
162 lines
6.6 KiB
/* |
|
Copyright (C) 2016 Kai Uwe Broulik <kde@privat.broulik.de> |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program 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 General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include "statusnotifier.h" |
|
|
|
#include <QAction> |
|
#include <QDBusConnectionInterface> |
|
#include <QMenu> |
|
#include <QTimer> |
|
|
|
#include <KIdleTime> |
|
#include <KLocalizedString> |
|
#include <KNotification> |
|
#include <KService> |
|
#include <KStatusNotifierItem> |
|
|
|
#include "drkonqi.h" |
|
#include "crashedapplication.h" |
|
|
|
StatusNotifier::StatusNotifier(QObject *parent) |
|
: QObject(parent) |
|
, m_autoCloseTimer(new QTimer(this)) |
|
, m_sni(new KStatusNotifierItem(this)) |
|
{ |
|
CrashedApplication *crashedApp = DrKonqi::crashedApplication(); |
|
|
|
// this is used for both the SNI tooltip as well as the notification |
|
m_title = i18nc("Placeholder is an application name; it crashed", "%1 Closed Unexpectedly", crashedApp->name()); |
|
|
|
// if nobody bothered to look at the crash after 1 minute, just close |
|
m_autoCloseTimer->setSingleShot(true); |
|
m_autoCloseTimer->setInterval(60000); |
|
m_autoCloseTimer->start(); |
|
connect(m_autoCloseTimer, &QTimer::timeout, this, &StatusNotifier::expired); |
|
connect(this, &StatusNotifier::activated, this, [this] { |
|
deleteLater(); |
|
}); |
|
|
|
KService::Ptr service = KService::serviceByStorageId(crashedApp->fakeExecutableBaseName()); |
|
if (service) { |
|
m_iconName = service->icon(); |
|
} |
|
|
|
m_sni->setTitle(m_title); |
|
m_sni->setIconByName(QStringLiteral("tools-report-bug")); |
|
m_sni->setStatus(KStatusNotifierItem::Active); |
|
m_sni->setCategory(KStatusNotifierItem::SystemServices); |
|
m_sni->setToolTip(!m_iconName.isEmpty() ? m_iconName : m_sni->iconName(), m_sni->title(), i18n("Please report this error to help improve this software.")); |
|
connect(m_sni, &KStatusNotifierItem::activateRequested, this, &StatusNotifier::activated); |
|
|
|
// you cannot turn off that "Do you really want to quit?" message, so we'll add our own below |
|
m_sni->setStandardActionsEnabled(false); |
|
|
|
QMenu *sniMenu = new QMenu(); |
|
QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("tools-report-bug")), |
|
i18n("Report &Bug"), nullptr); |
|
connect(action, &QAction::triggered, this, &StatusNotifier::activated); |
|
sniMenu->addAction(action); |
|
sniMenu->setDefaultAction(action); |
|
|
|
if (canBeRestarted(crashedApp)) { |
|
action = new QAction(QIcon::fromTheme(QStringLiteral("system-reboot")), |
|
i18n("&Restart Application"), nullptr); |
|
connect(action, &QAction::triggered, this, [crashedApp] { |
|
crashedApp->restart(); |
|
}); |
|
// once restarted successfully, disable restart option |
|
connect(crashedApp, &CrashedApplication::restarted, action, [action](bool success) { |
|
action->setEnabled(!success); |
|
}); |
|
sniMenu->addAction(action); |
|
} |
|
|
|
sniMenu->addSeparator(); |
|
|
|
action = new QAction(QIcon::fromTheme(QStringLiteral("application-exit")), |
|
i18nc("Allows the user to hide this notifier item", "Hide"), nullptr); |
|
connect(action, &QAction::triggered, this, &StatusNotifier::expired); |
|
sniMenu->addAction(action); |
|
|
|
m_sni->setContextMenu(sniMenu); |
|
|
|
// make sure the user doesn't miss the SNI by stopping the auto hide timer when the session becomes idle |
|
int idleId = KIdleTime::instance()->addIdleTimeout(30000); |
|
connect(KIdleTime::instance(), static_cast<void (KIdleTime::*)(int)>(&KIdleTime::timeoutReached), this, [this, idleId](int id) { |
|
if (idleId == id) { |
|
m_autoCloseTimer->stop(); |
|
} |
|
// this is apparently needed or else resumingFromIdle is never called |
|
KIdleTime::instance()->catchNextResumeEvent(); |
|
}); |
|
connect(KIdleTime::instance(), &KIdleTime::resumingFromIdle, this, [this] { |
|
if (!m_autoCloseTimer->isActive()) { |
|
m_autoCloseTimer->start(); |
|
} |
|
}); |
|
} |
|
|
|
StatusNotifier::~StatusNotifier() = default; |
|
|
|
void StatusNotifier::notify() |
|
{ |
|
CrashedApplication *crashedApp = DrKonqi::crashedApplication(); |
|
|
|
KNotification *notification = KNotification::event(QStringLiteral("applicationcrash"), |
|
m_title, |
|
i18n("Please report this error to help improve this software."), |
|
!m_iconName.isEmpty() ? m_iconName : QStringLiteral("tools-report-bug"), |
|
nullptr, |
|
KNotification::DefaultEvent | KNotification::SkipGrouping); |
|
|
|
QStringList actions = {i18nc("Notification action button, keep short", "Report Bug")}; |
|
|
|
bool restartEnabled = canBeRestarted(crashedApp); |
|
|
|
if (restartEnabled) { |
|
actions << i18nc("Notification action button, keep short", "Restart App"); |
|
} |
|
|
|
notification->setActions(actions); |
|
|
|
connect(notification, static_cast<void (KNotification::*)(unsigned int)>(&KNotification::activated), |
|
this, [this, crashedApp](int actionIndex) { |
|
// 0 = default action (NOTE this is not implemented by Plasma, clicking notification popup just closes it) |
|
// 1 = "Report Bug" button |
|
// 2 = "Restart App" button |
|
if (actionIndex == 0 || actionIndex == 1) { |
|
emit activated(); |
|
} else if (actionIndex == 2) { |
|
crashedApp->restart(); |
|
// keep status notifier there to allow reporting a bug when user chose to restart app |
|
} |
|
}); |
|
|
|
// when the SNI disappears you won't be able to interact with the notification anymore anyway, so close it |
|
connect(this, &StatusNotifier::activated, notification, &KNotification::close); |
|
connect(this, &StatusNotifier::expired, notification, &KNotification::close); |
|
} |
|
|
|
bool StatusNotifier::notificationServiceRegistered() |
|
{ |
|
return QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.Notifications")); |
|
} |
|
|
|
bool StatusNotifier::canBeRestarted(CrashedApplication *app) |
|
{ |
|
return !app->hasBeenRestarted() && app->fakeExecutableBaseName() != QLatin1String("drkonqi"); |
|
}
|
|
|