presentation/TaskEditorFields.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "TaskEditorFields.h" | ||
| 2 | #include "PimEditorTime.h" | ||
| 3 | #include "util/MessageId.h" | ||
| 4 | #include "data/RecurrenceRule.h" | ||
| 5 | #include <optional> | ||
| 6 | #include <cmath> | ||
| 7 | 32 | bool TaskEditorFields::apply(CalendarTask &task, const QVariantMap &fields) { | |
| 8 | 32 | const QString previousStatus = task.status; const int previousPercent = task.percentComplete; | |
| 9 |
1/1✓ Branch 3 → 4 taken 32 times.
|
32 | const bool hadSummary = !task.summary.trimmed().isEmpty(); |
| 10 |
2/2✓ Branch 6 → 7 taken 32 times.
✓ Branch 7 → 8 taken 32 times.
|
32 | const bool hadDue = task.due.isValid(), hadStart = task.dtStart.isValid(); |
| 11 |
3/3✓ Branch 12 → 13 taken 32 times.
✓ Branch 13 → 14 taken 32 times.
✓ Branch 14 → 15 taken 32 times.
|
64 | task.summary = fields.value(QStringLiteral("summary"), task.summary).toString().trimmed(); |
| 12 |
2/2✓ Branch 26 → 27 taken 32 times.
✓ Branch 27 → 28 taken 32 times.
|
64 | task.description = fields.value(QStringLiteral("description"), task.description).toString(); |
| 13 | const QString related = MessageId::canonicalFirst( | ||
| 14 |
1/1✓ Branch 38 → 39 taken 32 times.
|
96 | fields.value(QStringLiteral("relatedMessageId"), task.relatedMessageId) |
| 15 |
2/2✓ Branch 39 → 40 taken 32 times.
✓ Branch 40 → 41 taken 32 times.
|
32 | .toString()); |
| 16 |
1/2✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 32 times.
|
32 | if (related.size() > 998) |
| 17 | ✗ | return false; | |
| 18 | 32 | task.relatedMessageId = related; | |
| 19 | // S106-348: SUMMARY is optional in RFC 5545 — a synced task without one | ||
| 20 | // stays editable; only clearing a title that was there is refused (a new | ||
| 21 | // task's title is required by its caller). | ||
| 22 |
6/6✓ Branch 51 → 52 taken 3 times.
✓ Branch 51 → 54 taken 29 times.
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 2 times.
✓ Branch 55 → 56 taken 1 time.
✓ Branch 55 → 57 taken 31 times.
|
32 | if (task.summary.isEmpty() && hadSummary) return false; |
| 23 | 62 | const auto wholeNumber = [&fields](const QString &key, int fallback, int maximum) -> std::optional<int> { | |
| 24 | 62 | bool ok = false; | |
| 25 |
2/2✓ Branch 3 → 4 taken 62 times.
✓ Branch 4 → 5 taken 62 times.
|
62 | const double value = fields.value(key, fallback).toDouble(&ok); |
| 26 |
11/12✓ Branch 7 → 8 taken 61 times.
✓ Branch 7 → 13 taken 1 time.
✓ Branch 9 → 10 taken 61 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 60 times.
✓ Branch 10 → 13 taken 1 time.
✓ Branch 11 → 12 taken 59 times.
✓ Branch 11 → 13 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 58 times.
✓ Branch 15 → 16 taken 4 times.
✓ Branch 15 → 19 taken 58 times.
|
62 | if (!ok || !std::isfinite(value) || value < 0 || value > maximum || std::floor(value) != value) |
| 27 | 4 | return std::nullopt; | |
| 28 | 58 | return static_cast<int>(value); | |
| 29 | 31 | }; | |
| 30 |
1/1✓ Branch 60 → 61 taken 31 times.
|
62 | const auto priority = wholeNumber(QStringLiteral("priority"), task.priority, 9); |
| 31 |
1/1✓ Branch 66 → 67 taken 31 times.
|
62 | const auto percent = wholeNumber(QStringLiteral("percentComplete"), task.percentComplete, 100); |
| 32 |
5/6✓ Branch 70 → 71 taken 31 times.
✗ Branch 70 → 73 not taken.
✓ Branch 72 → 73 taken 4 times.
✓ Branch 72 → 74 taken 27 times.
✓ Branch 75 → 76 taken 4 times.
✓ Branch 75 → 77 taken 27 times.
|
31 | if (!priority || !percent) return false; |
| 33 | 27 | task.priority = *priority; | |
| 34 |
5/6✓ Branch 79 → 80 taken 5 times.
✓ Branch 79 → 84 taken 22 times.
✓ Branch 82 → 83 taken 5 times.
✗ Branch 82 → 84 not taken.
✓ Branch 85 → 86 taken 5 times.
✓ Branch 85 → 115 taken 22 times.
|
27 | if (*percent != task.percentComplete && task.status != QLatin1String("CANCELLED")) { |
| 35 | 5 | task.percentComplete = *percent; | |
| 36 |
2/2✓ Branch 88 → 89 taken 3 times.
✓ Branch 88 → 92 taken 2 times.
|
18 | task.status = *percent == 100 ? QStringLiteral("COMPLETED") |
| 37 |
8/8✓ Branch 93 → 94 taken 1 time.
✓ Branch 93 → 97 taken 1 time.
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 104 taken 4 times.
✓ Branch 104 → 105 taken 1 time.
✓ Branch 104 → 106 taken 4 times.
✓ Branch 106 → 107 taken 3 times.
✓ Branch 106 → 108 taken 2 times.
|
12 | : *percent == 0 ? QStringLiteral("NEEDS-ACTION") : QStringLiteral("IN-PROCESS"); |
| 38 |
3/3✓ Branch 109 → 110 taken 3 times.
✓ Branch 109 → 111 taken 2 times.
✓ Branch 110 → 112 taken 3 times.
|
5 | task.completedAt = *percent == 100 ? QDateTime::currentDateTimeUtc() : QDateTime{}; |
| 39 | } | ||
| 40 |
3/3✓ Branch 118 → 119 taken 27 times.
✓ Branch 121 → 122 taken 4 times.
✓ Branch 121 → 162 taken 23 times.
|
27 | if (fields.contains(QStringLiteral("status"))) { |
| 41 |
2/2✓ Branch 126 → 127 taken 4 times.
✓ Branch 127 → 128 taken 4 times.
|
8 | const auto status = fields.value(QStringLiteral("status")).toString(); |
| 42 |
3/6✓ Branch 140 → 141 taken 16 times.
✓ Branch 140 → 142 taken 4 times.
✗ Branch 142 → 143 not taken.
✓ Branch 142 → 144 taken 4 times.
✗ Branch 497 → 498 not taken.
✗ Branch 497 → 499 not taken.
|
20 | if (!QStringList{"NEEDS-ACTION", "IN-PROCESS", "COMPLETED", "CANCELLED"}.contains(status)) return false; |
| 43 | 4 | task.status = status; | |
| 44 |
3/3✓ Branch 147 → 148 taken 2 times.
✓ Branch 147 → 152 taken 2 times.
✓ Branch 148 → 149 taken 2 times.
|
4 | if (status == QLatin1String("COMPLETED")) { task.percentComplete = 100; task.completedAt = QDateTime::currentDateTimeUtc(); } |
| 45 | 2 | else task.completedAt = {}; | |
| 46 | 4 | } | |
| 47 |
5/6✓ Branch 163 → 164 taken 3 times.
✓ Branch 163 → 167 taken 24 times.
✓ Branch 165 → 166 taken 3 times.
✗ Branch 165 → 167 not taken.
✓ Branch 168 → 169 taken 3 times.
✓ Branch 168 → 171 taken 24 times.
|
27 | if (task.status == "COMPLETED" && previousStatus != "COMPLETED") { task.previousStatus = previousStatus; task.previousPercent = previousPercent; } |
| 48 |
4/4✓ Branch 171 → 172 taken 27 times.
✓ Branch 176 → 177 taken 27 times.
✓ Branch 177 → 178 taken 27 times.
✓ Branch 178 → 179 taken 27 times.
|
54 | const QString dueText = fields.value(QStringLiteral("due"), PimEditorTime::display(task.due, task.dueAllDay)).toString().trimmed(); |
| 49 |
2/2✓ Branch 186 → 187 taken 17 times.
✓ Branch 186 → 191 taken 10 times.
|
27 | if (dueText.isEmpty()) { |
| 50 | 17 | task.due = {}; | |
| 51 | 17 | task.dueAllDay = false; | |
| 52 | } else { | ||
| 53 | 10 | const bool dateOnly = dueText.size() == 10; | |
| 54 |
1/1✓ Branch 192 → 193 taken 10 times.
|
10 | const QDateTime due = PimEditorTime::parse(dueText, dateOnly); |
| 55 |
2/3✓ Branch 193 → 194 taken 10 times.
✗ Branch 194 → 195 not taken.
✓ Branch 194 → 196 taken 10 times.
|
10 | if (!due.isValid()) return false; |
| 56 | // A title-only save keeps the original zone, including custom TZIDs. | ||
| 57 |
3/3✓ Branch 196 → 197 taken 10 times.
✓ Branch 199 → 200 taken 7 times.
✓ Branch 199 → 213 taken 3 times.
|
10 | if (dueText != PimEditorTime::display(task.due, task.dueAllDay)) |
| 58 |
7/13✓ Branch 200 → 201 taken 2 times.
✓ Branch 200 → 207 taken 5 times.
✓ Branch 201 → 202 taken 2 times.
✓ Branch 202 → 203 taken 1 time.
✓ Branch 202 → 207 taken 1 time.
✗ Branch 203 → 204 not taken.
✓ Branch 203 → 207 taken 1 time.
✗ Branch 204 → 205 not taken.
✗ Branch 205 → 206 not taken.
✗ Branch 210 → 211 not taken.
✓ Branch 210 → 212 taken 7 times.
✗ Branch 530 → 531 not taken.
✗ Branch 530 → 532 not taken.
|
7 | task.due = !dateOnly && task.due.isValid() && !task.dueAllDay ? due.toTimeZone(task.due.timeZone()) : due; |
| 59 | 10 | task.dueAllDay = dateOnly; | |
| 60 | 10 | } | |
| 61 |
4/4✓ Branch 219 → 220 taken 27 times.
✓ Branch 220 → 221 taken 27 times.
✓ Branch 222 → 223 taken 13 times.
✓ Branch 222 → 272 taken 14 times.
|
27 | if (fields.contains("start")) { |
| 62 |
4/4✓ Branch 224 → 225 taken 13 times.
✓ Branch 225 → 226 taken 13 times.
✓ Branch 226 → 227 taken 13 times.
✓ Branch 227 → 228 taken 13 times.
|
13 | const QString start = fields.value("start").toString().trimmed(); |
| 63 |
1/2✓ Branch 233 → 234 taken 13 times.
✗ Branch 233 → 238 not taken.
|
13 | if (start.isEmpty()) { task.dtStart = {}; task.startAllDay = false; } |
| 64 | else { | ||
| 65 | ✗ | const bool dateOnly = start.size() == 10; const auto parsed = PimEditorTime::parse(start, dateOnly); | |
| 66 | ✗ | if (!parsed.isValid()) return false; | |
| 67 | ✗ | if (start != PimEditorTime::display(task.dtStart, task.startAllDay)) | |
| 68 | ✗ | task.dtStart = !dateOnly && task.dtStart.isValid() && !task.startAllDay ? parsed.toTimeZone(task.dtStart.timeZone()) : parsed; | |
| 69 | ✗ | task.startAllDay = dateOnly; | |
| 70 | ✗ | } | |
| 71 | 13 | } | |
| 72 |
4/4✓ Branch 272 → 273 taken 27 times.
✓ Branch 273 → 274 taken 27 times.
✓ Branch 275 → 276 taken 13 times.
✓ Branch 275 → 307 taken 14 times.
|
27 | if (fields.contains("rrule")) { |
| 73 |
3/3✓ Branch 277 → 278 taken 13 times.
✓ Branch 278 → 279 taken 13 times.
✓ Branch 279 → 280 taken 13 times.
|
13 | const QString rule = fields.value("rrule").toString(); |
| 74 |
3/20✗ Branch 284 → 285 not taken.
✓ Branch 284 → 295 taken 13 times.
✗ Branch 286 → 287 not taken.
✗ Branch 286 → 295 not taken.
✗ Branch 287 → 288 not taken.
✗ Branch 288 → 289 not taken.
✗ Branch 289 → 290 not taken.
✗ Branch 289 → 294 not taken.
✗ Branch 290 → 291 not taken.
✗ Branch 291 → 292 not taken.
✗ Branch 291 → 295 not taken.
✗ Branch 292 → 293 not taken.
✗ Branch 293 → 294 not taken.
✗ Branch 293 → 295 not taken.
✗ Branch 296 → 297 not taken.
✓ Branch 296 → 298 taken 13 times.
✗ Branch 298 → 299 not taken.
✓ Branch 298 → 300 taken 13 times.
✗ Branch 577 → 578 not taken.
✗ Branch 577 → 579 not taken.
|
13 | if (rule != task.rrule && !rule.isEmpty() && (!RecurrenceRule::parse(rule).fullySupported() || (!task.dtStart.isValid() && !task.due.isValid()))) return false; |
| 75 | 13 | task.rrule = rule; | |
| 76 | 13 | } | |
| 77 |
4/4✓ Branch 307 → 308 taken 27 times.
✓ Branch 308 → 309 taken 27 times.
✓ Branch 310 → 311 taken 13 times.
✓ Branch 310 → 322 taken 14 times.
|
27 | if (fields.contains("tags")) { |
| 78 |
5/5✓ Branch 312 → 313 taken 13 times.
✓ Branch 313 → 314 taken 13 times.
✓ Branch 314 → 315 taken 13 times.
✓ Branch 320 → 321 taken 13 times.
✓ Branch 321 → 322 taken 13 times.
|
13 | task.tags = fields.value("tags").toStringList(); task.tags.removeAll(""); task.tags.removeDuplicates(); |
| 79 | } | ||
| 80 |
3/7✓ Branch 322 → 323 taken 27 times.
✓ Branch 323 → 324 taken 27 times.
✗ Branch 325 → 326 not taken.
✓ Branch 325 → 336 taken 27 times.
✗ Branch 327 → 328 not taken.
✗ Branch 328 → 329 not taken.
✗ Branch 329 → 330 not taken.
|
27 | if (fields.contains("parentUid")) task.parentUid = fields.value("parentUid").toString(); |
| 81 |
3/7✓ Branch 336 → 337 taken 27 times.
✓ Branch 337 → 338 taken 27 times.
✗ Branch 339 → 340 not taken.
✓ Branch 339 → 348 taken 27 times.
✗ Branch 341 → 342 not taken.
✗ Branch 342 → 343 not taken.
✗ Branch 343 → 344 not taken.
|
27 | if (fields.contains("sortOrder")) task.sortOrder = fields.value("sortOrder").toLongLong(); |
| 82 |
4/4✓ Branch 348 → 349 taken 27 times.
✓ Branch 349 → 350 taken 27 times.
✓ Branch 351 → 352 taken 2 times.
✓ Branch 351 → 391 taken 25 times.
|
27 | if (fields.contains("alarmTimes")) { |
| 83 |
1/1✓ Branch 352 → 353 taken 2 times.
|
2 | task.alarmTimes.clear(); |
| 84 |
7/7✓ Branch 354 → 355 taken 2 times.
✓ Branch 355 → 356 taken 2 times.
✓ Branch 356 → 357 taken 2 times.
✓ Branch 360 → 361 taken 2 times.
✓ Branch 361 → 362 taken 2 times.
✓ Branch 384 → 363 taken 1 time.
✓ Branch 384 → 385 taken 2 times.
|
3 | for (const auto &value : fields.value("alarmTimes").toList()) { |
| 85 |
5/11✓ Branch 364 → 365 taken 1 time.
✓ Branch 365 → 366 taken 1 time.
✓ Branch 366 → 367 taken 1 time.
✗ Branch 366 → 368 not taken.
✓ Branch 367 → 370 taken 1 time.
✗ Branch 368 → 369 not taken.
✗ Branch 369 → 370 not taken.
✗ Branch 370 → 371 not taken.
✓ Branch 370 → 372 taken 1 time.
✗ Branch 634 → 635 not taken.
✗ Branch 634 → 636 not taken.
|
1 | const auto stamp = value.toDateTime().isValid() ? value.toDateTime() : QDateTime::fromString(value.toString(), Qt::ISODate); |
| 86 |
2/3✓ Branch 373 → 374 taken 1 time.
✗ Branch 374 → 375 not taken.
✓ Branch 374 → 376 taken 1 time.
|
1 | if (!stamp.isValid()) return false; |
| 87 |
1/1✓ Branch 376 → 377 taken 1 time.
|
1 | task.alarmTimes << stamp; |
| 88 | 3 | } | |
| 89 | } | ||
| 90 |
3/11✓ Branch 391 → 392 taken 27 times.
✗ Branch 392 → 393 not taken.
✓ Branch 392 → 399 taken 27 times.
✗ Branch 393 → 394 not taken.
✗ Branch 394 → 395 not taken.
✗ Branch 394 → 399 not taken.
✗ Branch 395 → 396 not taken.
✗ Branch 397 → 398 not taken.
✗ Branch 397 → 399 not taken.
✗ Branch 400 → 401 not taken.
✓ Branch 400 → 402 taken 27 times.
|
27 | if (task.dtStart.isValid() && task.due.isValid() && task.dtStart > task.due) return false; |
| 91 | // S106-349: removing the last date of a repeating task leaves its rule | ||
| 92 | // without an anchor (completion then refuses it) — refused like the editor. | ||
| 93 |
9/14✓ Branch 403 → 404 taken 1 time.
✓ Branch 403 → 411 taken 26 times.
✓ Branch 404 → 405 taken 1 time.
✓ Branch 405 → 406 taken 1 time.
✗ Branch 405 → 411 not taken.
✓ Branch 406 → 407 taken 1 time.
✓ Branch 407 → 408 taken 1 time.
✗ Branch 407 → 411 not taken.
✗ Branch 408 → 409 not taken.
✓ Branch 408 → 410 taken 1 time.
✗ Branch 409 → 410 not taken.
✗ Branch 409 → 411 not taken.
✓ Branch 412 → 413 taken 1 time.
✓ Branch 412 → 414 taken 26 times.
|
27 | if (!task.rrule.isEmpty() && !task.due.isValid() && !task.dtStart.isValid() && (hadDue || hadStart)) return false; |
| 94 | 26 | return true; | |
| 95 | 36 | } | |
| 96 |