kalarm

traywindow.cpp

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