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 <tdeapplication.h> 00028 #include <tdelocale.h> 00029 #include <tdeaboutdata.h> 00030 #include <tdepopupmenu.h> 00031 #include <tdemessagebox.h> 00032 #include <kstandarddirs.h> 00033 #include <kstdaction.h> 00034 #include <kstdguiitem.h> 00035 #include <tdeaccel.h> 00036 #include <tdeconfig.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 TDEActionCollection* 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->tdeaccel()->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(TDEPopupMenu* 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 += TDEGlobal::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(tqt_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 }