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 <tqtooltip.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 TQToolTip
00054 {
00055     public:
00056         TrayTooltip(TQWidget* parent) : TQToolTip(parent) { }
00057         virtual ~TrayTooltip() {}
00058     protected:
00059         virtual void maybeTip(const TQPoint&);
00060 };
00061 
00062 struct TipItem
00063 {
00064     TQDateTime  dateTime;
00065     TQString    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, TQT_SIGNAL(switched(bool)), TQT_SLOT(setEnabledStatus(bool)));
00091     KAlarm::createNewAlarmAction(i18n("&New Alarm..."), TQT_TQOBJECT(this), TQT_SLOT(slotNewAlarm()), actcol, "tNew")->plug(contextMenu());
00092     KAlarm::createNewFromTemplateAction(i18n("New Alarm From &Template"), TQT_TQOBJECT(this), TQT_SLOT(slotNewFromTemplate(const KAEvent&)), actcol, "tNewFromTempl")->plug(contextMenu());
00093     KStdAction::preferences(TQT_TQOBJECT(this), TQT_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(TQT_TQOBJECT(this), TQT_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::display();
00149 }
00150 
00151 /******************************************************************************
00152 * Called when the Quit context menu item is selected.
00153 */
00154 void TrayWindow::slotQuit()
00155 {
00156     theApp()->doQuit(this);
00157 }
00158 
00159 /******************************************************************************
00160 * Called when the Alarms Enabled action status has changed.
00161 * Updates the alarms enabled menu item check state, and the icon pixmap.
00162 */
00163 void TrayWindow::setEnabledStatus(bool status)
00164 {
00165     kdDebug(5950) << "TrayWindow::setEnabledStatus(" << (int)status << ")\n";
00166     setPixmap(status ? mPixmapEnabled : mPixmapDisabled);
00167 }
00168 
00169 /******************************************************************************
00170 *  Called when the mouse is clicked over the panel icon.
00171 *  A left click displays the KAlarm main window.
00172 *  A middle button click displays the New Alarm window.
00173 */
00174 void TrayWindow::mousePressEvent(TQMouseEvent* e)
00175 {
00176     if (e->button() == Qt::LeftButton  &&  !theApp()->wantRunInSystemTray())
00177     {
00178         // Left click: display/hide the first main window
00179         mAssocMainWindow = MainWindow::toggleWindow(mAssocMainWindow);
00180     }
00181     else if (e->button() == Qt::MidButton)
00182         MainWindow::executeNew();    // display a New Alarm dialog
00183     else
00184         KSystemTray::mousePressEvent(e);
00185 }
00186 
00187 /******************************************************************************
00188 *  Called when the mouse is released over the panel icon.
00189 *  The main window (if not hidden) is raised and made the active window.
00190 *  If this is done in mousePressEvent(), it doesn't work.
00191 */
00192 void TrayWindow::mouseReleaseEvent(TQMouseEvent* e)
00193 {
00194     if (e->button() == Qt::LeftButton  &&  mAssocMainWindow  &&  mAssocMainWindow->isVisible())
00195     {
00196         mAssocMainWindow->raise();
00197         mAssocMainWindow->setActiveWindow();
00198     }
00199     else
00200         KSystemTray::mouseReleaseEvent(e);
00201 }
00202 
00203 /******************************************************************************
00204 *  Called when the drag cursor enters the panel icon.
00205 */
00206 void TrayWindow::dragEnterEvent(TQDragEnterEvent* e)
00207 {
00208     MainWindow::executeDragEnterEvent(e);
00209 }
00210 
00211 /******************************************************************************
00212 *  Called when an object is dropped on the panel icon.
00213 *  If the object is recognised, the edit alarm dialog is opened appropriately.
00214 */
00215 void TrayWindow::dropEvent(TQDropEvent* e)
00216 {
00217     MainWindow::executeDropEvent(0, e);
00218 }
00219 
00220 /******************************************************************************
00221 *  Return the tooltip text showing alarms due in the next 24 hours.
00222 *  The limit of 24 hours is because only times, not dates, are displayed.
00223 */
00224 void TrayWindow::tooltipAlarmText(TQString& text) const
00225 {
00226     KAEvent event;
00227     const TQString& prefix = Preferences::tooltipTimeToPrefix();
00228     int maxCount = Preferences::tooltipAlarmCount();
00229     TQDateTime now = TQDateTime::currentDateTime();
00230 
00231     // Get today's and tomorrow's alarms, sorted in time order
00232     TQValueList<TipItem> items;
00233     TQValueList<TipItem>::Iterator iit;
00234     KCal::Event::List events = AlarmCalendar::activeCalendar()->eventsWithAlarms(now.date(), now.addDays(1));
00235     for (KCal::Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
00236     {
00237         KCal::Event* kcalEvent = *it;
00238         event.set(*kcalEvent);
00239         if (event.enabled()  &&  !event.expired()  &&  event.action() == KAEvent::MESSAGE)
00240         {
00241             TipItem item;
00242             DateTime dateTime = event.displayDateTime();
00243             if (dateTime.date() > now.date())
00244             {
00245                 // Ignore alarms after tomorrow at the current clock time
00246                 if (dateTime.date() != now.date().addDays(1)
00247                 ||  dateTime.time() >= now.time())
00248                     continue;
00249             }
00250             item.dateTime = dateTime.dateTime();
00251 
00252             // The alarm is due today, or early tomorrow
00253             bool space = false;
00254             if (Preferences::showTooltipAlarmTime())
00255             {
00256                 item.text += KGlobal::locale()->formatTime(item.dateTime.time());
00257                 item.text += ' ';
00258                 space = true;
00259             }
00260             if (Preferences::showTooltipTimeToAlarm())
00261             {
00262                 int mins = (now.secsTo(item.dateTime) + 59) / 60;
00263                 if (mins < 0)
00264                     mins = 0;
00265                 char minutes[3] = "00";
00266                 minutes[0] = (mins%60) / 10 + '0';
00267                 minutes[1] = (mins%60) % 10 + '0';
00268                 if (Preferences::showTooltipAlarmTime())
00269                     item.text += i18n("prefix + hours:minutes", "(%1%2:%3)").arg(prefix).arg(mins/60).arg(minutes);
00270                 else
00271                     item.text += i18n("prefix + hours:minutes", "%1%2:%3").arg(prefix).arg(mins/60).arg(minutes);
00272                 item.text += ' ';
00273                 space = true;
00274             }
00275             if (space)
00276                 item.text += ' ';
00277             item.text += AlarmText::summary(event);
00278 
00279             // Insert the item into the list in time-sorted order
00280             for (iit = items.begin();  iit != items.end();  ++iit)
00281             {
00282                 if (item.dateTime <= (*iit).dateTime)
00283                     break;
00284             }
00285             items.insert(iit, item);
00286         }
00287         }
00288     kdDebug(5950) << "TrayWindow::tooltipAlarmText():\n";
00289     int count = 0;
00290     for (iit = items.begin();  iit != items.end();  ++iit)
00291     {
00292         kdDebug(5950) << "-- " << (count+1) << ") " << (*iit).text << endl;
00293         text += '\n';
00294         text += (*iit).text;
00295         if (++count == maxCount)
00296             break;
00297     }
00298 }
00299 
00300 /******************************************************************************
00301 * Called when the associated main window is closed.
00302 */
00303 void TrayWindow::removeWindow(MainWindow* win)
00304 {
00305     if (win == mAssocMainWindow)
00306         mAssocMainWindow = 0;
00307 }
00308 
00309 
00310 #ifdef HAVE_X11_HEADERS
00311     #include <X11/X.h>
00312     #include <X11/Xlib.h>
00313     #include <X11/Xutil.h>
00314 #endif
00315 
00316 /******************************************************************************
00317 * Check whether the widget is in the system tray.
00318 * Note that it is only sometime AFTER the show event that the system tray
00319 * becomes the widget's parent. So for a definitive status, call this method
00320 * only after waiting a bit...
00321 * Reply = true if the widget is in the system tray, or its status can't be determined.
00322 *       = false if it is not currently in the system tray.
00323 */
00324 bool TrayWindow::inSystemTray() const
00325 {
00326 #ifdef HAVE_X11_HEADERS
00327     Window  xParent;    // receives parent window
00328     Window  root;
00329     Window* children = 0;
00330     unsigned int nchildren;
00331     // Find the X parent window of the widget. This is not the same as the TQt parent widget.
00332     if (!XQueryTree(qt_xdisplay(), winId(), &root, &xParent, &children, &nchildren))
00333         return true;    // error determining its parent X window
00334     if (children)
00335         XFree(children);
00336 
00337     // If it is in the system tray, the system tray window will be its X parent.
00338     // Otherwise, the root window will be its X parent.
00339     return xParent != root;
00340 #else
00341     return true;
00342 #endif // HAVE_X11_HEADERS
00343 }
00344 
00345 
00346 /******************************************************************************
00347 *  Displays the appropriate tooltip depending on preference settings.
00348 */
00349 void TrayTooltip::maybeTip(const TQPoint&)
00350 {
00351     TrayWindow* parent = (TrayWindow*)parentWidget();
00352     TQString text;
00353     if (Daemon::monitoringAlarms())
00354         text = kapp->aboutData()->programName();
00355     else
00356         text = i18n("%1 - disabled").arg(kapp->aboutData()->programName());
00357     kdDebug(5950) << "TrayTooltip::maybeTip(): " << text << endl;
00358     if (Preferences::tooltipAlarmCount())
00359         parent->tooltipAlarmText(text);
00360     tip(parent->rect(), text);
00361 }