2 This file is part of the KDE reminder agent.
4 Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
20 As a special exception, permission is given to link this program
21 with any edition of Qt, and distribute the resulting executable,
22 without including the source code for Qt in the source distribution.
25 #include "koalarmclient.h"
26 #include "alarmdialog.h"
27 #include "alarmdockwindow.h"
28 #include "korgacadaptor.h"
30 #include <CalendarSupport/Utils>
31 #include <AkonadiCore/ChangeRecorder>
32 #include <AkonadiCore/Collection>
33 #include <kdbusconnectionpool.h>
34 #include <AkonadiCore/EntityTreeModel>
35 #include <AkonadiCore/Item>
36 #include <AkonadiCore/ItemFetchScope>
37 #include <AkonadiCore/Session>
39 #include <KCalCore/Calendar>
41 #include <KCheckableProxyModel>
43 #include <KConfigGroup>
44 #include <QApplication>
46 #include "koalarmclient_debug.h"
48 using namespace KCalCore
;
50 KOAlarmClient::KOAlarmClient(QObject
*parent
)
51 : QObject(parent
), mDocker(0), mDialog(0)
53 new KOrgacAdaptor(this);
54 KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/ac"), this);
55 qCDebug(KOALARMCLIENT_LOG
);
57 if (dockerEnabled()) {
58 mDocker
= new AlarmDockWindow
;
59 connect(this, &KOAlarmClient::reminderCount
, mDocker
, &AlarmDockWindow::slotUpdate
);
60 connect(mDocker
, &AlarmDockWindow::quitSignal
, this, &KOAlarmClient::slotQuit
);
62 QStringList mimeTypes
;
63 mimeTypes
<< Event::eventMimeType() << Todo::todoMimeType();
64 mCalendar
= Akonadi::ETMCalendar::Ptr(new Akonadi::ETMCalendar(mimeTypes
));
65 mCalendar
->setObjectName(QStringLiteral("KOrgac's calendar"));
66 mETM
= mCalendar
->entityTreeModel();
68 connect(&mCheckTimer
, &QTimer::timeout
, this, &KOAlarmClient::checkAlarms
);
69 connect(mETM
, &Akonadi::EntityTreeModel::collectionPopulated
, this, &KOAlarmClient::deferredInit
);
70 connect(mETM
, &Akonadi::EntityTreeModel::collectionTreeFetched
, this, &KOAlarmClient::deferredInit
);
72 KConfigGroup
alarmGroup(KSharedConfig::openConfig(), "Alarms");
73 const int interval
= alarmGroup
.readEntry("Interval", 60);
74 qCDebug(KOALARMCLIENT_LOG
) << "KOAlarmClient check interval:" << interval
<< "seconds.";
75 mLastChecked
= alarmGroup
.readEntry("CalendarsLastChecked", QDateTime());
78 mCheckTimer
.start(1000 * interval
); // interval in seconds
79 connect(qApp
, &QApplication::commitDataRequest
, this, &KOAlarmClient::slotCommitData
);
82 KOAlarmClient::~KOAlarmClient()
88 void checkAllItems(KCheckableProxyModel
*model
, const QModelIndex
&parent
= QModelIndex())
90 const int rowCount
= model
->rowCount(parent
);
91 for (int row
= 0; row
< rowCount
; ++row
) {
92 QModelIndex index
= model
->index(row
, 0, parent
);
93 model
->setData(index
, Qt::Checked
, Qt::CheckStateRole
);
95 if (model
->rowCount(index
) > 0) {
96 checkAllItems(model
, index
);
101 void KOAlarmClient::deferredInit()
103 if (!collectionsAvailable()) {
107 qCDebug(KOALARMCLIENT_LOG
) << "Performing delayed initialization.";
109 // load reminders that were active when quitting
110 KConfigGroup
genGroup(KSharedConfig::openConfig(), "General");
111 const int numReminders
= genGroup
.readEntry("Reminders", 0);
113 for (int i
= 1; i
<= numReminders
; ++i
) {
114 const QString
group(QStringLiteral("Incidence-%1").arg(i
));
115 const KConfigGroup
incGroup(KSharedConfig::openConfig(), group
);
117 const QUrl url
= incGroup
.readEntry("AkonadiUrl");
118 Akonadi::Item::Id akonadiItemId
= -1;
119 if (!url
.isValid()) {
120 // logic to migrate old KOrganizer incidence uid's to a Akonadi item.
121 const QString uid
= incGroup
.readEntry("UID");
122 if (!uid
.isEmpty()) {
123 akonadiItemId
= mCalendar
->item(uid
).id();
126 akonadiItemId
= Akonadi::Item::fromUrl(url
).id();
129 if (akonadiItemId
>= 0) {
130 const QDateTime dt
= incGroup
.readEntry("RemindAt", QDateTime());
131 Akonadi::Item i
= mCalendar
->item(Akonadi::Item::fromUrl(url
).id());
132 if (CalendarSupport::hasIncidence(i
) && !CalendarSupport::incidence(i
)->alarms().isEmpty()) {
133 createReminder(mCalendar
, i
, dt
, QString());
138 KCheckableProxyModel
*checkableModel
= mCalendar
->checkableProxyModel();
139 checkAllItems(checkableModel
);
141 // Now that everything is set up, a first check for reminders can be performed.
145 bool KOAlarmClient::dockerEnabled()
147 KConfig
korgConfig(QStandardPaths::locate(QStandardPaths::ConfigLocation
, QStringLiteral("korganizerrc")));
148 KConfigGroup
generalGroup(&korgConfig
, "System Tray");
149 return generalGroup
.readEntry("ShowReminderDaemon", true);
152 bool KOAlarmClient::collectionsAvailable() const
154 // The list of collections must be available.
155 if (!mETM
->isCollectionTreeFetched()) {
159 // All collections must be populated.
160 const int rowCount
= mETM
->rowCount();
161 for (int row
= 0; row
< rowCount
; ++row
) {
162 static const int column
= 0;
163 const QModelIndex index
= mETM
->index(row
, column
);
165 mETM
->data(index
, Akonadi::EntityTreeModel::IsPopulatedRole
).toBool();
174 void KOAlarmClient::checkAlarms()
176 KConfigGroup
cfg(KSharedConfig::openConfig(), "General");
178 if (!cfg
.readEntry("Enabled", true)) {
182 // We do not want to miss any reminders, so don't perform check unless
183 // the collections are available and populated.
184 if (!collectionsAvailable()) {
185 qCDebug(KOALARMCLIENT_LOG
) << "Collections are not available; aborting check.";
189 QDateTime from
= mLastChecked
.addSecs(1);
190 mLastChecked
= QDateTime::currentDateTime();
192 qCDebug(KOALARMCLIENT_LOG
) << "Check:" << from
.toString() << " -" << mLastChecked
.toString();
194 const Alarm::List alarms
= mCalendar
->alarms(KDateTime(from
, KDateTime::LocalZone
),
195 KDateTime(mLastChecked
, KDateTime::LocalZone
),
196 true /* exclude blocked alarms */);
198 foreach (const Alarm::Ptr
&alarm
, alarms
) {
199 const QString uid
= alarm
->customProperty("ETMCalendar", "parentUid");
200 const Akonadi::Item::Id id
= mCalendar
->item(uid
).id();
201 const Akonadi::Item item
= mCalendar
->item(id
);
203 createReminder(mCalendar
, item
, from
, alarm
->text());
207 void KOAlarmClient::createReminder(const Akonadi::ETMCalendar::Ptr
&calendar
,
208 const Akonadi::Item
&aitem
,
209 const QDateTime
&remindAtDate
,
210 const QString
&displayText
)
212 if (!CalendarSupport::hasIncidence(aitem
)) {
217 mDialog
= new AlarmDialog(calendar
);
218 connect(this, &KOAlarmClient::saveAllSignal
, mDialog
, &AlarmDialog::slotSave
);
220 connect(mDialog
, &AlarmDialog::reminderCount
, mDocker
, &AlarmDockWindow::slotUpdate
);
221 connect(mDocker
, &AlarmDockWindow::suspendAllSignal
, mDialog
, &AlarmDialog::suspendAll
);
222 connect(mDocker
, &AlarmDockWindow::dismissAllSignal
, mDialog
, &AlarmDialog::dismissAll
);
226 mDialog
->addIncidence(aitem
, remindAtDate
, displayText
);
231 void KOAlarmClient::slotQuit()
233 Q_EMIT
saveAllSignal();
238 void KOAlarmClient::saveLastCheckTime()
240 KConfigGroup
cg(KSharedConfig::openConfig(), "Alarms");
241 cg
.writeEntry("CalendarsLastChecked", mLastChecked
);
242 KSharedConfig::openConfig()->sync();
245 void KOAlarmClient::quit()
247 qCDebug(KOALARMCLIENT_LOG
);
251 void KOAlarmClient::slotCommitData(QSessionManager
&)
253 Q_EMIT
saveAllSignal();
257 void KOAlarmClient::forceAlarmCheck()
263 QString
KOAlarmClient::dumpDebug()
265 KConfigGroup
cfg(KSharedConfig::openConfig(), "Alarms");
266 const QDateTime lastChecked
= cfg
.readEntry("CalendarsLastChecked", QDateTime());
267 const QString str
= QStringLiteral("Last Check: %1").arg(lastChecked
.toString());
271 QStringList
KOAlarmClient::dumpAlarms()
273 const KDateTime start
= KDateTime(QDateTime::currentDateTime().date(),
274 QTime(0, 0), KDateTime::LocalZone
);
275 const KDateTime end
= start
.addDays(1).addSecs(-1);
278 const Alarm::List alarms
= mCalendar
->alarms(start
, end
);
279 lst
.reserve(1 + (alarms
.isEmpty() ? 1 : alarms
.count()));
280 // Don't translate, this is for debugging purposes.
281 lst
<< QStringLiteral("AlarmDeamon::dumpAlarms() from ") + start
.toString() + QLatin1String(" to ") +
284 if (alarms
.isEmpty()) {
285 lst
<< QStringLiteral("No alarm found.");
288 foreach (const Alarm::Ptr
&a
, alarms
) {
289 const Incidence::Ptr parentIncidence
= mCalendar
->incidence(a
->parentUid());
290 lst
<< QStringLiteral(" ") + parentIncidence
->summary() + QLatin1String(" (") + a
->time().toString() + QLatin1Char(')');
297 void KOAlarmClient::hide()
303 void KOAlarmClient::show()
306 if (dockerEnabled()) {
307 mDocker
= new AlarmDockWindow
;
308 connect(this, &KOAlarmClient::reminderCount
, mDocker
, &AlarmDockWindow::slotUpdate
);
309 connect(mDocker
, &AlarmDockWindow::quitSignal
, this, &KOAlarmClient::slotQuit
);
313 connect(mDialog
, &AlarmDialog::reminderCount
, mDocker
, &AlarmDockWindow::slotUpdate
);
314 connect(mDocker
, &AlarmDockWindow::suspendAllSignal
, mDialog
, &AlarmDialog::suspendAll
);
315 connect(mDocker
, &AlarmDockWindow::dismissAllSignal
, mDialog
, &AlarmDialog::dismissAll
);