• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.12
  • kdepim
  • kalarm
kalarmapp.cpp
Go to the documentation of this file.
1 /*
2  * kalarmapp.cpp - the KAlarm application object
3  * Program: kalarm
4  * Copyright © 2001-2014 by David Jarvie <djarvie@kde.org>
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 #include "kalarmapp.moc"
23 
24 #include "alarmcalendar.h"
25 #include "alarmlistview.h"
26 #include "alarmtime.h"
27 #include "autoqpointer.h"
28 #include "commandoptions.h"
29 #include "dbushandler.h"
30 #include "editdlgtypes.h"
31 #ifdef USE_AKONADI
32 #include "collectionmodel.h"
33 #else
34 #include "eventlistmodel.h"
35 #endif
36 #include "functions.h"
37 #include "kamail.h"
38 #include "mainwindow.h"
39 #include "messagebox.h"
40 #include "messagewin.h"
41 #include "preferences.h"
42 #include "prefdlg.h"
43 #include "shellprocess.h"
44 #include "startdaytimer.h"
45 #include "traywindow.h"
46 
47 #include "kspeechinterface.h"
48 
49 #include <kalarmcal/datetime.h>
50 #include <kalarmcal/karecurrence.h>
51 
52 #include <klocale.h>
53 #include <kstandarddirs.h>
54 #include <kconfig.h>
55 #include <kaboutdata.h>
56 #include <ktemporaryfile.h>
57 #include <kfileitem.h>
58 #include <kglobal.h>
59 #include <kstandardguiitem.h>
60 #include <kservicetypetrader.h>
61 #include <kspeech.h>
62 #include <ktoolinvocation.h>
63 #include <netwm.h>
64 #include <kdebug.h>
65 #include <kshell.h>
66 #include <ksystemtrayicon.h>
67 #include <ksystemtimezone.h>
68 
69 #include <QObject>
70 #include <QTimer>
71 #include <QRegExp>
72 #include <QFile>
73 #include <QByteArray>
74 #include <QTextStream>
75 #include <QtDBus/QtDBus>
76 
77 #include <stdlib.h>
78 #include <ctype.h>
79 #include <iostream>
80 #include <climits>
81 
82 static const QLatin1String KTTSD_DBUS_SERVICE("org.kde.kttsd");
83 static const QLatin1String KTTDS_DBUS_PATH("/KSpeech");
84 
85 #ifdef USE_AKONADI
86 static const int AKONADI_TIMEOUT = 30; // timeout (seconds) for Akonadi collections to be populated
87 #endif
88 
89 static void setEventCommandError(const KAEvent&, KAEvent::CmdErrType);
90 static void clearEventCommandError(const KAEvent&, KAEvent::CmdErrType);
91 
92 /******************************************************************************
93 * Find the maximum number of seconds late which a late-cancel alarm is allowed
94 * to be. This is calculated as the late cancel interval, plus a few seconds
95 * leeway to cater for any timing irregularities.
96 */
97 static inline int maxLateness(int lateCancel)
98 {
99  static const int LATENESS_LEEWAY = 5;
100  int lc = (lateCancel >= 1) ? (lateCancel - 1)*60 : 0;
101  return LATENESS_LEEWAY + lc;
102 }
103 
104 
105 KAlarmApp* KAlarmApp::theInstance = 0;
106 int KAlarmApp::mActiveCount = 0;
107 int KAlarmApp::mFatalError = 0;
108 QString KAlarmApp::mFatalMessage;
109 
110 
111 /******************************************************************************
112 * Construct the application.
113 */
114 KAlarmApp::KAlarmApp()
115  : KUniqueApplication(),
116  mInitialised(false),
117  mQuitting(false),
118  mReadOnly(false),
119  mLoginAlarmsDone(false),
120  mDBusHandler(new DBusHandler()),
121  mTrayWindow(0),
122  mAlarmTimer(0),
123  mArchivedPurgeDays(-1), // default to not purging
124  mPurgeDaysQueued(-1),
125  mKSpeech(0),
126  mPendingQuit(false),
127  mCancelRtcWake(false),
128  mProcessingQueue(false),
129  mSessionClosingDown(false),
130  mAlarmsEnabled(true),
131  mSpeechEnabled(false)
132 {
133  KGlobal::locale()->insertCatalog(QLatin1String("libkdepim"));
134  KGlobal::locale()->insertCatalog(QLatin1String("libkpimutils"));
135  kDebug();
136 #ifndef NDEBUG
137  KAlarm::setTestModeConditions();
138 #endif
139 
140  setQuitOnLastWindowClosed(false);
141  Preferences::self(); // read KAlarm configuration
142  if (!Preferences::noAutoStart())
143  {
144  Preferences::setAutoStart(true);
145  Preferences::self()->writeConfig();
146  }
147  Preferences::connect(SIGNAL(startOfDayChanged(QTime)), this, SLOT(changeStartOfDay()));
148  Preferences::connect(SIGNAL(workTimeChanged(QTime,QTime,QBitArray)), this, SLOT(slotWorkTimeChanged(QTime,QTime,QBitArray)));
149  Preferences::connect(SIGNAL(holidaysChanged(KHolidays::HolidayRegion)), this, SLOT(slotHolidaysChanged(KHolidays::HolidayRegion)));
150  Preferences::connect(SIGNAL(feb29TypeChanged(Feb29Type)), this, SLOT(slotFeb29TypeChanged(Feb29Type)));
151  Preferences::connect(SIGNAL(showInSystemTrayChanged(bool)), this, SLOT(slotShowInSystemTrayChanged()));
152  Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(setArchivePurgeDays()));
153  Preferences::connect(SIGNAL(messageFontChanged(QFont)), this, SLOT(slotMessageFontChanged(QFont)));
154  slotFeb29TypeChanged(Preferences::defaultFeb29Type());
155 
156  connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceUnregistered(QString)),
157  SLOT(slotDBusServiceUnregistered(QString)));
158  KAEvent::setStartOfDay(Preferences::startOfDay());
159  KAEvent::setWorkTime(Preferences::workDays(), Preferences::workDayStart(), Preferences::workDayEnd());
160  KAEvent::setHolidays(Preferences::holidays());
161  KAEvent::setDefaultFont(Preferences::messageFont());
162  if (initialise()) // initialise calendars and alarm timer
163  {
164 #ifdef USE_AKONADI
165  connect(AkonadiModel::instance(), SIGNAL(collectionAdded(Akonadi::Collection)),
166  SLOT(purgeNewArchivedDefault(Akonadi::Collection)));
167  connect(AkonadiModel::instance(), SIGNAL(collectionTreeFetched(Akonadi::Collection::List)),
168  SLOT(checkWritableCalendar()));
169  connect(AkonadiModel::instance(), SIGNAL(migrationCompleted()),
170  SLOT(checkWritableCalendar()));
171 #endif
172 
173  KConfigGroup config(KGlobal::config(), "General");
174  mNoSystemTray = config.readEntry("NoSystemTray", false);
175  mOldShowInSystemTray = wantShowInSystemTray();
176  DateTime::setStartOfDay(Preferences::startOfDay());
177  mPrefsArchivedColour = Preferences::archivedColour();
178  }
179 
180  // Check if the speech synthesis daemon is installed
181  mSpeechEnabled = (KServiceTypeTrader::self()->query(QLatin1String("DBUS/Text-to-Speech"), QLatin1String("Name == 'KTTSD'")).count() > 0);
182  if (!mSpeechEnabled) { kDebug() << "Speech synthesis disabled (KTTSD not found)"; }
183  // Check if KOrganizer is installed
184  const QString korg = QLatin1String("korganizer");
185  mKOrganizerEnabled = !KStandardDirs::locate("exe", korg).isNull() || !KStandardDirs::findExe(korg).isNull();
186  if (!mKOrganizerEnabled) { kDebug() << "KOrganizer options disabled (KOrganizer not found)"; }
187 }
188 
189 /******************************************************************************
190 */
191 KAlarmApp::~KAlarmApp()
192 {
193  while (!mCommandProcesses.isEmpty())
194  {
195  ProcData* pd = mCommandProcesses[0];
196  mCommandProcesses.pop_front();
197  delete pd;
198  }
199  AlarmCalendar::terminateCalendars();
200 }
201 
202 /******************************************************************************
203 * Return the one and only KAlarmApp instance.
204 * If it doesn't already exist, it is created first.
205 */
206 KAlarmApp* KAlarmApp::getInstance()
207 {
208  if (!theInstance)
209  {
210  theInstance = new KAlarmApp;
211 
212  if (mFatalError)
213  theInstance->quitFatal();
214  }
215  return theInstance;
216 }
217 
218 /******************************************************************************
219 * (Re)initialise things which are tidied up/closed by quitIf().
220 * Reinitialisation can be necessary if session restoration finds nothing to
221 * restore and starts quitting the application, but KAlarm then starts up again
222 * before the application has exited.
223 * Reply = true if calendars were initialised successfully,
224 * false if they were already initialised, or if initialisation failed.
225 */
226 bool KAlarmApp::initialise()
227 {
228  if (!mAlarmTimer)
229  {
230  mAlarmTimer = new QTimer(this);
231  mAlarmTimer->setSingleShot(true);
232  connect(mAlarmTimer, SIGNAL(timeout()), SLOT(checkNextDueAlarm()));
233  }
234  if (!AlarmCalendar::resources())
235  {
236  kDebug() << "initialising calendars";
237  if (AlarmCalendar::initialiseCalendars())
238  {
239  connect(AlarmCalendar::resources(), SIGNAL(earliestAlarmChanged()), SLOT(checkNextDueAlarm()));
240 #ifdef USE_AKONADI
241  connect(AlarmCalendar::resources(), SIGNAL(atLoginEventAdded(KAEvent)), SLOT(atLoginEventAdded(KAEvent)));
242 #endif
243  return true;
244  }
245  }
246  return false;
247 }
248 
249 /******************************************************************************
250 * Restore the saved session if required.
251 */
252 bool KAlarmApp::restoreSession()
253 {
254  if (!isSessionRestored())
255  return false;
256  if (mFatalError)
257  {
258  quitFatal();
259  return false;
260  }
261 
262  // Process is being restored by session management.
263  kDebug() << "Restoring";
264  ++mActiveCount;
265  // Create the session config object now.
266  // This is necessary since if initCheck() below causes calendars to be updated,
267  // the session config created after that points to an invalid file, resulting
268  // in no windows being restored followed by a later crash.
269  kapp->sessionConfig();
270 
271  // When KAlarm is session restored, automatically set start-at-login to true.
272  Preferences::self()->readConfig();
273  Preferences::setAutoStart(true);
274  Preferences::setNoAutoStart(false);
275  Preferences::setAskAutoStart(true); // cancel any start-at-login prompt suppression
276  Preferences::self()->writeConfig();
277 
278  if (!initCheck(true)) // open the calendar file (needed for main windows), don't process queue yet
279  {
280  --mActiveCount;
281  quitIf(1, true); // error opening the main calendar - quit
282  return false;
283  }
284  MainWindow* trayParent = 0;
285  for (int i = 1; KMainWindow::canBeRestored(i); ++i)
286  {
287  QString type = KMainWindow::classNameOfToplevel(i);
288  if (type == QLatin1String("MainWindow"))
289  {
290  MainWindow* win = MainWindow::create(true);
291  win->restore(i, false);
292  if (win->isHiddenTrayParent())
293  trayParent = win;
294  else
295  win->show();
296  }
297  else if (type == QLatin1String("MessageWin"))
298  {
299  MessageWin* win = new MessageWin;
300  win->restore(i, false);
301  if (win->isValid())
302  win->show();
303  else
304  delete win;
305  }
306  }
307 
308  // Try to display the system tray icon if it is configured to be shown
309  if (trayParent || wantShowInSystemTray())
310  {
311  if (!MainWindow::count())
312  kWarning() << "no main window to be restored!?";
313  else
314  {
315  displayTrayIcon(true, trayParent);
316  // Occasionally for no obvious reason, the main main window is
317  // shown when it should be hidden, so hide it just to be sure.
318  if (trayParent)
319  trayParent->hide();
320  }
321  }
322 
323  --mActiveCount;
324  if (quitIf(0)) // quit if no windows are open
325  return false; // quitIf() can sometimes return, despite calling exit()
326 
327  // Check whether the KDE time zone daemon is running (but don't hold up initialisation)
328  QTimer::singleShot(0, this, SLOT(checkKtimezoned()));
329 
330  startProcessQueue(); // start processing the execution queue
331  return true;
332 }
333 
334 /******************************************************************************
335 * Called for a KUniqueApplication when a new instance of the application is
336 * started.
337 */
338 int KAlarmApp::newInstance()
339 {
340  kDebug();
341  if (mFatalError)
342  {
343  quitFatal();
344  return 1;
345  }
346  ++mActiveCount;
347  int exitCode = 0; // default = success
348  static bool firstInstance = true;
349  bool dontRedisplay = false;
350  if (!firstInstance || !isSessionRestored())
351  {
352  CommandOptions options; // fetch and parse command line options
353 #ifndef NDEBUG
354  if (options.simulationTime().isValid())
355  KAlarm::setSimulatedSystemTime(options.simulationTime());
356 #endif
357  CommandOptions::Command command = options.command();
358  if (options.disableAll())
359  setAlarmsEnabled(false); // disable alarm monitoring
360  switch (command)
361  {
362  case CommandOptions::TRIGGER_EVENT:
363  case CommandOptions::CANCEL_EVENT:
364  {
365  // Display or delete the event with the specified event ID
366  EventFunc function = (command == CommandOptions::TRIGGER_EVENT) ? EVENT_TRIGGER : EVENT_CANCEL;
367  // Open the calendar, don't start processing execution queue yet,
368  // and wait for the Akonadi collection to be populated.
369 #ifdef USE_AKONADI
370  if (!initCheck(true, true, options.eventId().collectionId()))
371 #else
372  if (!initCheck(true))
373 #endif
374  exitCode = 1;
375  else
376  {
377  startProcessQueue(); // start processing the execution queue
378  dontRedisplay = true;
379 #ifdef USE_AKONADI
380  if (!handleEvent(options.eventId(), function, true))
381 #else
382  if (!handleEvent(options.eventId(), function))
383 #endif
384  {
385 #ifdef USE_AKONADI
386  CommandOptions::printError(i18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not unique", QLatin1String("--") + options.commandName(), options.eventId().eventId()));
387 #else
388  CommandOptions::printError(i18nc("@info:shell", "%1: Event <resource>%2</resource> not found", QLatin1String("--") + options.commandName(), options.eventId()));
389 #endif
390  exitCode = 1;
391  }
392  }
393  break;
394  }
395  case CommandOptions::LIST:
396  // Output a list of scheduled alarms to stdout.
397  // Open the calendar, don't start processing execution queue yet,
398  // and wait for all Akonadi collections to be populated.
399  mReadOnly = true; // don't need write access to calendars
400 #ifdef USE_AKONADI
401  if (!initCheck(true, true))
402 #else
403  if (!initCheck(true))
404 #endif
405  exitCode = 1;
406  else
407  {
408  dontRedisplay = true;
409  QStringList alarms = scheduledAlarmList();
410  for (int i = 0, count = alarms.count(); i < count; ++i)
411  std::cout << alarms[i].toUtf8().constData() << std::endl;
412  }
413  break;
414  case CommandOptions::EDIT:
415  // Edit a specified existing alarm.
416  // Open the calendar and wait for the Akonadi collection to be populated.
417 #ifdef USE_AKONADI
418  if (!initCheck(false, true, options.eventId().collectionId()))
419 #else
420  if (!initCheck())
421 #endif
422  exitCode = 1;
423  else if (!KAlarm::editAlarmById(options.eventId()))
424  {
425 #ifdef USE_AKONADI
426  CommandOptions::printError(i18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", QLatin1String("--") + options.commandName(), options.eventId().eventId()));
427 #else
428  CommandOptions::printError(i18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", QLatin1String("--") + options.commandName(), options.eventId()));
429 #endif
430  exitCode = 1;
431  }
432  break;
433 
434  case CommandOptions::EDIT_NEW:
435  {
436  // Edit a new alarm, and optionally preset selected values
437  if (!initCheck())
438  exitCode = 1;
439  else
440  {
441  // Use AutoQPointer to guard against crash on application exit while
442  // the dialogue is still open. It prevents double deletion (both on
443  // deletion of parent, and on return from this function).
444  AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(false, options.editType());
445  if (options.alarmTime().isValid())
446  editDlg->setTime(options.alarmTime());
447  if (options.recurrence())
448  editDlg->setRecurrence(*options.recurrence(), options.subRepeatInterval(), options.subRepeatCount());
449  else if (options.flags() & KAEvent::REPEAT_AT_LOGIN)
450  editDlg->setRepeatAtLogin();
451  editDlg->setAction(options.editAction(), AlarmText(options.text()));
452  if (options.lateCancel())
453  editDlg->setLateCancel(options.lateCancel());
454  if (options.flags() & KAEvent::COPY_KORGANIZER)
455  editDlg->setShowInKOrganizer(true);
456  switch (options.editType())
457  {
458  case EditAlarmDlg::DISPLAY:
459  {
460  // EditAlarmDlg::create() always returns EditDisplayAlarmDlg for type = DISPLAY
461  EditDisplayAlarmDlg* dlg = qobject_cast<EditDisplayAlarmDlg*>(editDlg);
462  if (options.fgColour().isValid())
463  dlg->setFgColour(options.fgColour());
464  if (options.bgColour().isValid())
465  dlg->setBgColour(options.bgColour());
466  if (!options.audioFile().isEmpty()
467  || options.flags() & (KAEvent::BEEP | KAEvent::SPEAK))
468  {
469  KAEvent::Flags flags = options.flags();
470  Preferences::SoundType type = (flags & KAEvent::BEEP) ? Preferences::Sound_Beep
471  : (flags & KAEvent::SPEAK) ? Preferences::Sound_Speak
472  : Preferences::Sound_File;
473  dlg->setAudio(type, options.audioFile(), options.audioVolume(), (flags & KAEvent::REPEAT_SOUND ? 0 : -1));
474  }
475  if (options.reminderMinutes())
476  dlg->setReminder(options.reminderMinutes(), (options.flags() & KAEvent::REMINDER_ONCE));
477  if (options.flags() & KAEvent::CONFIRM_ACK)
478  dlg->setConfirmAck(true);
479  if (options.flags() & KAEvent::AUTO_CLOSE)
480  dlg->setAutoClose(true);
481  break;
482  }
483  case EditAlarmDlg::COMMAND:
484  break;
485  case EditAlarmDlg::EMAIL:
486  {
487  // EditAlarmDlg::create() always returns EditEmailAlarmDlg for type = EMAIL
488  EditEmailAlarmDlg* dlg = qobject_cast<EditEmailAlarmDlg*>(editDlg);
489  if (options.fromID()
490  || !options.addressees().isEmpty()
491  || !options.subject().isEmpty()
492  || !options.attachments().isEmpty())
493  dlg->setEmailFields(options.fromID(), options.addressees(), options.subject(), options.attachments());
494  if (options.flags() & KAEvent::EMAIL_BCC)
495  dlg->setBcc(true);
496  break;
497  }
498  case EditAlarmDlg::AUDIO:
499  {
500  // EditAlarmDlg::create() always returns EditAudioAlarmDlg for type = AUDIO
501  EditAudioAlarmDlg* dlg = qobject_cast<EditAudioAlarmDlg*>(editDlg);
502  if (!options.audioFile().isEmpty() || options.audioVolume() >= 0)
503  dlg->setAudio(options.audioFile(), options.audioVolume());
504  break;
505  }
506  case EditAlarmDlg::NO_TYPE:
507  break;
508  }
509  KAlarm::execNewAlarmDlg(editDlg);
510  }
511  break;
512  }
513  case CommandOptions::EDIT_NEW_PRESET:
514  // Edit a new alarm, preset with a template
515  if (!initCheck())
516  exitCode = 1;
517  else
518  KAlarm::editNewAlarm(options.templateName());
519  break;
520 
521  case CommandOptions::NEW:
522  // Display a message or file, execute a command, or send an email
523  if (!initCheck()
524  || !scheduleEvent(options.editAction(), options.text(), options.alarmTime(),
525  options.lateCancel(), options.flags(), options.bgColour(),
526  options.fgColour(), QFont(), options.audioFile(), options.audioVolume(),
527  options.reminderMinutes(), (options.recurrence() ? *options.recurrence() : KARecurrence()),
528  options.subRepeatInterval(), options.subRepeatCount(),
529  options.fromID(), options.addressees(),
530  options.subject(), options.attachments()))
531  exitCode = 1;
532  break;
533 
534  case CommandOptions::TRAY:
535  // Display only the system tray icon
536  if (Preferences::showInSystemTray() && KSystemTrayIcon::isSystemTrayAvailable())
537  {
538  if (!initCheck() // open the calendar, start processing execution queue
539  || !displayTrayIcon(true))
540  exitCode = 1;
541  break;
542  }
543  // fall through to NONE
544  case CommandOptions::NONE:
545  // No arguments - run interactively & display the main window
546 #ifndef NDEBUG
547  if (options.simulationTime().isValid() && !firstInstance)
548  break; // simulating time: don't open main window if already running
549 #endif
550  if (!initCheck())
551  exitCode = 1;
552  else
553  {
554  MainWindow* win = MainWindow::create();
555  if (command == CommandOptions::TRAY)
556  win->setWindowState(win->windowState() | Qt::WindowMinimized);
557  win->show();
558  }
559  break;
560 
561  case CommandOptions::CMD_ERROR:
562  mReadOnly = true; // don't need write access to calendars
563  exitCode = 1;
564  break;
565  }
566  }
567 
568  // If this is the first time through, redisplay any alarm message windows
569  // from last time.
570  if (firstInstance && !dontRedisplay && !exitCode)
571  {
572  /* First time through, so redisplay alarm message windows from last time.
573  * But it is possible for session restoration in some circumstances to
574  * not create any windows, in which case the alarm calendars will have
575  * been deleted - if so, don't try to do anything. (This has been known
576  * to happen under the Xfce desktop.)
577  */
578  if (AlarmCalendar::resources())
579  MessageWin::redisplayAlarms();
580  }
581 
582  --mActiveCount;
583  firstInstance = false;
584 
585  // Quit the application if this was the last/only running "instance" of the program.
586  // Executing 'return' doesn't work very well since the program continues to
587  // run if no windows were created.
588  quitIf(exitCode);
589 
590  // Check whether the KDE time zone daemon is running (but don't hold up initialisation)
591  QTimer::singleShot(0, this, SLOT(checkKtimezoned()));
592 
593  return exitCode;
594 }
595 
596 void KAlarmApp::checkKtimezoned()
597 {
598  // Check that the KDE time zone daemon is running
599  static bool done = false;
600  if (done)
601  return;
602  done = true;
603 #if KDE_IS_VERSION(4,5,70)
604  if (!KSystemTimeZones::isTimeZoneDaemonAvailable())
605  {
606  kDebug() << "ktimezoned not running: using UTC only";
607  KAMessageBox::information(MainWindow::mainMainWindow(),
608  i18nc("@info", "Time zones are not accessible:<nl/>KAlarm will use the UTC time zone.<nl/><nl/>(The KDE time zone service is not available:<nl/>check that <application>ktimezoned</application> is installed.)"),
609  QString(), QLatin1String("tzunavailable"));
610  }
611 #endif
612 }
613 
614 /******************************************************************************
615 * Quit the program, optionally only if there are no more "instances" running.
616 * Reply = true if program exited.
617 */
618 bool KAlarmApp::quitIf(int exitCode, bool force)
619 {
620  if (force)
621  {
622  // Quit regardless, except for message windows
623  mQuitting = true;
624  MainWindow::closeAll();
625  mQuitting = false;
626  displayTrayIcon(false);
627  if (MessageWin::instanceCount(true)) // ignore always-hidden windows (e.g. audio alarms)
628  return false;
629  }
630  else if (mQuitting)
631  return false; // MainWindow::closeAll() causes quitIf() to be called again
632  else
633  {
634  // Quit only if there are no more "instances" running
635  mPendingQuit = false;
636  if (mActiveCount > 0 || MessageWin::instanceCount(true)) // ignore always-hidden windows (e.g. audio alarms)
637  return false;
638  int mwcount = MainWindow::count();
639  MainWindow* mw = mwcount ? MainWindow::firstWindow() : 0;
640  if (mwcount > 1 || (mwcount && (!mw->isHidden() || !mw->isTrayParent())))
641  return false;
642  // There are no windows left except perhaps a main window which is a hidden
643  // tray icon parent, or an always-hidden message window.
644  if (mTrayWindow)
645  {
646  // There is a system tray icon.
647  // Don't exit unless the system tray doesn't seem to exist.
648  if (checkSystemTray())
649  return false;
650  }
651  if (!mActionQueue.isEmpty() || !mCommandProcesses.isEmpty())
652  {
653  // Don't quit yet if there are outstanding actions on the execution queue
654  mPendingQuit = true;
655  mPendingQuitCode = exitCode;
656  return false;
657  }
658  }
659 
660  // This was the last/only running "instance" of the program, so exit completely.
661  // NOTE: Everything which is terminated/deleted here must where applicable
662  // be initialised in the initialise() method, in case KAlarm is
663  // started again before application exit completes!
664  kDebug() << exitCode << ": quitting";
665  MessageWin::stopAudio(true);
666  if (mCancelRtcWake)
667  {
668  KAlarm::setRtcWakeTime(0, 0);
669  KAlarm::deleteRtcWakeConfig();
670  }
671  delete mAlarmTimer; // prevent checking for alarms after deleting calendars
672  mAlarmTimer = 0;
673  mInitialised = false; // prevent processQueue() from running
674  AlarmCalendar::terminateCalendars();
675  exit(exitCode);
676  return true; // sometimes we actually get to here, despite calling exit()
677 }
678 
679 /******************************************************************************
680 * Called when the Quit menu item is selected.
681 * Closes the system tray window and all main windows, but does not exit the
682 * program if other windows are still open.
683 */
684 void KAlarmApp::doQuit(QWidget* parent)
685 {
686  kDebug();
687  if (KAMessageBox::warningContinueCancel(parent, KMessageBox::Cancel,
688  i18nc("@info", "Quitting will disable alarms (once any alarm message windows are closed)."),
689  QString(), KStandardGuiItem::quit(), Preferences::QUIT_WARN
690  ) != KMessageBox::Yes)
691  return;
692  if (!KAlarm::checkRtcWakeConfig(true).isEmpty())
693  {
694  // A wake-on-suspend alarm is set
695  if (KAMessageBox::warningContinueCancel(parent, KMessageBox::Cancel,
696  i18nc("@info", "Quitting will cancel the scheduled Wake from Suspend."),
697  QString(), KStandardGuiItem::quit()
698  ) != KMessageBox::Yes)
699  return;
700  mCancelRtcWake = true;
701  }
702  if (!Preferences::autoStart())
703  {
704  int option = KMessageBox::No;
705  if (!Preferences::autoStartChangedByUser())
706  {
707  option = KAMessageBox::questionYesNoCancel(parent,
708  i18nc("@info", "Do you want to start KAlarm at login?<nl/>"
709  "(Note that alarms will be disabled if KAlarm is not started.)"),
710  QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(),
711  KStandardGuiItem::cancel(), Preferences::ASK_AUTO_START);
712  }
713  switch (option)
714  {
715  case KMessageBox::Yes:
716  Preferences::setAutoStart(true);
717  Preferences::setNoAutoStart(false);
718  break;
719  case KMessageBox::No:
720  Preferences::setNoAutoStart(true);
721  break;
722  case KMessageBox::Cancel:
723  default:
724  return;
725  }
726  Preferences::self()->writeConfig();
727  }
728  quitIf(0, true);
729 }
730 
731 /******************************************************************************
732 * Called when the session manager is about to close down the application.
733 */
734 void KAlarmApp::commitData(QSessionManager& sm)
735 {
736  mSessionClosingDown = true;
737  KUniqueApplication::commitData(sm);
738  mSessionClosingDown = false; // reset in case shutdown is cancelled
739 }
740 
741 /******************************************************************************
742 * Display an error message for a fatal error. Prevent further actions since
743 * the program state is unsafe.
744 */
745 void KAlarmApp::displayFatalError(const QString& message)
746 {
747  if (!mFatalError)
748  {
749  mFatalError = 1;
750  mFatalMessage = message;
751  if (theInstance)
752  QTimer::singleShot(0, theInstance, SLOT(quitFatal()));
753  }
754 }
755 
756 /******************************************************************************
757 * Quit the program, once the fatal error message has been acknowledged.
758 */
759 void KAlarmApp::quitFatal()
760 {
761  switch (mFatalError)
762  {
763  case 0:
764  case 2:
765  return;
766  case 1:
767  mFatalError = 2;
768  KMessageBox::error(0, mFatalMessage); // this is an application modal window
769  mFatalError = 3;
770  // fall through to '3'
771  case 3:
772  if (theInstance)
773  theInstance->quitIf(1, true);
774  break;
775  }
776  QTimer::singleShot(1000, this, SLOT(quitFatal()));
777 }
778 
779 /******************************************************************************
780 * Called by the alarm timer when the next alarm is due.
781 * Also called when the execution queue has finished processing to check for the
782 * next alarm.
783 */
784 void KAlarmApp::checkNextDueAlarm()
785 {
786  if (!mAlarmsEnabled)
787  return;
788  // Find the first alarm due
789  KAEvent* nextEvent = AlarmCalendar::resources()->earliestAlarm();
790  if (!nextEvent)
791  return; // there are no alarms pending
792  KDateTime nextDt = nextEvent->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
793  KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
794  qint64 interval = now.secsTo_long(nextDt);
795  kDebug() << "now:" << qPrintable(now.toString(QLatin1String("%Y-%m-%d %H:%M %:Z"))) << ", next:" << qPrintable(nextDt.toString(QLatin1String("%Y-%m-%d %H:%M %:Z"))) << ", due:" << interval;
796  if (interval <= 0)
797  {
798  // Queue the alarm
799  queueAlarmId(*nextEvent);
800  kDebug() << nextEvent->id() << ": due now";
801  QTimer::singleShot(0, this, SLOT(processQueue()));
802  }
803  else
804  {
805  // No alarm is due yet, so set timer to wake us when it's due.
806  // Check for integer overflow before setting timer.
807 #ifndef HIBERNATION_SIGNAL
808  /* TODO: REPLACE THIS CODE WHEN A SYSTEM NOTIFICATION SIGNAL BECOMES
809  * AVAILABLE FOR WAKEUP FROM HIBERNATION.
810  * Re-evaluate the next alarm time every minute, in case the
811  * system clock jumps. The most common case when the clock jumps
812  * is when a laptop wakes from hibernation. If timers were left to
813  * run, they would trigger late by the length of time the system
814  * was asleep.
815  */
816  if (interval > 60) // 1 minute
817  interval = 60;
818 #endif
819  interval *= 1000;
820  if (interval > INT_MAX)
821  interval = INT_MAX;
822  kDebug() << nextEvent->id() << "wait" << interval/1000 << "seconds";
823  mAlarmTimer->start(static_cast<int>(interval));
824  }
825 }
826 
827 /******************************************************************************
828 * Called by the alarm timer when the next alarm is due.
829 * Also called when the execution queue has finished processing to check for the
830 * next alarm.
831 */
832 void KAlarmApp::queueAlarmId(const KAEvent& event)
833 {
834 #ifdef USE_AKONADI
835  EventId id(event);
836 #else
837  const QString id(event.id());
838 #endif
839  for (int i = 0, end = mActionQueue.count(); i < end; ++i)
840  {
841  if (mActionQueue[i].function == EVENT_HANDLE && mActionQueue[i].eventId == id)
842  return; // the alarm is already queued
843  }
844  mActionQueue.enqueue(ActionQEntry(EVENT_HANDLE, id));
845 }
846 
847 /******************************************************************************
848 * Start processing the execution queue.
849 */
850 void KAlarmApp::startProcessQueue()
851 {
852  if (!mInitialised)
853  {
854  kDebug();
855  mInitialised = true;
856  QTimer::singleShot(0, this, SLOT(processQueue())); // process anything already queued
857  }
858 }
859 
860 /******************************************************************************
861 * The main processing loop for KAlarm.
862 * All KAlarm operations involving opening or updating calendar files are called
863 * from this loop to ensure that only one operation is active at any one time.
864 * This precaution is necessary because KAlarm's activities are mostly
865 * asynchronous, being in response to D-Bus calls from other programs or timer
866 * events, any of which can be received in the middle of performing another
867 * operation. If a calendar file is opened or updated while another calendar
868 * operation is in progress, the program has been observed to hang, or the first
869 * calendar call has failed with data loss - clearly unacceptable!!
870 */
871 void KAlarmApp::processQueue()
872 {
873  if (mInitialised && !mProcessingQueue)
874  {
875  kDebug();
876  mProcessingQueue = true;
877 
878  // Refresh alarms if that's been queued
879  KAlarm::refreshAlarmsIfQueued();
880 
881  if (!mLoginAlarmsDone)
882  {
883  // Queue all at-login alarms once only, at program start-up.
884  // First, cancel any scheduled reminders or deferrals for them,
885  // since these will be superseded by the new at-login trigger.
886  KAEvent::List events = AlarmCalendar::resources()->atLoginAlarms();
887  for (int i = 0, end = events.count(); i < end; ++i)
888  {
889  KAEvent event = *events[i];
890  if (!cancelReminderAndDeferral(event))
891  {
892  if (mAlarmsEnabled)
893  queueAlarmId(event);
894  }
895  }
896  mLoginAlarmsDone = true;
897  }
898 
899  // Process queued events
900  while (!mActionQueue.isEmpty())
901  {
902  ActionQEntry& entry = mActionQueue.head();
903  if (entry.eventId.isEmpty())
904  {
905  // It's a new alarm
906  switch (entry.function)
907  {
908  case EVENT_TRIGGER:
909  execAlarm(entry.event, entry.event.firstAlarm(), false);
910  break;
911  case EVENT_HANDLE:
912  KAlarm::addEvent(entry.event, 0, 0, KAlarm::ALLOW_KORG_UPDATE | KAlarm::NO_RESOURCE_PROMPT);
913  break;
914  case EVENT_CANCEL:
915  break;
916  }
917  }
918  else
919  handleEvent(entry.eventId, entry.function);
920  mActionQueue.dequeue();
921  }
922 
923  // Purge the default archived alarms resource if it's time to do so
924  if (mPurgeDaysQueued >= 0)
925  {
926  KAlarm::purgeArchive(mPurgeDaysQueued);
927  mPurgeDaysQueued = -1;
928  }
929 
930  // Now that the queue has been processed, quit if a quit was queued
931  if (mPendingQuit)
932  {
933  if (quitIf(mPendingQuitCode))
934  return; // quitIf() can sometimes return, despite calling exit()
935  }
936 
937  mProcessingQueue = false;
938 
939  // Schedule the application to be woken when the next alarm is due
940  checkNextDueAlarm();
941  }
942 }
943 
944 #ifdef USE_AKONADI
945 /******************************************************************************
946 * Called when a repeat-at-login alarm has been added externally.
947 * Queues the alarm for triggering.
948 * First, cancel any scheduled reminder or deferral for it, since these will be
949 * superseded by the new at-login trigger.
950 */
951 void KAlarmApp::atLoginEventAdded(const KAEvent& event)
952 {
953  KAEvent ev = event;
954  if (!cancelReminderAndDeferral(ev))
955  {
956  if (mAlarmsEnabled)
957  {
958  mActionQueue.enqueue(ActionQEntry(EVENT_HANDLE, EventId(ev)));
959  if (mInitialised)
960  QTimer::singleShot(0, this, SLOT(processQueue()));
961  }
962  }
963 }
964 #endif
965 
966 /******************************************************************************
967 * Called when the system tray main window is closed.
968 */
969 void KAlarmApp::removeWindow(TrayWindow*)
970 {
971  mTrayWindow = 0;
972 }
973 
974 /******************************************************************************
975 * Display or close the system tray icon.
976 */
977 bool KAlarmApp::displayTrayIcon(bool show, MainWindow* parent)
978 {
979  kDebug();
980  static bool creating = false;
981  if (show)
982  {
983  if (!mTrayWindow && !creating)
984  {
985  if (!KSystemTrayIcon::isSystemTrayAvailable())
986  return false;
987  if (!MainWindow::count())
988  {
989  // We have to have at least one main window to act
990  // as parent to the system tray icon (even if the
991  // window is hidden).
992  creating = true; // prevent main window constructor from creating an additional tray icon
993  parent = MainWindow::create();
994  creating = false;
995  }
996  mTrayWindow = new TrayWindow(parent ? parent : MainWindow::firstWindow());
997  connect(mTrayWindow, SIGNAL(deleted()), SIGNAL(trayIconToggled()));
998  emit trayIconToggled();
999 
1000  if (!checkSystemTray())
1001  quitIf(0); // exit the application if there are no open windows
1002  }
1003  }
1004  else
1005  {
1006  delete mTrayWindow;
1007  mTrayWindow = 0;
1008  }
1009  return true;
1010 }
1011 
1012 /******************************************************************************
1013 * Check whether the system tray icon has been housed in the system tray.
1014 */
1015 bool KAlarmApp::checkSystemTray()
1016 {
1017  if (!mTrayWindow)
1018  return true;
1019  if (KSystemTrayIcon::isSystemTrayAvailable() == mNoSystemTray)
1020  {
1021  kDebug() << "changed ->" << mNoSystemTray;
1022  mNoSystemTray = !mNoSystemTray;
1023 
1024  // Store the new setting in the config file, so that if KAlarm exits it will
1025  // restart with the correct default.
1026  KConfigGroup config(KGlobal::config(), "General");
1027  config.writeEntry("NoSystemTray", mNoSystemTray);
1028  config.sync();
1029 
1030  // Update other settings
1031  slotShowInSystemTrayChanged();
1032  }
1033  return !mNoSystemTray;
1034 }
1035 
1036 /******************************************************************************
1037 * Return the main window associated with the system tray icon.
1038 */
1039 MainWindow* KAlarmApp::trayMainWindow() const
1040 {
1041  return mTrayWindow ? mTrayWindow->assocMainWindow() : 0;
1042 }
1043 
1044 /******************************************************************************
1045 * Called when the show-in-system-tray preference setting has changed, to show
1046 * or hide the system tray icon.
1047 */
1048 void KAlarmApp::slotShowInSystemTrayChanged()
1049 {
1050  bool newShowInSysTray = wantShowInSystemTray();
1051  if (newShowInSysTray != mOldShowInSystemTray)
1052  {
1053  // The system tray run mode has changed
1054  ++mActiveCount; // prevent the application from quitting
1055  MainWindow* win = mTrayWindow ? mTrayWindow->assocMainWindow() : 0;
1056  delete mTrayWindow; // remove the system tray icon if it is currently shown
1057  mTrayWindow = 0;
1058  mOldShowInSystemTray = newShowInSysTray;
1059  if (newShowInSysTray)
1060  {
1061  // Show the system tray icon
1062  displayTrayIcon(true);
1063  }
1064  else
1065  {
1066  // Stop showing the system tray icon
1067  if (win && win->isHidden())
1068  {
1069  if (MainWindow::count() > 1)
1070  delete win;
1071  else
1072  {
1073  win->setWindowState(win->windowState() | Qt::WindowMinimized);
1074  win->show();
1075  }
1076  }
1077  }
1078  --mActiveCount;
1079  }
1080 }
1081 
1082 /******************************************************************************
1083 * Called when the start-of-day time preference setting has changed.
1084 * Change alarm times for date-only alarms.
1085 */
1086 void KAlarmApp::changeStartOfDay()
1087 {
1088  DateTime::setStartOfDay(Preferences::startOfDay());
1089  KAEvent::setStartOfDay(Preferences::startOfDay());
1090  AlarmCalendar::resources()->adjustStartOfDay();
1091 }
1092 
1093 /******************************************************************************
1094 * Called when the default alarm message font preference setting has changed.
1095 * Notify KAEvent.
1096 */
1097 void KAlarmApp::slotMessageFontChanged(const QFont& font)
1098 {
1099  KAEvent::setDefaultFont(font);
1100 }
1101 
1102 /******************************************************************************
1103 * Called when the working time preference settings have changed.
1104 * Notify KAEvent.
1105 */
1106 void KAlarmApp::slotWorkTimeChanged(const QTime& start, const QTime& end, const QBitArray& days)
1107 {
1108  KAEvent::setWorkTime(days, start, end);
1109 }
1110 
1111 /******************************************************************************
1112 * Called when the holiday region preference setting has changed.
1113 * Notify KAEvent.
1114 */
1115 void KAlarmApp::slotHolidaysChanged(const KHolidays::HolidayRegion& holidays)
1116 {
1117  KAEvent::setHolidays(holidays);
1118 }
1119 
1120 /******************************************************************************
1121 * Called when the date for February 29th recurrences has changed in the
1122 * preferences settings.
1123 */
1124 void KAlarmApp::slotFeb29TypeChanged(Preferences::Feb29Type type)
1125 {
1126  KARecurrence::Feb29Type rtype;
1127  switch (type)
1128  {
1129  default:
1130  case Preferences::Feb29_None: rtype = KARecurrence::Feb29_None; break;
1131  case Preferences::Feb29_Feb28: rtype = KARecurrence::Feb29_Feb28; break;
1132  case Preferences::Feb29_Mar1: rtype = KARecurrence::Feb29_Mar1; break;
1133  }
1134  KARecurrence::setDefaultFeb29Type(rtype);
1135 }
1136 
1137 /******************************************************************************
1138 * Return whether the program is configured to be running in the system tray.
1139 */
1140 bool KAlarmApp::wantShowInSystemTray() const
1141 {
1142  return Preferences::showInSystemTray() && KSystemTrayIcon::isSystemTrayAvailable();
1143 }
1144 
1145 /******************************************************************************
1146 * Called when all calendars have been fetched at startup.
1147 * Check whether there are any writable active calendars, and if not, warn the
1148 * user.
1149 */
1150 void KAlarmApp::checkWritableCalendar()
1151 {
1152 kDebug();
1153  if (mReadOnly)
1154  return; // don't need write access to calendars
1155 #ifdef USE_AKONADI
1156  if (!AkonadiModel::instance()->isCollectionTreeFetched()
1157  || !AkonadiModel::instance()->isMigrationCompleted())
1158  return;
1159 #endif
1160  static bool done = false;
1161  if (done)
1162  return;
1163  done = true;
1164 kDebug()<<"checking";
1165  // Find whether there are any writable active alarm calendars
1166 #ifdef USE_AKONADI
1167  bool active = !CollectionControlModel::enabledCollections(CalEvent::ACTIVE, true).isEmpty();
1168 #else
1169  bool active = AlarmResources::instance()->activeCount(CalEvent::ACTIVE, true);
1170 #endif
1171  if (!active)
1172  {
1173  kWarning() << "No writable active calendar";
1174  KAMessageBox::information(MainWindow::mainMainWindow(),
1175  i18nc("@info", "Alarms cannot be created or updated, because no writable active alarm calendar is enabled.<nl/><nl/>"
1176  "To fix this, use <interface>View | Show Calendars</interface> to check or change calendar statuses."),
1177  QString(), QLatin1String("noWritableCal"));
1178  }
1179 }
1180 
1181 #ifdef USE_AKONADI
1182 /******************************************************************************
1183 * Called when a new collection has been added, or when a collection has been
1184 * set as the standard collection for its type.
1185 * If it is the default archived calendar, purge its old alarms if necessary.
1186 */
1187 void KAlarmApp::purgeNewArchivedDefault(const Akonadi::Collection& collection)
1188 {
1189  Akonadi::Collection col(collection);
1190  if (CollectionControlModel::isStandard(col, CalEvent::ARCHIVED))
1191  {
1192  // Allow time (1 minute) for AkonadiModel to be populated with the
1193  // collection's events before purging it.
1194  kDebug() << collection.id() << ": standard archived...";
1195  QTimer::singleShot(60000, this, SLOT(purgeAfterDelay()));
1196  }
1197 }
1198 
1199 /******************************************************************************
1200 * Called after a delay, after the default archived calendar has been added to
1201 * AkonadiModel.
1202 * Purge old alarms from it if necessary.
1203 */
1204 void KAlarmApp::purgeAfterDelay()
1205 {
1206  if (mArchivedPurgeDays >= 0)
1207  purge(mArchivedPurgeDays);
1208  else
1209  setArchivePurgeDays();
1210 }
1211 #endif
1212 
1213 /******************************************************************************
1214 * Called when the length of time to keep archived alarms changes in KAlarm's
1215 * preferences.
1216 * Set the number of days to keep archived alarms.
1217 * Alarms which are older are purged immediately, and at the start of each day.
1218 */
1219 void KAlarmApp::setArchivePurgeDays()
1220 {
1221  int newDays = Preferences::archivedKeepDays();
1222  if (newDays != mArchivedPurgeDays)
1223  {
1224  int oldDays = mArchivedPurgeDays;
1225  mArchivedPurgeDays = newDays;
1226  if (mArchivedPurgeDays <= 0)
1227  StartOfDayTimer::disconnect(this);
1228  if (mArchivedPurgeDays < 0)
1229  return; // keep indefinitely, so don't purge
1230  if (oldDays < 0 || mArchivedPurgeDays < oldDays)
1231  {
1232  // Alarms are now being kept for less long, so purge them
1233  purge(mArchivedPurgeDays);
1234  if (!mArchivedPurgeDays)
1235  return; // don't archive any alarms
1236  }
1237  // Start the purge timer to expire at the start of the next day
1238  // (using the user-defined start-of-day time).
1239  StartOfDayTimer::connect(this, SLOT(slotPurge()));
1240  }
1241 }
1242 
1243 /******************************************************************************
1244 * Purge all archived events from the calendar whose end time is longer ago than
1245 * 'daysToKeep'. All events are deleted if 'daysToKeep' is zero.
1246 */
1247 void KAlarmApp::purge(int daysToKeep)
1248 {
1249  if (mPurgeDaysQueued < 0 || daysToKeep < mPurgeDaysQueued)
1250  mPurgeDaysQueued = daysToKeep;
1251 
1252  // Do the purge once any other current operations are completed
1253  processQueue();
1254 }
1255 
1256 
1257 /******************************************************************************
1258 * Output a list of pending alarms, with their next scheduled occurrence.
1259 */
1260 QStringList KAlarmApp::scheduledAlarmList()
1261 {
1262 #ifdef USE_AKONADI
1263  QVector<KAEvent> events = KAlarm::getSortedActiveEvents(this);
1264 #else
1265  KAEvent::List events = KAlarm::getSortedActiveEvents();
1266 #endif
1267  QStringList alarms;
1268  for (int i = 0, count = events.count(); i < count; ++i)
1269  {
1270 #ifdef USE_AKONADI
1271  KAEvent* event = &events[i];
1272 #else
1273  KAEvent* event = events[i];
1274 #endif
1275  KDateTime dateTime = event->nextTrigger(KAEvent::DISPLAY_TRIGGER).effectiveKDateTime().toLocalZone();
1276 #ifdef USE_AKONADI
1277  Akonadi::Collection c(event->collectionId());
1278  AkonadiModel::instance()->refresh(c);
1279  QString text(c.resource() + QLatin1String(":"));
1280 #else
1281  QString text;
1282 #endif
1283  text += event->id() + QLatin1Char(' ')
1284  + dateTime.toString(QLatin1String("%Y%m%dT%H%M "))
1285  + AlarmText::summary(*event, 1);
1286  alarms << text;
1287  }
1288  return alarms;
1289 }
1290 
1291 /******************************************************************************
1292 * Enable or disable alarm monitoring.
1293 */
1294 void KAlarmApp::setAlarmsEnabled(bool enabled)
1295 {
1296  if (enabled != mAlarmsEnabled)
1297  {
1298  mAlarmsEnabled = enabled;
1299  emit alarmEnabledToggled(enabled);
1300  if (!enabled)
1301  KAlarm::cancelRtcWake(0);
1302  else if (!mProcessingQueue)
1303  checkNextDueAlarm();
1304  }
1305 }
1306 
1307 /******************************************************************************
1308 * Spread or collect alarm message and error message windows.
1309 */
1310 void KAlarmApp::spreadWindows(bool spread)
1311 {
1312  spread = MessageWin::spread(spread);
1313  emit spreadWindowsToggled(spread);
1314 }
1315 
1316 /******************************************************************************
1317 * Called when the spread status of message windows changes.
1318 * Set the 'spread windows' action state.
1319 */
1320 void KAlarmApp::setSpreadWindowsState(bool spread)
1321 {
1322  emit spreadWindowsToggled(spread);
1323 }
1324 
1325 /******************************************************************************
1326 * Called to schedule a new alarm, either in response to a DCOP notification or
1327 * to command line options.
1328 * Reply = true unless there was a parameter error or an error opening calendar file.
1329 */
1330 bool KAlarmApp::scheduleEvent(KAEvent::SubAction action, const QString& text, const KDateTime& dateTime,
1331  int lateCancel, KAEvent::Flags flags, const QColor& bg, const QColor& fg,
1332  const QFont& font, const QString& audioFile, float audioVolume, int reminderMinutes,
1333  const KARecurrence& recurrence, int repeatInterval, int repeatCount,
1334 #ifdef USE_AKONADI
1335  uint mailFromID, const KCalCore::Person::List& mailAddresses,
1336 #else
1337  uint mailFromID, const QList<KCal::Person>& mailAddresses,
1338 #endif
1339  const QString& mailSubject, const QStringList& mailAttachments)
1340 {
1341  kDebug() << text;
1342  if (!dateTime.isValid())
1343  return false;
1344  KDateTime now = KDateTime::currentUtcDateTime();
1345  if (lateCancel && dateTime < now.addSecs(-maxLateness(lateCancel)))
1346  return true; // alarm time was already archived too long ago
1347  KDateTime alarmTime = dateTime;
1348  // Round down to the nearest minute to avoid scheduling being messed up
1349  if (!dateTime.isDateOnly())
1350  alarmTime.setTime(QTime(alarmTime.time().hour(), alarmTime.time().minute(), 0));
1351 
1352  KAEvent event(alarmTime, text, bg, fg, font, action, lateCancel, flags, true);
1353  if (reminderMinutes)
1354  {
1355  bool onceOnly = flags & KAEvent::REMINDER_ONCE;
1356  event.setReminder(reminderMinutes, onceOnly);
1357  }
1358  if (!audioFile.isEmpty())
1359  event.setAudioFile(audioFile, audioVolume, -1, 0, (flags & KAEvent::REPEAT_SOUND) ? 0 : -1);
1360  if (!mailAddresses.isEmpty())
1361  event.setEmail(mailFromID, mailAddresses, mailSubject, mailAttachments);
1362  event.setRecurrence(recurrence);
1363  event.setFirstRecurrence();
1364  event.setRepetition(Repetition(repeatInterval, repeatCount - 1));
1365  event.endChanges();
1366  if (alarmTime <= now)
1367  {
1368  // Alarm is due for display already.
1369  // First execute it once without adding it to the calendar file.
1370  if (!mInitialised)
1371  mActionQueue.enqueue(ActionQEntry(event, EVENT_TRIGGER));
1372  else
1373  execAlarm(event, event.firstAlarm(), false);
1374  // If it's a recurring alarm, reschedule it for its next occurrence
1375  if (!event.recurs()
1376  || event.setNextOccurrence(now) == KAEvent::NO_OCCURRENCE)
1377  return true;
1378  // It has recurrences in the future
1379  }
1380 
1381  // Queue the alarm for insertion into the calendar file
1382  mActionQueue.enqueue(ActionQEntry(event));
1383  if (mInitialised)
1384  QTimer::singleShot(0, this, SLOT(processQueue()));
1385  return true;
1386 }
1387 
1388 /******************************************************************************
1389 * Called in response to a D-Bus request to trigger or cancel an event.
1390 * Optionally display the event. Delete the event from the calendar file and
1391 * from every main window instance.
1392 */
1393 #ifdef USE_AKONADI
1394 bool KAlarmApp::dbusHandleEvent(const EventId& eventID, EventFunc function)
1395 #else
1396 bool KAlarmApp::dbusHandleEvent(const QString& eventID, EventFunc function)
1397 #endif
1398 {
1399  kDebug() << eventID;
1400  mActionQueue.append(ActionQEntry(function, eventID));
1401  if (mInitialised)
1402  QTimer::singleShot(0, this, SLOT(processQueue()));
1403  return true;
1404 }
1405 
1406 /******************************************************************************
1407 * Called in response to a D-Bus request to list all pending alarms.
1408 */
1409 QString KAlarmApp::dbusList()
1410 {
1411  kDebug();
1412  return scheduledAlarmList().join(QLatin1String("\n")) + QLatin1Char('\n');
1413 }
1414 
1415 /******************************************************************************
1416 * Either:
1417 * a) Display the event and then delete it if it has no outstanding repetitions.
1418 * b) Delete the event.
1419 * c) Reschedule the event for its next repetition. If none remain, delete it.
1420 * If the event is deleted, it is removed from the calendar file and from every
1421 * main window instance.
1422 * Reply = false if event ID not found, or if more than one event with the same
1423 * ID is found.
1424 */
1425 #ifdef USE_AKONADI
1426 bool KAlarmApp::handleEvent(const EventId& id, EventFunc function, bool checkDuplicates)
1427 #else
1428 bool KAlarmApp::handleEvent(const QString& eventID, EventFunc function)
1429 #endif
1430 {
1431  // Delete any expired wake-on-suspend config data
1432  KAlarm::checkRtcWakeConfig();
1433 
1434 #ifdef USE_AKONADI
1435  const QString eventID(id.eventId());
1436  KAEvent* event = AlarmCalendar::resources()->event(id, checkDuplicates);
1437  if (!event)
1438  {
1439  if (id.collectionId() != -1)
1440  kWarning() << "Event ID not found, or duplicated:" << eventID;
1441  else
1442  kWarning() << "Event ID not found:" << eventID;
1443  return false;
1444  }
1445 #else
1446  KAEvent* event = AlarmCalendar::resources()->event(eventID);
1447  if (!event)
1448  {
1449  kWarning() << "Event ID not found:" << eventID;
1450  return false;
1451  }
1452 #endif
1453  switch (function)
1454  {
1455  case EVENT_CANCEL:
1456  kDebug() << eventID << ", CANCEL";
1457  KAlarm::deleteEvent(*event, true);
1458  break;
1459 
1460  case EVENT_TRIGGER: // handle it if it's due, else execute it regardless
1461  case EVENT_HANDLE: // handle it if it's due
1462  {
1463  KDateTime now = KDateTime::currentUtcDateTime();
1464  kDebug() << eventID << "," << (function==EVENT_TRIGGER?"TRIGGER:":"HANDLE:") << qPrintable(now.dateTime().toString(QLatin1String("yyyy-MM-dd hh:mm"))) << "UTC";
1465  bool updateCalAndDisplay = false;
1466  bool alarmToExecuteValid = false;
1467  KAAlarm alarmToExecute;
1468  bool restart = false;
1469  // Check all the alarms in turn.
1470  // Note that the main alarm is fetched before any other alarms.
1471  for (KAAlarm alarm = event->firstAlarm();
1472  alarm.isValid();
1473  alarm = (restart ? event->firstAlarm() : event->nextAlarm(alarm)), restart = false)
1474  {
1475  // Check if the alarm is due yet.
1476  KDateTime nextDT = alarm.dateTime(true).effectiveKDateTime();
1477  int secs = nextDT.secsTo(now);
1478  if (secs < 0)
1479  {
1480  // The alarm appears to be in the future.
1481  // Check if it's an invalid local clock time during a daylight
1482  // saving time shift, which has actually passed.
1483  if (alarm.dateTime().timeSpec() != KDateTime::ClockTime
1484  || nextDT > now.toTimeSpec(KDateTime::ClockTime))
1485  {
1486  // This alarm is definitely not due yet
1487  kDebug() << "Alarm" << alarm.type() << "at" << nextDT.dateTime() << ": not due";
1488  continue;
1489  }
1490  }
1491  bool reschedule = false;
1492  bool rescheduleWork = false;
1493  if ((event->workTimeOnly() || event->holidaysExcluded()) && !alarm.deferred())
1494  {
1495  // The alarm is restricted to working hours and/or non-holidays
1496  // (apart from deferrals). This needs to be re-evaluated every
1497  // time it triggers, since working hours could change.
1498  if (alarm.dateTime().isDateOnly())
1499  {
1500  KDateTime dt(nextDT);
1501  dt.setDateOnly(true);
1502  reschedule = !event->isWorkingTime(dt);
1503  }
1504  else
1505  reschedule = !event->isWorkingTime(nextDT);
1506  rescheduleWork = reschedule;
1507  if (reschedule)
1508  kDebug() << "Alarm" << alarm.type() << "at" << nextDT.dateTime() << ": not during working hours";
1509  }
1510  if (!reschedule && alarm.repeatAtLogin())
1511  {
1512  // Alarm is to be displayed at every login.
1513  kDebug() << "REPEAT_AT_LOGIN";
1514  // Check if the main alarm is already being displayed.
1515  // (We don't want to display both at the same time.)
1516  if (alarmToExecute.isValid())
1517  continue;
1518 
1519  // Set the time to display if it's a display alarm
1520  alarm.setTime(now);
1521  }
1522  if (!reschedule && event->lateCancel())
1523  {
1524  // Alarm is due, and it is to be cancelled if too late.
1525  kDebug() << "LATE_CANCEL";
1526  bool cancel = false;
1527  if (alarm.dateTime().isDateOnly())
1528  {
1529  // The alarm has no time, so cancel it if its date is too far past
1530  int maxlate = event->lateCancel() / 1440; // maximum lateness in days
1531  KDateTime limit(DateTime(nextDT.addDays(maxlate + 1)).effectiveKDateTime());
1532  if (now >= limit)
1533  {
1534  // It's too late to display the scheduled occurrence.
1535  // Find the last previous occurrence of the alarm.
1536  DateTime next;
1537  KAEvent::OccurType type = event->previousOccurrence(now, next, true);
1538  switch (type & ~KAEvent::OCCURRENCE_REPEAT)
1539  {
1540  case KAEvent::FIRST_OR_ONLY_OCCURRENCE:
1541  case KAEvent::RECURRENCE_DATE:
1542  case KAEvent::RECURRENCE_DATE_TIME:
1543  case KAEvent::LAST_RECURRENCE:
1544  limit.setDate(next.date().addDays(maxlate + 1));
1545  if (now >= limit)
1546  {
1547  if (type == KAEvent::LAST_RECURRENCE
1548  || (type == KAEvent::FIRST_OR_ONLY_OCCURRENCE && !event->recurs()))
1549  cancel = true; // last occurrence (and there are no repetitions)
1550  else
1551  reschedule = true;
1552  }
1553  break;
1554  case KAEvent::NO_OCCURRENCE:
1555  default:
1556  reschedule = true;
1557  break;
1558  }
1559  }
1560  }
1561  else
1562  {
1563  // The alarm is timed. Allow it to be the permitted amount late before cancelling it.
1564  int maxlate = maxLateness(event->lateCancel());
1565  if (secs > maxlate)
1566  {
1567  // It's over the maximum interval late.
1568  // Find the most recent occurrence of the alarm.
1569  DateTime next;
1570  KAEvent::OccurType type = event->previousOccurrence(now, next, true);
1571  switch (type & ~KAEvent::OCCURRENCE_REPEAT)
1572  {
1573  case KAEvent::FIRST_OR_ONLY_OCCURRENCE:
1574  case KAEvent::RECURRENCE_DATE:
1575  case KAEvent::RECURRENCE_DATE_TIME:
1576  case KAEvent::LAST_RECURRENCE:
1577  if (next.effectiveKDateTime().secsTo(now) > maxlate)
1578  {
1579  if (type == KAEvent::LAST_RECURRENCE
1580  || (type == KAEvent::FIRST_OR_ONLY_OCCURRENCE && !event->recurs()))
1581  cancel = true; // last occurrence (and there are no repetitions)
1582  else
1583  reschedule = true;
1584  }
1585  break;
1586  case KAEvent::NO_OCCURRENCE:
1587  default:
1588  reschedule = true;
1589  break;
1590  }
1591  }
1592  }
1593 
1594  if (cancel)
1595  {
1596  // All recurrences are finished, so cancel the event
1597  event->setArchive();
1598  if (cancelAlarm(*event, alarm.type(), false))
1599  return true; // event has been deleted
1600  updateCalAndDisplay = true;
1601  continue;
1602  }
1603  }
1604  if (reschedule)
1605  {
1606  // The latest repetition was too long ago, so schedule the next one
1607  switch (rescheduleAlarm(*event, alarm, false, (rescheduleWork ? nextDT : KDateTime())))
1608  {
1609  case 1:
1610  // A working-time-only alarm has been rescheduled and the
1611  // rescheduled time is already due. Start processing the
1612  // event again.
1613  alarmToExecuteValid = false;
1614  restart = true;
1615  break;
1616  case -1:
1617  return true; // event has been deleted
1618  default:
1619  break;
1620  }
1621  updateCalAndDisplay = true;
1622  continue;
1623  }
1624  if (!alarmToExecuteValid)
1625  {
1626  kDebug() << "Alarm" << alarm.type() << ": execute";
1627  alarmToExecute = alarm; // note the alarm to be displayed
1628  alarmToExecuteValid = true; // only trigger one alarm for the event
1629  }
1630  else
1631  kDebug() << "Alarm" << alarm.type() << ": skip";
1632  }
1633 
1634  // If there is an alarm to execute, do this last after rescheduling/cancelling
1635  // any others. This ensures that the updated event is only saved once to the calendar.
1636  if (alarmToExecute.isValid())
1637  execAlarm(*event, alarmToExecute, true, !alarmToExecute.repeatAtLogin());
1638  else
1639  {
1640  if (function == EVENT_TRIGGER)
1641  {
1642  // The alarm is to be executed regardless of whether it's due.
1643  // Only trigger one alarm from the event - we don't want multiple
1644  // identical messages, for example.
1645  KAAlarm alarm = event->firstAlarm();
1646  if (alarm.isValid())
1647  execAlarm(*event, alarm, false);
1648  }
1649  if (updateCalAndDisplay)
1650  KAlarm::updateEvent(*event); // update the window lists and calendar file
1651  else if (function != EVENT_TRIGGER) { kDebug() << "No action"; }
1652  }
1653  break;
1654  }
1655  }
1656  return true;
1657 }
1658 
1659 /******************************************************************************
1660 * Called when an alarm action has completed, to perform any post-alarm actions.
1661 */
1662 void KAlarmApp::alarmCompleted(const KAEvent& event)
1663 {
1664  if (!event.postAction().isEmpty())
1665  {
1666  // doShellCommand() will error if the user is not authorised to run
1667  // shell commands.
1668  QString command = event.postAction();
1669  kDebug() << event.id() << ":" << command;
1670  doShellCommand(command, event, 0, ProcData::POST_ACTION);
1671  }
1672 }
1673 
1674 /******************************************************************************
1675 * Reschedule the alarm for its next recurrence after now. If none remain,
1676 * delete it. If the alarm is deleted and it is the last alarm for its event,
1677 * the event is removed from the calendar file and from every main window
1678 * instance.
1679 * If 'nextDt' is valid, the event is rescheduled for the next non-working
1680 * time occurrence after that.
1681 * Reply = 1 if 'nextDt' is valid and the rescheduled event is already due
1682 * = -1 if the event has been deleted
1683 * = 0 otherwise.
1684 */
1685 int KAlarmApp::rescheduleAlarm(KAEvent& event, const KAAlarm& alarm, bool updateCalAndDisplay, const KDateTime& nextDt)
1686 {
1687  kDebug() << "Alarm type:" << alarm.type();
1688  int reply = 0;
1689  bool update = false;
1690  event.startChanges();
1691  if (alarm.repeatAtLogin())
1692  {
1693  // Leave an alarm which repeats at every login until its main alarm triggers
1694  if (!event.reminderActive() && event.reminderMinutes() < 0)
1695  {
1696  // Executing an at-login alarm: first schedule the reminder
1697  // which occurs AFTER the main alarm.
1698  event.activateReminderAfter(KDateTime::currentUtcDateTime());
1699  update = true;
1700  }
1701  }
1702  else if (alarm.isReminder() || alarm.deferred())
1703  {
1704  // It's a reminder alarm or an extra deferred alarm, so delete it
1705  event.removeExpiredAlarm(alarm.type());
1706  update = true;
1707  }
1708  else
1709  {
1710  // Reschedule the alarm for its next occurrence.
1711  bool cancelled = false;
1712  DateTime last = event.mainDateTime(false); // note this trigger time
1713  if (last != event.mainDateTime(true))
1714  last = DateTime(); // but ignore sub-repetition triggers
1715  bool next = nextDt.isValid();
1716  KDateTime next_dt = nextDt;
1717  KDateTime now = KDateTime::currentUtcDateTime();
1718  do
1719  {
1720  KAEvent::OccurType type = event.setNextOccurrence(next ? next_dt : now);
1721  switch (type)
1722  {
1723  case KAEvent::NO_OCCURRENCE:
1724  // All repetitions are finished, so cancel the event
1725  kDebug() << "No occurrence";
1726  if (event.reminderMinutes() < 0 && last.isValid()
1727  && alarm.type() != KAAlarm::AT_LOGIN_ALARM && !event.mainExpired())
1728  {
1729  // Set the reminder which is now due after the last main alarm trigger.
1730  // Note that at-login reminders are scheduled in execAlarm().
1731  event.activateReminderAfter(last);
1732  updateCalAndDisplay = true;
1733  }
1734  if (cancelAlarm(event, alarm.type(), updateCalAndDisplay))
1735  return -1;
1736  break;
1737  default:
1738  if (!(type & KAEvent::OCCURRENCE_REPEAT))
1739  break;
1740  // Next occurrence is a repeat, so fall through to recurrence handling
1741  case KAEvent::RECURRENCE_DATE:
1742  case KAEvent::RECURRENCE_DATE_TIME:
1743  case KAEvent::LAST_RECURRENCE:
1744  // The event is due by now and repetitions still remain, so rewrite the event
1745  if (updateCalAndDisplay)
1746  update = true;
1747  break;
1748  case KAEvent::FIRST_OR_ONLY_OCCURRENCE:
1749  // The first occurrence is still due?!?, so don't do anything
1750  break;
1751  }
1752  if (cancelled)
1753  break;
1754  if (event.deferred())
1755  {
1756  // Just in case there's also a deferred alarm, ensure it's removed
1757  event.removeExpiredAlarm(KAAlarm::DEFERRED_ALARM);
1758  update = true;
1759  }
1760  if (next)
1761  {
1762  // The alarm is restricted to working hours and/or non-holidays.
1763  // Check if the calculated next time is valid.
1764  next_dt = event.mainDateTime(true).effectiveKDateTime();
1765  if (event.mainDateTime(false).isDateOnly())
1766  {
1767  KDateTime dt(next_dt);
1768  dt.setDateOnly(true);
1769  next = !event.isWorkingTime(dt);
1770  }
1771  else
1772  next = !event.isWorkingTime(next_dt);
1773  }
1774  } while (next && next_dt <= now);
1775  reply = (!cancelled && next_dt.isValid() && (next_dt <= now)) ? 1 : 0;
1776 
1777  if (event.reminderMinutes() < 0 && last.isValid()
1778  && alarm.type() != KAAlarm::AT_LOGIN_ALARM)
1779  {
1780  // Set the reminder which is now due after the last main alarm trigger.
1781  // Note that at-login reminders are scheduled in execAlarm().
1782  event.activateReminderAfter(last);
1783  }
1784  }
1785  event.endChanges();
1786  if (update)
1787  KAlarm::updateEvent(event); // update the window lists and calendar file
1788  return reply;
1789 }
1790 
1791 /******************************************************************************
1792 * Delete the alarm. If it is the last alarm for its event, the event is removed
1793 * from the calendar file and from every main window instance.
1794 * Reply = true if event has been deleted.
1795 */
1796 bool KAlarmApp::cancelAlarm(KAEvent& event, KAAlarm::Type alarmType, bool updateCalAndDisplay)
1797 {
1798  kDebug();
1799  if (alarmType == KAAlarm::MAIN_ALARM && !event.displaying() && event.toBeArchived())
1800  {
1801  // The event is being deleted. Save it in the archived resources first.
1802  KAEvent ev(event);
1803  KAlarm::addArchivedEvent(ev);
1804  }
1805  event.removeExpiredAlarm(alarmType);
1806  if (!event.alarmCount())
1807  {
1808  KAlarm::deleteEvent(event, false);
1809  return true;
1810  }
1811  if (updateCalAndDisplay)
1812  KAlarm::updateEvent(event); // update the window lists and calendar file
1813  return false;
1814 }
1815 
1816 /******************************************************************************
1817 * Cancel any reminder or deferred alarms in an repeat-at-login event.
1818 * This should be called when the event is first loaded.
1819 * If there are no more alarms left in the event, the event is removed from the
1820 * calendar file and from every main window instance.
1821 * Reply = true if event has been deleted.
1822 */
1823 bool KAlarmApp::cancelReminderAndDeferral(KAEvent& event)
1824 {
1825  return cancelAlarm(event, KAAlarm::REMINDER_ALARM, false)
1826  || cancelAlarm(event, KAAlarm::DEFERRED_REMINDER_ALARM, false)
1827  || cancelAlarm(event, KAAlarm::DEFERRED_ALARM, true);
1828 }
1829 
1830 /******************************************************************************
1831 * Execute an alarm by displaying its message or file, or executing its command.
1832 * Reply = ShellProcess instance if a command alarm
1833 * = MessageWin if an audio alarm
1834 * != 0 if successful
1835 * = -1 if execution has not completed
1836 * = 0 if the alarm is disabled, or if an error message was output.
1837 */
1838 void* KAlarmApp::execAlarm(KAEvent& event, const KAAlarm& alarm, bool reschedule, bool allowDefer, bool noPreAction)
1839 {
1840  if (!mAlarmsEnabled || !event.enabled())
1841  {
1842  // The event (or all events) is disabled
1843  kDebug() << event.id() << ": disabled";
1844  if (reschedule)
1845  rescheduleAlarm(event, alarm, true);
1846  return 0;
1847  }
1848 
1849  void* result = (void*)1;
1850  event.setArchive();
1851 
1852  switch (alarm.action())
1853  {
1854  case KAAlarm::COMMAND:
1855  if (!event.commandDisplay())
1856  {
1857  // execCommandAlarm() will error if the user is not authorised
1858  // to run shell commands.
1859  result = execCommandAlarm(event, alarm);
1860  if (reschedule)
1861  rescheduleAlarm(event, alarm, true);
1862  break;
1863  }
1864  // fall through to MESSAGE
1865  case KAAlarm::MESSAGE:
1866  case KAAlarm::FILE:
1867  {
1868  // Display a message, file or command output, provided that the same event
1869  // isn't already being displayed
1870 #ifdef USE_AKONADI
1871  MessageWin* win = MessageWin::findEvent(EventId(event));
1872 #else
1873  MessageWin* win = MessageWin::findEvent(event.id());
1874 #endif
1875  // Find if we're changing a reminder message to the real message
1876  bool reminder = (alarm.type() & KAAlarm::REMINDER_ALARM);
1877  bool replaceReminder = !reminder && win && (win->alarmType() & KAAlarm::REMINDER_ALARM);
1878  if (!reminder
1879  && (!event.deferred() || (event.extraActionOptions() & KAEvent::ExecPreActOnDeferral))
1880  && (replaceReminder || !win) && !noPreAction
1881  && !event.preAction().isEmpty())
1882  {
1883  // It's not a reminder alarm, and it's not a deferred alarm unless the
1884  // pre-alarm action applies to deferred alarms, and there is no message
1885  // window (other than a reminder window) currently displayed for this
1886  // alarm, and we need to execute a command before displaying the new window.
1887  //
1888  // NOTE: The pre-action is not executed for a recurring alarm if an
1889  // alarm message window for a previous occurrence is still visible.
1890  // Check whether the command is already being executed for this alarm.
1891  for (int i = 0, end = mCommandProcesses.count(); i < end; ++i)
1892  {
1893  ProcData* pd = mCommandProcesses[i];
1894  if (pd->event->id() == event.id() && (pd->flags & ProcData::PRE_ACTION))
1895  {
1896  kDebug() << "Already executing pre-DISPLAY command";
1897  return pd->process; // already executing - don't duplicate the action
1898  }
1899  }
1900 
1901  // doShellCommand() will error if the user is not authorised to run
1902  // shell commands.
1903  QString command = event.preAction();
1904  kDebug() << "Pre-DISPLAY command:" << command;
1905  int flags = (reschedule ? ProcData::RESCHEDULE : 0) | (allowDefer ? ProcData::ALLOW_DEFER : 0);
1906  if (doShellCommand(command, event, &alarm, (flags | ProcData::PRE_ACTION)))
1907  {
1908  AlarmCalendar::resources()->setAlarmPending(&event);
1909  return result; // display the message after the command completes
1910  }
1911  // Error executing command
1912  if (event.cancelOnPreActionError())
1913  {
1914  // Cancel the rest of the alarm execution
1915  kDebug() << event.id() << ": pre-action failed: cancelled";
1916  if (reschedule)
1917  rescheduleAlarm(event, alarm, true);
1918  return 0;
1919  }
1920  // Display the message even though it failed
1921  }
1922 
1923  if (!win)
1924  {
1925  // There isn't already a message for this event
1926  int flags = (reschedule ? 0 : MessageWin::NO_RESCHEDULE) | (allowDefer ? 0 : MessageWin::NO_DEFER);
1927  (new MessageWin(&event, alarm, flags))->show();
1928  }
1929  else if (replaceReminder)
1930  {
1931  // The caption needs to be changed from "Reminder" to "Message"
1932  win->cancelReminder(event, alarm);
1933  }
1934  else if (!win->hasDefer() && !alarm.repeatAtLogin())
1935  {
1936  // It's a repeat-at-login message with no Defer button,
1937  // which has now reached its final trigger time and needs
1938  // to be replaced with a new message.
1939  win->showDefer();
1940  win->showDateTime(event, alarm);
1941  }
1942  else
1943  {
1944  // Use the existing message window
1945  }
1946  if (win)
1947  {
1948  // Raise the existing message window and replay any sound
1949  win->repeat(alarm); // N.B. this reschedules the alarm
1950  }
1951  break;
1952  }
1953  case KAAlarm::EMAIL:
1954  {
1955  kDebug() << "EMAIL to:" << event.emailAddresses(QLatin1String(","));
1956  QStringList errmsgs;
1957  KAMail::JobData data(event, alarm, reschedule, (reschedule || allowDefer));
1958  data.queued = true;
1959  int ans = KAMail::send(data, errmsgs);
1960  if (ans)
1961  {
1962  // The email has either been sent or failed - not queued
1963  if (ans < 0)
1964  result = 0; // failure
1965  data.queued = false;
1966  emailSent(data, errmsgs, (ans > 0));
1967  }
1968  else
1969  {
1970  result = (void*)-1; // email has been queued
1971  }
1972  if (reschedule)
1973  rescheduleAlarm(event, alarm, true);
1974  break;
1975  }
1976  case KAAlarm::AUDIO:
1977  {
1978  // Play the sound, provided that the same event
1979  // isn't already playing
1980 #ifdef USE_AKONADI
1981  MessageWin* win = MessageWin::findEvent(EventId(event));
1982 #else
1983  MessageWin* win = MessageWin::findEvent(event.id());
1984 #endif
1985  if (!win)
1986  {
1987  // There isn't already a message for this event.
1988  int flags = (reschedule ? 0 : MessageWin::NO_RESCHEDULE) | MessageWin::ALWAYS_HIDE;
1989  win = new MessageWin(&event, alarm, flags);
1990  }
1991  else
1992  {
1993  // There's an existing message window: replay the sound
1994  win->repeat(alarm); // N.B. this reschedules the alarm
1995  }
1996  return win;
1997  }
1998  default:
1999  return 0;
2000  }
2001  return result;
2002 }
2003 
2004 /******************************************************************************
2005 * Called when sending an email has completed.
2006 */
2007 void KAlarmApp::emailSent(KAMail::JobData& data, const QStringList& errmsgs, bool copyerr)
2008 {
2009  if (!errmsgs.isEmpty())
2010  {
2011  // Some error occurred, although the email may have been sent successfully
2012  if (errmsgs.count() > 1)
2013  kDebug() << (copyerr ? "Copy error:" : "Failed:") << errmsgs[1];
2014  MessageWin::showError(data.event, data.alarm.dateTime(), errmsgs);
2015  }
2016  else if (data.queued)
2017  emit execAlarmSuccess();
2018 }
2019 
2020 /******************************************************************************
2021 * Execute the command specified in a command alarm.
2022 * To connect to the output ready signals of the process, specify a slot to be
2023 * called by supplying 'receiver' and 'slot' parameters.
2024 */
2025 ShellProcess* KAlarmApp::execCommandAlarm(const KAEvent& event, const KAAlarm& alarm, const QObject* receiver, const char* slot)
2026 {
2027  // doShellCommand() will error if the user is not authorised to run
2028  // shell commands.
2029  int flags = (event.commandXterm() ? ProcData::EXEC_IN_XTERM : 0)
2030  | (event.commandDisplay() ? ProcData::DISP_OUTPUT : 0);
2031  QString command = event.cleanText();
2032  if (event.commandScript())
2033  {
2034  // Store the command script in a temporary file for execution
2035  kDebug() << "Script";
2036  QString tmpfile = createTempScriptFile(command, false, event, alarm);
2037  if (tmpfile.isEmpty())
2038  {
2039  setEventCommandError(event, KAEvent::CMD_ERROR);
2040  return 0;
2041  }
2042  return doShellCommand(tmpfile, event, &alarm, (flags | ProcData::TEMP_FILE), receiver, slot);
2043  }
2044  else
2045  {
2046  kDebug() << command;
2047  return doShellCommand(command, event, &alarm, flags, receiver, slot);
2048  }
2049 }
2050 
2051 /******************************************************************************
2052 * Execute a shell command line specified by an alarm.
2053 * If the PRE_ACTION bit of 'flags' is set, the alarm will be executed via
2054 * execAlarm() once the command completes, the execAlarm() parameters being
2055 * derived from the remaining bits in 'flags'.
2056 * 'flags' must contain the bit PRE_ACTION or POST_ACTION if and only if it is
2057 * a pre- or post-alarm action respectively.
2058 * To connect to the output ready signals of the process, specify a slot to be
2059 * called by supplying 'receiver' and 'slot' parameters.
2060 *
2061 * Note that if shell access is not authorised, the attempt to run the command
2062 * will be errored.
2063 */
2064 ShellProcess* KAlarmApp::doShellCommand(const QString& command, const KAEvent& event, const KAAlarm* alarm, int flags, const QObject* receiver, const char* slot)
2065 {
2066  kDebug() << command << "," << event.id();
2067  QIODevice::OpenMode mode = QIODevice::WriteOnly;
2068  QString cmd;
2069  QString tmpXtermFile;
2070  if (flags & ProcData::EXEC_IN_XTERM)
2071  {
2072  // Execute the command in a terminal window.
2073  cmd = composeXTermCommand(command, event, alarm, flags, tmpXtermFile);
2074  }
2075  else
2076  {
2077  cmd = command;
2078  mode = QIODevice::ReadWrite;
2079  }
2080 
2081  ProcData* pd = 0;
2082  ShellProcess* proc = 0;
2083  if (!cmd.isEmpty())
2084  {
2085  // Use ShellProcess, which automatically checks whether the user is
2086  // authorised to run shell commands.
2087  proc = new ShellProcess(cmd);
2088  proc->setEnv(QLatin1String("KALARM_UID"), event.id(), true);
2089  proc->setOutputChannelMode(KProcess::MergedChannels); // combine stdout & stderr
2090  connect(proc, SIGNAL(shellExited(ShellProcess*)), SLOT(slotCommandExited(ShellProcess*)));
2091  if ((flags & ProcData::DISP_OUTPUT) && receiver && slot)
2092  {
2093  connect(proc, SIGNAL(receivedStdout(ShellProcess*)), receiver, slot);
2094  connect(proc, SIGNAL(receivedStderr(ShellProcess*)), receiver, slot);
2095  }
2096  if (mode == QIODevice::ReadWrite && !event.logFile().isEmpty())
2097  {
2098  // Output is to be appended to a log file.
2099  // Set up a logging process to write the command's output to.
2100  QString heading;
2101  if (alarm && alarm->dateTime().isValid())
2102  {
2103  QString dateTime = alarm->dateTime().formatLocale();
2104  heading.sprintf("\n******* KAlarm %s *******\n", dateTime.toLatin1().data());
2105  }
2106  else
2107  heading = QLatin1String("\n******* KAlarm *******\n");
2108  QFile logfile(event.logFile());
2109  if (logfile.open(QIODevice::Append | QIODevice::Text))
2110  {
2111  QTextStream out(&logfile);
2112  out << heading;
2113  logfile.close();
2114  }
2115  proc->setStandardOutputFile(event.logFile(), QIODevice::Append);
2116  }
2117  pd = new ProcData(proc, new KAEvent(event), (alarm ? new KAAlarm(*alarm) : 0), flags);
2118  if (flags & ProcData::TEMP_FILE)
2119  pd->tempFiles += command;
2120  if (!tmpXtermFile.isEmpty())
2121  pd->tempFiles += tmpXtermFile;
2122  mCommandProcesses.append(pd);
2123  if (proc->start(mode))
2124  return proc;
2125  }
2126 
2127  // Error executing command - report it
2128  kWarning() << "Command failed to start";
2129  commandErrorMsg(proc, event, alarm, flags);
2130  if (pd)
2131  {
2132  mCommandProcesses.removeAt(mCommandProcesses.indexOf(pd));
2133  delete pd;
2134  }
2135  return 0;
2136 }
2137 
2138 /******************************************************************************
2139 * Compose a command line to execute the given command in a terminal window.
2140 * 'tempScriptFile' receives the name of a temporary script file which is
2141 * invoked by the command line, if applicable.
2142 * Reply = command line, or empty string if error.
2143 */
2144 QString KAlarmApp::composeXTermCommand(const QString& command, const KAEvent& event, const KAAlarm* alarm, int flags, QString& tempScriptFile) const
2145 {
2146  kDebug() << command << "," << event.id();
2147  tempScriptFile.clear();
2148  QString cmd = Preferences::cmdXTermCommand();
2149  cmd.replace(QLatin1String("%t"), KGlobal::mainComponent().aboutData()->programName()); // set the terminal window title
2150  if (cmd.indexOf(QLatin1String("%C")) >= 0)
2151  {
2152  // Execute the command from a temporary script file
2153  if (flags & ProcData::TEMP_FILE)
2154  cmd.replace(QLatin1String("%C"), command); // the command is already calling a temporary file
2155  else
2156  {
2157  tempScriptFile = createTempScriptFile(command, true, event, *alarm);
2158  if (tempScriptFile.isEmpty())
2159  return QString();
2160  cmd.replace(QLatin1String("%C"), tempScriptFile); // %C indicates where to insert the command
2161  }
2162  }
2163  else if (cmd.indexOf(QLatin1String("%W")) >= 0)
2164  {
2165  // Execute the command from a temporary script file,
2166  // with a sleep after the command is executed
2167  tempScriptFile = createTempScriptFile(command + QLatin1String("\nsleep 86400\n"), true, event, *alarm);
2168  if (tempScriptFile.isEmpty())
2169  return QString();
2170  cmd.replace(QLatin1String("%W"), tempScriptFile); // %w indicates where to insert the command
2171  }
2172  else if (cmd.indexOf(QLatin1String("%w")) >= 0)
2173  {
2174  // Append a sleep to the command.
2175  // Quote the command in case it contains characters such as [>|;].
2176  QString exec = KShell::quoteArg(command + QLatin1String("; sleep 86400"));
2177  cmd.replace(QLatin1String("%w"), exec); // %w indicates where to insert the command string
2178  }
2179  else
2180  {
2181  // Set the command to execute.
2182  // Put it in quotes in case it contains characters such as [>|;].
2183  QString exec = KShell::quoteArg(command);
2184  if (cmd.indexOf(QLatin1String("%c")) >= 0)
2185  cmd.replace(QLatin1String("%c"), exec); // %c indicates where to insert the command string
2186  else
2187  cmd.append(exec); // otherwise, simply append the command string
2188  }
2189  return cmd;
2190 }
2191 
2192 /******************************************************************************
2193 * Create a temporary script file containing the specified command string.
2194 * Reply = path of temporary file, or null string if error.
2195 */
2196 QString KAlarmApp::createTempScriptFile(const QString& command, bool insertShell, const KAEvent& event, const KAAlarm& alarm) const
2197 {
2198  KTemporaryFile tmpFile;
2199  tmpFile.setAutoRemove(false); // don't delete file when it is destructed
2200  if (!tmpFile.open())
2201  kError() << "Unable to create a temporary script file";
2202  else
2203  {
2204  tmpFile.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser);
2205  QTextStream stream(&tmpFile);
2206  if (insertShell)
2207  stream << "#!" << ShellProcess::shellPath() << "\n";
2208  stream << command;
2209  stream.flush();
2210  if (tmpFile.error() != QFile::NoError)
2211  kError() << "Error" << tmpFile.errorString() << " writing to temporary script file";
2212  else
2213  return tmpFile.fileName();
2214  }
2215 
2216  QStringList errmsgs(i18nc("@info", "Error creating temporary script file"));
2217  MessageWin::showError(event, alarm.dateTime(), errmsgs, QLatin1String("Script"));
2218  return QString();
2219 }
2220 
2221 /******************************************************************************
2222 * Called when a command alarm's execution completes.
2223 */
2224 void KAlarmApp::slotCommandExited(ShellProcess* proc)
2225 {
2226  kDebug();
2227  // Find this command in the command list
2228  for (int i = 0, end = mCommandProcesses.count(); i < end; ++i)
2229  {
2230  ProcData* pd = mCommandProcesses[i];
2231  if (pd->process == proc)
2232  {
2233  // Found the command. Check its exit status.
2234  bool executeAlarm = pd->preAction();
2235  ShellProcess::Status status = proc->status();
2236  if (status == ShellProcess::SUCCESS && !proc->exitCode())
2237  {
2238  kDebug() << pd->event->id() << ": SUCCESS";
2239  clearEventCommandError(*pd->event, pd->preAction() ? KAEvent::CMD_ERROR_PRE
2240  : pd->postAction() ? KAEvent::CMD_ERROR_POST
2241  : KAEvent::CMD_ERROR);
2242  }
2243  else
2244  {
2245  QString errmsg = proc->errorMessage();
2246  if (status == ShellProcess::SUCCESS || status == ShellProcess::NOT_FOUND)
2247  kWarning() << pd->event->id() << ":" << errmsg << "exit status =" << status << ", code =" << proc->exitCode();
2248  else
2249  kWarning() << pd->event->id() << ":" << errmsg << "exit status =" << status;
2250  if (pd->messageBoxParent)
2251  {
2252  // Close the existing informational KMessageBox for this process
2253  QList<KDialog*> dialogs = pd->messageBoxParent->findChildren<KDialog*>();
2254  if (!dialogs.isEmpty())
2255  delete dialogs[0];
2256  setEventCommandError(*pd->event, pd->preAction() ? KAEvent::CMD_ERROR_PRE
2257  : pd->postAction() ? KAEvent::CMD_ERROR_POST
2258  : KAEvent::CMD_ERROR);
2259  if (!pd->tempFile())
2260  {
2261  errmsg += QLatin1Char('\n');
2262  errmsg += proc->command();
2263  }
2264  KAMessageBox::error(pd->messageBoxParent, errmsg);
2265  }
2266  else
2267  commandErrorMsg(proc, *pd->event, pd->alarm, pd->flags);
2268 
2269  if (executeAlarm && pd->event->cancelOnPreActionError())
2270  {
2271  kDebug() << pd->event->id() << ": pre-action failed: cancelled";
2272  if (pd->reschedule())
2273  rescheduleAlarm(*pd->event, *pd->alarm, true);
2274  executeAlarm = false;
2275  }
2276  }
2277  if (pd->preAction())
2278  AlarmCalendar::resources()->setAlarmPending(pd->event, false);
2279  if (executeAlarm)
2280  execAlarm(*pd->event, *pd->alarm, pd->reschedule(), pd->allowDefer(), true);
2281  mCommandProcesses.removeAt(i);
2282  delete pd;
2283  break;
2284  }
2285  }
2286 
2287  // If there are now no executing shell commands, quit if a quit was queued
2288  if (mPendingQuit && mCommandProcesses.isEmpty())
2289  quitIf(mPendingQuitCode);
2290 }
2291 
2292 /******************************************************************************
2293 * Output an error message for a shell command, and record the alarm's error status.
2294 */
2295 void KAlarmApp::commandErrorMsg(const ShellProcess* proc, const KAEvent& event, const KAAlarm* alarm, int flags)
2296 {
2297  KAEvent::CmdErrType cmderr;
2298  QStringList errmsgs;
2299  QString dontShowAgain;
2300  if (flags & ProcData::PRE_ACTION)
2301  {
2302  if (event.dontShowPreActionError())
2303  return; // don't notify user of any errors for the alarm
2304  errmsgs += i18nc("@info", "Pre-alarm action:");
2305  dontShowAgain = QLatin1String("Pre");
2306  cmderr = KAEvent::CMD_ERROR_PRE;
2307  }
2308  else if (flags & ProcData::POST_ACTION)
2309  {
2310  errmsgs += i18nc("@info", "Post-alarm action:");
2311  dontShowAgain = QLatin1String("Post");
2312  cmderr = (event.commandError() == KAEvent::CMD_ERROR_PRE)
2313  ? KAEvent::CMD_ERROR_PRE_POST : KAEvent::CMD_ERROR_POST;
2314  }
2315  else
2316  {
2317  dontShowAgain = QLatin1String("Exec");
2318  cmderr = KAEvent::CMD_ERROR;
2319  }
2320 
2321  // Record the alarm's error status
2322  setEventCommandError(event, cmderr);
2323  // Display an error message
2324  if (proc)
2325  {
2326  errmsgs += proc->errorMessage();
2327  if (!(flags & ProcData::TEMP_FILE))
2328  errmsgs += proc->command();
2329  dontShowAgain += QString::number(proc->status());
2330  }
2331  MessageWin::showError(event, (alarm ? alarm->dateTime() : DateTime()), errmsgs, dontShowAgain);
2332 }
2333 
2334 /******************************************************************************
2335 * Notes that an informational KMessageBox is displayed for this process.
2336 */
2337 void KAlarmApp::commandMessage(ShellProcess* proc, QWidget* parent)
2338 {
2339  // Find this command in the command list
2340  for (int i = 0, end = mCommandProcesses.count(); i < end; ++i)
2341  {
2342  ProcData* pd = mCommandProcesses[i];
2343  if (pd->process == proc)
2344  {
2345  pd->messageBoxParent = parent;
2346  break;
2347  }
2348  }
2349 }
2350 
2351 /******************************************************************************
2352 * Return a D-Bus interface object for KSpeech.
2353 * The KTTSD D-Bus service is started if necessary.
2354 * If the service cannot be started, 'error' is set to an error text.
2355 */
2356 OrgKdeKSpeechInterface* KAlarmApp::kspeechInterface(QString& error) const
2357 {
2358  error.clear();
2359  QDBusConnection client = QDBusConnection::sessionBus();
2360  if (!client.interface()->isServiceRegistered(KTTSD_DBUS_SERVICE))
2361  {
2362  // kttsd is not running, so start it
2363  delete mKSpeech;
2364  mKSpeech = 0;
2365  if (KToolInvocation::startServiceByDesktopName(QLatin1String("kttsd"), QStringList(), &error))
2366  {
2367  kDebug() << "Failed to start kttsd:" << error;
2368  return 0;
2369  }
2370  }
2371  if (!mKSpeech)
2372  {
2373  mKSpeech = new OrgKdeKSpeechInterface(KTTSD_DBUS_SERVICE, KTTDS_DBUS_PATH, QDBusConnection::sessionBus());
2374  mKSpeech->setParent(theApp());
2375  mKSpeech->setApplicationName(KGlobal::mainComponent().aboutData()->programName());
2376  mKSpeech->setDefaultPriority(KSpeech::jpMessage);
2377  }
2378  return mKSpeech;
2379 }
2380 
2381 /******************************************************************************
2382 * Called when a D-Bus service unregisters.
2383 * If it's the KTTSD service, delete the KSpeech interface object.
2384 */
2385 void KAlarmApp::slotDBusServiceUnregistered(const QString& serviceName)
2386 {
2387  if (serviceName == KTTSD_DBUS_SERVICE)
2388  {
2389  delete mKSpeech;
2390  mKSpeech = 0;
2391  }
2392 }
2393 
2394 /******************************************************************************
2395 * If this is the first time through, open the calendar file, and start
2396 * processing the execution queue.
2397 */
2398 #ifdef USE_AKONADI
2399 bool KAlarmApp::initCheck(bool calendarOnly, bool waitForCollection, Akonadi::Collection::Id collectionId)
2400 #else
2401 bool KAlarmApp::initCheck(bool calendarOnly)
2402 #endif
2403 {
2404  static bool firstTime = true;
2405  if (firstTime)
2406  kDebug() << "first time";
2407 
2408  if (initialise() || firstTime)
2409  {
2410  /* Need to open the display calendar now, since otherwise if display
2411  * alarms are immediately due, they will often be processed while
2412  * MessageWin::redisplayAlarms() is executing open() (but before open()
2413  * completes), which causes problems!!
2414  */
2415  AlarmCalendar::displayCalendar()->open();
2416 
2417  if (!AlarmCalendar::resources()->open())
2418  return false;
2419  }
2420  if (firstTime)
2421  {
2422  setArchivePurgeDays();
2423 
2424  // Warn the user if there are no writable active alarm calendars
2425  checkWritableCalendar();
2426 
2427  firstTime = false;
2428  }
2429 
2430  if (!calendarOnly)
2431  startProcessQueue(); // start processing the execution queue
2432 
2433 #ifdef USE_AKONADI
2434  if (waitForCollection)
2435  {
2436 #if KDE_IS_VERSION(4,9,80)
2437  // Wait for one or all Akonadi collections to be populated
2438  if (!CollectionControlModel::instance()->waitUntilPopulated(collectionId, AKONADI_TIMEOUT))
2439  return false;
2440 #endif
2441  }
2442 #endif
2443  return true;
2444 }
2445 
2446 /******************************************************************************
2447 * Called when an audio thread starts or stops.
2448 */
2449 void KAlarmApp::notifyAudioPlaying(bool playing)
2450 {
2451  emit audioPlaying(playing);
2452 }
2453 
2454 /******************************************************************************
2455 * Stop audio play.
2456 */
2457 void KAlarmApp::stopAudio()
2458 {
2459  MessageWin::stopAudio();
2460 }
2461 
2462 
2463 void setEventCommandError(const KAEvent& event, KAEvent::CmdErrType err)
2464 {
2465  if (err == KAEvent::CMD_ERROR_POST && event.commandError() == KAEvent::CMD_ERROR_PRE)
2466  err = KAEvent::CMD_ERROR_PRE_POST;
2467  event.setCommandError(err);
2468 #ifdef USE_AKONADI
2469  KAEvent* ev = AlarmCalendar::resources()->event(EventId(event));
2470 #else
2471  KAEvent* ev = AlarmCalendar::resources()->event(event.id());
2472 #endif
2473  if (ev && ev->commandError() != err)
2474  ev->setCommandError(err);
2475 #ifdef USE_AKONADI
2476  AkonadiModel::instance()->updateCommandError(event);
2477 #else
2478  EventListModel::alarms()->updateCommandError(event.id());
2479 #endif
2480 }
2481 
2482 void clearEventCommandError(const KAEvent& event, KAEvent::CmdErrType err)
2483 {
2484  KAEvent::CmdErrType newerr = static_cast<KAEvent::CmdErrType>(event.commandError() & ~err);
2485  event.setCommandError(newerr);
2486 #ifdef USE_AKONADI
2487  KAEvent* ev = AlarmCalendar::resources()->event(EventId(event));
2488 #else
2489  KAEvent* ev = AlarmCalendar::resources()->event(event.id());
2490 #endif
2491  if (ev)
2492  {
2493  newerr = static_cast<KAEvent::CmdErrType>(ev->commandError() & ~err);
2494  ev->setCommandError(newerr);
2495  }
2496 #ifdef USE_AKONADI
2497  AkonadiModel::instance()->updateCommandError(event);
2498 #else
2499  EventListModel::alarms()->updateCommandError(event.id());
2500 #endif
2501 }
2502 
2503 
2504 KAlarmApp::ProcData::ProcData(ShellProcess* p, KAEvent* e, KAAlarm* a, int f)
2505  : process(p),
2506  event(e),
2507  alarm(a),
2508  messageBoxParent(0),
2509  flags(f)
2510 { }
2511 
2512 KAlarmApp::ProcData::~ProcData()
2513 {
2514  while (!tempFiles.isEmpty())
2515  {
2516  // Delete the temporary file called by the XTerm command
2517  QFile f(tempFiles.first());
2518  f.remove();
2519  tempFiles.removeFirst();
2520  }
2521  delete process;
2522  delete event;
2523  delete alarm;
2524 }
2525 
2526 // vim: et sw=4:
PreferencesBase::Feb29_Feb28
Definition: kalarmconfig.h:25
dbushandler.h
CommandOptions::attachments
QStringList attachments() const
Definition: commandoptions.h:83
CommandOptions::recurrence
KARecurrence * recurrence() const
Definition: commandoptions.h:69
KAMessageBox::questionYesNoCancel
static int questionYesNoCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|WindowModal))
KAlarmApp::spreadWindows
void spreadWindows(bool)
Definition: kalarmapp.cpp:1310
CommandOptions::alarmTime
KDateTime alarmTime() const
Definition: commandoptions.h:68
CommandOptions::addressees
QList< KCal::Person > addressees() const
Definition: commandoptions.h:81
KAlarmApp::rescheduleAlarm
void rescheduleAlarm(KAEvent &e, const KAAlarm &a)
Definition: kalarmapp.h:84
KAlarmApp::setAlarmsEnabled
void setAlarmsEnabled(bool)
Definition: kalarmapp.cpp:1294
maxLateness
static int maxLateness(int lateCancel)
Definition: kalarmapp.cpp:97
DBusHandler
Definition: dbushandler.h:40
ShellProcess::exitCode
int exitCode() const
EventListModel::alarms
static EventListModel * alarms()
Definition: eventlistmodel.cpp:56
EditAlarmDlg::EMAIL
Definition: editdlg.h:65
CommandOptions::CMD_ERROR
Definition: commandoptions.h:45
EditDisplayAlarmDlg::setAudio
void setAudio(Preferences::SoundType, const QString &file=QString(), float volume=-1, int repeatPause=-1)
Definition: editdlgtypes.cpp:446
CommandOptions::printError
static void printError(const QString &errmsg)
Definition: commandoptions.cpp:499
MainWindow::count
static int count()
Definition: mainwindow.h:101
KAlarmApp::emailSent
void emailSent(KAMail::JobData &, const QStringList &errmsgs, bool copyerr=false)
Definition: kalarmapp.cpp:2007
AlarmCalendar::atLoginAlarms
KAEvent::List atLoginAlarms() const
Definition: alarmcalendar.cpp:2185
EditAlarmDlg::DISPLAY
Definition: editdlg.h:65
EditAlarmDlg::COMMAND
Definition: editdlg.h:65
ShellProcess::NOT_FOUND
KAMail::JobData::alarm
KAAlarm alarm
Definition: kamail.h:62
ShellProcess::SUCCESS
text
virtual QByteArray text(quint32 serialNumber) const =0
MessageWin::spread
static bool spread(bool scatter)
Definition: messagewin.cpp:1341
CommandOptions::text
QString text() const
Definition: commandoptions.h:67
CommandOptions::LIST
Definition: commandoptions.h:54
KAlarmApp::wantShowInSystemTray
bool wantShowInSystemTray() const
Definition: kalarmapp.cpp:1140
AlarmCalendar::resources
static AlarmCalendar * resources()
Definition: alarmcalendar.h:130
alarmtime.h
QWidget
EventListModel::updateCommandError
void updateCommandError(const QString &eventId)
Definition: eventlistmodel.cpp:524
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
CollectionControlModel::isStandard
static bool isStandard(Akonadi::Collection &, CalEvent::Type)
Return whether a collection is the standard collection for a specified mime type. ...
Definition: collectionmodel.cpp:1016
KAlarmApp::spreadWindowsToggled
void spreadWindowsToggled(bool)
AkonadiModel::refresh
bool refresh(Akonadi::Collection &) const
Refresh the specified collection instance with up to date data.
Definition: akonadimodel.cpp:1744
KAlarmApp::stopAudio
void stopAudio()
Definition: kalarmapp.cpp:2457
KAMail::JobData::event
KAEvent event
Definition: kamail.h:61
AlarmCalendar::terminateCalendars
static void terminateCalendars()
Definition: alarmcalendar.cpp:119
ShellProcess::status
Status status() const
PreferencesBase::Feb29_None
Definition: kalarmconfig.h:25
PreferencesBase::defaultFeb29Type
static Feb29Type defaultFeb29Type()
Get In non-leap years, repeat yearly February 29th alarms on:
Definition: kalarmconfig.h:1377
CommandOptions::CANCEL_EVENT
Definition: commandoptions.h:49
alarmcalendar.h
CommandOptions::NEW
Definition: commandoptions.h:53
PreferencesBase::noAutoStart
static bool noAutoStart()
Get Suppress autostart at login.
Definition: kalarmconfig.h:340
KAMail::JobData::queued
bool queued
Definition: kamail.h:66
KAlarmApp::KAlarmApp
KAlarmApp()
Definition: kalarmapp.cpp:114
KDialog
TrayWindow::assocMainWindow
MainWindow * assocMainWindow() const
Definition: traywindow.h:52
KAlarmApp::commandMessage
void commandMessage(ShellProcess *, QWidget *parent)
Definition: kalarmapp.cpp:2337
KTTSD_DBUS_SERVICE
static const QLatin1String KTTSD_DBUS_SERVICE("org.kde.kttsd")
prefdlg.h
QObject
setEventCommandError
static void setEventCommandError(const KAEvent &, KAEvent::CmdErrType)
Definition: kalarmapp.cpp:2463
TrayWindow
Definition: traywindow.h:45
MainWindow::firstWindow
static MainWindow * firstWindow()
Definition: mainwindow.h:100
commandoptions.h
Preferences::setAskAutoStart
static void setAskAutoStart(bool yes)
Definition: preferences.cpp:112
PreferencesBase::setAutoStart
static void setAutoStart(bool v)
Set Start at login.
Definition: kalarmconfig.h:303
AlarmCalendar::open
bool open()
Definition: alarmcalendar.cpp:250
clearEventCommandError
static void clearEventCommandError(const KAEvent &, KAEvent::CmdErrType)
Definition: kalarmapp.cpp:2482
PreferencesBase::Feb29Type
Feb29Type
Definition: kalarmconfig.h:25
PreferencesBase::messageFont
static QFont messageFont()
Get Message font.
Definition: kalarmconfig.h:228
StartOfDayTimer::connect
static void connect(QObject *receiver, const char *member)
Connect to the timer signal.
Definition: startdaytimer.h:44
PreferencesBase::archivedKeepDays
static int archivedKeepDays()
Get Days to keep expired alarms.
Definition: kalarmconfig.h:926
ShellProcess::shellPath
static const QByteArray & shellPath()
MessageWin::ALWAYS_HIDE
Definition: messagewin.h:68
MainWindow::closeAll
static void closeAll()
Definition: mainwindow.cpp:319
CommandOptions::Command
Command
Definition: commandoptions.h:43
MainWindow::isTrayParent
bool isTrayParent() const
Definition: mainwindow.cpp:306
CommandOptions::EDIT
Definition: commandoptions.h:50
KAlarmApp::scheduleEvent
bool scheduleEvent(KAEvent::SubAction, const QString &text, const KDateTime &, int lateCancel, KAEvent::Flags flags, const QColor &bg, const QColor &fg, const QFont &, const QString &audioFile, float audioVolume, int reminderMinutes, const KARecurrence &recurrence, int repeatInterval, int repeatCount, uint mailFromID=0, const QList< KCal::Person > &mailAddresses=QList< KCal::Person >(), const QString &mailSubject=QString(), const QStringList &mailAttachments=QStringList())
Definition: kalarmapp.cpp:1330
EditEmailAlarmDlg::setBcc
void setBcc(bool)
Definition: editdlgtypes.cpp:1240
KAlarmApp::trayIconToggled
void trayIconToggled()
KAlarmApp::quitIf
bool quitIf()
Definition: kalarmapp.h:69
CommandOptions::bgColour
QColor bgColour() const
Definition: commandoptions.h:73
AlarmCalendar::earliestAlarm
KAEvent * earliestAlarm() const
Definition: alarmcalendar.cpp:2279
CommandOptions::command
Command command() const
Definition: commandoptions.h:57
MessageWin
MessageWin: A window to display an alarm or error message.
Definition: messagewin.h:61
CommandOptions::lateCancel
int lateCancel() const
Definition: commandoptions.h:72
CommandOptions::commandName
QString commandName() const
Definition: commandoptions.h:58
Preferences::startOfDay
static QTime startOfDay()
Definition: preferences.h:55
AlarmCalendar::adjustStartOfDay
void adjustStartOfDay()
Definition: alarmcalendar.cpp:2331
CommandOptions::reminderMinutes
int reminderMinutes() const
Definition: commandoptions.h:75
PreferencesBase::autoStart
static bool autoStart()
Get Start at login.
Definition: kalarmconfig.h:313
CollectionControlModel::instance
static CollectionControlModel * instance()
Definition: collectionmodel.cpp:653
Preferences::cmdXTermCommand
static QString cmdXTermCommand()
Definition: preferences.cpp:281
KAlarmApp::trayMainWindow
MainWindow * trayMainWindow() const
Definition: kalarmapp.cpp:1039
EditDisplayAlarmDlg::setReminder
void setReminder(int minutes, bool onceOnly)
Definition: editdlgtypes.cpp:450
EditDisplayAlarmDlg::setBgColour
void setBgColour(const QColor &)
Definition: editdlgtypes.cpp:428
KAlarmApp::removeWindow
void removeWindow(TrayWindow *)
Definition: kalarmapp.cpp:969
KAlarmApp::alarmEnabledToggled
void alarmEnabledToggled(bool)
autoqpointer.h
KAlarmApp::execCommandAlarm
ShellProcess * execCommandAlarm(const KAEvent &, const KAAlarm &, const QObject *receiver=0, const char *slot=0)
Definition: kalarmapp.cpp:2025
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:122
KAlarmApp::notifyAudioPlaying
void notifyAudioPlaying(bool playing)
Definition: kalarmapp.cpp:2449
CommandOptions::subject
QString subject() const
Definition: commandoptions.h:84
Preferences::autoStartChangedByUser
static bool autoStartChangedByUser()
Definition: preferences.h:44
StartOfDayTimer::disconnect
static void disconnect(QObject *receiver, const char *member=0)
Disconnect from the timer signal.
Definition: startdaytimer.h:51
Preferences::QUIT_WARN
static const QLatin1String QUIT_WARN
Definition: preferences.h:84
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
CommandOptions::audioFile
QString audioFile() const
Definition: commandoptions.h:76
CommandOptions::templateName
QString templateName() const
Definition: commandoptions.h:64
Preferences::ASK_AUTO_START
static const QLatin1String ASK_AUTO_START
Definition: preferences.h:85
MessageWin::stopAudio
static void stopAudio(bool wait=false)
Definition: messagewin.cpp:1525
messagewin.h
displays an alarm message
mainwindow.h
main application window
KAlarmApp::getInstance
static KAlarmApp * getInstance()
Definition: kalarmapp.cpp:206
MessageWin::showDefer
void showDefer()
Definition: messagewin.cpp:838
MessageWin::hasDefer
bool hasDefer() const
Definition: messagewin.cpp:830
KAlarmApp::commitData
virtual void commitData(QSessionManager &)
Definition: kalarmapp.cpp:734
eventlistmodel.h
EditDisplayAlarmDlg::setFgColour
void setFgColour(const QColor &)
Definition: editdlgtypes.cpp:433
EditEmailAlarmDlg::setEmailFields
void setEmailFields(uint fromID, const QList< KCal::Person > &, const QString &subject, const QStringList &attachments)
Definition: editdlgtypes.cpp:1224
EditAlarmDlg::create
static EditAlarmDlg * create(bool Template, Type, QWidget *parent=0, GetResourceType=RES_PROMPT)
Definition: editdlg.cpp:106
ShellProcess::errorMessage
QString errorMessage() const
MainWindow::isHiddenTrayParent
bool isHiddenTrayParent() const
Definition: mainwindow.h:80
MainWindow::create
static MainWindow * create(bool restored=false)
Definition: mainwindow.cpp:137
CommandOptions::audioVolume
float audioVolume() const
Definition: commandoptions.h:77
messagebox.h
PreferencesBase::Feb29_Mar1
Definition: kalarmconfig.h:25
CommandOptions::NONE
Definition: commandoptions.h:46
theApp
KAlarmApp * theApp()
Definition: kalarmapp.h:259
EditAlarmDlg::NO_TYPE
Definition: editdlg.h:65
MessageWin::alarmType
KAAlarm::Type alarmType() const
Definition: messagewin.h:78
MessageWin::instanceCount
static int instanceCount(bool excludeAlwaysHidden=false)
Definition: messagewin.cpp:816
MessageWin::redisplayAlarms
static void redisplayAlarms()
Definition: messagewin.cpp:1152
CommandOptions::flags
KAEvent::Flags flags() const
Definition: commandoptions.h:86
CommandOptions::EDIT_NEW
Definition: commandoptions.h:52
Preferences::workDayEnd
static QTime workDayEnd()
Definition: preferences.h:58
MessageWin::findEvent
static MessageWin * findEvent(const QString &eventId)
Definition: messagewin.cpp:1417
AutoQPointer
editdlgtypes.h
preferences.h
KAlarmApp
Definition: kalarmapp.h:54
Preferences::connect
static void connect(const char *signal, const QObject *receiver, const char *member)
Definition: preferences.cpp:292
EditDisplayAlarmDlg::setAutoClose
void setAutoClose(bool)
Definition: editdlgtypes.cpp:442
PreferencesBase::Sound_Speak
Definition: kalarmconfig.h:22
Preferences::holidays
static const KHolidays::HolidayRegion & holidays()
Definition: preferences.cpp:150
CollectionControlModel::enabledCollections
static Akonadi::Collection::List enabledCollections(CalEvent::Type, bool writable)
Return the enabled collections which contain a specified mime type.
Definition: collectionmodel.cpp:1227
PreferencesBase::archivedColour
static QColor archivedColour()
Get Archived alarm color.
Definition: kalarmconfig.h:892
MessageWin::isValid
bool isValid() const
Definition: messagewin.h:83
Preferences::workDays
static QBitArray workDays()
Definition: preferences.cpp:187
EditEmailAlarmDlg
Definition: editdlgtypes.h:199
CommandOptions::TRIGGER_EVENT
Definition: commandoptions.h:48
collectionmodel.h
MessageWin::showDateTime
void showDateTime(const KAEvent &, const KAAlarm &)
Definition: messagewin.cpp:877
kamail.h
EventId
Unique event identifier for Akonadi.
Definition: eventid.h:38
KAMessageBox::warningContinueCancel
static int warningContinueCancel(QWidget *parent, ButtonCode defaultButton, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const QString &dontAskAgainName=QString())
PreferencesBase::SoundType
SoundType
Definition: kalarmconfig.h:22
traywindow.h
MessageWin::repeat
void repeat(const KAAlarm &)
Definition: messagewin.cpp:1776
CommandOptions::disableAll
bool disableAll() const
Definition: commandoptions.h:87
CommandOptions::subRepeatCount
int subRepeatCount() const
Definition: commandoptions.h:70
functions.h
miscellaneous functions
alarmlistview.h
CommandOptions::editType
EditAlarmDlg::Type editType() const
Definition: commandoptions.h:65
MessageWin::showError
static void showError(const KAEvent &, const DateTime &alarmDateTime, const QStringList &errmsgs, const QString &dontShowAgain=QString())
Definition: messagewin.cpp:287
ShellProcess::Status
Status
kalarm.h
MessageWin::cancelReminder
void cancelReminder(const KAEvent &, const KAAlarm &)
Definition: messagewin.cpp:852
PreferencesBase::Sound_File
Definition: kalarmconfig.h:22
KAlarmApp::restoreSession
bool restoreSession()
Definition: kalarmapp.cpp:252
KAlarmApp::audioPlaying
void audioPlaying(bool)
AlarmCalendar::setAlarmPending
void setAlarmPending(KAEvent *, bool pending=true)
Definition: alarmcalendar.cpp:2302
MessageWin::show
virtual void show()
Definition: messagewin.cpp:1816
EditAudioAlarmDlg::setAudio
void setAudio(const QString &file, float volume=-1)
Definition: editdlgtypes.cpp:1555
KAlarmApp::displayTrayIcon
bool displayTrayIcon(bool show, MainWindow *=0)
Definition: kalarmapp.cpp:977
PreferencesBase::Sound_Beep
Definition: kalarmconfig.h:22
EditDisplayAlarmDlg
Definition: editdlgtypes.h:57
CommandOptions::fromID
uint fromID() const
Definition: commandoptions.h:85
AlarmCalendar::initialiseCalendars
static bool initialiseCalendars()
Definition: alarmcalendar.cpp:88
Preferences::self
static Preferences * self()
Definition: preferences.cpp:80
KAMail::JobData
Definition: kamail.h:56
KAlarmApp::execAlarm
void * execAlarm(KAEvent &, const KAAlarm &, bool reschedule, bool allowDefer=true, bool noPreAction=false)
Definition: kalarmapp.cpp:1838
PreferencesBase::showInSystemTray
static bool showInSystemTray()
Get Show in system tray.
Definition: kalarmconfig.h:257
KUniqueApplication
KAlarmApp::kspeechInterface
OrgKdeKSpeechInterface * kspeechInterface(QString &error) const
Definition: kalarmapp.cpp:2356
shellprocess.h
CommandOptions::subRepeatInterval
int subRepeatInterval() const
Definition: commandoptions.h:71
KAMail::send
static int send(JobData &, QStringList &errmsgs)
Definition: kamail.cpp:113
EditDisplayAlarmDlg::setConfirmAck
void setConfirmAck(bool)
Definition: editdlgtypes.cpp:438
CommandOptions::editAction
KAEvent::SubAction editAction() const
Definition: commandoptions.h:66
CommandOptions::eventId
QString eventId() const
Definition: commandoptions.h:62
EditAlarmDlg::AUDIO
Definition: editdlg.h:65
AlarmCalendar::event
KAEvent * event(const QString &uniqueId)
Definition: alarmcalendar.cpp:1820
MainWindow::show
virtual void show()
Definition: mainwindow.cpp:429
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:288
ShellProcess::command
const QString & command() const
KAlarmApp::displayFatalError
static void displayFatalError(const QString &message)
Definition: kalarmapp.cpp:745
PreferencesBase::setNoAutoStart
static void setNoAutoStart(bool v)
Set Suppress autostart at login.
Definition: kalarmconfig.h:330
MessageWin::NO_RESCHEDULE
Definition: messagewin.h:66
KAlarmApp::dbusList
QString dbusList()
Definition: kalarmapp.cpp:1409
KAlarmApp::execAlarmSuccess
void execAlarmSuccess()
AlarmCalendar::displayCalendar
static AlarmCalendar * displayCalendar()
Definition: alarmcalendar.h:131
KAlarmApp::~KAlarmApp
~KAlarmApp()
Definition: kalarmapp.cpp:191
MainWindow
Definition: mainwindow.h:71
KAlarmApp::doQuit
void doQuit(QWidget *parent)
Definition: kalarmapp.cpp:684
startdaytimer.h
MessageWin::NO_DEFER
Definition: messagewin.h:67
ShellProcess
CommandOptions::simulationTime
KDateTime simulationTime() const
Definition: commandoptions.h:89
Preferences::workDayStart
static QTime workDayStart()
Definition: preferences.h:57
KAlarmApp::setSpreadWindowsState
void setSpreadWindowsState(bool spread)
Definition: kalarmapp.cpp:1320
KAlarmApp::processQueue
void processQueue()
Definition: kalarmapp.cpp:871
CommandOptions::EDIT_NEW_PRESET
Definition: commandoptions.h:51
CommandOptions
Definition: commandoptions.h:40
KTTDS_DBUS_PATH
static const QLatin1String KTTDS_DBUS_PATH("/KSpeech")
KAlarmApp::newInstance
virtual int newInstance()
Definition: kalarmapp.cpp:338
EditAudioAlarmDlg
Definition: editdlgtypes.h:274
CommandOptions::fgColour
QColor fgColour() const
Definition: commandoptions.h:74
ShellProcess::start
bool start(OpenMode=ReadWrite)
QList< KCal::Person >
KAlarmApp::alarmCompleted
void alarmCompleted(const KAEvent &)
Definition: kalarmapp.cpp:1662
CommandOptions::TRAY
Definition: commandoptions.h:47
AkonadiModel::updateCommandError
void updateCommandError(const KAEvent &)
To be called when the command error status of an alarm has changed, to set in the Akonadi database an...
Definition: akonadimodel.cpp:810
KAMessageBox::information
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Options(Notify|WindowModal))
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:10 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal