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 ¬ificationIcon, 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: