File indexing completed on 2024-04-28 05:52:08

0001 /*
0002     SPDX-FileCopyrightText: 1998-1999 Matthias Hölzer-Klüpfel <matthias@hoelzer-kluepfel.de>
0003     SPDX-FileCopyrightText: 2002-2003 Martin Willers <willers@xm-arts.de>
0004     SPDX-FileCopyrightText: 2007-2009 Stefan Böhmann <kde@hilefoks.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 #include "toplevel.h"
0009 #include "timeedit.h"
0010 #include "settings.h"
0011 
0012 #include <QAction>
0013 #include <QActionGroup>
0014 #include <QApplication>
0015 #include <QBrush>
0016 #include <QMenu>
0017 #include <QString>
0018 #include <QTimer>
0019 #include <QPainter>
0020 
0021 #include <KAboutData>
0022 #include <KActionCollection>
0023 #include <KConfigGroup>
0024 #include <KHelpMenu>
0025 #include <KIconLoader>
0026 #include <KNotification>
0027 #include <KNotifyConfigWidget>
0028 #include <KSharedConfig>
0029 #include <KIconUtils>
0030 
0031 TopLevel::TopLevel(const KAboutData *aboutData, const QString &trayIcon, const QString &notificationIcon, QWidget *parent)
0032   : QSystemTrayIcon( parent ),
0033     m_popup(),
0034     m_trayIconName(trayIcon),
0035     m_notificationIconName(notificationIcon),
0036     m_runningTeaTime( 0 ),
0037     m_pausedRemainingTeaTime( 0 ),
0038     m_nextNotificationTime( 0 )
0039 {
0040     repaintTrayIcon(); // Set initial icon
0041     KSharedConfigPtr config = KSharedConfig::openConfig();
0042     KConfigGroup tealistGroup( config, QStringLiteral("Tealist")) ;
0043 
0044     if( tealistGroup.exists() ) {
0045         for(unsigned index=0;; ++index) {
0046             const QString time = QString::asprintf("Tea%d Time", index);
0047             const QString key = QString::asprintf("Tea%d Name", index);
0048             if (tealistGroup.hasKey(time) && tealistGroup.hasKey(key)) {
0049                 if( int temp = ( tealistGroup.readEntry( time, QString() ) ).toUInt() ) {
0050                     m_tealist.append( Tea( tealistGroup.readEntry( key, i18n( "Unknown Tea" ) ), temp ) );
0051                 }
0052             } else {
0053                 break;
0054             }
0055         }
0056     }
0057 
0058     // If the list of teas is empty insert a set of default teas.
0059     if( m_tealist.isEmpty() ) {
0060         m_tealist.append( Tea(i18n( "Black Tea" ), 180 ) );
0061         m_tealist.append( Tea(i18n( "Earl Grey" ), 300 ) );
0062         m_tealist.append( Tea(i18n( "Fruit Tea" ), 480 ) );
0063         m_tealist.append( Tea(i18n( "Green Tea" ), 120 ) );
0064     }
0065 
0066 
0067     m_teaActionGroup=new QActionGroup( this );
0068     m_actionCollection = new KActionCollection(this);
0069 
0070     // build the context menu
0071     action = m_actionCollection->addAction( QStringLiteral( "resume" ));
0072     action->setIcon(QIcon::fromTheme( QStringLiteral( "media-playback-start" ) ) );
0073     action->setText( i18n( "&Resume" ) );
0074     action->setEnabled( false );
0075     connect(action, &QAction::triggered, this, &TopLevel::resumeTea);
0076 
0077     action = m_actionCollection->addAction( QStringLiteral( "pause" ));
0078     action->setIcon(QIcon::fromTheme( QStringLiteral( "media-playback-pause" ) ) );
0079     action->setText( i18n( "&Pause" ) );
0080     action->setEnabled( false );
0081     connect(action, &QAction::triggered, this, &TopLevel::stopTea);
0082 
0083     action = m_actionCollection->addAction( QStringLiteral( "stop" ));
0084     action->setIcon(QIcon::fromTheme( QStringLiteral( "edit-delete" ) ) );
0085     action->setText( i18n( "&Stop" ) );
0086     action->setEnabled( false );
0087     connect(action, &QAction::triggered, this, &TopLevel::cancelTea);
0088 
0089     action = m_actionCollection->addAction( QStringLiteral( "configure" ));
0090     action->setIcon(QIcon::fromTheme( QStringLiteral( "configure" ) ) );
0091     action->setText(i18n( "&Configure..." ) );
0092     connect(action, &QAction::triggered, this, &TopLevel::showSettingsDialog);
0093 
0094     action = KStandardAction::configureNotifications(this, &TopLevel::configureNotifications, m_actionCollection);
0095     action = KStandardAction::quit(qApp, &QCoreApplication::quit, m_actionCollection);
0096     action->setShortcut(0);
0097 
0098     action = m_actionCollection->addAction( QStringLiteral( "custom" ));
0099     action->setText(i18n( "&Custom..." ) );
0100     connect(action, &QAction::triggered, this, &TopLevel::showTimeEditDialog);
0101 
0102     m_helpMenu = new KHelpMenu( nullptr, *aboutData, false );
0103 
0104     setContextMenu(new QMenu);
0105 
0106     loadTeaMenuItems();
0107     contextMenu()->addSeparator();
0108     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("resume")) );
0109     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("pause")) );
0110     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("stop")) );
0111     contextMenu()->addSeparator();
0112     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("custom")) );
0113     contextMenu()->addSeparator();
0114     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("configure")) );
0115     contextMenu()->addAction( m_actionCollection->action(KStandardAction::name(KStandardAction::ConfigureNotifications)) );
0116     contextMenu()->addMenu( m_helpMenu->menu() );
0117     contextMenu()->addSeparator();
0118     contextMenu()->addAction( m_actionCollection->action(KStandardAction::name(KStandardAction::Quit)) );
0119 
0120     m_timer = new QTimer( this );
0121 
0122     connect(m_timer, &QTimer::timeout, this, &TopLevel::teaTimeEvent);
0123     connect(contextMenu(), &QMenu::triggered, this, &TopLevel::slotRunTea);
0124     connect(this, &TopLevel::activated, this, &TopLevel::showPopup);
0125 
0126     loadConfig();
0127     checkState();
0128     show();
0129 }
0130 
0131 
0132 TopLevel::~TopLevel()
0133 {
0134     delete m_helpMenu;
0135     delete m_timer;
0136     delete m_teaActionGroup;
0137 }
0138 
0139 
0140 void TopLevel::checkState() {
0141     const bool state = m_runningTeaTime != 0;
0142 
0143     m_teaActionGroup->setEnabled( !state );
0144     m_actionCollection->action(QStringLiteral("stop"))->setEnabled( state );
0145     m_actionCollection->action(QStringLiteral("custom"))->setEnabled( !state );
0146 
0147     m_actionCollection->action(QStringLiteral("resume"))->setEnabled( false );
0148     m_actionCollection->action(QStringLiteral("pause"))->setEnabled( state && m_runningTeaTime > 0 );
0149 
0150     if (m_pausedRemainingTeaTime > 0 && !state) {
0151         m_actionCollection->action(QStringLiteral("resume"))->setEnabled( true );
0152         m_actionCollection->action(QStringLiteral("stop"))->setEnabled( true );
0153     }
0154 
0155     if( !state && m_pausedRemainingTeaTime == 0 ) {
0156         setTooltipText( i18n( "No steeping tea." ) );
0157     }
0158 }
0159 
0160 
0161 void TopLevel::setTeaList(const QList<Tea> &tealist) {
0162     m_tealist=tealist;
0163 
0164     // Save list...
0165     KSharedConfigPtr config = KSharedConfig::openConfig();
0166     KConfigGroup tealistGroup( config, QStringLiteral("Tealist") );
0167 
0168     tealistGroup.deleteGroup();
0169 
0170     for(int i=0; i<m_tealist.size(); ++i) {
0171         tealistGroup.writeEntry(QStringLiteral( "Tea%1 Time" ).arg( i ), m_tealist.at( i ).time() );
0172         tealistGroup.writeEntry(QStringLiteral( "Tea%1 Name" ).arg( i ), m_tealist.at( i ).name() );
0173     }
0174     tealistGroup.config()->sync();
0175 
0176 
0177     const auto actions = m_teaActionGroup->actions();
0178     for ( QAction* a : actions ) {
0179         m_teaActionGroup->removeAction( a );
0180     }
0181 
0182     contextMenu()->clear();
0183 
0184     loadTeaMenuItems();
0185     contextMenu()->addSeparator();
0186     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("resume")) );
0187     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("pause")) );
0188     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("stop")) );
0189     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("custom")) );
0190     contextMenu()->addSeparator();
0191     contextMenu()->addAction( m_actionCollection->action(QStringLiteral("configure")) );
0192     contextMenu()->addAction( m_actionCollection->action(KStandardAction::name(KStandardAction::ConfigureNotifications)) );
0193     contextMenu()->addMenu(m_helpMenu->menu() );
0194     contextMenu()->addSeparator();
0195     contextMenu()->addAction( m_actionCollection->action(KStandardAction::name(KStandardAction::Quit)) );
0196 
0197     connect(contextMenu(), &QMenu::triggered, this, &TopLevel::slotRunTea);
0198 
0199     loadConfig();
0200 }
0201 
0202 
0203 void TopLevel::loadTeaMenuItems() {
0204     int i=0;
0205 
0206     for (const Tea &t : std::as_const(m_tealist)) {
0207         QAction *a = contextMenu()->addAction(
0208                    i18nc( "%1 - name of the tea, %2 - the predefined time for "
0209                           "the tea", "%1 (%2)", t.name(), t.timeToString() )
0210                      );
0211 
0212         a->setData( ++i );
0213         m_teaActionGroup->addAction( a );
0214     }
0215 }
0216 
0217 
0218 void TopLevel::showSettingsDialog()
0219 {
0220     SettingsDialog( this, m_tealist ).exec();
0221 }
0222 
0223 
0224 void TopLevel::showTimeEditDialog()
0225 {
0226     TimeEditDialog( this ).exec();
0227 }
0228 
0229 
0230 void TopLevel::slotRunTea(QAction *a)
0231 {
0232     int index = a->data().toInt();
0233     if( index <= 0 ) {
0234         return;
0235     }
0236 
0237     --index;
0238 
0239     if( index > m_tealist.size() ) {
0240         return;
0241     }
0242 
0243     runTea( m_tealist.at( index ) );
0244 }
0245 
0246 
0247 void TopLevel::runTea(const Tea &tea)
0248 {
0249     m_nextNotificationTime = 0;
0250     m_runningTea = tea;
0251     m_runningTeaTime = m_runningTea.time();
0252 
0253     checkState();
0254     repaintTrayIcon();
0255 
0256     m_timer->start( 1000 );
0257 }
0258 
0259 
0260 void TopLevel::repaintTrayIcon()
0261 {
0262     QPixmap icon = QIcon::fromTheme(m_trayIconName).pixmap(KIconLoader::SizeLarge);
0263     if ( m_runningTeaTime <= 0) {
0264         setIcon(icon);
0265         return;
0266     }
0267 
0268     if (m_usevisualize) {
0269         QPainter painter( &icon );
0270         painter.setRenderHint( QPainter::Antialiasing );
0271 
0272         const QRectF rectangle( 1, icon.height() / 3 + 1, icon.width() / 1.5 - 2, icon.height() / 1.5 - 2 );
0273 
0274         const int startAngle = 90 * 16;
0275         const int angleSpan = -( 360*16.0 / m_runningTea.time() * m_runningTeaTime );
0276 
0277         painter.setBrush( QColor( 0, 255, 0, 90 ) );
0278         painter.drawPie( rectangle, startAngle, 360*16 + angleSpan );
0279         painter.setBrush( QColor( 255, 0, 0, 90 ) );
0280         painter.drawPie( rectangle, startAngle, angleSpan );
0281 
0282         setIcon(icon);
0283     } else {
0284         QPixmap overlayedIcon = KIconUtils::addOverlays(m_trayIconName, {QStringLiteral( "alarm-symbolic" )}).pixmap(KIconLoader::SizeLarge);
0285         setIcon(overlayedIcon);
0286     }
0287 }
0288 
0289 
0290 void TopLevel::teaTimeEvent()
0291 {
0292     QString title = i18n( "The Tea Cooker" );
0293 
0294     if( m_runningTeaTime == 0 ) {
0295         m_timer->stop();
0296         m_pausedRemainingTeaTime = 0;
0297 
0298         QString content = i18n( "%1 is now ready!", m_runningTea.name() );
0299 
0300         //NOTICE Timeout is set t ~o24 days when no auto hide is request. Ok - nearly the same...
0301         if( m_usepopup ) {
0302             showMessage( title, content, QSystemTrayIcon::Information, m_autohide ? m_autohidetime*1000 : 2100000000 );
0303         }
0304 
0305         KNotification::event( QStringLiteral( "ready" ), content );
0306 
0307         if( m_usereminder && m_remindertime > 0 ) {
0308             setTooltipText( content );
0309 
0310             m_timer->start( 1000 );
0311             m_nextNotificationTime -= m_remindertime;
0312             --m_runningTeaTime;
0313         }
0314         checkState();
0315         repaintTrayIcon();
0316     }
0317     else if(m_runningTeaTime<0) {
0318         QString content = i18n( "%1 is ready since %2!", m_runningTea.name(), Tea::int2time( m_runningTeaTime*-1, true ) );
0319         setTooltipText( content );
0320 
0321         if( m_runningTeaTime == m_nextNotificationTime ) {
0322             if( m_usepopup ) {
0323                 showMessage( title, content, QSystemTrayIcon::Information, m_autohide ? m_autohidetime*1000 : 2100000000 );
0324             }
0325 
0326             KNotification::event( QLatin1String( "reminder" ), content );
0327 
0328             m_nextNotificationTime -= m_remindertime;
0329         }
0330         --m_runningTeaTime;
0331     }
0332     else {
0333         --m_runningTeaTime;
0334         setTooltipText( i18nc( "%1 is the time, %2 is the name of the tea", "%1 left for %2.", Tea::int2time( m_runningTeaTime, true ), m_runningTea.name() ) );
0335     }
0336 
0337     if( m_usevisualize ) {
0338         repaintTrayIcon();
0339     }
0340 }
0341 
0342 
0343 void TopLevel::cancelTea()
0344 {
0345     m_timer->stop();
0346     m_runningTeaTime = 0;
0347     m_pausedRemainingTeaTime = 0;
0348 
0349     checkState();
0350     repaintTrayIcon();
0351 }
0352 
0353 
0354 void TopLevel::stopTea()
0355 {
0356     m_timer->stop();
0357     m_pausedRemainingTeaTime = m_runningTeaTime;
0358     m_runningTeaTime = 0;
0359 
0360     checkState();
0361 }
0362 
0363 
0364 void TopLevel::resumeTea()
0365 {
0366     m_runningTeaTime = m_pausedRemainingTeaTime;
0367     m_pausedRemainingTeaTime = 0;
0368 
0369     checkState();
0370 
0371     m_timer->start( 1000 );
0372 }
0373 
0374 
0375 void TopLevel::configureNotifications()
0376 {
0377     KNotifyConfigWidget::configure();
0378 }
0379 
0380 void TopLevel::loadConfig()
0381 {
0382     KSharedConfigPtr config = KSharedConfig::openConfig();
0383     KConfigGroup generalGroup( config, QStringLiteral("General") );
0384 
0385     m_usepopup=generalGroup.readEntry( "UsePopup", true );
0386     m_autohide=generalGroup.readEntry( "PopupAutoHide", false );
0387     m_autohidetime=generalGroup.readEntry( "PopupAutoHideTime", 30 );
0388     m_usereminder=generalGroup.readEntry( "UseReminder", false );
0389     m_remindertime=generalGroup.readEntry( "ReminderTime", 60 );
0390     m_usevisualize=generalGroup.readEntry( "UseVisualize", true );
0391 }
0392 
0393 
0394 void TopLevel::showPopup(QSystemTrayIcon::ActivationReason reason)
0395 {
0396     if ( reason != QSystemTrayIcon::Trigger ) {
0397         return;
0398     }
0399     if ( m_popup )
0400     {
0401         m_popup->close();
0402     }
0403     else
0404     {
0405         m_popup = new KNotification(QStringLiteral("popup"), KNotification::Persistent, this);
0406         m_popup->setComponentName(QStringLiteral("kteatime"));
0407         m_popup->setTitle(i18n( "The Tea Cooker" ));
0408         m_popup->setText(toolTip());
0409         m_popup->setIconName(m_notificationIconName);
0410         m_popup->sendEvent();
0411     }
0412 }
0413 
0414 
0415 void TopLevel::setTooltipText(const QString& content)
0416 {
0417     setToolTip( content );
0418     if (m_popup)
0419     {
0420         m_popup->setText(content);
0421     }
0422 }
0423 
0424 
0425 // kate: word-wrap off; encoding utf-8; indent-width 4; tab-width 4; line-numbers on; mixed-indent off; remove-trailing-space-save on; replace-tabs-save on; replace-tabs on; space-indent on;
0426 // vim:set spell et sw=4 ts=4 nowrap cino=l1,cs,U1: