CalendarSupport

freebusyitemmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
3 SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "freebusyitemmodel.h"
9
10#include <Akonadi/FreeBusyManager>
11
12#include <KLocalizedString>
13
14#include <QLocale>
15#include <QTimerEvent>
16
17using namespace CalendarSupport;
18
19class ItemPrivateData
20{
21public:
22 ItemPrivateData(ItemPrivateData *parent)
23 : parentItem(parent)
24 {
25 }
26
27 ~ItemPrivateData()
28 {
29 qDeleteAll(childItems);
30 }
31
32 ItemPrivateData *child(int row)
33 {
34 return childItems.value(row);
35 }
36
37 void appendChild(ItemPrivateData *item)
38 {
39 childItems.append(item);
40 }
41
42 ItemPrivateData *removeChild(int row)
43 {
44 return childItems.takeAt(row);
45 }
46
47 [[nodiscard]] int childCount() const
48 {
49 return childItems.count();
50 }
51
52 [[nodiscard]] int row() const
53 {
54 if (parentItem) {
55 return parentItem->childItems.indexOf(const_cast<ItemPrivateData *>(this));
56 }
57 return 0;
58 }
59
60 ItemPrivateData *parent()
61 {
62 return parentItem;
63 }
64
65private:
66 QList<ItemPrivateData *> childItems;
67 ItemPrivateData *parentItem = nullptr;
68};
69
70class CalendarSupport::FreeBusyItemModelPrivate
71{
72public:
73 ~FreeBusyItemModelPrivate()
74 {
75 delete mRootData;
76 }
77
78 QTimer mReloadTimer;
79 bool mForceDownload = false;
80 QList<FreeBusyItem::Ptr> mFreeBusyItems;
81 ItemPrivateData *mRootData = nullptr;
82};
83
84FreeBusyItemModel::FreeBusyItemModel(QObject *parent)
85 : QAbstractItemModel(parent)
86 , d(new CalendarSupport::FreeBusyItemModelPrivate)
87{
88 qRegisterMetaType<KCalendarCore::Attendee>();
89 qRegisterMetaType<KCalendarCore::FreeBusy::Ptr>("KCalendarCore::FreeBusy::Ptr");
90 qRegisterMetaType<KCalendarCore::Period>("KCalendarCore::Period");
91
92 Akonadi::FreeBusyManager *m = Akonadi::FreeBusyManager::self();
93 connect(m, &Akonadi::FreeBusyManager::freeBusyRetrieved, this, &FreeBusyItemModel::slotInsertFreeBusy);
94
95 connect(&d->mReloadTimer, &QTimer::timeout, this, &FreeBusyItemModel::autoReload);
96 d->mReloadTimer.setSingleShot(true);
97
98 d->mRootData = new ItemPrivateData(nullptr);
99}
100
101FreeBusyItemModel::~FreeBusyItemModel() = default;
102
103QVariant FreeBusyItemModel::data(const QModelIndex &index, int role) const
104{
105 if (!index.isValid()) {
106 return {};
107 }
108
109 auto data = (ItemPrivateData *)index.internalPointer();
110
111 if (data->parent() == d->mRootData) {
112 int row = index.row();
113 if (row >= d->mFreeBusyItems.size()) {
114 return {};
115 }
116
117 switch (role) {
118 case Qt::DisplayRole:
119 return d->mFreeBusyItems.at(row)->attendee().fullName();
120 case FreeBusyItemModel::AttendeeRole:
121 return QVariant::fromValue(d->mFreeBusyItems.at(row)->attendee());
122 case FreeBusyItemModel::FreeBusyRole:
123 if (d->mFreeBusyItems.at(row)->freeBusy()) {
124 return QVariant::fromValue(d->mFreeBusyItems.at(row)->freeBusy());
125 } else {
126 return {};
127 }
128 default:
129 return {};
130 }
131 }
132
133 FreeBusyItem::Ptr fbitem = d->mFreeBusyItems.at(data->parent()->row());
134 if (!fbitem->freeBusy() || index.row() >= fbitem->freeBusy()->busyPeriods().size()) {
135 return {};
136 }
137
138 KCalendarCore::FreeBusyPeriod period = fbitem->freeBusy()->fullBusyPeriods().at(index.row());
139 switch (role) {
140 case Qt::DisplayRole: // return something to make modeltest happy
141 return QStringLiteral("%1 - %2").arg(QLocale().toString(period.start().toLocalTime(), QLocale::ShortFormat),
143 case FreeBusyItemModel::FreeBusyPeriodRole:
144 return QVariant::fromValue(period);
145 default:
146 return {};
147 }
148}
149
150int FreeBusyItemModel::rowCount(const QModelIndex &parent) const
151{
152 ItemPrivateData *parentData = nullptr;
153 if (parent.column() > 0) {
154 return 0;
155 }
156
157 if (!parent.isValid()) {
158 parentData = d->mRootData;
159 } else {
160 parentData = static_cast<ItemPrivateData *>(parent.internalPointer());
161 }
162
163 return parentData->childCount();
164}
165
166int FreeBusyItemModel::columnCount(const QModelIndex &parent) const
167{
168 Q_UNUSED(parent)
169 return 1;
170}
171
172QModelIndex FreeBusyItemModel::index(int row, int column, const QModelIndex &parent) const
173{
174 if (!hasIndex(row, column, parent)) {
175 return {};
176 }
177
178 ItemPrivateData *parentData = nullptr;
179 if (!parent.isValid()) {
180 parentData = d->mRootData;
181 } else {
182 parentData = static_cast<ItemPrivateData *>(parent.internalPointer());
183 }
184
185 ItemPrivateData *childData = parentData->child(row);
186 if (childData) {
187 return createIndex(row, column, childData);
188 } else {
189 return {};
190 }
191}
192
194{
195 if (!child.isValid()) {
196 return {};
197 }
198
199 auto childData = static_cast<ItemPrivateData *>(child.internalPointer());
200 ItemPrivateData *parentData = childData->parent();
201 if (parentData == d->mRootData) {
202 return {};
203 }
204
205 return createIndex(parentData->row(), 0, parentData);
206}
207
208QVariant FreeBusyItemModel::headerData(int section, Qt::Orientation orientation, int role) const
209{
210 if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section == 0) {
211 return i18n("Attendee");
212 }
213 return {};
214}
215
216void FreeBusyItemModel::addItem(const FreeBusyItem::Ptr &freebusy)
217{
218 int row = d->mFreeBusyItems.size();
219 beginInsertRows(QModelIndex(), row, row);
220 d->mFreeBusyItems.append(freebusy);
221 auto data = new ItemPrivateData(d->mRootData);
222 d->mRootData->appendChild(data);
224
225 if (freebusy->freeBusy() && !freebusy->freeBusy()->fullBusyPeriods().isEmpty()) {
226 QModelIndex parent = index(row, 0);
227 setFreeBusyPeriods(parent, freebusy->freeBusy()->fullBusyPeriods());
228 }
229 updateFreeBusyData(freebusy);
230}
231
232void FreeBusyItemModel::setFreeBusyPeriods(const QModelIndex &parent, const KCalendarCore::FreeBusyPeriod::List &list)
233{
234 if (!parent.isValid()) {
235 return;
236 }
237
238 auto parentData = static_cast<ItemPrivateData *>(parent.internalPointer());
239 int fb_count = list.size();
240 int childCount = parentData->childCount();
241 QModelIndex first = index(0, 0, parent);
242 QModelIndex last = index(childCount - 1, 0, parent);
243
244 if (childCount > 0 && fb_count < childCount) {
245 beginRemoveRows(parent, fb_count - 1 < 0 ? 0 : fb_count - 1, childCount - 1);
246 for (int i = childCount - 1; i > fb_count; --i) {
247 delete parentData->removeChild(i);
248 }
250 if (fb_count > 0) {
251 last = index(fb_count - 1, 0, parent);
252 Q_EMIT dataChanged(first, last);
253 }
254 } else if (fb_count > childCount) {
255 beginInsertRows(parent, childCount, fb_count - 1);
256 for (int i = childCount; i < fb_count; ++i) {
257 auto childData = new ItemPrivateData(parentData);
258 parentData->appendChild(childData);
259 }
261 if (childCount > 0) {
262 last = index(childCount - 1, 0, parent);
263 Q_EMIT dataChanged(first, last);
264 }
265 } else if (fb_count == childCount && fb_count > 0) {
266 Q_EMIT dataChanged(first, last);
267 }
268}
269
270void FreeBusyItemModel::clear()
271{
273 d->mFreeBusyItems.clear();
274 delete d->mRootData;
275 d->mRootData = new ItemPrivateData(nullptr);
277}
278
279void FreeBusyItemModel::removeRow(int row)
280{
281 beginRemoveRows(QModelIndex(), row, row);
282 d->mFreeBusyItems.removeAt(row);
283 ItemPrivateData *data = d->mRootData->removeChild(row);
284 delete data;
286}
287
288void FreeBusyItemModel::removeItem(const FreeBusyItem::Ptr &freebusy)
289{
290 int row = d->mFreeBusyItems.indexOf(freebusy);
291 if (row >= 0) {
292 removeRow(row);
293 }
294}
295
296void FreeBusyItemModel::removeAttendee(const KCalendarCore::Attendee &attendee)
297{
298 FreeBusyItem::Ptr anItem;
299 for (int i = 0; i < d->mFreeBusyItems.count(); ++i) {
300 anItem = d->mFreeBusyItems[i];
301 if (anItem->attendee() == attendee) {
302 if (anItem->updateTimerID() != 0) {
303 killTimer(anItem->updateTimerID());
304 }
305 removeRow(i);
306 break;
307 }
308 }
309}
310
311bool FreeBusyItemModel::containsAttendee(const KCalendarCore::Attendee &attendee)
312{
313 FreeBusyItem::Ptr anItem;
314 for (int i = 0; i < d->mFreeBusyItems.count(); ++i) {
315 anItem = d->mFreeBusyItems[i];
316 if (anItem->attendee() == attendee) {
317 return true;
318 }
319 }
320 return false;
321}
322
323void FreeBusyItemModel::updateFreeBusyData(const FreeBusyItem::Ptr &item)
324{
325 if (item->isDownloading()) {
326 // This item is already in the process of fetching the FB list
327 return;
328 }
329
330 if (item->updateTimerID() != 0) {
331 // An update timer is already running. Reset it
332 killTimer(item->updateTimerID());
333 }
334
335 // This item does not have a download running, and no timer is set
336 // Do the download in one second
337 item->setUpdateTimerID(startTimer(1000));
338}
339
340void FreeBusyItemModel::timerEvent(QTimerEvent *event)
341{
342 killTimer(event->timerId());
343 for (FreeBusyItem::Ptr item : std::as_const(d->mFreeBusyItems)) {
344 if (item->updateTimerID() == event->timerId()) {
345 item->setUpdateTimerID(0);
346 item->startDownload(d->mForceDownload);
347 return;
348 }
349 }
350}
351
352void FreeBusyItemModel::slotInsertFreeBusy(const KCalendarCore::FreeBusy::Ptr &fb, const QString &email)
353{
354 if (!fb) {
355 return;
356 }
357
358 if (fb->fullBusyPeriods().isEmpty()) {
359 return;
360 }
361
362 fb->sortList();
363
364 for (FreeBusyItem::Ptr item : std::as_const(d->mFreeBusyItems)) {
365 if (item->email() == email) {
366 item->setFreeBusy(fb);
367 const int row = d->mFreeBusyItems.indexOf(item);
368 const QModelIndex parent = index(row, 0);
370 setFreeBusyPeriods(parent, fb->fullBusyPeriods());
371 }
372 }
373}
374
375void FreeBusyItemModel::autoReload()
376{
377 d->mForceDownload = false;
378 reload();
379}
380
381void FreeBusyItemModel::reload()
382{
383 for (FreeBusyItem::Ptr item : std::as_const(d->mFreeBusyItems)) {
384 if (d->mForceDownload) {
385 item->startDownload(d->mForceDownload);
386 } else {
387 updateFreeBusyData(item);
388 }
389 }
390}
391
392void FreeBusyItemModel::triggerReload()
393{
394 d->mReloadTimer.start(1000);
395}
396
397void FreeBusyItemModel::cancelReload()
398{
399 d->mReloadTimer.stop();
400}
401
402#include "moc_freebusyitemmodel.cpp"
QDateTime end() const
QDateTime start() const
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QModelIndex createIndex(int row, int column, const void *ptr) const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
bool hasIndex(int row, int column, const QModelIndex &parent) const const
QDateTime toLocalTime() const const
void append(QList< T > &&value)
qsizetype count() const const
qsizetype indexOf(const AT &value, qsizetype from) const const
qsizetype size() const const
T takeAt(qsizetype i)
T value(qsizetype i) const const
void * internalPointer() const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
virtual bool event(QEvent *e)
void killTimer(int id)
QObject * parent() const const
int startTimer(int interval, Qt::TimerType timerType)
DisplayRole
Orientation
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:07:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.