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

kalarm

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

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