File indexing completed on 2024-04-14 05:46:47

0001 /*
0002    Copyright (C) 2005-2006 Tom Albers <toma@kde.org>
0003    Copyright (C) 2011 Aurélien Gâteau <agateau@kde.org>
0004 
0005    Orginal copied from ksynaptics:
0006       Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
0007 
0008    This program is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This program is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016    General Public License for more details.
0017 
0018    You should have received a copy of the GNU General Public License
0019    along with this program; if not, write to the Free Software
0020    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "rsidock.h"
0024 #include "rsistats.h"
0025 #include "rsistatwidget.h"
0026 #include "setup.h"
0027 
0028 #include <QIcon>
0029 #include <QPointer>
0030 #include <QTextDocument>
0031 
0032 #include <KAboutData>
0033 #include <KConfigGroup>
0034 #include <KHelpMenu>
0035 #include <KLocalizedString>
0036 #include <KMessageBox>
0037 #include <KNotifyConfigWidget>
0038 #include <KStandardShortcut>
0039 #include <KWindowSystem>
0040 #include <QDebug>
0041 #include <QDialogButtonBox>
0042 #include <QMenu>
0043 #include <QPushButton>
0044 #include <QVBoxLayout>
0045 
0046 template<typename Func, typename Object>
0047 static QAction *doAddAction(QMenu *menu, const QString &text, Object *receiver, Func func)
0048 {
0049     QAction *action = menu->addAction(text);
0050     QObject::connect(action, &QAction::triggered, receiver, func);
0051     return action;
0052 }
0053 
0054 template<typename Func, typename Object>
0055 static QAction *doAddAction(QMenu *menu, const QIcon &icon, const QString &text, Object *receiver, Func func)
0056 {
0057     QAction *action = doAddAction(menu, text, receiver, func);
0058     action->setIcon(icon);
0059     return action;
0060 }
0061 
0062 RSIDock::RSIDock(QObject *parent)
0063     : KStatusNotifierItem(parent)
0064     , m_suspended(false)
0065     , m_statsDialog(nullptr)
0066     , m_statsWidget(nullptr)
0067 {
0068     setCategory(ApplicationStatus);
0069     setStatus(Active);
0070 
0071     const KAboutData &aboutData = KAboutData::applicationData();
0072     setTitle(aboutData.displayName());
0073     setToolTipTitle(aboutData.displayName());
0074 
0075     m_help = new KHelpMenu(nullptr, aboutData);
0076 
0077     QMenu *menu = contextMenu();
0078     doAddAction(menu, QIcon::fromTheme("kde"), i18n("About &KDE"), m_help, &KHelpMenu::aboutKDE);
0079     doAddAction(menu, i18n("&About RSIBreak"), m_help, &KHelpMenu::aboutApplication);
0080     doAddAction(menu, QIcon::fromTheme("help-contents"), i18n("RSIBreak &Handbook"), m_help, &KHelpMenu::appHelpActivated);
0081 
0082     menu->addSeparator();
0083     doAddAction(menu, QIcon::fromTheme("tools-report-bug"), i18n("&Report Bug..."), m_help, &KHelpMenu::reportBug);
0084     doAddAction(menu, i18n("Switch application &language..."), m_help, &KHelpMenu::switchApplicationLanguage);
0085 
0086     menu->addSeparator();
0087     m_suspendItem = doAddAction(menu, QIcon::fromTheme("media-playback-pause"), i18n("&Suspend RSIBreak"), this, &RSIDock::slotToggleSuspend);
0088     doAddAction(menu, QIcon::fromTheme("view-statistics"), i18n("&Usage Statistics"), this, &RSIDock::slotShowStatistics);
0089     doAddAction(menu, QIcon::fromTheme("preferences-desktop-notification"), i18n("Configure &Notifications..."), this, &RSIDock::slotConfigureNotifications);
0090     doAddAction(menu, QIcon::fromTheme("configure"), i18n("&Configure RSIBreak..."), this, &RSIDock::slotConfigure);
0091 
0092     connect(this, &RSIDock::activateRequested, this, &RSIDock::slotShowStatistics);
0093 }
0094 
0095 RSIDock::~RSIDock()
0096 {
0097     delete m_help;
0098     delete m_statsWidget;
0099     delete m_statsDialog;
0100     m_statsWidget = nullptr;
0101 }
0102 
0103 void RSIDock::doResume()
0104 {
0105     if (m_suspended)
0106         slotToggleSuspend();
0107 }
0108 
0109 void RSIDock::doSuspend()
0110 {
0111     if (!m_suspended)
0112         slotToggleSuspend();
0113 }
0114 
0115 void RSIDock::slotConfigureNotifications()
0116 {
0117     KNotifyConfigWidget::configure(nullptr);
0118 }
0119 
0120 void RSIDock::slotConfigure()
0121 {
0122     // don't think it is needed, because setup is not accessed after the
0123     // exec call, but better safe than crash.
0124     QPointer<Setup> setup = new Setup(nullptr);
0125     emit dialogEntered();
0126     if (setup->exec() == QDialog::Accepted)
0127         emit configChanged();
0128     delete setup;
0129 
0130     if (!m_suspended)
0131         emit dialogLeft();
0132 }
0133 
0134 void RSIDock::slotToggleSuspend()
0135 {
0136     if (m_suspended) {
0137         emit suspend(false);
0138 
0139         setIconByName("rsibreak0");
0140         m_suspendItem->setIcon(QIcon::fromTheme("media-playback-pause"));
0141         m_suspendItem->setText(i18n("&Suspend RSIBreak"));
0142     } else {
0143         emit suspend(true);
0144 
0145         setIconByName("rsibreakx");
0146         m_suspendItem->setIcon(QIcon::fromTheme("media-playback-start"));
0147         m_suspendItem->setText(i18n("&Resume RSIBreak"));
0148     }
0149 
0150     m_suspended = !m_suspended;
0151 }
0152 
0153 void RSIDock::slotShowStatistics()
0154 {
0155     if (!m_statsDialog) {
0156         m_statsDialog = new QDialog(nullptr);
0157         m_statsDialog->setWindowTitle(i18n("Usage Statistics"));
0158         QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
0159         QVBoxLayout *mainLayout = new QVBoxLayout;
0160         m_statsDialog->setLayout(mainLayout);
0161         QPushButton *user1Button = new QPushButton;
0162         buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole);
0163         connect(buttonBox, &QDialogButtonBox::accepted, m_statsDialog, &QDialog::accept);
0164         connect(buttonBox, &QDialogButtonBox::rejected, m_statsDialog, &QDialog::reject);
0165 
0166         user1Button->setText(i18n("Reset"));
0167 
0168         m_statsWidget = new RSIStatWidget(m_statsDialog);
0169         connect(user1Button, &QPushButton::clicked, this, &RSIDock::slotResetStats);
0170 
0171         mainLayout->addWidget(m_statsWidget);
0172         mainLayout->addWidget(buttonBox);
0173     }
0174 
0175     if (m_statsDialog->isVisible() && KWindowInfo(m_statsDialog->winId(), NET::WMDesktop).isOnCurrentDesktop()) {
0176         m_statsDialog->hide();
0177     } else {
0178         m_statsDialog->show();
0179 
0180         if (!m_statsDialog->isActiveWindow())
0181             KWindowSystem::forceActiveWindow(m_statsDialog->winId());
0182 
0183         m_statsDialog->raise();
0184     }
0185 }
0186 
0187 void RSIDock::slotResetStats()
0188 {
0189     int i = KMessageBox::warningContinueCancel(nullptr,
0190                                                i18n("This will reset all statistics to zero. "
0191                                                     "Is that what you want?"),
0192                                                i18n("Reset the statistics"),
0193                                                KGuiItem(i18n("Reset")),
0194                                                KStandardGuiItem::cancel(),
0195                                                "resetStatistics");
0196 
0197     if (i == KMessageBox::Continue)
0198         RSIGlobals::instance()->stats()->reset();
0199 }
0200 
0201 static QString colorizedText(const QString &text, const QColor &color)
0202 {
0203     return QString("<font color='%1'>&#9679;</font> %2").arg(color.name(), text.toHtmlEscaped());
0204 }
0205 
0206 void RSIDock::setCounters(int tiny_left, int big_left)
0207 {
0208     if (m_suspended)
0209         setToolTipSubTitle(i18n("Suspended"));
0210     else {
0211         bool tinyBreaks = RSIGlobals::instance()->useTinyBreaks();
0212 
0213         QColor tinyColor;
0214         if (tinyBreaks) {
0215             tinyColor = RSIGlobals::instance()->getTinyBreakColor(tiny_left);
0216             RSIGlobals::instance()->stats()->setColor(LAST_TINY_BREAK, tinyColor);
0217         }
0218 
0219         QColor bigColor = RSIGlobals::instance()->getBigBreakColor(big_left);
0220         RSIGlobals::instance()->stats()->setColor(LAST_BIG_BREAK, bigColor);
0221 
0222         // Only add the line for the tiny break when there is not
0223         // a big break planned at the same time.
0224 
0225         QStringList lines;
0226         if (tinyBreaks && tiny_left != big_left) {
0227             QString formattedText = RSIGlobals::instance()->formatSeconds(tiny_left);
0228             if (!formattedText.isNull()) {
0229                 lines << colorizedText(i18n("%1 remaining until next short break", formattedText), tinyColor);
0230             }
0231         }
0232 
0233         // do the same for the big break
0234         if (big_left > 0)
0235             lines << colorizedText(i18n("%1 remaining until next long break", RSIGlobals::instance()->formatSeconds(big_left)), bigColor);
0236         setToolTipSubTitle(lines.join("<br />"));
0237     }
0238 }