kalarm

traywindow.cpp

00001 /*
00002  *  traywindow.cpp  -  the KDE system tray applet
00003  *  Program:  kalarm
00004  *  Copyright © 2002-2005,2007 by David Jarvie <software@astrojar.org.uk>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 
00023 #include <stdlib.h>
00024 
00025 #include <qtooltip.h>
00026 
00027 #include <kapplication.h>
00028 #include <klocale.h>
00029 #include <kaboutdata.h>
00030 #include <kpopupmenu.h>
00031 #include <kmessagebox.h>
00032 #include <kstandarddirs.h>
00033 #include <kstdaction.h>
00034 #include <kstdguiitem.h>
00035 #include <kaccel.h>
00036 #include <kconfig.h>
00037 #include <kdebug.h>
00038 
00039 #include "alarmcalendar.h"
00040 #include "alarmlistview.h"
00041 #include "alarmtext.h"
00042 #include "daemon.h"
00043 #include "functions.h"
00044 #include "kalarmapp.h"
00045 #include "mainwindow.h"
00046 #include "messagewin.h"
00047 #include "prefdlg.h"
00048 #include "preferences.h"
00049 #include "templatemenuaction.h"
00050 #include "traywindow.moc"
00051 
00052 
00053 class TrayTooltip : public QToolTip
00054 {
00055     public:
00056         TrayTooltip(QWidget* parent) : QToolTip(parent) { }
00057         virtual ~TrayTooltip() {}
00058     protected:
00059         virtual void maybeTip(const QPoint&);
00060 };
00061 
00062 struct TipItem
00063 {
00064     QDateTime  dateTime;
00065     QString    text;
00066 };
00067 
00068 
00069 /*=============================================================================
00070 = Class: TrayWindow
00071 = The KDE system tray window.
00072 =============================================================================*/
00073 
00074 TrayWindow::TrayWindow(MainWindow* parent, const char* name)
00075     : KSystemTray((theApp()->wantRunInSystemTray() ? parent : 0), name),
00076       mAssocMainWindow(parent)
00077 {
00078     kdDebug(5950) << "TrayWindow::TrayWindow()\n";
00079     // Set up GUI icons
00080     mPixmapEnabled  = loadIcon("kalarm");
00081     mPixmapDisabled = loadIcon("kalarm_disabled");
00082     if (mPixmapEnabled.isNull() || mPixmapDisabled.isNull())
00083         KMessageBox::sorry(this, i18n("Cannot load system tray icon."));
00084     setAcceptDrops(true);         // allow drag-and-drop onto this window
00085 
00086     // Set up the context menu
00087     KActionCollection* actcol = actionCollection();
00088     AlarmEnableAction* a = Daemon::createAlarmEnableAction(actcol, "tAlarmEnable");
00089     a->plug(contextMenu());
00090     connect(a, SIGNAL(switched(bool)), SLOT(setEnabledStatus(bool)));
00091     KAlarm::createNewAlarmAction(i18n("&New Alarm..."), this, SLOT(slotNewAlarm()), actcol, "tNew")->plug(contextMenu());
00092     KAlarm::createNewFromTemplateAction(i18n("New Alarm From &Template"), this, SLOT(slotNewFromTemplate(const KAEvent&)), actcol, "tNewFromTempl")->plug(contextMenu());
00093     KStdAction::preferences(this, SLOT(slotPreferences()), actcol)->plug(contextMenu());
00094 
00095     // Replace the default handler for the Quit context menu item
00096     const char* quitName = KStdAction::name(KStdAction::Quit);
00097     actcol->remove(actcol->action(quitName));
00098     actcol->accel()->remove(quitName);
00099     KStdAction::quit(this, SLOT(slotQuit()), actcol);
00100 
00101     // Set icon to correspond with the alarms enabled menu status
00102     Daemon::checkStatus();
00103     setEnabledStatus(Daemon::monitoringAlarms());
00104 
00105     mTooltip = new TrayTooltip(this);
00106 }
00107 
00108 TrayWindow::~TrayWindow()
00109 {
00110     kdDebug(5950) << "TrayWindow::~TrayWindow()\n";
00111     delete mTooltip;
00112     mTooltip = 0;
00113     theApp()->removeWindow(this);
00114     emit deleted();
00115 }
00116 
00117 /******************************************************************************
00118 * Called just before the context menu is displayed.
00119 * Update the Alarms Enabled item status.
00120 */
00121 void TrayWindow::contextMenuAboutToShow(KPopupMenu* menu)
00122 {
00123     KSystemTray::contextMenuAboutToShow(menu);     // needed for KDE <= 3.1 compatibility
00124     Daemon::checkStatus();
00125 }
00126 
00127 /******************************************************************************
00128 *  Called when the "New Alarm" menu item is selected to edit a new alarm.
00129 */
00130 void TrayWindow::slotNewAlarm()
00131 {
00132     MainWindow::executeNew();
00133 }
00134 
00135 /******************************************************************************
00136 *  Called when the "New Alarm" menu item is selected to edit a new alarm.
00137 */
00138 void TrayWindow::slotNewFromTemplate(const KAEvent& event)
00139 {
00140     MainWindow::executeNew(event);
00141 }
00142 
00143 /******************************************************************************
00144 *  Called when the "Configure KAlarm" menu item is selected.
00145 */
00146 void TrayWindow::slotPreferences()
00147 {
00148     KAlarmPrefDlg prefDlg;
00149     prefDlg.exec();
00150 }
00151 
00152 /******************************************************************************
00153 * Called when the Quit context menu item is selected.
00154 */
00155 void TrayWindow::slotQuit()
00156 {
00157     theApp()->doQuit(this);
00158 }
00159 
00160 /******************************************************************************
00161 * Called when the Alarms Enabled action status has changed.
00162 * Updates the alarms enabled menu item check state, and the icon pixmap.
00163 */
00164 void TrayWindow::setEnabledStatus(bool status)
00165 {
00166     kdDebug(5950) << "TrayWindow::setEnabledStatus(" << (int)status << ")\n";
00167     setPixmap(status ? mPixmapEnabled : mPixmapDisabled);
00168 }
00169 
00170 /******************************************************************************
00171 *  Called when the mouse is clicked over the panel icon.
00172 *  A left click displays the KAlarm main window.
00173 *  A middle button click displays the New Alarm window.
00174 */
00175 void TrayWindow::mousePressEvent(QMouseEvent* e)
00176 {
00177     if (e->button() == LeftButton  &&  !theApp()->wantRunInSystemTray())
00178     {
00179         // Left click: display/hide the first main window
00180         mAssocMainWindow = MainWindow::toggleWindow(mAssocMainWindow);
00181     }
00182     else if (e->button() == MidButton)
00183         MainWindow::executeNew();    // display a New Alarm dialog
00184     else
00185         KSystemTray::mousePressEvent(e);
00186 }
00187 
00188 /******************************************************************************
00189 *  Called when the mouse is released over the panel icon.
00190 *  The main window (if not hidden) is raised and made the active window.
00191 *  If this is done in mousePressEvent(), it doesn't work.
00192 */
00193 void TrayWindow::mouseReleaseEvent(QMouseEvent* e)
00194 {
00195     if (e->button() == LeftButton  &&  mAssocMainWindow  &&  mAssocMainWindow->isVisible())
00196     {
00197         mAssocMainWindow->raise();
00198         mAssocMainWindow->setActiveWindow();
00199     }
00200     else
00201         KSystemTray::mouseReleaseEvent(e);
00202 }
00203 
00204 /******************************************************************************
00205 *  Called when the drag cursor enters the panel icon.
00206 */
00207 void TrayWindow::dragEnterEvent(QDragEnterEvent* e)
00208 {
00209     MainWindow::executeDragEnterEvent(e);
00210 }
00211 
00212 /******************************************************************************
00213 *  Called when an object is dropped on the panel icon.
00214 *  If the object is recognised, the edit alarm dialog is opened appropriately.
00215 */
00216 void TrayWindow::dropEvent(QDropEvent* e)
00217 {
00218     MainWindow::executeDropEvent(0, e);
00219 }
00220 
00221 /******************************************************************************
00222 *  Return the tooltip text showing alarms due in the next 24 hours.
00223 *  The limit of 24 hours is because only times, not dates, are displayed.
00224 */
00225 void TrayWindow::tooltipAlarmText(QString& text) const
00226 {
00227     KAEvent event;
00228     const QString& prefix = Preferences::tooltipTimeToPrefix();
00229     int maxCount = Preferences::tooltipAlarmCount();
00230     QDateTime now = QDateTime::currentDateTime();
00231 
00232     // Get today's and tomorrow's alarms, sorted in time order
00233     QValueList<TipItem> items;
00234     QValueList<TipItem>::Iterator iit;
00235     KCal::Event::List events = AlarmCalendar::activeCalendar()->eventsWithAlarms(now.date(), now.addDays(1));
00236     for (KCal::Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
00237     {
00238         KCal::Event* kcalEvent = *it;
00239         event.set(*kcalEvent);
00240         if (event.enabled()  &&  !event.expired()  &&  event.action() == KAEvent::MESSAGE)
00241         {
00242             TipItem item;
00243             DateTime dateTime = event.nextDateTime(false);
00244             if (dateTime.date() > now.date())
00245             {
00246                 // Ignore alarms after tomorrow at the current clock time
00247                 if (dateTime.date() != now.date().addDays(1)
00248                 ||  dateTime.time() >= now.time())
00249                     continue;
00250             }
00251             item.dateTime = dateTime.dateTime();
00252 
00253             // The alarm is due today, or early tomorrow
00254             bool space = false;
00255             if (Preferences::showTooltipAlarmTime())
00256             {
00257                 item.text += KGlobal::locale()->formatTime(item.dateTime.time());
00258                 item.text += ' ';
00259                 space = true;
00260             }
00261             if (Preferences::showTooltipTimeToAlarm())
00262             {
00263                 int mins = (now.secsTo(item.dateTime) + 59) / 60;
00264                 if (mins < 0)
00265                     mins = 0;
00266                 char minutes[3] = "00";
00267                 minutes[0] = (mins%60) / 10 + '0';
00268                 minutes[1] = (mins%60) % 10 + '0';
00269                 if (Preferences::showTooltipAlarmTime())
00270                     item.text += i18n("prefix + hours:minutes", "(%1%2:%3)").arg(prefix).arg(mins/60).arg(minutes);
00271                 else
00272                     item.text += i18n("prefix + hours:minutes", "%1%2:%3").arg(prefix).arg(mins/60).arg(minutes);
00273                 item.text += ' ';
00274                 space = true;
00275             }
00276             if (space)
00277                 item.text += ' ';
00278             item.text += AlarmText::summary(event);
00279 
00280             // Insert the item into the list in time-sorted order
00281             for (iit = items.begin();  iit != items.end();  ++iit)
00282             {
00283                 if (item.dateTime <= (*iit).dateTime)
00284                     break;
00285             }
00286             items.insert(iit, item);
00287         }
00288         }
00289     kdDebug(5950) << "TrayWindow::tooltipAlarmText():\n";
00290     int count = 0;
00291     for (iit = items.begin();  iit != items.end();  ++iit)
00292     {
00293         kdDebug(5950) << "-- " << (count+1) << ") " << (*iit).text << endl;
00294         text += '\n';
00295         text += (*iit).text;
00296         if (++count == maxCount)
00297             break;
00298     }
00299 }
00300 
00301 /******************************************************************************
00302 * Called when the associated main window is closed.
00303 */
00304 void TrayWindow::removeWindow(MainWindow* win)
00305 {
00306     if (win == mAssocMainWindow)
00307         mAssocMainWindow = 0;
00308 }
00309 
00310 
00311 #ifdef HAVE_X11_HEADERS
00312     #include <X11/X.h>
00313     #include <X11/Xlib.h>
00314     #include <X11/Xutil.h>
00315 #endif
00316 
00317 /******************************************************************************
00318 * Check whether the widget is in the system tray.
00319 * Note that it is only sometime AFTER the show event that the system tray
00320 * becomes the widget's parent. So for a definitive status, call this method
00321 * only after waiting a bit...
00322 * Reply = true if the widget is in the system tray, or its status can't be determined.
00323 *       = false if it is not currently in the system tray.
00324 */
00325 bool TrayWindow::inSystemTray() const
00326 {
00327 #ifdef HAVE_X11_HEADERS
00328     Window  xParent;    // receives parent window
00329     Window  root;
00330     Window* children = 0;
00331     unsigned int nchildren;
00332     // Find the X parent window of the widget. This is not the same as the Qt parent widget.
00333     if (!XQueryTree(qt_xdisplay(), winId(), &root, &xParent, &children, &nchildren))
00334         return true;    // error determining its parent X window
00335     if (children)
00336         XFree(children);
00337 
00338     // If it is in the system tray, the system tray window will be its X parent.
00339     // Otherwise, the root window will be its X parent.
00340     return xParent != root;
00341 #else
00342     return true;
00343 #endif // HAVE_X11_HEADERS
00344 }
00345 
00346 
00347 /******************************************************************************
00348 *  Displays the appropriate tooltip depending on preference settings.
00349 */
00350 void TrayTooltip::maybeTip(const QPoint&)
00351 {
00352     TrayWindow* parent = (TrayWindow*)parentWidget();
00353     QString text;
00354     if (Daemon::monitoringAlarms())
00355         text = kapp->aboutData()->programName();
00356     else
00357         text = i18n("%1 - disabled").arg(kapp->aboutData()->programName());
00358     kdDebug(5950) << "TrayTooltip::maybeTip(): " << text << endl;
00359     if (Preferences::tooltipAlarmCount())
00360         parent->tooltipAlarmText(text);
00361     tip(parent->rect(), text);
00362 }
KDE Home | KDE Accessibility Home | Description of Access Keys