diff options
Diffstat (limited to 'kalarm/traywindow.cpp')
-rw-r--r-- | kalarm/traywindow.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/kalarm/traywindow.cpp b/kalarm/traywindow.cpp new file mode 100644 index 000000000..e968e02ba --- /dev/null +++ b/kalarm/traywindow.cpp @@ -0,0 +1,361 @@ +/* + * traywindow.cpp - the KDE system tray applet + * Program: kalarm + * Copyright © 2002-2005,2007 by David Jarvie <software@astrojar.org.uk> + * + * 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 "kalarm.h" + +#include <stdlib.h> + +#include <qtooltip.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kaboutdata.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kstdaction.h> +#include <kstdguiitem.h> +#include <kaccel.h> +#include <kconfig.h> +#include <kdebug.h> + +#include "alarmcalendar.h" +#include "alarmlistview.h" +#include "alarmtext.h" +#include "daemon.h" +#include "functions.h" +#include "kalarmapp.h" +#include "mainwindow.h" +#include "messagewin.h" +#include "prefdlg.h" +#include "preferences.h" +#include "templatemenuaction.h" +#include "traywindow.moc" + + +class TrayTooltip : public QToolTip +{ + public: + TrayTooltip(QWidget* parent) : QToolTip(parent) { } + virtual ~TrayTooltip() {} + protected: + virtual void maybeTip(const QPoint&); +}; + +struct TipItem +{ + QDateTime dateTime; + QString text; +}; + + +/*============================================================================= += Class: TrayWindow += The KDE system tray window. +=============================================================================*/ + +TrayWindow::TrayWindow(MainWindow* parent, const char* name) + : KSystemTray((theApp()->wantRunInSystemTray() ? parent : 0), name), + mAssocMainWindow(parent) +{ + kdDebug(5950) << "TrayWindow::TrayWindow()\n"; + // Set up GUI icons + mPixmapEnabled = loadIcon("kalarm"); + mPixmapDisabled = loadIcon("kalarm_disabled"); + if (mPixmapEnabled.isNull() || mPixmapDisabled.isNull()) + KMessageBox::sorry(this, i18n("Cannot load system tray icon.")); + setAcceptDrops(true); // allow drag-and-drop onto this window + + // Set up the context menu + KActionCollection* actcol = actionCollection(); + AlarmEnableAction* a = Daemon::createAlarmEnableAction(actcol, "tAlarmEnable"); + a->plug(contextMenu()); + connect(a, SIGNAL(switched(bool)), SLOT(setEnabledStatus(bool))); + KAlarm::createNewAlarmAction(i18n("&New Alarm..."), this, SLOT(slotNewAlarm()), actcol, "tNew")->plug(contextMenu()); + KAlarm::createNewFromTemplateAction(i18n("New Alarm From &Template"), this, SLOT(slotNewFromTemplate(const KAEvent&)), actcol, "tNewFromTempl")->plug(contextMenu()); + KStdAction::preferences(this, SLOT(slotPreferences()), actcol)->plug(contextMenu()); + + // Replace the default handler for the Quit context menu item + const char* quitName = KStdAction::name(KStdAction::Quit); + actcol->remove(actcol->action(quitName)); + actcol->accel()->remove(quitName); + KStdAction::quit(this, SLOT(slotQuit()), actcol); + + // Set icon to correspond with the alarms enabled menu status + Daemon::checkStatus(); + setEnabledStatus(Daemon::monitoringAlarms()); + + mTooltip = new TrayTooltip(this); +} + +TrayWindow::~TrayWindow() +{ + kdDebug(5950) << "TrayWindow::~TrayWindow()\n"; + delete mTooltip; + mTooltip = 0; + theApp()->removeWindow(this); + emit deleted(); +} + +/****************************************************************************** +* Called just before the context menu is displayed. +* Update the Alarms Enabled item status. +*/ +void TrayWindow::contextMenuAboutToShow(KPopupMenu* menu) +{ + KSystemTray::contextMenuAboutToShow(menu); // needed for KDE <= 3.1 compatibility + Daemon::checkStatus(); +} + +/****************************************************************************** +* Called when the "New Alarm" menu item is selected to edit a new alarm. +*/ +void TrayWindow::slotNewAlarm() +{ + MainWindow::executeNew(); +} + +/****************************************************************************** +* Called when the "New Alarm" menu item is selected to edit a new alarm. +*/ +void TrayWindow::slotNewFromTemplate(const KAEvent& event) +{ + MainWindow::executeNew(event); +} + +/****************************************************************************** +* Called when the "Configure KAlarm" menu item is selected. +*/ +void TrayWindow::slotPreferences() +{ + KAlarmPrefDlg::display(); +} + +/****************************************************************************** +* Called when the Quit context menu item is selected. +*/ +void TrayWindow::slotQuit() +{ + theApp()->doQuit(this); +} + +/****************************************************************************** +* Called when the Alarms Enabled action status has changed. +* Updates the alarms enabled menu item check state, and the icon pixmap. +*/ +void TrayWindow::setEnabledStatus(bool status) +{ + kdDebug(5950) << "TrayWindow::setEnabledStatus(" << (int)status << ")\n"; + setPixmap(status ? mPixmapEnabled : mPixmapDisabled); +} + +/****************************************************************************** +* Called when the mouse is clicked over the panel icon. +* A left click displays the KAlarm main window. +* A middle button click displays the New Alarm window. +*/ +void TrayWindow::mousePressEvent(QMouseEvent* e) +{ + if (e->button() == LeftButton && !theApp()->wantRunInSystemTray()) + { + // Left click: display/hide the first main window + mAssocMainWindow = MainWindow::toggleWindow(mAssocMainWindow); + } + else if (e->button() == MidButton) + MainWindow::executeNew(); // display a New Alarm dialog + else + KSystemTray::mousePressEvent(e); +} + +/****************************************************************************** +* Called when the mouse is released over the panel icon. +* The main window (if not hidden) is raised and made the active window. +* If this is done in mousePressEvent(), it doesn't work. +*/ +void TrayWindow::mouseReleaseEvent(QMouseEvent* e) +{ + if (e->button() == LeftButton && mAssocMainWindow && mAssocMainWindow->isVisible()) + { + mAssocMainWindow->raise(); + mAssocMainWindow->setActiveWindow(); + } + else + KSystemTray::mouseReleaseEvent(e); +} + +/****************************************************************************** +* Called when the drag cursor enters the panel icon. +*/ +void TrayWindow::dragEnterEvent(QDragEnterEvent* e) +{ + MainWindow::executeDragEnterEvent(e); +} + +/****************************************************************************** +* Called when an object is dropped on the panel icon. +* If the object is recognised, the edit alarm dialog is opened appropriately. +*/ +void TrayWindow::dropEvent(QDropEvent* e) +{ + MainWindow::executeDropEvent(0, e); +} + +/****************************************************************************** +* Return the tooltip text showing alarms due in the next 24 hours. +* The limit of 24 hours is because only times, not dates, are displayed. +*/ +void TrayWindow::tooltipAlarmText(QString& text) const +{ + KAEvent event; + const QString& prefix = Preferences::tooltipTimeToPrefix(); + int maxCount = Preferences::tooltipAlarmCount(); + QDateTime now = QDateTime::currentDateTime(); + + // Get today's and tomorrow's alarms, sorted in time order + QValueList<TipItem> items; + QValueList<TipItem>::Iterator iit; + KCal::Event::List events = AlarmCalendar::activeCalendar()->eventsWithAlarms(now.date(), now.addDays(1)); + for (KCal::Event::List::ConstIterator it = events.begin(); it != events.end(); ++it) + { + KCal::Event* kcalEvent = *it; + event.set(*kcalEvent); + if (event.enabled() && !event.expired() && event.action() == KAEvent::MESSAGE) + { + TipItem item; + DateTime dateTime = event.displayDateTime(); + if (dateTime.date() > now.date()) + { + // Ignore alarms after tomorrow at the current clock time + if (dateTime.date() != now.date().addDays(1) + || dateTime.time() >= now.time()) + continue; + } + item.dateTime = dateTime.dateTime(); + + // The alarm is due today, or early tomorrow + bool space = false; + if (Preferences::showTooltipAlarmTime()) + { + item.text += KGlobal::locale()->formatTime(item.dateTime.time()); + item.text += ' '; + space = true; + } + if (Preferences::showTooltipTimeToAlarm()) + { + int mins = (now.secsTo(item.dateTime) + 59) / 60; + if (mins < 0) + mins = 0; + char minutes[3] = "00"; + minutes[0] = (mins%60) / 10 + '0'; + minutes[1] = (mins%60) % 10 + '0'; + if (Preferences::showTooltipAlarmTime()) + item.text += i18n("prefix + hours:minutes", "(%1%2:%3)").arg(prefix).arg(mins/60).arg(minutes); + else + item.text += i18n("prefix + hours:minutes", "%1%2:%3").arg(prefix).arg(mins/60).arg(minutes); + item.text += ' '; + space = true; + } + if (space) + item.text += ' '; + item.text += AlarmText::summary(event); + + // Insert the item into the list in time-sorted order + for (iit = items.begin(); iit != items.end(); ++iit) + { + if (item.dateTime <= (*iit).dateTime) + break; + } + items.insert(iit, item); + } + } + kdDebug(5950) << "TrayWindow::tooltipAlarmText():\n"; + int count = 0; + for (iit = items.begin(); iit != items.end(); ++iit) + { + kdDebug(5950) << "-- " << (count+1) << ") " << (*iit).text << endl; + text += '\n'; + text += (*iit).text; + if (++count == maxCount) + break; + } +} + +/****************************************************************************** +* Called when the associated main window is closed. +*/ +void TrayWindow::removeWindow(MainWindow* win) +{ + if (win == mAssocMainWindow) + mAssocMainWindow = 0; +} + + +#ifdef HAVE_X11_HEADERS + #include <X11/X.h> + #include <X11/Xlib.h> + #include <X11/Xutil.h> +#endif + +/****************************************************************************** +* Check whether the widget is in the system tray. +* Note that it is only sometime AFTER the show event that the system tray +* becomes the widget's parent. So for a definitive status, call this method +* only after waiting a bit... +* Reply = true if the widget is in the system tray, or its status can't be determined. +* = false if it is not currently in the system tray. +*/ +bool TrayWindow::inSystemTray() const +{ +#ifdef HAVE_X11_HEADERS + Window xParent; // receives parent window + Window root; + Window* children = 0; + unsigned int nchildren; + // Find the X parent window of the widget. This is not the same as the Qt parent widget. + if (!XQueryTree(qt_xdisplay(), winId(), &root, &xParent, &children, &nchildren)) + return true; // error determining its parent X window + if (children) + XFree(children); + + // If it is in the system tray, the system tray window will be its X parent. + // Otherwise, the root window will be its X parent. + return xParent != root; +#else + return true; +#endif // HAVE_X11_HEADERS +} + + +/****************************************************************************** +* Displays the appropriate tooltip depending on preference settings. +*/ +void TrayTooltip::maybeTip(const QPoint&) +{ + TrayWindow* parent = (TrayWindow*)parentWidget(); + QString text; + if (Daemon::monitoringAlarms()) + text = kapp->aboutData()->programName(); + else + text = i18n("%1 - disabled").arg(kapp->aboutData()->programName()); + kdDebug(5950) << "TrayTooltip::maybeTip(): " << text << endl; + if (Preferences::tooltipAlarmCount()) + parent->tooltipAlarmText(text); + tip(parent->rect(), text); +} |