From kde-core-devel Tue Sep 22 12:19:57 2009 From: =?UTF-8?B?QXVyw6lsaWVuIEfDonRlYXU=?= Date: Tue, 22 Sep 2009 12:19:57 +0000 To: kde-core-devel Subject: [PATCH] Turn Powerdevil suspend notification into a dialog Message-Id: <4AB8C0ED.60402 () canonical ! com> X-MARC-Message: https://marc.info/?l=kde-core-devel&m=125362617220332 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--------------000606040905010607000207" This is a multi-part message in MIME format. --------------000606040905010607000207 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Hi, In case you missed it, an earlier discussion about notification capabilities on this list diverged a bit on Powerdevil suspend notification. I am referring to the notification which appears when your laptop is running out of battery. Powerdevil shows a notification to tell you it will suspend the machine in a few seconds, unless you click the "Cancel" button of the notification. I believe this is one of the few cases where using a dialog is more appropriate than using a notification because the system is about to do something very drastic, so it should ensure the user does not miss it. The dialog approach has a drawback though: it can steal the focus, resulting in the user accidentally triggering one of the dialog buttons if the dialog receive a keystroke intended for the previously active window. Attached first patch turns Powerdevil suspend notification into a dialog. To avoid the focus-stealing problem it opens the dialog as always-on-top and on all desktops, but keeps the previously active window active. I have been running it for a while without problems. It also adds a few niceties: - A button to trigger the action right now. - A real-time updated count down in the dialog text, so that the user knows in real time how much time is left. Here is a screenshot of the dialog: http://imagebin.ca/view/OT7MHYEn.html (Unfortunately updating the text of a KMessageBox is not possible at runtime, so I had to create a KDialog-based dialog. This could be fixed by giving an object name to the text label created by KMessageBox) The second patch increases the default timeout from 10 seconds to 30 seconds, giving the user more time to react. What do you think about this? Aurelien --------------000606040905010607000207 Content-Type: text/x-diff; name="0001-Turn-low-battery-notifications-into-a-countdown-dial.patch" Content-Transfer-Encoding: 8bit Content-Disposition: inline; filename*0="0001-Turn-low-battery-notifications-into-a-countdown-dial.pa"; filename*1="tch" From 64b0a6eb0eda9d209b18509d032204314b4c4dcd Mon Sep 17 00:00:00 2001 From: Aurelien Gateau Date: Tue, 22 Sep 2009 10:25:59 +0200 Subject: [PATCH 1/2] Turn low battery notifications into a countdown dialog --- workspace/powerdevil/daemon/CMakeLists.txt | 1 + workspace/powerdevil/daemon/CountDownDialog.cpp | 124 ++++++++++++++++++++++ workspace/powerdevil/daemon/CountDownDialog.h | 49 +++++++++ workspace/powerdevil/daemon/PowerDevilDaemon.cpp | 116 ++++++++++---------- workspace/powerdevil/daemon/PowerDevilDaemon.h | 4 + 5 files changed, 236 insertions(+), 58 deletions(-) create mode 100644 workspace/powerdevil/daemon/CountDownDialog.cpp create mode 100644 workspace/powerdevil/daemon/CountDownDialog.h diff --git a/workspace/powerdevil/daemon/CMakeLists.txt b/workspace/powerdevil/daemon/CMakeLists.txt index 970315e..7df020c 100644 --- a/workspace/powerdevil/daemon/CMakeLists.txt +++ b/workspace/powerdevil/daemon/CMakeLists.txt @@ -7,6 +7,7 @@ set( kded_powerdevil_SRCS PowerDevilDaemon.cpp SuspensionLockHandler.cpp PowerManagementConnector.cpp + CountDownDialog.cpp ) kde4_add_kcfg_files(kded_powerdevil_SRCS ../PowerDevilSettings.kcfgc) diff --git a/workspace/powerdevil/daemon/CountDownDialog.cpp b/workspace/powerdevil/daemon/CountDownDialog.cpp new file mode 100644 index 0000000..dd206e9 --- /dev/null +++ b/workspace/powerdevil/daemon/CountDownDialog.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (C) 2009 by Canonical Ltd * + * Author: Aurélien Gâteau * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ +#include "CountDownDialog.h" + +#include +#include + +#include +#include +#include + +CountDownDialog::CountDownDialog(PowerDevilDaemon::IdleAction action, int countDown) +: m_action(action) +, m_countDown(countDown) +{ + KIcon icon = KIcon("battery-low"); + setWindowIcon(icon); + + setCaption(i18n("Battery Level is Critical")); + + QLabel *iconLabel = new QLabel; + iconLabel->setPixmap(icon.pixmap(KIconLoader::SizeLarge, KIconLoader::SizeLarge)); + iconLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_label = new QLabel; + m_label->setWordWrap(true); + + QWidget *mainWidget = new QWidget; + QHBoxLayout *layout = new QHBoxLayout(mainWidget); + layout->setMargin(0); + layout->setSpacing(KDialog::spacingHint()); + layout->addWidget(iconLabel); + layout->addWidget(m_label); + setMainWidget(mainWidget); + + setButtons(KDialog::Cancel | KDialog::Ok); + + KGuiItem item; + switch (action) { + case PowerDevilDaemon::Shutdown: + item = KGuiItem(i18n("Shutdown"), "system-shutdown"); + break; + case PowerDevilDaemon::S2Disk: + item = KGuiItem(i18n("Suspend to Disk"), "system-suspend-hibernate"); + break; + case PowerDevilDaemon::S2Ram: + item = KGuiItem(i18n("Suspend to RAM"), "system-suspend"); + break; + case PowerDevilDaemon::Standby: + item = KGuiItem(i18n("Standby")); + break; + default: + Q_ASSERT(false); + break; + } + setButtonGuiItem(KDialog::Ok, item); + + updateText(); + + QTimer *timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, SIGNAL(timeout()), SLOT(decreaseCountDown())); + timer->start(); +} + +void CountDownDialog::decreaseCountDown() +{ + --m_countDown; + updateText(); + if (m_countDown == 0) { + button(KDialog::Ok)->click(); + } +} + +void CountDownDialog::updateText() +{ + QString text; + switch (m_action) { + case PowerDevilDaemon::Shutdown: + text = i18np("Your battery level is critical, the computer will be halted in 1 second.", + "Your battery level is critical, the computer will be halted in %1 seconds.", + m_countDown); + break; + case PowerDevilDaemon::S2Disk: + text = i18np("Your battery level is critical, the computer will be suspended to disk in 1 second.", + "Your battery level is critical, the computer will be suspended to disk in %1 seconds.", + m_countDown); + break; + case PowerDevilDaemon::S2Ram: + text = i18np("Your battery level is critical, the computer will be suspended to RAM in 1 second.", + "Your battery level is critical, the computer will be suspended to RAM in %1 seconds.", + m_countDown); + break; + case PowerDevilDaemon::Standby: + text = i18np("Your battery level is critical, the computer will be put into standby in 1 second.", + "Your battery level is critical, the computer will be put into standby in %1 seconds.", + m_countDown); + break; + default: + Q_ASSERT(false); + break; + } + m_label->setText(text); +} + +#include "CountDownDialog.moc" diff --git a/workspace/powerdevil/daemon/CountDownDialog.h b/workspace/powerdevil/daemon/CountDownDialog.h new file mode 100644 index 0000000..856bebf --- /dev/null +++ b/workspace/powerdevil/daemon/CountDownDialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2009 by Canonical Ltd * + * Author: Aurélien Gâteau * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ +#ifndef COUNTDOWNDIALOG_H +#define COUNTDOWNDIALOG_H + +#include + +#include "PowerDevilDaemon.h" + +class QLabel; + +/** + * A dialog showing a count down message before auto-accepting itself + */ +class CountDownDialog : public KDialog +{ +Q_OBJECT +public: + CountDownDialog(PowerDevilDaemon::IdleAction action, int countDown); + +private Q_SLOTS: + void decreaseCountDown(); + +private: + void updateText(); + + PowerDevilDaemon::IdleAction m_action; + QLabel *m_label; + int m_countDown; +}; + +#endif /* COUNTDOWNDIALOG_H */ diff --git a/workspace/powerdevil/daemon/PowerDevilDaemon.cpp b/workspace/powerdevil/daemon/PowerDevilDaemon.cpp index 11910dc..5daac70 100644 --- a/workspace/powerdevil/daemon/PowerDevilDaemon.cpp +++ b/workspace/powerdevil/daemon/PowerDevilDaemon.cpp @@ -36,11 +36,13 @@ #include #include #include +#include #include #include #include +#include "CountDownDialog.h" #include "PowerDevilSettings.h" #include "powerdeviladaptor.h" #include "PowerManagementConnector.h" @@ -478,64 +480,10 @@ void PowerDevilDaemon::batteryChargePercentChanged(int percent, const QString &u } if (charge <= PowerDevilSettings::batteryCriticalLevel()) { - switch (PowerDevilSettings::batLowAction()) { - case Shutdown: - if (PowerDevilSettings::waitBeforeSuspending()) { - emitNotification("criticalbattery", - i18np("Your battery level is critical, the computer will " - "be halted in 1 second.", - "Your battery level is critical, the computer will " - "be halted in %1 seconds.", - PowerDevilSettings::waitBeforeSuspendingTime()), - SLOT(shutdown()), "dialog-warning"); - } else { - shutdown(); - } - break; - case S2Disk: - if (PowerDevilSettings::waitBeforeSuspending()) { - emitNotification("criticalbattery", - i18np("Your battery level is critical, the computer will " - "be suspended to disk in 1 second.", - "Your battery level is critical, the computer will " - "be suspended to disk in %1 seconds.", - PowerDevilSettings::waitBeforeSuspendingTime()), - SLOT(suspendToDisk()), "dialog-warning"); - } else { - suspendToDisk(); - } - break; - case S2Ram: - if (PowerDevilSettings::waitBeforeSuspending()) { - emitNotification("criticalbattery", - i18np("Your battery level is critical, the computer " - "will be suspended to RAM in 1 second.", - "Your battery level is critical, the computer " - "will be suspended to RAM in %1 seconds.", - PowerDevilSettings::waitBeforeSuspendingTime()), - SLOT(suspendToRam()), "dialog-warning"); - } else { - suspendToRam(); - } - break; - case Standby: - if (PowerDevilSettings::waitBeforeSuspending()) { - emitNotification("criticalbattery", - i18np("Your battery level is critical, the computer " - "will be put into standby in 1 second.", - "Your battery level is critical, the computer " - "will be put into standby in %1 seconds.", - PowerDevilSettings::waitBeforeSuspendingTime()), - SLOT(standby()), "dialog-warning"); - } else { - standby(); - } - break; - default: - emitNotification("criticalbattery", i18n("Your battery level is critical: " - "save your work as soon as possible."), - 0, "dialog-warning"); - break; + if (PowerDevilSettings::waitBeforeSuspending()) { + showCountDownDialog(); + } else { + triggerBatLowAction(); } } else if (charge == PowerDevilSettings::batteryWarningLevel()) { emitNotification("warningbattery", i18n("Your battery has reached the warning level."), @@ -548,6 +496,58 @@ void PowerDevilDaemon::batteryChargePercentChanged(int percent, const QString &u } } +void PowerDevilDaemon::triggerBatLowAction() +{ + switch (PowerDevilSettings::batLowAction()) { + case Shutdown: + shutdown(); + break; + case S2Disk: + suspendToDisk(); + break; + case S2Ram: + suspendToRam(); + break; + case Standby: + standby(); + break; + default: + emitNotification("criticalbattery", i18n("Your battery level is critical: " + "save your work as soon as possible."), + 0, "dialog-warning"); + break; + } +} + +void PowerDevilDaemon::showCountDownDialog() +{ + PowerDevilDaemon::IdleAction action = static_cast(PowerDevilSettings::batLowAction()); + switch (action) { + case Shutdown: + case S2Disk: + case S2Ram: + case Standby: { + CountDownDialog *dlg = new CountDownDialog(action, PowerDevilSettings::waitBeforeSuspendingTime()); + connect(dlg, SIGNAL(accepted()), SLOT(triggerBatLowAction())); + dlg->setAttribute(Qt::WA_DeleteOnClose, true); + + // Show the dialog on top, but keep current active window activated + dlg->setWindowFlags(dlg->windowFlags() | Qt::WindowStaysOnTopHint); + int oldWid = KWindowSystem::activeWindow(); + dlg->show(); + KWindowSystem::setOnAllDesktops(dlg->winId(), true); + if (oldWid) { + KWindowSystem::forceActiveWindow(oldWid); + } + break; + } + default: + // Will show warning + triggerBatLowAction(); + break; + } +} + void PowerDevilDaemon::buttonPressed(int but) { if (!checkIfCurrentSessionActive() || d->screenSaverIface->GetActive()) { diff --git a/workspace/powerdevil/daemon/PowerDevilDaemon.h b/workspace/powerdevil/daemon/PowerDevilDaemon.h index bff900f..b0f60bb 100644 --- a/workspace/powerdevil/daemon/PowerDevilDaemon.h +++ b/workspace/powerdevil/daemon/PowerDevilDaemon.h @@ -100,6 +100,8 @@ private Q_SLOTS: void batteryRemainingTimeChanged(int time); + void triggerBatLowAction(); + Q_SIGNALS: void lidClosed(int code, const QString &action); void errorTriggered(const QString &error); @@ -128,6 +130,8 @@ private: bool checkIfCurrentSessionActive(); + void showCountDownDialog(); + public: enum IdleAction { None = 0, -- 1.6.3.3 --------------000606040905010607000207 Content-Type: text/x-diff; name="0002-Increase-WaitBeforeSuspendingTime-from-10-to-30-seco.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename*0="0002-Increase-WaitBeforeSuspendingTime-from-10-to-30-seco.pa"; filename*1="tch" From 0d89c6ac98973a48b2b14afa8ceba179e9ecb4b8 Mon Sep 17 00:00:00 2001 From: Aurelien Gateau Date: Tue, 22 Sep 2009 10:42:18 +0200 Subject: [PATCH 2/2] Increase WaitBeforeSuspendingTime from 10 to 30 seconds Gives more time for the user to handle the message. --- workspace/powerdevil/PowerDevilSettings.kcfg | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/workspace/powerdevil/PowerDevilSettings.kcfg b/workspace/powerdevil/PowerDevilSettings.kcfg index 816abdd..f6dfe19 100644 --- a/workspace/powerdevil/PowerDevilSettings.kcfg +++ b/workspace/powerdevil/PowerDevilSettings.kcfg @@ -32,7 +32,7 @@ true - 10 + 30 -1 -- 1.6.3.3 --------------000606040905010607000207--