kalarm

traywindow.cpp
1 /*
2  * traywindow.cpp - the KDE system tray applet
3  * Program: kalarm
4  * Copyright © 2002-2005,2007 by David Jarvie <software@astrojar.org.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "kalarm.h"
22 
23 #include <stdlib.h>
24 
25 #include <tqtooltip.h>
26 
27 #include <tdeapplication.h>
28 #include <tdelocale.h>
29 #include <tdeaboutdata.h>
30 #include <tdepopupmenu.h>
31 #include <tdemessagebox.h>
32 #include <kstandarddirs.h>
33 #include <kstdaction.h>
34 #include <kstdguiitem.h>
35 #include <tdeaccel.h>
36 #include <tdeconfig.h>
37 #include <kdebug.h>
38 
39 #include "alarmcalendar.h"
40 #include "alarmlistview.h"
41 #include "alarmtext.h"
42 #include "daemon.h"
43 #include "functions.h"
44 #include "kalarmapp.h"
45 #include "mainwindow.h"
46 #include "messagewin.h"
47 #include "prefdlg.h"
48 #include "preferences.h"
49 #include "templatemenuaction.h"
50 #include "traywindow.moc"
51 
52 
53 class TrayTooltip : public TQToolTip
54 {
55  public:
56  TrayTooltip(TQWidget* parent) : TQToolTip(parent) { }
57  virtual ~TrayTooltip() {}
58  protected:
59  virtual void maybeTip(const TQPoint&);
60 };
61 
62 struct TipItem
63 {
64  TQDateTime dateTime;
65  TQString text;
66 };
67 
68 
69 /*=============================================================================
70 = Class: TrayWindow
71 = The KDE system tray window.
72 =============================================================================*/
73 
74 TrayWindow::TrayWindow(MainWindow* parent, const char* name)
75  : KSystemTray((theApp()->wantRunInSystemTray() ? parent : 0), name),
76  mAssocMainWindow(parent)
77 {
78  kdDebug(5950) << "TrayWindow::TrayWindow()\n";
79  // Set up GUI icons
80  mPixmapEnabled = loadIcon("kalarm");
81  mPixmapDisabled = loadIcon("kalarm_disabled");
82  if (mPixmapEnabled.isNull() || mPixmapDisabled.isNull())
83  KMessageBox::sorry(this, i18n("Cannot load system tray icon."));
84  setAcceptDrops(true); // allow drag-and-drop onto this window
85 
86  // Set up the context menu
87  TDEActionCollection* actcol = actionCollection();
88  AlarmEnableAction* a = Daemon::createAlarmEnableAction(actcol, "tAlarmEnable");
89  a->plug(contextMenu());
90  connect(a, TQ_SIGNAL(switched(bool)), TQ_SLOT(setEnabledStatus(bool)));
91  KAlarm::createNewAlarmAction(i18n("&New Alarm..."), this, TQ_SLOT(slotNewAlarm()), actcol, "tNew")->plug(contextMenu());
92  KAlarm::createNewFromTemplateAction(i18n("New Alarm From &Template"), this, TQ_SLOT(slotNewFromTemplate(const KAEvent&)), actcol, "tNewFromTempl")->plug(contextMenu());
93  KStdAction::preferences(this, TQ_SLOT(slotPreferences()), actcol)->plug(contextMenu());
94 
95  // Replace the default handler for the Quit context menu item
96  const char* quitName = KStdAction::name(KStdAction::Quit);
97  actcol->remove(actcol->action(quitName));
98  actcol->tdeaccel()->remove(quitName);
99  KStdAction::quit(this, TQ_SLOT(slotQuit()), actcol);
100 
101  // Set icon to correspond with the alarms enabled menu status
102  Daemon::checkStatus();
103  setEnabledStatus(Daemon::monitoringAlarms());
104 
105  mTooltip = new TrayTooltip(this);
106 }
107 
108 TrayWindow::~TrayWindow()
109 {
110  kdDebug(5950) << "TrayWindow::~TrayWindow()\n";
111  delete mTooltip;
112  mTooltip = 0;
113  theApp()->removeWindow(this);
114  emit deleted();
115 }
116 
117 /******************************************************************************
118 * Called just before the context menu is displayed.
119 * Update the Alarms Enabled item status.
120 */
121 void TrayWindow::contextMenuAboutToShow(TDEPopupMenu* menu)
122 {
123  KSystemTray::contextMenuAboutToShow(menu); // needed for KDE <= 3.1 compatibility
124  Daemon::checkStatus();
125 }
126 
127 /******************************************************************************
128 * Called when the "New Alarm" menu item is selected to edit a new alarm.
129 */
130 void TrayWindow::slotNewAlarm()
131 {
132  MainWindow::executeNew();
133 }
134 
135 /******************************************************************************
136 * Called when the "New Alarm" menu item is selected to edit a new alarm.
137 */
138 void TrayWindow::slotNewFromTemplate(const KAEvent& event)
139 {
140  MainWindow::executeNew(event);
141 }
142 
143 /******************************************************************************
144 * Called when the "Configure KAlarm" menu item is selected.
145 */
146 void TrayWindow::slotPreferences()
147 {
148  KAlarmPrefDlg::display();
149 }
150 
151 /******************************************************************************
152 * Called when the Quit context menu item is selected.
153 */
154 void TrayWindow::slotQuit()
155 {
156  theApp()->doQuit(this);
157 }
158 
159 /******************************************************************************
160 * Called when the Alarms Enabled action status has changed.
161 * Updates the alarms enabled menu item check state, and the icon pixmap.
162 */
163 void TrayWindow::setEnabledStatus(bool status)
164 {
165  kdDebug(5950) << "TrayWindow::setEnabledStatus(" << (int)status << ")\n";
166  setPixmap(status ? mPixmapEnabled : mPixmapDisabled);
167 }
168 
169 /******************************************************************************
170 * Called when the mouse is clicked over the panel icon.
171 * A left click displays the KAlarm main window.
172 * A middle button click displays the New Alarm window.
173 */
174 void TrayWindow::mousePressEvent(TQMouseEvent* e)
175 {
176  if (e->button() == TQt::LeftButton && !theApp()->wantRunInSystemTray())
177  {
178  // Left click: display/hide the first main window
179  mAssocMainWindow = MainWindow::toggleWindow(mAssocMainWindow);
180  }
181  else if (e->button() == TQt::MidButton)
182  MainWindow::executeNew(); // display a New Alarm dialog
183  else
184  KSystemTray::mousePressEvent(e);
185 }
186 
187 /******************************************************************************
188 * Called when the mouse is released over the panel icon.
189 * The main window (if not hidden) is raised and made the active window.
190 * If this is done in mousePressEvent(), it doesn't work.
191 */
192 void TrayWindow::mouseReleaseEvent(TQMouseEvent* e)
193 {
194  if (e->button() == TQt::LeftButton && mAssocMainWindow && mAssocMainWindow->isVisible())
195  {
196  mAssocMainWindow->raise();
197  mAssocMainWindow->setActiveWindow();
198  }
199  else
200  KSystemTray::mouseReleaseEvent(e);
201 }
202 
203 /******************************************************************************
204 * Called when the drag cursor enters the panel icon.
205 */
206 void TrayWindow::dragEnterEvent(TQDragEnterEvent* e)
207 {
208  MainWindow::executeDragEnterEvent(e);
209 }
210 
211 /******************************************************************************
212 * Called when an object is dropped on the panel icon.
213 * If the object is recognised, the edit alarm dialog is opened appropriately.
214 */
215 void TrayWindow::dropEvent(TQDropEvent* e)
216 {
217  MainWindow::executeDropEvent(0, e);
218 }
219 
220 /******************************************************************************
221 * Return the tooltip text showing alarms due in the next 24 hours.
222 * The limit of 24 hours is because only times, not dates, are displayed.
223 */
224 void TrayWindow::tooltipAlarmText(TQString& text) const
225 {
226  KAEvent event;
227  const TQString& prefix = Preferences::tooltipTimeToPrefix();
228  int maxCount = Preferences::tooltipAlarmCount();
229  TQDateTime now = TQDateTime::currentDateTime();
230 
231  // Get today's and tomorrow's alarms, sorted in time order
232  TQValueList<TipItem> items;
233  TQValueList<TipItem>::Iterator iit;
234  KCal::Event::List events = AlarmCalendar::activeCalendar()->eventsWithAlarms(now.date(), now.addDays(1));
235  for (KCal::Event::List::ConstIterator it = events.begin(); it != events.end(); ++it)
236  {
237  KCal::Event* kcalEvent = *it;
238  event.set(*kcalEvent);
239  if (event.enabled() && !event.expired() && event.action() == KAEvent::MESSAGE)
240  {
241  TipItem item;
242  DateTime dateTime = event.displayDateTime();
243  if (dateTime.date() > now.date())
244  {
245  // Ignore alarms after tomorrow at the current clock time
246  if (dateTime.date() != now.date().addDays(1)
247  || dateTime.time() >= now.time())
248  continue;
249  }
250  item.dateTime = dateTime.dateTime();
251 
252  // The alarm is due today, or early tomorrow
253  bool space = false;
254  if (Preferences::showTooltipAlarmTime())
255  {
256  item.text += TDEGlobal::locale()->formatTime(item.dateTime.time());
257  item.text += ' ';
258  space = true;
259  }
260  if (Preferences::showTooltipTimeToAlarm())
261  {
262  int mins = (now.secsTo(item.dateTime) + 59) / 60;
263  if (mins < 0)
264  mins = 0;
265  char minutes[3] = "00";
266  minutes[0] = (mins%60) / 10 + '0';
267  minutes[1] = (mins%60) % 10 + '0';
268  if (Preferences::showTooltipAlarmTime())
269  item.text += i18n("prefix + hours:minutes", "(%1%2:%3)").arg(prefix).arg(mins/60).arg(minutes);
270  else
271  item.text += i18n("prefix + hours:minutes", "%1%2:%3").arg(prefix).arg(mins/60).arg(minutes);
272  item.text += ' ';
273  space = true;
274  }
275  if (space)
276  item.text += ' ';
277  item.text += AlarmText::summary(event);
278 
279  // Insert the item into the list in time-sorted order
280  for (iit = items.begin(); iit != items.end(); ++iit)
281  {
282  if (item.dateTime <= (*iit).dateTime)
283  break;
284  }
285  items.insert(iit, item);
286  }
287  }
288  kdDebug(5950) << "TrayWindow::tooltipAlarmText():\n";
289  int count = 0;
290  for (iit = items.begin(); iit != items.end(); ++iit)
291  {
292  kdDebug(5950) << "-- " << (count+1) << ") " << (*iit).text << endl;
293  text += '\n';
294  text += (*iit).text;
295  if (++count == maxCount)
296  break;
297  }
298 }
299 
300 /******************************************************************************
301 * Called when the associated main window is closed.
302 */
303 void TrayWindow::removeWindow(MainWindow* win)
304 {
305  if (win == mAssocMainWindow)
306  mAssocMainWindow = 0;
307 }
308 
309 
310 #ifdef HAVE_X11_HEADERS
311  #include <X11/X.h>
312  #include <X11/Xlib.h>
313  #include <X11/Xutil.h>
314 #endif
315 
316 /******************************************************************************
317 * Check whether the widget is in the system tray.
318 * Note that it is only sometime AFTER the show event that the system tray
319 * becomes the widget's parent. So for a definitive status, call this method
320 * only after waiting a bit...
321 * Reply = true if the widget is in the system tray, or its status can't be determined.
322 * = false if it is not currently in the system tray.
323 */
324 bool TrayWindow::inSystemTray() const
325 {
326 #ifdef HAVE_X11_HEADERS
327  Window xParent; // receives parent window
328  Window root;
329  Window* children = 0;
330  unsigned int nchildren;
331  // Find the X parent window of the widget. This is not the same as the TQt parent widget.
332  if (!XQueryTree(tqt_xdisplay(), winId(), &root, &xParent, &children, &nchildren))
333  return true; // error determining its parent X window
334  if (children)
335  XFree(children);
336 
337  // If it is in the system tray, the system tray window will be its X parent.
338  // Otherwise, the root window will be its X parent.
339  return xParent != root;
340 #else
341  return true;
342 #endif // HAVE_X11_HEADERS
343 }
344 
345 
346 /******************************************************************************
347 * Displays the appropriate tooltip depending on preference settings.
348 */
349 void TrayTooltip::maybeTip(const TQPoint&)
350 {
351  TrayWindow* parent = (TrayWindow*)parentWidget();
352  TQString text;
353  if (Daemon::monitoringAlarms())
354  text = kapp->aboutData()->programName();
355  else
356  text = i18n("%1 - disabled").arg(kapp->aboutData()->programName());
357  kdDebug(5950) << "TrayTooltip::maybeTip(): " << text << endl;
358  if (Preferences::tooltipAlarmCount())
359  parent->tooltipAlarmText(text);
360  tip(parent->rect(), text);
361 }
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232
miscellaneous functions
the KAlarm application object
main application window
displays an alarm message