2 * kalarmapp.cpp - the KAlarm application object
4 * Copyright © 2001-2012 by David Jarvie <djarvie@kde.org>
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.
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.
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.
22 #include "kalarmapp.moc"
24 #include "alarmcalendar.h"
25 #include "alarmlistview.h"
26 #include "autoqpointer.h"
27 #include "commandoptions.h"
28 #include "dbushandler.h"
29 #include "editdlgtypes.h"
31 #include "collectionmodel.h"
33 #include "eventlistmodel.h"
35 #include "functions.h"
37 #include "mainwindow.h"
38 #include "messagebox.h"
39 #include "messagewin.h"
40 #include "preferences.h"
42 #include "shellprocess.h"
43 #include "startdaytimer.h"
44 #include "traywindow.h"
46 #include "kspeechinterface.h"
48 #include <kalarmcal/datetime.h>
49 #include <kalarmcal/karecurrence.h>
52 #include <kstandarddirs.h>
54 #include <kaboutdata.h>
55 #include <ktemporaryfile.h>
56 #include <kfileitem.h>
58 #include <kstandardguiitem.h>
59 #include <kservicetypetrader.h>
61 #include <ktoolinvocation.h>
65 #include <ksystemtrayicon.h>
66 #include <ksystemtimezone.h>
73 #include <QTextStream>
74 #include <QtDBus/QtDBus>
81 static const char* KTTSD_DBUS_SERVICE
= "org.kde.kttsd";
82 static const char* KTTDS_DBUS_PATH
= "/KSpeech";
84 static void setEventCommandError(const KAEvent
&, KAEvent::CmdErrType
);
85 static void clearEventCommandError(const KAEvent
&, KAEvent::CmdErrType
);
87 /******************************************************************************
88 * Find the maximum number of seconds late which a late-cancel alarm is allowed
89 * to be. This is calculated as the late cancel interval, plus a few seconds
90 * leeway to cater for any timing irregularities.
92 static inline int maxLateness(int lateCancel
)
94 static const int LATENESS_LEEWAY
= 5;
95 int lc
= (lateCancel
>= 1) ? (lateCancel
- 1)*60 : 0;
96 return LATENESS_LEEWAY
+ lc
;
100 KAlarmApp
* KAlarmApp::theInstance
= 0;
101 int KAlarmApp::mActiveCount
= 0;
102 int KAlarmApp::mFatalError
= 0;
103 QString
KAlarmApp::mFatalMessage
;
106 /******************************************************************************
107 * Construct the application.
109 KAlarmApp::KAlarmApp()
110 : KUniqueApplication(),
113 mLoginAlarmsDone(false),
114 mDBusHandler(new DBusHandler()),
116 mAlarmTimer(new QTimer(this)),
117 mArchivedPurgeDays(-1), // default to not purging
118 mPurgeDaysQueued(-1),
121 mCancelRtcWake(false),
122 mProcessingQueue(false),
123 mSessionClosingDown(false),
124 mAlarmsEnabled(true),
125 mSpeechEnabled(false)
127 KGlobal::locale()->insertCatalog("libkdepim");
130 KAlarm::setTestModeConditions();
132 mAlarmTimer
->setSingleShot(true);
133 connect(mAlarmTimer
, SIGNAL(timeout()), SLOT(checkNextDueAlarm()));
135 setQuitOnLastWindowClosed(false);
136 Preferences::self()->readConfig();
137 if (!Preferences::noAutoStart())
139 Preferences::setAutoStart(true);
140 Preferences::self()->writeConfig();
142 Preferences::connect(SIGNAL(startOfDayChanged(QTime
)), this, SLOT(changeStartOfDay()));
143 Preferences::connect(SIGNAL(workTimeChanged(QTime
,QTime
,QBitArray
)), this, SLOT(slotWorkTimeChanged(QTime
,QTime
,QBitArray
)));
144 Preferences::connect(SIGNAL(holidaysChanged(KHolidays::HolidayRegion
)), this, SLOT(slotHolidaysChanged(KHolidays::HolidayRegion
)));
145 Preferences::connect(SIGNAL(feb29TypeChanged(Feb29Type
)), this, SLOT(slotFeb29TypeChanged(Feb29Type
)));
146 Preferences::connect(SIGNAL(showInSystemTrayChanged(bool)), this, SLOT(slotShowInSystemTrayChanged()));
147 Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(setArchivePurgeDays()));
148 Preferences::connect(SIGNAL(messageFontChanged(QFont
)), this, SLOT(slotMessageFontChanged(QFont
)));
149 slotFeb29TypeChanged(Preferences::defaultFeb29Type());
151 connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceUnregistered(QString
)),
152 SLOT(slotDBusServiceUnregistered(QString
)));
153 KAEvent::setStartOfDay(Preferences::startOfDay());
154 KAEvent::setWorkTime(Preferences::workDays(), Preferences::workDayStart(), Preferences::workDayEnd());
155 KAEvent::setHolidays(Preferences::holidays());
156 KAEvent::setDefaultFont(Preferences::messageFont());
157 if (AlarmCalendar::initialiseCalendars())
159 connect(AlarmCalendar::resources(), SIGNAL(earliestAlarmChanged()), SLOT(checkNextDueAlarm()));
161 connect(AlarmCalendar::resources(), SIGNAL(atLoginEventAdded(KAEvent
)), SLOT(atLoginEventAdded(KAEvent
)));
162 connect(AkonadiModel::instance(), SIGNAL(collectionAdded(Akonadi::Collection
)),
163 SLOT(purgeNewArchivedDefault(Akonadi::Collection
)));
166 KConfigGroup
config(KGlobal::config(), "General");
167 mNoSystemTray
= config
.readEntry("NoSystemTray", false);
168 mOldShowInSystemTray
= wantShowInSystemTray();
169 DateTime::setStartOfDay(Preferences::startOfDay());
170 mPrefsArchivedColour
= Preferences::archivedColour();
173 // Check if the speech synthesis daemon is installed
174 mSpeechEnabled
= (KServiceTypeTrader::self()->query("DBUS/Text-to-Speech", "Name == 'KTTSD'").count() > 0);
175 if (!mSpeechEnabled
) { kDebug() << "Speech synthesis disabled (KTTSD not found)"; }
176 // Check if KOrganizer is installed
177 QString korg
= QLatin1String("korganizer");
178 mKOrganizerEnabled
= !KStandardDirs::locate("exe", korg
).isNull() || !KStandardDirs::findExe(korg
).isNull();
179 if (!mKOrganizerEnabled
) { kDebug() << "KOrganizer options disabled (KOrganizer not found)"; }
182 /******************************************************************************
184 KAlarmApp::~KAlarmApp()
186 while (!mCommandProcesses
.isEmpty())
188 ProcData
* pd
= mCommandProcesses
[0];
189 mCommandProcesses
.pop_front();
192 AlarmCalendar::terminateCalendars();
195 /******************************************************************************
196 * Return the one and only KAlarmApp instance.
197 * If it doesn't already exist, it is created first.
199 KAlarmApp
* KAlarmApp::getInstance()
203 theInstance
= new KAlarmApp
;
206 theInstance
->quitFatal();
211 /******************************************************************************
212 * Restore the saved session if required.
214 bool KAlarmApp::restoreSession()
216 if (!isSessionRestored())
224 // Process is being restored by session management.
225 kDebug() << "Restoring";
227 // Create the session config object now.
228 // This is necessary since if initCheck() below causes calendars to be updated,
229 // the session config created after that points to an invalid file, resulting
230 // in no windows being restored followed by a later crash.
231 kapp
->sessionConfig();
233 // When KAlarm is session restored, automatically set start-at-login to true.
234 Preferences::self()->readConfig();
235 Preferences::setAutoStart(true);
236 Preferences::setNoAutoStart(false);
237 Preferences::setAskAutoStart(true); // cancel any start-at-login prompt suppression
238 Preferences::self()->writeConfig();
240 if (!initCheck(true)) // open the calendar file (needed for main windows), don't process queue yet
243 quitIf(1, true); // error opening the main calendar - quit
246 MainWindow
* trayParent
= 0;
247 for (int i
= 1; KMainWindow::canBeRestored(i
); ++i
)
249 QString type
= KMainWindow::classNameOfToplevel(i
);
250 if (type
== QLatin1String("MainWindow"))
252 MainWindow
* win
= MainWindow::create(true);
253 win
->restore(i
, false);
254 if (win
->isHiddenTrayParent())
259 else if (type
== QLatin1String("MessageWin"))
261 MessageWin
* win
= new MessageWin
;
262 win
->restore(i
, false);
270 // Try to display the system tray icon if it is configured to be shown
271 if (trayParent
|| wantShowInSystemTray())
273 if (!MainWindow::count())
274 kWarning() << "no main window to be restored!?";
277 displayTrayIcon(true, trayParent
);
278 // Occasionally for no obvious reason, the main main window is
279 // shown when it should be hidden, so hide it just to be sure.
286 if (quitIf(0)) // quit if no windows are open
287 return false; // quitIf() can sometimes return, despite calling exit()
289 // Check whether the KDE time zone daemon is running (but don't hold up initialisation)
290 QTimer::singleShot(0, this, SLOT(checkKtimezoned()));
292 startProcessQueue(); // start processing the execution queue
296 /******************************************************************************
297 * Called for a KUniqueApplication when a new instance of the application is
300 int KAlarmApp::newInstance()
309 int exitCode
= 0; // default = success
310 static bool firstInstance
= true;
311 bool dontRedisplay
= false;
312 if (!firstInstance
|| !isSessionRestored())
314 CommandOptions options
; // fetch and parse command line options
316 if (options
.simulationTime().isValid())
317 KAlarm::setSimulatedSystemTime(options
.simulationTime());
319 CommandOptions::Command command
= options
.command();
320 if (options
.disableAll())
321 setAlarmsEnabled(false); // disable alarm monitoring
324 case CommandOptions::TRIGGER_EVENT
:
325 case CommandOptions::CANCEL_EVENT
:
327 // Display or delete the event with the specified event ID
328 EventFunc function
= (command
== CommandOptions::TRIGGER_EVENT
) ? EVENT_TRIGGER
: EVENT_CANCEL
;
329 if (!initCheck(true)) // open the calendar, don't start processing execution queue yet
333 startProcessQueue(); // start processing the execution queue
334 if (!handleEvent(options
.eventId(), function
))
339 case CommandOptions::EDIT
:
340 // Edit a specified existing alarm
343 else if (!KAlarm::editAlarmById(options
.eventId()))
345 CommandOptions::printError(i18nc("@info:shell", "<icode>%1</icode>: Event <resource>%2</resource> not found, or not editable", QString::fromLatin1("--edit"), options
.eventId()));
350 case CommandOptions::EDIT_NEW
:
352 // Edit a new alarm, and optionally preset selected values
357 // Use AutoQPointer to guard against crash on application exit while
358 // the dialogue is still open. It prevents double deletion (both on
359 // deletion of parent, and on return from this function).
360 AutoQPointer
<EditAlarmDlg
> editDlg
= EditAlarmDlg::create(false, options
.editType());
361 if (options
.alarmTime().isValid())
362 editDlg
->setTime(options
.alarmTime());
363 if (options
.recurrence())
364 editDlg
->setRecurrence(*options
.recurrence(), options
.subRepeatInterval(), options
.subRepeatCount());
365 else if (options
.flags() & KAEvent::REPEAT_AT_LOGIN
)
366 editDlg
->setRepeatAtLogin();
367 editDlg
->setAction(options
.editAction(), AlarmText(options
.text()));
368 if (options
.lateCancel())
369 editDlg
->setLateCancel(options
.lateCancel());
370 if (options
.flags() & KAEvent::COPY_KORGANIZER
)
371 editDlg
->setShowInKOrganizer(true);
372 switch (options
.editType())
374 case EditAlarmDlg::DISPLAY
:
376 // EditAlarmDlg::create() always returns EditDisplayAlarmDlg for type = DISPLAY
377 EditDisplayAlarmDlg
* dlg
= qobject_cast
<EditDisplayAlarmDlg
*>(editDlg
);
378 if (options
.fgColour().isValid())
379 dlg
->setFgColour(options
.fgColour());
380 if (options
.bgColour().isValid())
381 dlg
->setBgColour(options
.bgColour());
382 if (!options
.audioFile().isEmpty()
383 || options
.flags() & (KAEvent::BEEP
| KAEvent::SPEAK
))
385 KAEvent::Flags flags
= options
.flags();
386 Preferences::SoundType type
= (flags
& KAEvent::BEEP
) ? Preferences::Sound_Beep
387 : (flags
& KAEvent::SPEAK
) ? Preferences::Sound_Speak
388 : Preferences::Sound_File
;
389 dlg
->setAudio(type
, options
.audioFile(), options
.audioVolume(), (flags
& KAEvent::REPEAT_SOUND
? 0 : -1));
391 if (options
.reminderMinutes())
392 dlg
->setReminder(options
.reminderMinutes(), (options
.flags() & KAEvent::REMINDER_ONCE
));
393 if (options
.flags() & KAEvent::CONFIRM_ACK
)
394 dlg
->setConfirmAck(true);
395 if (options
.flags() & KAEvent::AUTO_CLOSE
)
396 dlg
->setAutoClose(true);
399 case EditAlarmDlg::COMMAND
:
401 case EditAlarmDlg::EMAIL
:
403 // EditAlarmDlg::create() always returns EditEmailAlarmDlg for type = EMAIL
404 EditEmailAlarmDlg
* dlg
= qobject_cast
<EditEmailAlarmDlg
*>(editDlg
);
406 || !options
.addressees().isEmpty()
407 || !options
.subject().isEmpty()
408 || !options
.attachments().isEmpty())
409 dlg
->setEmailFields(options
.fromID(), options
.addressees(), options
.subject(), options
.attachments());
410 if (options
.flags() & KAEvent::EMAIL_BCC
)
414 case EditAlarmDlg::AUDIO
:
416 // EditAlarmDlg::create() always returns EditAudioAlarmDlg for type = AUDIO
417 EditAudioAlarmDlg
* dlg
= qobject_cast
<EditAudioAlarmDlg
*>(editDlg
);
418 if (!options
.audioFile().isEmpty() || options
.audioVolume() >= 0)
419 dlg
->setAudio(options
.audioFile(), options
.audioVolume());
422 case EditAlarmDlg::NO_TYPE
:
425 KAlarm::execNewAlarmDlg(editDlg
);
429 case CommandOptions::EDIT_NEW_PRESET
:
430 // Edit a new alarm, preset with a template
434 KAlarm::editNewAlarm(options
.templateName());
437 case CommandOptions::NEW
:
438 // Display a message or file, execute a command, or send an email
440 || !scheduleEvent(options
.editAction(), options
.text(), options
.alarmTime(),
441 options
.lateCancel(), options
.flags(), options
.bgColour(),
442 options
.fgColour(), QFont(), options
.audioFile(), options
.audioVolume(),
443 options
.reminderMinutes(), (options
.recurrence() ? *options
.recurrence() : KARecurrence()),
444 options
.subRepeatInterval(), options
.subRepeatCount(),
445 options
.fromID(), options
.addressees(),
446 options
.subject(), options
.attachments()))
450 case CommandOptions::TRAY
:
451 // Display only the system tray icon
452 if (Preferences::showInSystemTray() && KSystemTrayIcon::isSystemTrayAvailable())
454 if (!initCheck() // open the calendar, start processing execution queue
455 || !displayTrayIcon(true))
459 // fall through to NONE
460 case CommandOptions::NONE
:
461 // No arguments - run interactively & display the main window
463 if (options
.simulationTime().isValid() && !firstInstance
)
464 break; // simulating time: don't open main window if already running
470 MainWindow
* win
= MainWindow::create();
471 if (command
== CommandOptions::TRAY
)
472 win
->setWindowState(win
->windowState() | Qt::WindowMinimized
);
477 case CommandOptions::CMD_ERROR
:
483 // If this is the first time through, redisplay any alarm message windows
485 if (firstInstance
&& !dontRedisplay
&& !exitCode
)
487 /* First time through, so redisplay alarm message windows from last time.
488 * But it is possible for session restoration in some circumstances to
489 * not create any windows, in which case the alarm calendars will have
490 * been deleted - if so, don't try to do anything. (This has been known
491 * to happen under the Xfce desktop.)
493 if (AlarmCalendar::resources())
494 MessageWin::redisplayAlarms();
498 firstInstance
= false;
500 // Quit the application if this was the last/only running "instance" of the program.
501 // Executing 'return' doesn't work very well since the program continues to
502 // run if no windows were created.
505 // Check whether the KDE time zone daemon is running (but don't hold up initialisation)
506 QTimer::singleShot(0, this, SLOT(checkKtimezoned()));
511 void KAlarmApp::checkKtimezoned()
513 // Check that the KDE time zone daemon is running
514 static bool done
= false;
518 #if KDE_IS_VERSION(4,5,70)
519 if (!KSystemTimeZones::isTimeZoneDaemonAvailable())
521 kDebug() << "ktimezoned not running: using UTC only";
522 KAMessageBox::information(MainWindow::mainMainWindow(),
523 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.)"),
524 QString(), QLatin1String("tzunavailable"));
529 /******************************************************************************
530 * Quit the program, optionally only if there are no more "instances" running.
531 * Reply = true if program exited.
533 bool KAlarmApp::quitIf(int exitCode
, bool force
)
537 // Quit regardless, except for message windows
539 MainWindow::closeAll();
541 displayTrayIcon(false);
542 if (MessageWin::instanceCount(true)) // ignore always-hidden windows (e.g. audio alarms)
546 return false; // MainWindow::closeAll() causes quitIf() to be called again
549 // Quit only if there are no more "instances" running
550 mPendingQuit
= false;
551 if (mActiveCount
> 0 || MessageWin::instanceCount(true)) // ignore always-hidden windows (e.g. audio alarms)
553 int mwcount
= MainWindow::count();
554 MainWindow
* mw
= mwcount
? MainWindow::firstWindow() : 0;
555 if (mwcount
> 1 || (mwcount
&& (!mw
->isHidden() || !mw
->isTrayParent())))
557 // There are no windows left except perhaps a main window which is a hidden
558 // tray icon parent, or an always-hidden message window.
561 // There is a system tray icon.
562 // Don't exit unless the system tray doesn't seem to exist.
563 if (checkSystemTray())
566 if (!mDcopQueue
.isEmpty() || !mCommandProcesses
.isEmpty())
568 // Don't quit yet if there are outstanding actions on the execution queue
570 mPendingQuitCode
= exitCode
;
575 // This was the last/only running "instance" of the program, so exit completely.
576 kDebug() << exitCode
<< ": quitting";
577 MessageWin::stopAudio(true);
580 KAlarm::setRtcWakeTime(0, 0);
581 KAlarm::deleteRtcWakeConfig();
583 delete mAlarmTimer
; // prevent checking for alarms after deleting calendars
585 mInitialised
= false; // prevent processQueue() from running
586 AlarmCalendar::terminateCalendars();
588 return true; // sometimes we actually get to here, despite calling exit()
591 /******************************************************************************
592 * Called when the Quit menu item is selected.
593 * Closes the system tray window and all main windows, but does not exit the
594 * program if other windows are still open.
596 void KAlarmApp::doQuit(QWidget
* parent
)
599 if (KAMessageBox::warningContinueCancel(parent
, KMessageBox::Cancel
,
600 i18nc("@info", "Quitting will disable alarms (once any alarm message windows are closed)."),
601 QString(), KStandardGuiItem::quit(), Preferences::QUIT_WARN
602 ) != KMessageBox::Yes
)
604 if (!KAlarm::checkRtcWakeConfig(true).isEmpty())
606 // A wake-on-suspend alarm is set
607 if (KAMessageBox::warningContinueCancel(parent
, KMessageBox::Cancel
,
608 i18nc("@info", "Quitting will cancel the scheduled Wake from Suspend."),
609 QString(), KStandardGuiItem::quit()
610 ) != KMessageBox::Yes
)
612 mCancelRtcWake
= true;
614 if (!Preferences::autoStart())
616 int option
= KMessageBox::No
;
617 if (!Preferences::autoStartChangedByUser())
619 option
= KAMessageBox::questionYesNoCancel(parent
,
620 i18nc("@info", "Do you want to start KAlarm at login?<nl/>"
621 "(Note that alarms will be disabled if KAlarm is not started.)"),
622 QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(),
623 KStandardGuiItem::cancel(), Preferences::ASK_AUTO_START
);
627 case KMessageBox::Yes
:
628 Preferences::setAutoStart(true);
629 Preferences::setNoAutoStart(false);
631 case KMessageBox::No
:
632 Preferences::setNoAutoStart(true);
634 case KMessageBox::Cancel
:
638 Preferences::self()->writeConfig();
643 /******************************************************************************
644 * Called when the session manager is about to close down the application.
646 void KAlarmApp::commitData(QSessionManager
& sm
)
648 mSessionClosingDown
= true;
649 KUniqueApplication::commitData(sm
);
650 mSessionClosingDown
= false; // reset in case shutdown is cancelled
653 /******************************************************************************
654 * Display an error message for a fatal error. Prevent further actions since
655 * the program state is unsafe.
657 void KAlarmApp::displayFatalError(const QString
& message
)
662 mFatalMessage
= message
;
664 QTimer::singleShot(0, theInstance
, SLOT(quitFatal()));
668 /******************************************************************************
669 * Quit the program, once the fatal error message has been acknowledged.
671 void KAlarmApp::quitFatal()
680 KMessageBox::error(0, mFatalMessage
); // this is an application modal window
682 // fall through to '3'
685 theInstance
->quitIf(1, true);
688 QTimer::singleShot(1000, this, SLOT(quitFatal()));
691 /******************************************************************************
692 * Called by the alarm timer when the next alarm is due.
693 * Also called when the execution queue has finished processing to check for the
696 void KAlarmApp::checkNextDueAlarm()
700 // Find the first alarm due
701 KAEvent
* nextEvent
= AlarmCalendar::resources()->earliestAlarm();
703 return; // there are no alarms pending
704 KDateTime nextDt
= nextEvent
->nextTrigger(KAEvent::ALL_TRIGGER
).effectiveKDateTime();
705 KDateTime now
= KDateTime::currentDateTime(Preferences::timeZone());
706 qint64 interval
= now
.secsTo_long(nextDt
);
707 kDebug() << "now:" << qPrintable(now
.toString("%Y-%m-%d %H:%M %:Z")) << ", next:" << qPrintable(nextDt
.toString("%Y-%m-%d %H:%M %:Z")) << ", due:" << interval
;
711 queueAlarmId(nextEvent
->id());
712 kDebug() << nextEvent
->id() << ": due now";
713 QTimer::singleShot(0, this, SLOT(processQueue()));
717 // No alarm is due yet, so set timer to wake us when it's due.
718 // Check for integer overflow before setting timer.
719 #ifndef HIBERNATION_SIGNAL
720 /* TODO: REPLACE THIS CODE WHEN A SYSTEM NOTIFICATION SIGNAL BECOMES
721 * AVAILABLE FOR WAKEUP FROM HIBERNATION.
722 * Re-evaluate the next alarm time every minute, in case the
723 * system clock jumps. The most common case when the clock jumps
724 * is when a laptop wakes from hibernation. If timers were left to
725 * run, they would trigger late by the length of time the system
728 if (interval
> 60) // 1 minute
732 if (interval
> INT_MAX
)
734 kDebug() << nextEvent
->id() << "wait" << interval
/1000 << "seconds";
735 mAlarmTimer
->start(static_cast<int>(interval
));
739 /******************************************************************************
740 * Called by the alarm timer when the next alarm is due.
741 * Also called when the execution queue has finished processing to check for the
744 void KAlarmApp::queueAlarmId(const QString
& id
)
746 for (int i
= 0, end
= mDcopQueue
.count(); i
< end
; ++i
)
748 if (mDcopQueue
[i
].function
== EVENT_HANDLE
&& mDcopQueue
[i
].eventId
== id
)
749 return; // the alarm is already queued
751 mDcopQueue
.enqueue(DcopQEntry(EVENT_HANDLE
, id
));
754 /******************************************************************************
755 * Start processing the execution queue.
757 void KAlarmApp::startProcessQueue()
763 QTimer::singleShot(0, this, SLOT(processQueue())); // process anything already queued
767 /******************************************************************************
768 * The main processing loop for KAlarm.
769 * All KAlarm operations involving opening or updating calendar files are called
770 * from this loop to ensure that only one operation is active at any one time.
771 * This precaution is necessary because KAlarm's activities are mostly
772 * asynchronous, being in response to D-Bus calls from other programs or timer
773 * events, any of which can be received in the middle of performing another
774 * operation. If a calendar file is opened or updated while another calendar
775 * operation is in progress, the program has been observed to hang, or the first
776 * calendar call has failed with data loss - clearly unacceptable!!
778 void KAlarmApp::processQueue()
780 if (mInitialised
&& !mProcessingQueue
)
783 mProcessingQueue
= true;
785 // Refresh alarms if that's been queued
786 KAlarm::refreshAlarmsIfQueued();
788 if (!mLoginAlarmsDone
)
790 // Queue all at-login alarms once only, at program start-up.
791 // First, cancel any scheduled reminders or deferrals for them,
792 // since these will be superseded by the new at-login trigger.
793 KAEvent::List events
= AlarmCalendar::resources()->atLoginAlarms();
794 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
796 KAEvent event
= *events
[i
];
797 if (!cancelReminderAndDeferral(event
))
800 queueAlarmId(event
.id());
803 mLoginAlarmsDone
= true;
806 // Process queued events
807 while (!mDcopQueue
.isEmpty())
809 DcopQEntry
& entry
= mDcopQueue
.head();
810 if (entry
.eventId
.isEmpty())
813 switch (entry
.function
)
816 execAlarm(entry
.event
, entry
.event
.firstAlarm(), false);
819 KAlarm::addEvent(entry
.event
, 0, 0, KAlarm::ALLOW_KORG_UPDATE
| KAlarm::NO_RESOURCE_PROMPT
);
826 handleEvent(entry
.eventId
, entry
.function
);
827 mDcopQueue
.dequeue();
830 // Purge the default archived alarms resource if it's time to do so
831 if (mPurgeDaysQueued
>= 0)
833 KAlarm::purgeArchive(mPurgeDaysQueued
);
834 mPurgeDaysQueued
= -1;
837 // Now that the queue has been processed, quit if a quit was queued
840 if (quitIf(mPendingQuitCode
))
841 return; // quitIf() can sometimes return, despite calling exit()
844 mProcessingQueue
= false;
846 // Schedule the application to be woken when the next alarm is due
852 /******************************************************************************
853 * Called when a repeat-at-login alarm has been added externally.
854 * Queues the alarm for triggering.
855 * First, cancel any scheduled reminder or deferral for it, since these will be
856 * superseded by the new at-login trigger.
858 void KAlarmApp::atLoginEventAdded(const KAEvent
& event
)
861 if (!cancelReminderAndDeferral(ev
))
865 mDcopQueue
.enqueue(DcopQEntry(EVENT_HANDLE
, ev
.id()));
867 QTimer::singleShot(0, this, SLOT(processQueue()));
873 /******************************************************************************
874 * Called when the system tray main window is closed.
876 void KAlarmApp::removeWindow(TrayWindow
*)
881 /******************************************************************************
882 * Display or close the system tray icon.
884 bool KAlarmApp::displayTrayIcon(bool show
, MainWindow
* parent
)
887 static bool creating
= false;
890 if (!mTrayWindow
&& !creating
)
892 if (!KSystemTrayIcon::isSystemTrayAvailable())
894 if (!MainWindow::count())
896 // We have to have at least one main window to act
897 // as parent to the system tray icon (even if the
898 // window is hidden).
899 creating
= true; // prevent main window constructor from creating an additional tray icon
900 parent
= MainWindow::create();
903 mTrayWindow
= new TrayWindow(parent
? parent
: MainWindow::firstWindow());
904 connect(mTrayWindow
, SIGNAL(deleted()), SIGNAL(trayIconToggled()));
905 emit
trayIconToggled();
907 if (!checkSystemTray())
908 quitIf(0); // exit the application if there are no open windows
919 /******************************************************************************
920 * Check whether the system tray icon has been housed in the system tray.
922 bool KAlarmApp::checkSystemTray()
926 if (KSystemTrayIcon::isSystemTrayAvailable() == mNoSystemTray
)
928 kDebug() << "changed ->" << mNoSystemTray
;
929 mNoSystemTray
= !mNoSystemTray
;
931 // Store the new setting in the config file, so that if KAlarm exits it will
932 // restart with the correct default.
933 KConfigGroup
config(KGlobal::config(), "General");
934 config
.writeEntry("NoSystemTray", mNoSystemTray
);
937 // Update other settings
938 slotShowInSystemTrayChanged();
940 return !mNoSystemTray
;
943 /******************************************************************************
944 * Return the main window associated with the system tray icon.
946 MainWindow
* KAlarmApp::trayMainWindow() const
948 return mTrayWindow
? mTrayWindow
->assocMainWindow() : 0;
951 /******************************************************************************
952 * Called when the show-in-system-tray preference setting has changed, to show
953 * or hide the system tray icon.
955 void KAlarmApp::slotShowInSystemTrayChanged()
957 bool newShowInSysTray
= wantShowInSystemTray();
958 if (newShowInSysTray
!= mOldShowInSystemTray
)
960 // The system tray run mode has changed
961 ++mActiveCount
; // prevent the application from quitting
962 MainWindow
* win
= mTrayWindow
? mTrayWindow
->assocMainWindow() : 0;
963 delete mTrayWindow
; // remove the system tray icon if it is currently shown
965 mOldShowInSystemTray
= newShowInSysTray
;
966 if (newShowInSysTray
)
968 // Show the system tray icon
969 displayTrayIcon(true);
973 // Stop showing the system tray icon
974 if (win
&& win
->isHidden())
976 if (MainWindow::count() > 1)
980 win
->setWindowState(win
->windowState() | Qt::WindowMinimized
);
989 /******************************************************************************
990 * Called when the start-of-day time preference setting has changed.
991 * Change alarm times for date-only alarms.
993 void KAlarmApp::changeStartOfDay()
995 DateTime::setStartOfDay(Preferences::startOfDay());
996 KAEvent::setStartOfDay(Preferences::startOfDay());
997 AlarmCalendar::resources()->adjustStartOfDay();
1000 /******************************************************************************
1001 * Called when the default alarm message font preference setting has changed.
1004 void KAlarmApp::slotMessageFontChanged(const QFont
& font
)
1006 KAEvent::setDefaultFont(font
);
1009 /******************************************************************************
1010 * Called when the working time preference settings have changed.
1013 void KAlarmApp::slotWorkTimeChanged(const QTime
& start
, const QTime
& end
, const QBitArray
& days
)
1015 KAEvent::setWorkTime(days
, start
, end
);
1018 /******************************************************************************
1019 * Called when the holiday region preference setting has changed.
1022 void KAlarmApp::slotHolidaysChanged(const KHolidays::HolidayRegion
& holidays
)
1024 KAEvent::setHolidays(holidays
);
1027 /******************************************************************************
1028 * Called when the date for February 29th recurrences has changed in the
1029 * preferences settings.
1031 void KAlarmApp::slotFeb29TypeChanged(Preferences::Feb29Type type
)
1033 KARecurrence::Feb29Type rtype
;
1037 case Preferences::Feb29_None
: rtype
= KARecurrence::Feb29_None
; break;
1038 case Preferences::Feb29_Feb28
: rtype
= KARecurrence::Feb29_Feb28
; break;
1039 case Preferences::Feb29_Mar1
: rtype
= KARecurrence::Feb29_Mar1
; break;
1041 KARecurrence::setDefaultFeb29Type(rtype
);
1044 /******************************************************************************
1045 * Return whether the program is configured to be running in the system tray.
1047 bool KAlarmApp::wantShowInSystemTray() const
1049 return Preferences::showInSystemTray() && KSystemTrayIcon::isSystemTrayAvailable();
1053 /******************************************************************************
1054 * Called when a new collection has been added, or when a collection has been
1055 * set as the standard collection for its type.
1056 * If it is the default archived calendar, purge its old alarms if necessary.
1058 void KAlarmApp::purgeNewArchivedDefault(const Akonadi::Collection
& collection
)
1060 Akonadi::Collection
col(collection
);
1061 if (CollectionControlModel::isStandard(col
, CalEvent::ARCHIVED
))
1063 // Allow time (1 minute) for AkonadiModel to be populated with the
1064 // collection's events before purging it.
1065 kDebug() << collection
.id() << ": standard archived...";
1066 QTimer::singleShot(60000, this, SLOT(purgeAfterDelay()));
1070 /******************************************************************************
1071 * Called after a delay, after the default archived calendar has been added to
1073 * Purge old alarms from it if necessary.
1075 void KAlarmApp::purgeAfterDelay()
1077 if (mArchivedPurgeDays
>= 0)
1078 purge(mArchivedPurgeDays
);
1080 setArchivePurgeDays();
1084 /******************************************************************************
1085 * Called when the length of time to keep archived alarms changes in KAlarm's
1087 * Set the number of days to keep archived alarms.
1088 * Alarms which are older are purged immediately, and at the start of each day.
1090 void KAlarmApp::setArchivePurgeDays()
1092 int newDays
= Preferences::archivedKeepDays();
1093 if (newDays
!= mArchivedPurgeDays
)
1095 int oldDays
= mArchivedPurgeDays
;
1096 mArchivedPurgeDays
= newDays
;
1097 if (mArchivedPurgeDays
<= 0)
1098 StartOfDayTimer::disconnect(this);
1099 if (mArchivedPurgeDays
< 0)
1100 return; // keep indefinitely, so don't purge
1101 if (oldDays
< 0 || mArchivedPurgeDays
< oldDays
)
1103 // Alarms are now being kept for less long, so purge them
1104 purge(mArchivedPurgeDays
);
1105 if (!mArchivedPurgeDays
)
1106 return; // don't archive any alarms
1108 // Start the purge timer to expire at the start of the next day
1109 // (using the user-defined start-of-day time).
1110 StartOfDayTimer::connect(this, SLOT(slotPurge()));
1114 /******************************************************************************
1115 * Purge all archived events from the calendar whose end time is longer ago than
1116 * 'daysToKeep'. All events are deleted if 'daysToKeep' is zero.
1118 void KAlarmApp::purge(int daysToKeep
)
1120 if (mPurgeDaysQueued
< 0 || daysToKeep
< mPurgeDaysQueued
)
1121 mPurgeDaysQueued
= daysToKeep
;
1123 // Do the purge once any other current operations are completed
1128 /******************************************************************************
1129 * Enable or disable alarm monitoring.
1131 void KAlarmApp::setAlarmsEnabled(bool enabled
)
1133 if (enabled
!= mAlarmsEnabled
)
1135 mAlarmsEnabled
= enabled
;
1136 emit
alarmEnabledToggled(enabled
);
1138 KAlarm::cancelRtcWake(0);
1139 else if (!mProcessingQueue
)
1140 checkNextDueAlarm();
1144 /******************************************************************************
1145 * Spread or collect alarm message and error message windows.
1147 void KAlarmApp::spreadWindows(bool spread
)
1149 spread
= MessageWin::spread(spread
);
1150 emit
spreadWindowsToggled(spread
);
1153 /******************************************************************************
1154 * Called when the spread status of message windows changes.
1155 * Set the 'spread windows' action state.
1157 void KAlarmApp::setSpreadWindowsState(bool spread
)
1159 emit
spreadWindowsToggled(spread
);
1162 /******************************************************************************
1163 * Called to schedule a new alarm, either in response to a DCOP notification or
1164 * to command line options.
1165 * Reply = true unless there was a parameter error or an error opening calendar file.
1167 bool KAlarmApp::scheduleEvent(KAEvent::SubAction action
, const QString
& text
, const KDateTime
& dateTime
,
1168 int lateCancel
, KAEvent::Flags flags
, const QColor
& bg
, const QColor
& fg
,
1169 const QFont
& font
, const QString
& audioFile
, float audioVolume
, int reminderMinutes
,
1170 const KARecurrence
& recurrence
, int repeatInterval
, int repeatCount
,
1172 uint mailFromID
, const KCalCore::Person::List
& mailAddresses
,
1174 uint mailFromID
, const QList
<KCal::Person
>& mailAddresses
,
1176 const QString
& mailSubject
, const QStringList
& mailAttachments
)
1179 if (!dateTime
.isValid())
1181 KDateTime now
= KDateTime::currentUtcDateTime();
1182 if (lateCancel
&& dateTime
< now
.addSecs(-maxLateness(lateCancel
)))
1183 return true; // alarm time was already archived too long ago
1184 KDateTime alarmTime
= dateTime
;
1185 // Round down to the nearest minute to avoid scheduling being messed up
1186 if (!dateTime
.isDateOnly())
1187 alarmTime
.setTime(QTime(alarmTime
.time().hour(), alarmTime
.time().minute(), 0));
1189 KAEvent
event(alarmTime
, text
, bg
, fg
, font
, action
, lateCancel
, flags
, true);
1190 if (reminderMinutes
)
1192 bool onceOnly
= flags
& KAEvent::REMINDER_ONCE
;
1193 event
.setReminder(reminderMinutes
, onceOnly
);
1195 if (!audioFile
.isEmpty())
1196 event
.setAudioFile(audioFile
, audioVolume
, -1, 0, (flags
& KAEvent::REPEAT_SOUND
) ? 0 : -1);
1197 if (!mailAddresses
.isEmpty())
1198 event
.setEmail(mailFromID
, mailAddresses
, mailSubject
, mailAttachments
);
1199 event
.setRecurrence(recurrence
);
1200 event
.setFirstRecurrence();
1201 event
.setRepetition(Repetition(repeatInterval
, repeatCount
- 1));
1203 if (alarmTime
<= now
)
1205 // Alarm is due for display already.
1206 // First execute it once without adding it to the calendar file.
1208 mDcopQueue
.enqueue(DcopQEntry(event
, EVENT_TRIGGER
));
1210 execAlarm(event
, event
.firstAlarm(), false);
1211 // If it's a recurring alarm, reschedule it for its next occurrence
1213 || event
.setNextOccurrence(now
) == KAEvent::NO_OCCURRENCE
)
1215 // It has recurrences in the future
1218 // Queue the alarm for insertion into the calendar file
1219 mDcopQueue
.enqueue(DcopQEntry(event
));
1221 QTimer::singleShot(0, this, SLOT(processQueue()));
1225 /******************************************************************************
1226 * Called in response to a D-Bus request to trigger or cancel an event.
1227 * Optionally display the event. Delete the event from the calendar file and
1228 * from every main window instance.
1230 bool KAlarmApp::dbusHandleEvent(const QString
& eventID
, EventFunc function
)
1232 kDebug() << eventID
;
1233 mDcopQueue
.append(DcopQEntry(function
, eventID
));
1235 QTimer::singleShot(0, this, SLOT(processQueue()));
1239 /******************************************************************************
1241 * a) Display the event and then delete it if it has no outstanding repetitions.
1242 * b) Delete the event.
1243 * c) Reschedule the event for its next repetition. If none remain, delete it.
1244 * If the event is deleted, it is removed from the calendar file and from every
1245 * main window instance.
1246 * Reply = false if event ID not found, or if more than one event with the same
1249 bool KAlarmApp::handleEvent(const QString
& eventID
, EventFunc function
)
1251 // Delete any expired wake-on-suspend config data
1252 KAlarm::checkRtcWakeConfig();
1255 KAEvent::List events
= AlarmCalendar::resources()->events(eventID
);
1256 if (events
.count() > 1)
1258 kWarning() << "Multiple events found with ID" << eventID
;
1261 if (events
.isEmpty())
1263 KAEvent
* event
= AlarmCalendar::resources()->event(eventID
);
1267 kWarning() << "Event ID not found:" << eventID
;
1271 KAEvent
* event
= events
[0];
1276 kDebug() << eventID
<< ", CANCEL";
1277 KAlarm::deleteEvent(*event
, true);
1280 case EVENT_TRIGGER
: // handle it if it's due, else execute it regardless
1281 case EVENT_HANDLE
: // handle it if it's due
1283 KDateTime now
= KDateTime::currentUtcDateTime();
1284 kDebug() << eventID
<< "," << (function
==EVENT_TRIGGER
?"TRIGGER:":"HANDLE:") << qPrintable(now
.dateTime().toString("yyyy-MM-dd hh:mm")) << "UTC";
1285 bool updateCalAndDisplay
= false;
1286 bool alarmToExecuteValid
= false;
1287 KAAlarm alarmToExecute
;
1288 bool restart
= false;
1289 // Check all the alarms in turn.
1290 // Note that the main alarm is fetched before any other alarms.
1291 for (KAAlarm alarm
= event
->firstAlarm();
1293 alarm
= (restart
? event
->firstAlarm() : event
->nextAlarm(alarm
)), restart
= false)
1295 // Check if the alarm is due yet.
1296 KDateTime nextDT
= alarm
.dateTime(true).effectiveKDateTime();
1297 int secs
= nextDT
.secsTo(now
);
1300 // The alarm appears to be in the future.
1301 // Check if it's an invalid local clock time during a daylight
1302 // saving time shift, which has actually passed.
1303 if (alarm
.dateTime().timeSpec() != KDateTime::ClockTime
1304 || nextDT
> now
.toTimeSpec(KDateTime::ClockTime
))
1306 // This alarm is definitely not due yet
1307 kDebug() << "Alarm" << alarm
.type() << "at" << nextDT
.dateTime() << ": not due";
1311 bool reschedule
= false;
1312 bool rescheduleWork
= false;
1313 if ((event
->workTimeOnly() || event
->holidaysExcluded()) && !alarm
.deferred())
1315 // The alarm is restricted to working hours and/or non-holidays
1316 // (apart from deferrals). This needs to be re-evaluated every
1317 // time it triggers, since working hours could change.
1318 if (alarm
.dateTime().isDateOnly())
1320 KDateTime
dt(nextDT
);
1321 dt
.setDateOnly(true);
1322 reschedule
= !event
->isWorkingTime(dt
);
1325 reschedule
= !event
->isWorkingTime(nextDT
);
1326 rescheduleWork
= reschedule
;
1328 kDebug() << "Alarm" << alarm
.type() << "at" << nextDT
.dateTime() << ": not during working hours";
1330 if (!reschedule
&& alarm
.repeatAtLogin())
1332 // Alarm is to be displayed at every login.
1333 kDebug() << "REPEAT_AT_LOGIN";
1334 // Check if the main alarm is already being displayed.
1335 // (We don't want to display both at the same time.)
1336 if (alarmToExecute
.isValid())
1339 // Set the time to display if it's a display alarm
1342 if (!reschedule
&& event
->lateCancel())
1344 // Alarm is due, and it is to be cancelled if too late.
1345 kDebug() << "LATE_CANCEL";
1346 bool cancel
= false;
1347 if (alarm
.dateTime().isDateOnly())
1349 // The alarm has no time, so cancel it if its date is too far past
1350 int maxlate
= event
->lateCancel() / 1440; // maximum lateness in days
1351 KDateTime
limit(DateTime(nextDT
.addDays(maxlate
+ 1)).effectiveKDateTime());
1354 // It's too late to display the scheduled occurrence.
1355 // Find the last previous occurrence of the alarm.
1357 KAEvent::OccurType type
= event
->previousOccurrence(now
, next
, true);
1358 switch (type
& ~KAEvent::OCCURRENCE_REPEAT
)
1360 case KAEvent::FIRST_OR_ONLY_OCCURRENCE
:
1361 case KAEvent::RECURRENCE_DATE
:
1362 case KAEvent::RECURRENCE_DATE_TIME
:
1363 case KAEvent::LAST_RECURRENCE
:
1364 limit
.setDate(next
.date().addDays(maxlate
+ 1));
1367 if (type
== KAEvent::LAST_RECURRENCE
1368 || (type
== KAEvent::FIRST_OR_ONLY_OCCURRENCE
&& !event
->recurs()))
1369 cancel
= true; // last occurrence (and there are no repetitions)
1374 case KAEvent::NO_OCCURRENCE
:
1383 // The alarm is timed. Allow it to be the permitted amount late before cancelling it.
1384 int maxlate
= maxLateness(event
->lateCancel());
1387 // It's over the maximum interval late.
1388 // Find the most recent occurrence of the alarm.
1390 KAEvent::OccurType type
= event
->previousOccurrence(now
, next
, true);
1391 switch (type
& ~KAEvent::OCCURRENCE_REPEAT
)
1393 case KAEvent::FIRST_OR_ONLY_OCCURRENCE
:
1394 case KAEvent::RECURRENCE_DATE
:
1395 case KAEvent::RECURRENCE_DATE_TIME
:
1396 case KAEvent::LAST_RECURRENCE
:
1397 if (next
.effectiveKDateTime().secsTo(now
) > maxlate
)
1399 if (type
== KAEvent::LAST_RECURRENCE
1400 || (type
== KAEvent::FIRST_OR_ONLY_OCCURRENCE
&& !event
->recurs()))
1401 cancel
= true; // last occurrence (and there are no repetitions)
1406 case KAEvent::NO_OCCURRENCE
:
1416 // All recurrences are finished, so cancel the event
1417 event
->setArchive();
1418 if (cancelAlarm(*event
, alarm
.type(), false))
1419 return true; // event has been deleted
1420 updateCalAndDisplay
= true;
1426 // The latest repetition was too long ago, so schedule the next one
1427 switch (rescheduleAlarm(*event
, alarm
, false, (rescheduleWork
? nextDT
: KDateTime())))
1430 // A working-time-only alarm has been rescheduled and the
1431 // rescheduled time is already due. Start processing the
1433 alarmToExecuteValid
= false;
1437 return true; // event has been deleted
1441 updateCalAndDisplay
= true;
1444 if (!alarmToExecuteValid
)
1446 kDebug() << "Alarm" << alarm
.type() << ": execute";
1447 alarmToExecute
= alarm
; // note the alarm to be displayed
1448 alarmToExecuteValid
= true; // only trigger one alarm for the event
1451 kDebug() << "Alarm" << alarm
.type() << ": skip";
1454 // If there is an alarm to execute, do this last after rescheduling/cancelling
1455 // any others. This ensures that the updated event is only saved once to the calendar.
1456 if (alarmToExecute
.isValid())
1457 execAlarm(*event
, alarmToExecute
, true, !alarmToExecute
.repeatAtLogin());
1460 if (function
== EVENT_TRIGGER
)
1462 // The alarm is to be executed regardless of whether it's due.
1463 // Only trigger one alarm from the event - we don't want multiple
1464 // identical messages, for example.
1465 KAAlarm alarm
= event
->firstAlarm();
1466 if (alarm
.isValid())
1467 execAlarm(*event
, alarm
, false);
1469 if (updateCalAndDisplay
)
1470 KAlarm::updateEvent(*event
); // update the window lists and calendar file
1471 else if (function
!= EVENT_TRIGGER
) { kDebug() << "No action"; }
1479 /******************************************************************************
1480 * Called when an alarm action has completed, to perform any post-alarm actions.
1482 void KAlarmApp::alarmCompleted(const KAEvent
& event
)
1484 if (!event
.postAction().isEmpty())
1486 // doShellCommand() will error if the user is not authorised to run
1488 QString command
= event
.postAction();
1489 kDebug() << event
.id() << ":" << command
;
1490 doShellCommand(command
, event
, 0, ProcData::POST_ACTION
);
1494 /******************************************************************************
1495 * Reschedule the alarm for its next recurrence after now. If none remain,
1496 * delete it. If the alarm is deleted and it is the last alarm for its event,
1497 * the event is removed from the calendar file and from every main window
1499 * If 'nextDt' is valid, the event is rescheduled for the next non-working
1500 * time occurrence after that.
1501 * Reply = 1 if 'nextDt' is valid and the rescheduled event is already due
1502 * = -1 if the event has been deleted
1505 int KAlarmApp::rescheduleAlarm(KAEvent
& event
, const KAAlarm
& alarm
, bool updateCalAndDisplay
, const KDateTime
& nextDt
)
1507 kDebug() << "Alarm type:" << alarm
.type();
1509 bool update
= false;
1510 event
.startChanges();
1511 if (alarm
.repeatAtLogin())
1513 // Leave an alarm which repeats at every login until its main alarm triggers
1514 if (!event
.reminderActive() && event
.reminderMinutes() < 0)
1516 // Executing an at-login alarm: first schedule the reminder
1517 // which occurs AFTER the main alarm.
1518 event
.activateReminderAfter(KDateTime::currentUtcDateTime());
1522 else if (alarm
.isReminder() || alarm
.deferred())
1524 // It's a reminder alarm or an extra deferred alarm, so delete it
1525 event
.removeExpiredAlarm(alarm
.type());
1530 // Reschedule the alarm for its next occurrence.
1531 bool cancelled
= false;
1532 DateTime last
= event
.mainDateTime(false); // note this trigger time
1533 if (last
!= event
.mainDateTime(true))
1534 last
= DateTime(); // but ignore sub-repetition triggers
1535 bool next
= nextDt
.isValid();
1536 KDateTime next_dt
= nextDt
;
1537 KDateTime now
= KDateTime::currentUtcDateTime();
1540 KAEvent::OccurType type
= event
.setNextOccurrence(next
? next_dt
: now
);
1543 case KAEvent::NO_OCCURRENCE
:
1544 // All repetitions are finished, so cancel the event
1545 kDebug() << "No occurrence";
1546 if (event
.reminderMinutes() < 0 && last
.isValid()
1547 && alarm
.type() != KAAlarm::AT_LOGIN_ALARM
&& !event
.mainExpired())
1549 // Set the reminder which is now due after the last main alarm trigger.
1550 // Note that at-login reminders are scheduled in execAlarm().
1551 event
.activateReminderAfter(last
);
1552 updateCalAndDisplay
= true;
1554 if (cancelAlarm(event
, alarm
.type(), updateCalAndDisplay
))
1558 if (!(type
& KAEvent::OCCURRENCE_REPEAT
))
1560 // Next occurrence is a repeat, so fall through to recurrence handling
1561 case KAEvent::RECURRENCE_DATE
:
1562 case KAEvent::RECURRENCE_DATE_TIME
:
1563 case KAEvent::LAST_RECURRENCE
:
1564 // The event is due by now and repetitions still remain, so rewrite the event
1565 if (updateCalAndDisplay
)
1568 case KAEvent::FIRST_OR_ONLY_OCCURRENCE
:
1569 // The first occurrence is still due?!?, so don't do anything
1574 if (event
.deferred())
1576 // Just in case there's also a deferred alarm, ensure it's removed
1577 event
.removeExpiredAlarm(KAAlarm::DEFERRED_ALARM
);
1582 // The alarm is restricted to working hours and/or non-holidays.
1583 // Check if the calculated next time is valid.
1584 next_dt
= event
.mainDateTime(true).effectiveKDateTime();
1585 if (event
.mainDateTime(false).isDateOnly())
1587 KDateTime
dt(next_dt
);
1588 dt
.setDateOnly(true);
1589 next
= !event
.isWorkingTime(dt
);
1592 next
= !event
.isWorkingTime(next_dt
);
1594 } while (next
&& next_dt
<= now
);
1595 reply
= (!cancelled
&& next_dt
.isValid() && (next_dt
<= now
)) ? 1 : 0;
1597 if (event
.reminderMinutes() < 0 && last
.isValid()
1598 && alarm
.type() != KAAlarm::AT_LOGIN_ALARM
)
1600 // Set the reminder which is now due after the last main alarm trigger.
1601 // Note that at-login reminders are scheduled in execAlarm().
1602 event
.activateReminderAfter(last
);
1607 KAlarm::updateEvent(event
); // update the window lists and calendar file
1611 /******************************************************************************
1612 * Delete the alarm. If it is the last alarm for its event, the event is removed
1613 * from the calendar file and from every main window instance.
1614 * Reply = true if event has been deleted.
1616 bool KAlarmApp::cancelAlarm(KAEvent
& event
, KAAlarm::Type alarmType
, bool updateCalAndDisplay
)
1619 if (alarmType
== KAAlarm::MAIN_ALARM
&& !event
.displaying() && event
.toBeArchived())
1621 // The event is being deleted. Save it in the archived resources first.
1623 KAlarm::addArchivedEvent(ev
);
1625 event
.removeExpiredAlarm(alarmType
);
1626 if (!event
.alarmCount())
1628 KAlarm::deleteEvent(event
, false);
1631 if (updateCalAndDisplay
)
1632 KAlarm::updateEvent(event
); // update the window lists and calendar file
1636 /******************************************************************************
1637 * Cancel any reminder or deferred alarms in an repeat-at-login event.
1638 * This should be called when the event is first loaded.
1639 * If there are no more alarms left in the event, the event is removed from the
1640 * calendar file and from every main window instance.
1641 * Reply = true if event has been deleted.
1643 bool KAlarmApp::cancelReminderAndDeferral(KAEvent
& event
)
1645 return cancelAlarm(event
, KAAlarm::REMINDER_ALARM
, false)
1646 || cancelAlarm(event
, KAAlarm::DEFERRED_REMINDER_ALARM
, false)
1647 || cancelAlarm(event
, KAAlarm::DEFERRED_ALARM
, true);
1650 /******************************************************************************
1651 * Execute an alarm by displaying its message or file, or executing its command.
1652 * Reply = ShellProcess instance if a command alarm
1653 * = MessageWin if an audio alarm
1654 * != 0 if successful
1655 * = -1 if execution has not completed
1656 * = 0 if the alarm is disabled, or if an error message was output.
1658 void* KAlarmApp::execAlarm(KAEvent
& event
, const KAAlarm
& alarm
, bool reschedule
, bool allowDefer
, bool noPreAction
)
1660 if (!mAlarmsEnabled
|| !event
.enabled())
1662 // The event (or all events) is disabled
1663 kDebug() << event
.id() << ": disabled";
1665 rescheduleAlarm(event
, alarm
, true);
1669 void* result
= (void*)1;
1672 switch (alarm
.action())
1674 case KAAlarm::COMMAND
:
1675 if (!event
.commandDisplay())
1677 // execCommandAlarm() will error if the user is not authorised
1678 // to run shell commands.
1679 result
= execCommandAlarm(event
, alarm
);
1681 rescheduleAlarm(event
, alarm
, true);
1684 // fall through to MESSAGE
1685 case KAAlarm::MESSAGE
:
1688 // Display a message, file or command output, provided that the same event
1689 // isn't already being displayed
1691 MessageWin
* win
= MessageWin::findEvent(EventId(event
));
1693 MessageWin
* win
= MessageWin::findEvent(event
.id());
1695 // Find if we're changing a reminder message to the real message
1696 bool reminder
= (alarm
.type() & KAAlarm::REMINDER_ALARM
);
1697 bool replaceReminder
= !reminder
&& win
&& (win
->alarmType() & KAAlarm::REMINDER_ALARM
);
1699 && (!event
.deferred() || (event
.extraActionOptions() & KAEvent::ExecPreActOnDeferral
))
1700 && (replaceReminder
|| !win
) && !noPreAction
1701 && !event
.preAction().isEmpty())
1703 // It's not a reminder alarm, and it's not a deferred alarm unless the
1704 // pre-alarm action applies to deferred alarms, and there is no message
1705 // window (other than a reminder window) currently displayed for this
1706 // alarm, and we need to execute a command before displaying the new window.
1708 // NOTE: The pre-action is not executed for a recurring alarm if an
1709 // alarm message window for a previous occurrence is still visible.
1710 // Check whether the command is already being executed for this alarm.
1711 for (int i
= 0, end
= mCommandProcesses
.count(); i
< end
; ++i
)
1713 ProcData
* pd
= mCommandProcesses
[i
];
1714 if (pd
->event
->id() == event
.id() && (pd
->flags
& ProcData::PRE_ACTION
))
1716 kDebug() << "Already executing pre-DISPLAY command";
1717 return pd
->process
; // already executing - don't duplicate the action
1721 // doShellCommand() will error if the user is not authorised to run
1723 QString command
= event
.preAction();
1724 kDebug() << "Pre-DISPLAY command:" << command
;
1725 int flags
= (reschedule
? ProcData::RESCHEDULE
: 0) | (allowDefer
? ProcData::ALLOW_DEFER
: 0);
1726 if (doShellCommand(command
, event
, &alarm
, (flags
| ProcData::PRE_ACTION
)))
1728 AlarmCalendar::resources()->setAlarmPending(&event
);
1729 return result
; // display the message after the command completes
1731 // Error executing command
1732 if (event
.cancelOnPreActionError())
1734 // Cancel the rest of the alarm execution
1735 kDebug() << event
.id() << ": pre-action failed: cancelled";
1737 rescheduleAlarm(event
, alarm
, true);
1740 // Display the message even though it failed
1745 // There isn't already a message for this event
1746 int flags
= (reschedule
? 0 : MessageWin::NO_RESCHEDULE
) | (allowDefer
? 0 : MessageWin::NO_DEFER
);
1747 (new MessageWin(&event
, alarm
, flags
))->show();
1749 else if (replaceReminder
)
1751 // The caption needs to be changed from "Reminder" to "Message"
1752 win
->cancelReminder(event
, alarm
);
1754 else if (!win
->hasDefer() && !alarm
.repeatAtLogin())
1756 // It's a repeat-at-login message with no Defer button,
1757 // which has now reached its final trigger time and needs
1758 // to be replaced with a new message.
1760 win
->showDateTime(event
, alarm
);
1764 // Use the existing message window
1768 // Raise the existing message window and replay any sound
1769 win
->repeat(alarm
); // N.B. this reschedules the alarm
1773 case KAAlarm::EMAIL
:
1775 kDebug() << "EMAIL to:" << event
.emailAddresses(",");
1776 QStringList errmsgs
;
1777 KAMail::JobData
data(event
, alarm
, reschedule
, (reschedule
|| allowDefer
));
1779 int ans
= KAMail::send(data
, errmsgs
);
1782 // The email has either been sent or failed - not queued
1784 result
= 0; // failure
1785 data
.queued
= false;
1786 emailSent(data
, errmsgs
, (ans
> 0));
1790 result
= (void*)-1; // email has been queued
1793 rescheduleAlarm(event
, alarm
, true);
1796 case KAAlarm::AUDIO
:
1798 // Play the sound, provided that the same event
1799 // isn't already playing
1801 MessageWin
* win
= MessageWin::findEvent(EventId(event
));
1803 MessageWin
* win
= MessageWin::findEvent(event
.id());
1807 // There isn't already a message for this event.
1808 int flags
= (reschedule
? 0 : MessageWin::NO_RESCHEDULE
) | MessageWin::ALWAYS_HIDE
;
1809 win
= new MessageWin(&event
, alarm
, flags
);
1813 // There's an existing message window: replay the sound
1814 win
->repeat(alarm
); // N.B. this reschedules the alarm
1824 /******************************************************************************
1825 * Called when sending an email has completed.
1827 void KAlarmApp::emailSent(KAMail::JobData
& data
, const QStringList
& errmsgs
, bool copyerr
)
1829 if (!errmsgs
.isEmpty())
1831 // Some error occurred, although the email may have been sent successfully
1832 if (errmsgs
.count() > 1)
1833 kDebug() << (copyerr
? "Copy error:" : "Failed:") << errmsgs
[1];
1834 MessageWin::showError(data
.event
, data
.alarm
.dateTime(), errmsgs
);
1836 else if (data
.queued
)
1837 emit
execAlarmSuccess();
1840 /******************************************************************************
1841 * Execute the command specified in a command alarm.
1842 * To connect to the output ready signals of the process, specify a slot to be
1843 * called by supplying 'receiver' and 'slot' parameters.
1845 ShellProcess
* KAlarmApp::execCommandAlarm(const KAEvent
& event
, const KAAlarm
& alarm
, const QObject
* receiver
, const char* slot
)
1847 // doShellCommand() will error if the user is not authorised to run
1849 int flags
= (event
.commandXterm() ? ProcData::EXEC_IN_XTERM
: 0)
1850 | (event
.commandDisplay() ? ProcData::DISP_OUTPUT
: 0);
1851 QString command
= event
.cleanText();
1852 if (event
.commandScript())
1854 // Store the command script in a temporary file for execution
1855 kDebug() << "Script";
1856 QString tmpfile
= createTempScriptFile(command
, false, event
, alarm
);
1857 if (tmpfile
.isEmpty())
1859 setEventCommandError(event
, KAEvent::CMD_ERROR
);
1862 return doShellCommand(tmpfile
, event
, &alarm
, (flags
| ProcData::TEMP_FILE
), receiver
, slot
);
1866 kDebug() << command
;
1867 return doShellCommand(command
, event
, &alarm
, flags
, receiver
, slot
);
1871 /******************************************************************************
1872 * Execute a shell command line specified by an alarm.
1873 * If the PRE_ACTION bit of 'flags' is set, the alarm will be executed via
1874 * execAlarm() once the command completes, the execAlarm() parameters being
1875 * derived from the remaining bits in 'flags'.
1876 * 'flags' must contain the bit PRE_ACTION or POST_ACTION if and only if it is
1877 * a pre- or post-alarm action respectively.
1878 * To connect to the output ready signals of the process, specify a slot to be
1879 * called by supplying 'receiver' and 'slot' parameters.
1881 * Note that if shell access is not authorised, the attempt to run the command
1884 ShellProcess
* KAlarmApp::doShellCommand(const QString
& command
, const KAEvent
& event
, const KAAlarm
* alarm
, int flags
, const QObject
* receiver
, const char* slot
)
1886 kDebug() << command
<< "," << event
.id();
1887 QIODevice::OpenMode mode
= QIODevice::WriteOnly
;
1889 QString tmpXtermFile
;
1890 if (flags
& ProcData::EXEC_IN_XTERM
)
1892 // Execute the command in a terminal window.
1893 cmd
= composeXTermCommand(command
, event
, alarm
, flags
, tmpXtermFile
);
1898 mode
= QIODevice::ReadWrite
;
1902 ShellProcess
* proc
= 0;
1905 // Use ShellProcess, which automatically checks whether the user is
1906 // authorised to run shell commands.
1907 proc
= new ShellProcess(cmd
);
1908 proc
->setEnv(QLatin1String("KALARM_UID"), event
.id(), true);
1909 proc
->setOutputChannelMode(KProcess::MergedChannels
); // combine stdout & stderr
1910 connect(proc
, SIGNAL(shellExited(ShellProcess
*)), SLOT(slotCommandExited(ShellProcess
*)));
1911 if ((flags
& ProcData::DISP_OUTPUT
) && receiver
&& slot
)
1913 connect(proc
, SIGNAL(receivedStdout(ShellProcess
*)), receiver
, slot
);
1914 connect(proc
, SIGNAL(receivedStderr(ShellProcess
*)), receiver
, slot
);
1916 if (mode
== QIODevice::ReadWrite
&& !event
.logFile().isEmpty())
1918 // Output is to be appended to a log file.
1919 // Set up a logging process to write the command's output to.
1921 if (alarm
&& alarm
->dateTime().isValid())
1923 QString dateTime
= alarm
->dateTime().formatLocale();
1924 heading
.sprintf("\n******* KAlarm %s *******\n", dateTime
.toLatin1().data());
1927 heading
= QLatin1String("\n******* KAlarm *******\n");
1928 QFile
logfile(event
.logFile());
1929 if (logfile
.open(QIODevice::Append
| QIODevice::Text
))
1931 QTextStream
out(&logfile
);
1935 proc
->setStandardOutputFile(event
.logFile(), QIODevice::Append
);
1937 pd
= new ProcData(proc
, new KAEvent(event
), (alarm
? new KAAlarm(*alarm
) : 0), flags
);
1938 if (flags
& ProcData::TEMP_FILE
)
1939 pd
->tempFiles
+= command
;
1940 if (!tmpXtermFile
.isEmpty())
1941 pd
->tempFiles
+= tmpXtermFile
;
1942 mCommandProcesses
.append(pd
);
1943 if (proc
->start(mode
))
1947 // Error executing command - report it
1948 kWarning() << "Command failed to start";
1949 commandErrorMsg(proc
, event
, alarm
, flags
);
1952 mCommandProcesses
.removeAt(mCommandProcesses
.indexOf(pd
));
1958 /******************************************************************************
1959 * Compose a command line to execute the given command in a terminal window.
1960 * 'tempScriptFile' receives the name of a temporary script file which is
1961 * invoked by the command line, if applicable.
1962 * Reply = command line, or empty string if error.
1964 QString
KAlarmApp::composeXTermCommand(const QString
& command
, const KAEvent
& event
, const KAAlarm
* alarm
, int flags
, QString
& tempScriptFile
) const
1966 kDebug() << command
<< "," << event
.id();
1967 tempScriptFile
.clear();
1968 QString cmd
= Preferences::cmdXTermCommand();
1969 cmd
.replace("%t", KGlobal::mainComponent().aboutData()->programName()); // set the terminal window title
1970 if (cmd
.indexOf("%C") >= 0)
1972 // Execute the command from a temporary script file
1973 if (flags
& ProcData::TEMP_FILE
)
1974 cmd
.replace("%C", command
); // the command is already calling a temporary file
1977 tempScriptFile
= createTempScriptFile(command
, true, event
, *alarm
);
1978 if (tempScriptFile
.isEmpty())
1980 cmd
.replace("%C", tempScriptFile
); // %C indicates where to insert the command
1983 else if (cmd
.indexOf("%W") >= 0)
1985 // Execute the command from a temporary script file,
1986 // with a sleep after the command is executed
1987 tempScriptFile
= createTempScriptFile(command
+ QLatin1String("\nsleep 86400\n"), true, event
, *alarm
);
1988 if (tempScriptFile
.isEmpty())
1990 cmd
.replace("%W", tempScriptFile
); // %w indicates where to insert the command
1992 else if (cmd
.indexOf("%w") >= 0)
1994 // Append a sleep to the command.
1995 // Quote the command in case it contains characters such as [>|;].
1996 QString exec
= KShell::quoteArg(command
+ QLatin1String("; sleep 86400"));
1997 cmd
.replace("%w", exec
); // %w indicates where to insert the command string
2001 // Set the command to execute.
2002 // Put it in quotes in case it contains characters such as [>|;].
2003 QString exec
= KShell::quoteArg(command
);
2004 if (cmd
.indexOf("%c") >= 0)
2005 cmd
.replace("%c", exec
); // %c indicates where to insert the command string
2007 cmd
.append(exec
); // otherwise, simply append the command string
2012 /******************************************************************************
2013 * Create a temporary script file containing the specified command string.
2014 * Reply = path of temporary file, or null string if error.
2016 QString
KAlarmApp::createTempScriptFile(const QString
& command
, bool insertShell
, const KAEvent
& event
, const KAAlarm
& alarm
) const
2018 KTemporaryFile tmpFile
;
2019 tmpFile
.setAutoRemove(false); // don't delete file when it is destructed
2020 if (!tmpFile
.open())
2021 kError() << "Unable to create a temporary script file";
2024 tmpFile
.setPermissions(QFile::ReadUser
| QFile::WriteUser
| QFile::ExeUser
);
2025 QTextStream
stream(&tmpFile
);
2027 stream
<< "#!" << ShellProcess::shellPath() << "\n";
2030 if (tmpFile
.error() != QFile::NoError
)
2031 kError() << "Error" << tmpFile
.errorString() << " writing to temporary script file";
2033 return tmpFile
.fileName();
2036 QStringList
errmsgs(i18nc("@info", "Error creating temporary script file"));
2037 MessageWin::showError(event
, alarm
.dateTime(), errmsgs
, QLatin1String("Script"));
2041 /******************************************************************************
2042 * Called when a command alarm's execution completes.
2044 void KAlarmApp::slotCommandExited(ShellProcess
* proc
)
2047 // Find this command in the command list
2048 for (int i
= 0, end
= mCommandProcesses
.count(); i
< end
; ++i
)
2050 ProcData
* pd
= mCommandProcesses
[i
];
2051 if (pd
->process
== proc
)
2053 // Found the command. Check its exit status.
2054 bool executeAlarm
= pd
->preAction();
2055 ShellProcess::Status status
= proc
->status();
2056 if (status
== ShellProcess::SUCCESS
&& !proc
->exitCode())
2058 kDebug() << pd
->event
->id() << ": SUCCESS";
2059 clearEventCommandError(*pd
->event
, pd
->preAction() ? KAEvent::CMD_ERROR_PRE
2060 : pd
->postAction() ? KAEvent::CMD_ERROR_POST
2061 : KAEvent::CMD_ERROR
);
2065 QString errmsg
= proc
->errorMessage();
2066 if (status
== ShellProcess::SUCCESS
|| status
== ShellProcess::NOT_FOUND
)
2067 kWarning() << pd
->event
->id() << ":" << errmsg
<< "exit status =" << status
<< ", code =" << proc
->exitCode();
2069 kWarning() << pd
->event
->id() << ":" << errmsg
<< "exit status =" << status
;
2070 if (pd
->messageBoxParent
)
2072 // Close the existing informational KMessageBox for this process
2073 QList
<KDialog
*> dialogs
= pd
->messageBoxParent
->findChildren
<KDialog
*>();
2074 if (!dialogs
.isEmpty())
2076 setEventCommandError(*pd
->event
, pd
->preAction() ? KAEvent::CMD_ERROR_PRE
2077 : pd
->postAction() ? KAEvent::CMD_ERROR_POST
2078 : KAEvent::CMD_ERROR
);
2079 if (!pd
->tempFile())
2082 errmsg
+= proc
->command();
2084 KAMessageBox::error(pd
->messageBoxParent
, errmsg
);
2087 commandErrorMsg(proc
, *pd
->event
, pd
->alarm
, pd
->flags
);
2089 if (executeAlarm
&& pd
->event
->cancelOnPreActionError())
2091 kDebug() << pd
->event
->id() << ": pre-action failed: cancelled";
2092 if (pd
->reschedule())
2093 rescheduleAlarm(*pd
->event
, *pd
->alarm
, true);
2094 executeAlarm
= false;
2097 if (pd
->preAction())
2098 AlarmCalendar::resources()->setAlarmPending(pd
->event
, false);
2100 execAlarm(*pd
->event
, *pd
->alarm
, pd
->reschedule(), pd
->allowDefer(), true);
2101 mCommandProcesses
.removeAt(i
);
2107 // If there are now no executing shell commands, quit if a quit was queued
2108 if (mPendingQuit
&& mCommandProcesses
.isEmpty())
2109 quitIf(mPendingQuitCode
);
2112 /******************************************************************************
2113 * Output an error message for a shell command, and record the alarm's error status.
2115 void KAlarmApp::commandErrorMsg(const ShellProcess
* proc
, const KAEvent
& event
, const KAAlarm
* alarm
, int flags
)
2117 KAEvent::CmdErrType cmderr
;
2118 QStringList errmsgs
;
2119 QString dontShowAgain
;
2120 if (flags
& ProcData::PRE_ACTION
)
2122 if (event
.dontShowPreActionError())
2123 return; // don't notify user of any errors for the alarm
2124 errmsgs
+= i18nc("@info", "Pre-alarm action:");
2125 dontShowAgain
= QLatin1String("Pre");
2126 cmderr
= KAEvent::CMD_ERROR_PRE
;
2128 else if (flags
& ProcData::POST_ACTION
)
2130 errmsgs
+= i18nc("@info", "Post-alarm action:");
2131 dontShowAgain
= QLatin1String("Post");
2132 cmderr
= (event
.commandError() == KAEvent::CMD_ERROR_PRE
)
2133 ? KAEvent::CMD_ERROR_PRE_POST
: KAEvent::CMD_ERROR_POST
;
2137 dontShowAgain
= QLatin1String("Exec");
2138 cmderr
= KAEvent::CMD_ERROR
;
2141 // Record the alarm's error status
2142 setEventCommandError(event
, cmderr
);
2143 // Display an error message
2146 errmsgs
+= proc
->errorMessage();
2147 if (!(flags
& ProcData::TEMP_FILE
))
2148 errmsgs
+= proc
->command();
2149 dontShowAgain
+= QString::number(proc
->status());
2151 MessageWin::showError(event
, (alarm
? alarm
->dateTime() : DateTime()), errmsgs
, dontShowAgain
);
2154 /******************************************************************************
2155 * Notes that an informational KMessageBox is displayed for this process.
2157 void KAlarmApp::commandMessage(ShellProcess
* proc
, QWidget
* parent
)
2159 // Find this command in the command list
2160 for (int i
= 0, end
= mCommandProcesses
.count(); i
< end
; ++i
)
2162 ProcData
* pd
= mCommandProcesses
[i
];
2163 if (pd
->process
== proc
)
2165 pd
->messageBoxParent
= parent
;
2171 /******************************************************************************
2172 * Return a D-Bus interface object for KSpeech.
2173 * The KTTSD D-Bus service is started if necessary.
2174 * If the service cannot be started, 'error' is set to an error text.
2176 OrgKdeKSpeechInterface
* KAlarmApp::kspeechInterface(QString
& error
) const
2179 QDBusConnection client
= QDBusConnection::sessionBus();
2180 if (!client
.interface()->isServiceRegistered(KTTSD_DBUS_SERVICE
))
2182 // kttsd is not running, so start it
2185 if (KToolInvocation::startServiceByDesktopName(QLatin1String("kttsd"), QStringList(), &error
))
2187 kDebug() << "Failed to start kttsd:" << error
;
2193 mKSpeech
= new OrgKdeKSpeechInterface(KTTSD_DBUS_SERVICE
, KTTDS_DBUS_PATH
, QDBusConnection::sessionBus());
2194 mKSpeech
->setParent(theApp());
2195 mKSpeech
->setApplicationName(KGlobal::mainComponent().aboutData()->programName());
2196 mKSpeech
->setDefaultPriority(KSpeech::jpMessage
);
2201 /******************************************************************************
2202 * Called when a D-Bus service unregisters.
2203 * If it's the KTTSD service, delete the KSpeech interface object.
2205 void KAlarmApp::slotDBusServiceUnregistered(const QString
& serviceName
)
2207 if (serviceName
== KTTSD_DBUS_SERVICE
)
2214 /******************************************************************************
2215 * If this is the first time through, open the calendar file, and start
2216 * processing the execution queue.
2218 bool KAlarmApp::initCheck(bool calendarOnly
)
2220 static bool firstTime
= true;
2223 kDebug() << "first time";
2225 /* Need to open the display calendar now, since otherwise if display
2226 * alarms are immediately due, they will often be processed while
2227 * MessageWin::redisplayAlarms() is executing open() (but before open()
2228 * completes), which causes problems!!
2230 AlarmCalendar::displayCalendar()->open();
2232 if (!AlarmCalendar::resources()->open())
2234 setArchivePurgeDays();
2240 startProcessQueue(); // start processing the execution queue
2244 /******************************************************************************
2245 * Called when an audio thread starts or stops.
2247 void KAlarmApp::notifyAudioPlaying(bool playing
)
2249 emit
audioPlaying(playing
);
2252 /******************************************************************************
2255 void KAlarmApp::stopAudio()
2257 MessageWin::stopAudio();
2261 void setEventCommandError(const KAEvent
& event
, KAEvent::CmdErrType err
)
2263 if (err
== KAEvent::CMD_ERROR_POST
&& event
.commandError() == KAEvent::CMD_ERROR_PRE
)
2264 err
= KAEvent::CMD_ERROR_PRE_POST
;
2265 event
.setCommandError(err
);
2267 KAEvent
* ev
= AlarmCalendar::resources()->event(EventId(event
));
2269 KAEvent
* ev
= AlarmCalendar::resources()->event(event
.id());
2271 if (ev
&& ev
->commandError() != err
)
2272 ev
->setCommandError(err
);
2274 AkonadiModel::instance()->updateCommandError(event
);
2276 EventListModel::alarms()->updateCommandError(event
.id());
2280 void clearEventCommandError(const KAEvent
& event
, KAEvent::CmdErrType err
)
2282 KAEvent::CmdErrType newerr
= static_cast<KAEvent::CmdErrType
>(event
.commandError() & ~err
);
2283 event
.setCommandError(newerr
);
2285 KAEvent
* ev
= AlarmCalendar::resources()->event(EventId(event
));
2287 KAEvent
* ev
= AlarmCalendar::resources()->event(event
.id());
2291 newerr
= static_cast<KAEvent::CmdErrType
>(ev
->commandError() & ~err
);
2292 ev
->setCommandError(newerr
);
2295 AkonadiModel::instance()->updateCommandError(event
);
2297 EventListModel::alarms()->updateCommandError(event
.id());
2302 KAlarmApp::ProcData::ProcData(ShellProcess
* p
, KAEvent
* e
, KAAlarm
* a
, int f
)
2306 messageBoxParent(0),
2310 KAlarmApp::ProcData::~ProcData()
2312 while (!tempFiles
.isEmpty())
2314 // Delete the temporary file called by the XTerm command
2315 QFile
f(tempFiles
.first());
2317 tempFiles
.removeFirst();