KCalendarCore

todo.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  SPDX-FileCopyrightText: 2001-2003 Cornelius Schumacher <[email protected]>
5  SPDX-FileCopyrightText: 2009 Allen Winter <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
21 #include "todo.h"
22 #include "visitor.h"
23 #include "recurrence.h"
24 #include "utils_p.h"
25 
26 #include "kcalendarcore_debug.h"
27 
28 #include <QTime>
29 
30 using namespace KCalendarCore;
31 
36 //@cond PRIVATE
37 class Q_DECL_HIDDEN KCalendarCore::Todo::Private
38 {
39 public:
40  Private()
41  {}
42  Private(const KCalendarCore::Todo::Private &other)
43  {
44  init(other);
45  }
46 
47  void init(const KCalendarCore::Todo::Private &other);
48 
49  QDateTime mDtDue; // to-do due date (if there is one)
50  // ALSO the first occurrence of a recurring to-do
51  QDateTime mDtRecurrence; // next occurrence (for recurring to-dos)
52  QDateTime mCompleted; // to-do completion date (if it has been completed)
53  int mPercentComplete = 0; // to-do percent complete [0,100]
54 
58  bool recurTodo(Todo *todo);
59 };
60 
61 void KCalendarCore::Todo::Private::init(const KCalendarCore::Todo::Private &other)
62 {
63  mDtDue = other.mDtDue;
64  mDtRecurrence = other.mDtRecurrence;
65  mCompleted = other.mCompleted;
66  mPercentComplete = other.mPercentComplete;
67 }
68 
69 //@endcond
70 
71 Todo::Todo()
72  : d(new KCalendarCore::Todo::Private)
73 {
74 }
75 
76 Todo::Todo(const Todo &other)
77  : Incidence(other),
78  d(new KCalendarCore::Todo::Private(*other.d))
79 {
80 }
81 
82 Todo::Todo(const Incidence &other)
83  : Incidence(other)
84  , d(new KCalendarCore::Todo::Private)
85 {
86 }
87 
89 {
90  delete d;
91 }
92 
93 Todo *Todo::clone() const
94 {
95  return new Todo(*this);
96 }
97 
99 {
100  if (&other != this) {
101  Incidence::assign(other);
102  const Todo *t = static_cast<const Todo *>(&other);
103  d->init(*(t->d));
104  }
105  return *this;
106 }
107 
108 bool Todo::equals(const IncidenceBase &todo) const
109 {
110  if (!Incidence::equals(todo)) {
111  return false;
112  } else {
113  // If they weren't the same type IncidenceBase::equals would had returned false already
114  const Todo *t = static_cast<const Todo *>(&todo);
115  return ((dtDue() == t->dtDue()) ||
116  (!dtDue().isValid() && !t->dtDue().isValid())) &&
117  hasDueDate() == t->hasDueDate() &&
118  hasStartDate() == t->hasStartDate() &&
119  ((completed() == t->completed()) ||
120  (!completed().isValid() && !t->completed().isValid())) &&
121  hasCompletedDate() == t->hasCompletedDate() &&
122  percentComplete() == t->percentComplete();
123  }
124 }
125 
127 {
128  return TypeTodo;
129 }
130 
132 {
133  return QByteArrayLiteral("Todo");
134 }
135 void Todo::setDtDue(const QDateTime &dtDue, bool first)
136 {
137  startUpdates();
138 
139  //int diffsecs = d->mDtDue.secsTo(dtDue);
140 
141  /*if (mReadOnly) return;
142  const Alarm::List& alarms = alarms();
143  for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) {
144  if (alarm->enabled()) {
145  alarm->setTime(alarm->time().addSecs(diffsecs));
146  }
147  }*/
148 
149  if (recurs() && !first) {
150  d->mDtRecurrence = dtDue;
151  } else {
152  d->mDtDue = dtDue;
153  }
154 
155  if (recurs() && dtDue.isValid() && (!dtStart().isValid() || dtDue < recurrence()->startDateTime())) {
156  qCDebug(KCALCORE_LOG) << "To-do recurrences are now calculated against DTSTART. Fixing legacy to-do.";
157  setDtStart(dtDue);
158  }
159 
160  /*const Alarm::List& alarms = alarms();
161  for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next())
162  alarm->setAlarmStart(d->mDtDue);*/
163  setFieldDirty(FieldDtDue);
164  endUpdates();
165 }
166 
167 QDateTime Todo::dtDue(bool first) const
168 {
169  if (!hasDueDate()) {
170  return QDateTime();
171  }
172 
173  const QDateTime start = IncidenceBase::dtStart();
174  if (recurs() && !first && d->mDtRecurrence.isValid()) {
175  if (start.isValid()) {
176  // This is the normal case, recurring to-dos have a valid DTSTART.
177  const qint64 duration = start.daysTo(d->mDtDue);
178  QDateTime dt = d->mDtRecurrence.addDays(duration);
179  dt.setTime(d->mDtDue.time());
180  return dt;
181  } else {
182  // This is a legacy case, where recurrence was calculated against DTDUE
183  return d->mDtRecurrence;
184  }
185  }
186 
187  return d->mDtDue;
188 }
189 
190 bool Todo::hasDueDate() const
191 {
192  return d->mDtDue.isValid();
193 }
194 
195 bool Todo::hasStartDate() const
196 {
197  return IncidenceBase::dtStart().isValid();
198 }
199 
201 {
202  return dtStart(/*first=*/false);
203 }
204 
205 QDateTime Todo::dtStart(bool first) const
206 {
207  if (!hasStartDate()) {
208  return QDateTime();
209  }
210 
211  if (recurs() && !first && d->mDtRecurrence.isValid()) {
212  return d->mDtRecurrence;
213  } else {
214  return IncidenceBase::dtStart();
215  }
216 }
217 
218 bool Todo::isCompleted() const
219 {
220  return d->mPercentComplete == 100 || status() == StatusCompleted;
221 }
222 
223 void Todo::setCompleted(bool completed)
224 {
225  update();
226  if (completed) {
227  d->mPercentComplete = 100;
228  setStatus(StatusCompleted);
229  } else {
230  d->mPercentComplete = 0;
231  d->mCompleted = QDateTime();
232  setStatus(StatusNone);
233  }
234  setFieldDirty(FieldCompleted);
235  setFieldDirty(FieldStatus);
236  updated();
237 }
238 
240 {
241  if (hasCompletedDate()) {
242  return d->mCompleted;
243  } else {
244  return QDateTime();
245  }
246 }
247 
248 void Todo::setCompleted(const QDateTime &completed)
249 {
250  update();
251  if (!d->recurTodo(this)) {
252  d->mPercentComplete = 100;
253  d->mCompleted = completed.toUTC();
254  setFieldDirty(FieldCompleted);
255  }
256  updated();
257 }
258 
260 {
261  return d->mCompleted.isValid();
262 }
263 
265 {
266  return d->mPercentComplete;
267 }
268 
269 void Todo::setPercentComplete(int percent)
270 {
271  if (percent > 100) {
272  percent = 100;
273  } else if (percent < 0) {
274  percent = 0;
275  }
276 
277  update();
278  d->mPercentComplete = percent;
279  if (percent != 100) {
280  d->mCompleted = QDateTime();
281  }
282  setFieldDirty(FieldPercentComplete);
283  updated();
284 }
285 
286 bool Todo::isInProgress(bool first) const
287 {
288  if (isOverdue()) {
289  return false;
290  }
291 
292  if (d->mPercentComplete > 0) {
293  return true;
294  }
295 
296  if (hasStartDate() && hasDueDate()) {
297  if (allDay()) {
298  QDate currDate = QDate::currentDate();
299  if (dtStart(first).date() <= currDate && currDate < dtDue(first).date()) {
300  return true;
301  }
302  } else {
304  if (dtStart(first) <= currDate && currDate < dtDue(first)) {
305  return true;
306  }
307  }
308  }
309 
310  return false;
311 }
312 
313 bool Todo::isOpenEnded() const
314 {
315  if (!hasDueDate() && !isCompleted()) {
316  return true;
317  }
318  return false;
319 
320 }
321 
322 bool Todo::isNotStarted(bool first) const
323 {
324  if (d->mPercentComplete > 0) {
325  return false;
326  }
327 
328  if (!hasStartDate()) {
329  return false;
330  }
331 
332  if (allDay()) {
333  if (dtStart(first).date() >= QDate::currentDate()) {
334  return false;
335  }
336  } else {
337  if (dtStart(first) >= QDateTime::currentDateTimeUtc()) {
338  return false;
339  }
340  }
341  return true;
342 }
343 
344 void Todo::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
345 {
346  Incidence::shiftTimes(oldZone, newZone);
347  d->mDtDue = d->mDtDue.toTimeZone(oldZone);
348  d->mDtDue.setTimeZone(newZone);
349  if (recurs()) {
350  d->mDtRecurrence = d->mDtRecurrence.toTimeZone(oldZone);
351  d->mDtRecurrence.setTimeZone(newZone);
352  }
353  if (hasCompletedDate()) {
354  d->mCompleted = d->mCompleted.toTimeZone(oldZone);
355  d->mCompleted.setTimeZone(newZone);
356  }
357 }
358 
360 {
361  d->mDtRecurrence = dt;
362  setFieldDirty(FieldRecurrence);
363 }
364 
366 {
367  return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue;
368 }
369 
370 bool Todo::recursOn(const QDate &date, const QTimeZone &timeZone) const
371 {
372  QDate today = QDate::currentDate();
373  return
374  Incidence::recursOn(date, timeZone) &&
375  !(date < today && d->mDtRecurrence.date() < today &&
376  d->mDtRecurrence > recurrence()->startDateTime());
377 }
378 
379 bool Todo::isOverdue() const
380 {
381  if (!dtDue().isValid()) {
382  return false; // if it's never due, it can't be overdue
383  }
384 
385  const bool inPast = allDay() ? dtDue().date() < QDate::currentDate()
386  : dtDue() < QDateTime::currentDateTimeUtc();
387 
388  return inPast && !isCompleted();
389 }
390 
391 void Todo::setAllDay(bool allday)
392 {
393  if (allday != allDay() && !mReadOnly) {
394  if (hasDueDate()) {
395  setFieldDirty(FieldDtDue);
396  }
397  Incidence::setAllDay(allday);
398  }
399 }
400 
401 //@cond PRIVATE
402 bool Todo::Private::recurTodo(Todo *todo)
403 {
404  if (todo && todo->recurs()) {
405  Recurrence *r = todo->recurrence();
406  const QDateTime recurrenceEndDateTime = r->endDateTime();
407  QDateTime nextOccurrenceDateTime = r->getNextDateTime(todo->dtStart());
408 
409  if ((r->duration() == -1 ||
410  (nextOccurrenceDateTime.isValid() && recurrenceEndDateTime.isValid() &&
411  nextOccurrenceDateTime <= recurrenceEndDateTime))) {
412  // We convert to the same timeSpec so we get the correct .date()
413  const auto rightNow =
414  QDateTime::currentDateTimeUtc().toTimeZone(nextOccurrenceDateTime.timeZone());
415  const bool isDateOnly = todo->allDay();
416 
417  /* Now we search for the occurrence that's _after_ the currentUtcDateTime, or
418  * if it's dateOnly, the occurrrence that's _during or after today_.
419  * The reason we use "<" for date only, but "<=" for ocurrences with time is that
420  * if it's date only, the user can still complete that ocurrence today, so that's
421  * the current ocurrence that needs completing.
422  */
423  while (!todo->recursAt(nextOccurrenceDateTime) ||
424  (!isDateOnly && nextOccurrenceDateTime <= rightNow) ||
425  (isDateOnly && nextOccurrenceDateTime.date() < rightNow.date())) {
426 
427  if (!nextOccurrenceDateTime.isValid() ||
428  (nextOccurrenceDateTime > recurrenceEndDateTime && r->duration() != -1)) {
429  return false;
430  }
431  nextOccurrenceDateTime = r->getNextDateTime(nextOccurrenceDateTime);
432  }
433 
434  todo->setDtRecurrence(nextOccurrenceDateTime);
435  todo->setCompleted(false);
436  todo->setRevision(todo->revision() + 1);
437 
438  return true;
439  }
440  }
441 
442  return false;
443 }
444 //@endcond
445 
446 bool Todo::accept(Visitor &v, const IncidenceBase::Ptr &incidence)
447 {
448  return v.visit(incidence.staticCast<Todo>());
449 }
450 
452 {
453  switch (role) {
454  case RoleAlarmStartOffset:
455  return dtStart();
456  case RoleAlarmEndOffset:
457  return dtDue();
458  case RoleSort:
459  // Sorting to-dos first compares dtDue, then dtStart if
460  // dtDue doesn't exist
461  return hasDueDate() ? dtDue() : dtStart();
462  case RoleCalendarHashing:
463  return dtDue();
464  case RoleStartTimeZone:
465  return dtStart();
466  case RoleEndTimeZone:
467  return dtDue();
468  case RoleEndRecurrenceBase:
469  return dtDue();
470  case RoleDisplayStart:
471  case RoleDisplayEnd:
472  return dtDue().isValid() ? dtDue() : dtStart();
473  case RoleAlarm:
474  if (alarms().isEmpty()) {
475  return QDateTime();
476  } else {
477  Alarm::Ptr alarm = alarms().at(0);
478  if (alarm->hasStartOffset() && hasStartDate()) {
479  return dtStart();
480  } else if (alarm->hasEndOffset() && hasDueDate()) {
481  return dtDue();
482  } else {
483  // The application shouldn't add alarms on to-dos without dates.
484  return QDateTime();
485  }
486  }
487  case RoleRecurrenceStart:
488  if (dtStart().isValid()) {
489  return dtStart();
490  }
491  return dtDue(); //For the sake of backwards compatibility
492  //where we calculated recurrences based on dtDue
493  case RoleEnd:
494  return dtDue();
495  default:
496  return QDateTime();
497  }
498 }
499 
500 void Todo::setDateTime(const QDateTime &dateTime, DateTimeRole role)
501 {
502  switch (role) {
503  case RoleDnD:
504  setDtDue(dateTime);
505  break;
506  case RoleEnd:
507  setDtDue(dateTime, true);
508  break;
509  default:
510  qCDebug(KCALCORE_LOG) << "Unhandled role" << role;
511  }
512 }
513 
514 void Todo::virtual_hook(VirtualHook id, void *data)
515 {
516  Q_UNUSED(id);
517  Q_UNUSED(data);
518 }
519 
521 {
522  return Todo::todoMimeType();
523 }
524 
526 {
527  return QLatin1String("application/x-vnd.akonadi.calendar.todo");
528 }
529 
530 QLatin1String Todo::iconName(const QDateTime &recurrenceId) const
531 {
532  const bool usesCompletedTaskPixmap =
533  isCompleted() ||
534  (recurs() && recurrenceId.isValid() && (recurrenceId < dtStart(/*first=*/false)));
535 
536  if (usesCompletedTaskPixmap) {
537  return QLatin1String("task-complete");
538  } else {
539  return QLatin1String("view-calendar-tasks");
540  }
541 }
542 
543 void Todo::serialize(QDataStream &out) const
544 {
546  serializeQDateTimeAsKDateTime(out, d->mDtDue);
547  serializeQDateTimeAsKDateTime(out, d->mDtRecurrence);
548  serializeQDateTimeAsKDateTime(out, d->mCompleted);
549  out << d->mPercentComplete;
550 }
551 
552 void Todo::deserialize(QDataStream &in)
553 {
555  deserializeKDateTimeAsQDateTime(in, d->mDtDue);
556  deserializeKDateTimeAsQDateTime(in, d->mDtRecurrence);
557  deserializeKDateTimeAsQDateTime(in, d->mCompleted);
558  in >> d->mPercentComplete;
559 }
560 
562 {
563  return true;
564 }
bool isInProgress(bool first) const
Returns true, if the to-do is in-progress (started, or >0% completed); otherwise return false...
Definition: todo.cpp:286
QLatin1String iconName(const QDateTime &recurrenceId={}) const override
Returns the name of the icon that best represents this incidence.
Definition: todo.cpp:530
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrence.cpp:489
bool hasStartDate() const
Returns if the todo has a start datetime.
Definition: todo.cpp:195
bool isCompleted() const
Returns if the todo is 100% completed.
Definition: todo.cpp:218
QDateTime toUTC() const const
void setPercentComplete(int percent)
Sets what percentage of the to-do is completed.
Definition: todo.cpp:269
Todo()
Constructs an empty to-do.
This class provides the interface for a visitor of calendar components.
Definition: visitor.h:31
void serialize(QDataStream &out) const override
Sub-type specific serialization.
Definition: incidence.cpp:1073
void setDtRecurrence(const QDateTime &dt)
Sets the due date/time of the current occurrence if recurrent.
Definition: todo.cpp:359
void virtual_hook(VirtualHook id, void *data) override
Standard trick to add virtuals later.
Definition: todo.cpp:514
virtual bool recursOn(const QDate &date, const QTimeZone &timeZone) const
Returns true if the date specified is one on which the event will recur.
Definition: incidence.cpp:584
qint64 daysTo(const QDateTime &other) const const
QDateTime dtStart() const override
Definition: todo.cpp:200
QDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:423
bool recurs() const
Returns whether the event recurs at all.
Definition: incidence.cpp:575
QLatin1String mimeType() const override
Returns the Akonadi specific sub MIME type of a KCalendarCore::IncidenceBase item, e.g.
Definition: todo.cpp:520
void setTime(const QTime &time)
QByteArray typeStr() const override
Prints the type of incidence as a string.
bool isOpenEnded() const
Returns true, if the to-do is open-ended (no due date); false otherwise.
Definition: todo.cpp:313
int percentComplete() const
Returns what percentage of the to-do is completed.
Definition: todo.cpp:264
bool hasDueDate() const
Returns if the todo has a due datetime.
Definition: todo.cpp:190
QDateTime toTimeZone(const QTimeZone &timeZone) const const
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
Definition: todo.cpp:344
DateTimeRole
The different types of incidence date/times roles.
void setRevision(int rev)
Sets the number of revisions this incidence has seen.
Definition: incidence.cpp:370
void setCompleted(bool completed)
Sets completed state.
Definition: todo.cpp:223
An abstract class that provides a common base for all calendar incidence classes. ...
Definition: incidencebase.h:96
void setAllDay(bool allDay) override
Definition: incidence.cpp:338
IncidenceType type() const override
Returns the incidence type.
bool isOverdue() const
Returns true if this todo is overdue (e.g.
Definition: todo.cpp:379
bool supportsGroupwareCommunication() const override
Definition: todo.cpp:561
QDateTime dtDue(bool first=false) const
Returns the todo due datetime.
Definition: todo.cpp:167
void setDtDue(const QDateTime &dtDue, bool first=false)
Sets due date and time.
Status status() const
Returns the status of this message.
Todo * clone() const override
Returns an exact copy of this todo.
This file is part of the API for handling calendar data and defines the Todo class.
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
Definition: incidence.cpp:396
IncidenceBase & assign(const IncidenceBase &other) override
Provides polymorfic assignment.
bool recursAt(const QDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
Definition: incidence.cpp:589
void setAllDay(bool allDay) override
Definition: todo.cpp:391
Provides a To-do in the sense of RFC2445.
Definition: todo.h:31
IncidenceType
The different types of incidences, per RFC2445.
QDateTime completed() const
Returns the to-do was completion datetime.
Definition: todo.cpp:239
QDateTime getNextDateTime(const QDateTime &preDateTime) const
Returns the start date/time of the earliest recurrence with a start date/time after the specified dat...
static QLatin1String todoMimeType()
Returns the Akonadi specific sub MIME type of a KCalendarCore::Todo.
Definition: todo.cpp:525
QCA_EXPORT void init()
bool isNotStarted(bool first) const
Returns true, if the to-do has yet to be started (no start date and 0% completed); otherwise return f...
Definition: todo.cpp:322
bool isValid() const const
bool recursOn(const QDate &date, const QTimeZone &timeZone) const override
Returns true if the date specified is one on which the to-do will recur.
Definition: todo.cpp:370
Recurrence * recurrence() const
Returns the recurrence rule associated with this incidence.
Definition: incidence.cpp:547
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:76
QDate date() const const
bool hasCompletedDate() const
Returns if the to-do has a completion datetime.
Definition: todo.cpp:259
QDateTime dateTime(DateTimeRole role) const override
Returns a date/time corresponding to the specified DateTimeRole.
Definition: todo.cpp:451
QDate currentDate()
void deserialize(QDataStream &in) override
Sub-type specific deserialization.
Definition: incidence.cpp:1098
virtual bool visit(const Event::Ptr &event)
Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on a...
Definition: visitor.cpp:29
virtual QDateTime dtStart() const
Returns an incidence&#39;s starting date/time as a QDateTime.
QDateTime dtRecurrence() const
Returns the due date/time of the current occurrence if recurrent.
Definition: todo.cpp:365
Provides the abstract base class common to non-FreeBusy (Events, To-dos, Journals) calendar component...
Definition: incidence.h:56
void setDateTime(const QDateTime &dateTime, DateTimeRole role) override
Sets the date/time corresponding to the specified DateTimeRole.
Definition: todo.cpp:500
~Todo() override
Destroys a to-do.
QSharedPointer< X > staticCast() const const
QDateTime addDays(qint64 ndays) const const
IncidenceBase & assign(const IncidenceBase &other) override
Provides polymorfic assignment.
Definition: incidence.cpp:198
bool equals(const IncidenceBase &todo) const override
Compare this with todo for equality.
int revision() const
Returns the number of revisions this incidence has seen.
Definition: incidence.cpp:383
QDateTime currentDateTimeUtc()
bool equals(const IncidenceBase &incidence) const override
Compares this with Incidence incidence for equality.
Definition: incidence.cpp:211
Namespace for all KCalendarCore types.
Definition: alarm.h:36
QTimeZone timeZone() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Sep 20 2020 22:50:26 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.