MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 94.3% 33 / 0 / 35
Functions: 91.3% 21 / 0 / 23
Branches: 64.7% 11 / 0 / 17

presentation/QuickCalendarModel.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QSettings>
4
5 #include <QAbstractListModel>
6 #include <QDate>
7 #include <QThreadPool>
8 #include <QTimer>
9 #include <QVector>
10
11 #include "CalendarLayout.h"
12 #include "data/RecurrenceExpansion.h"
13
14 class CalendarStore;
15 // Displayed occurrences of [start, end] (series expanded, hidden calendars
16 // left out) — shared by the model's loader and "Next up" (S93.3.10).
17 QVector<CalendarOccurrence> calendarOccurrencesFor(const CalendarStore &store, const QDate &start,
18 const QDate &end, const QStringList &hiddenCalendars,
19 const QSet<QString> &unlimitedSeries = {});
20
21 // Day, week, month, year and agenda projections of the shared CalendarStore.
22 // A background worker opens its own store connection, expands recurrences
23 // (RecurrenceExpansion) and lays the occurrences out (CalendarLayout); the
24 // result is published with generation fencing. A step within the same view
25 // (same row count) updates the rows in place (dataChanged), so the views
26 // keep their delegates and rebind them; a view change resets the model.
27 // navigatorDays has its own signal and only changes when its content does
28 // (CAL-PERIOD-REBUILD, A/B in SPRINT-93 §14).
29 //
30 // Rows: one per day (day/week/month/agenda) or per month (year).
31 // `dayEvents` entries are CalendarLayout::toVariant() maps: summary, color,
32 // timeText, endTimeText, timeRangeText, durationMinutes, allDay (all-day
33 // band), dateOnly, multiDay, uid, accountId, calendarPath, calendarName,
34 // readOnly, pending, conflict, partial, recurring, location, description,
35 // dateText, longDateText, firstDayIso, lastDayIso, startIso, recurrenceIso, startMinute,
36 // endMinute, lane, laneCount.
37 // `weekSpans` holds one list per week row (month: 6, week/day: 1) of span
38 // maps (the same keys plus column, length, level, continuesBefore/After).
39 class QuickCalendarModel final : public QAbstractListModel {
40 107 Q_OBJECT
41 Q_PROPERTY(int year READ year NOTIFY monthChanged)
42 Q_PROPERTY(int month READ month NOTIFY monthChanged)
43 Q_PROPERTY(QString monthLabel READ monthLabel NOTIFY monthChanged)
44 Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
45 // F93-06: the view mode of the rows currently held. It changes when a
46 // load for another mode lands (after an empty reset), so a view binds
47 // the model only once its own data is there — no frame shows another
48 // mode's rows, and only the new view builds its delegates.
49 Q_PROPERTY(QString dataMode READ dataMode NOTIFY dataModeChanged)
50 // S93.5.5 / F93-16: a non-empty query shows the matching appointments
51 // of all visible calendars as an agenda (one year back, two ahead;
52 // a series by its next occurrence); clearing restores the view mode.
53 Q_PROPERTY(QString searchQuery READ searchQuery WRITE setSearchQuery NOTIFY searchQueryChanged)
54 Q_PROPERTY(QString viewMode READ viewMode WRITE setViewMode NOTIFY monthChanged)
55 Q_PROPERTY(QString periodLabel READ periodLabel NOTIFY monthChanged)
56 // S93.3.1: "KW 37" / "Week 37" for day and week views, "" otherwise.
57 Q_PROPERTY(QString weekLabel READ weekLabel NOTIFY monthChanged)
58 Q_PROPERTY(QString selectedDate READ selectedDate NOTIFY monthChanged)
59 Q_PROPERTY(QStringList hiddenCalendars READ hiddenCalendars NOTIFY hiddenCalendarsChanged)
60 // S93.3.7: first day of the week (1 = Monday … 7 = Sunday); defaults to
61 // the locale, a setting may override it.
62 Q_PROPERTY(int firstDayOfWeek READ firstDayOfWeek WRITE setFirstDayOfWeek NOTIFY monthChanged)
63 // S93.3.3: the week view shows Monday–Friday only.
64 Q_PROPERTY(bool workWeek READ workWeek WRITE setWorkWeek NOTIFY monthChanged)
65 // S93.5.7: open tasks with a due date as chips (QSettings calendar/showTasks).
66 Q_PROPERTY(bool showTasks READ showTasks WRITE setShowTasks NOTIFY showTasksChanged)
67 // Sprint 97 S97.5.10: contacts' birthdays as a read-only all-day layer
68 // (QSettings calendar/showBirthdays, off by default).
69 Q_PROPERTY(bool showBirthdays READ showBirthdays WRITE setShowBirthdays NOTIFY showTasksChanged)
70 Q_PROPERTY(int visibleDays READ visibleDays NOTIFY monthChanged)
71 Q_PROPERTY(QVariantList weekSpans READ weekSpans NOTIFY layoutChanged)
72 Q_PROPERTY(QString todayIso READ todayIso NOTIFY monthChanged)
73 // S93.3.2 sidebar navigator: first day of the shown month and its 42
74 // day maps (same keys as monthDays). Follows the anchor's month unless
75 // browsed with shiftNavigator().
76 Q_PROPERTY(QString navigatorMonth READ navigatorMonth NOTIFY monthChanged)
77 Q_PROPERTY(QString navigatorTitle READ navigatorTitle NOTIFY monthChanged)
78 // Own signal: a step inside the same month leaves the navigator untouched.
79 Q_PROPERTY(QVariantList navigatorDays READ navigatorDays NOTIFY navigatorDaysChanged)
80 // First and last date of the visible period (ISO).
81 Q_PROPERTY(QString rangeStart READ rangeStart NOTIFY monthChanged)
82 Q_PROPERTY(QString rangeEnd READ rangeEnd NOTIFY monthChanged)
83 public:
84 enum Role { DayNumber = Qt::UserRole + 1, InMonth, IsToday, DateIso, Events, MonthDays,
85 WeekNumber, IsWeekend, WeekdayName };
86 explicit QuickCalendarModel(QString databasePath, QObject *parent = nullptr);
87 ~QuickCalendarModel() override;
88 QHash<int, QByteArray> roleNames() const override;
89 int rowCount(const QModelIndex &parent = {}) const override;
90 QVariant data(const QModelIndex &index, int role) const override;
91 6 int year() const { return m_year; }
92 9 int month() const { return m_month; }
93 QString monthLabel() const;
94 QString periodLabel() const;
95 QString weekLabel() const;
96 43 QString searchQuery() const { return m_searchQuery; }
97 void setSearchQuery(const QString &query);
98 463 QString dataMode() const { return m_dataMode; }
99 21 QString selectedDate() const { return m_anchorDate.toString(Qt::ISODate); }
100 ✗ QString todayIso() const { return QDate::currentDate().toString(Qt::ISODate); }
101 2724 QString viewMode() const { return m_viewMode; }
102 void setViewMode(const QString &mode);
103 2444 int firstDayOfWeek() const { return m_firstDayOfWeek; }
104 void setFirstDayOfWeek(int day);
105 470 bool workWeek() const { return m_workWeek; }
106 45 bool showTasks() const { return m_showTasks; }
107 14 void setShowTasks(bool on) {
108
2/2
✓ Branch 2 → 3 taken 9 times.
✓ Branch 2 → 4 taken 5 times.
14 if (on == m_showTasks) return;
109 5 m_showTasks = on;
110
2/2
✓ Branch 4 → 5 taken 5 times.
✓ Branch 10 → 11 taken 5 times.
10 QSettings().setValue(QStringLiteral("calendar/showTasks"), on);
111 5 emit showTasksChanged();
112 5 reload();
113 }
114 79 bool showBirthdays() const { return m_showBirthdays; }
115 2 void setShowBirthdays(bool on) {
116
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 if (on == m_showBirthdays) return;
117 2 m_showBirthdays = on;
118
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
4 QSettings().setValue(QStringLiteral("calendar/showBirthdays"), on);
119 2 emit showTasksChanged();
120 2 reload();
121 }
122 // The contacts database the birthday layer reads ("" = no layer).
123 795 void setContactsDatabasePath(const QString &path) { m_contactsDatabasePath = path; }
124 void setWorkWeek(bool workWeek);
125 int visibleDays() const;
126 30513 QVariantList weekSpans() const { return m_weekSpans; }
127 ✗ QString navigatorMonth() const { return m_navigatorMonth.toString(Qt::ISODate); }
128 QString navigatorTitle() const;
129 49709 QVariantList navigatorDays() const { return m_navigatorDays; }
130 3681 QString rangeStart() const { return m_gridStart.toString(Qt::ISODate); }
131 937 QString rangeEnd() const { return m_gridEnd.toString(Qt::ISODate); }
132 Q_INVOKABLE void shiftNavigator(int months);
133 Q_INVOKABLE void shiftPeriod(int delta);
134 1106 bool loading() const { return m_loading; }
135 // Navigates to the period containing today and reloads.
136 Q_INVOKABLE void showCurrentMonth();
137 Q_INVOKABLE void shiftMonths(int delta);
138 Q_INVOKABLE void showDate(const QString &dateIso, const QString &mode);
139 // S93.3.6: the agenda shows further 30-day pages on demand.
140 Q_INVOKABLE void loadMoreAgenda();
141 // Sprint 102: shows every occurrence of one series (DisplayLimit) until
142 // the model is destroyed.
143 2 void showAllOccurrences(const QString &seriesKey) {
144
4/7
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 2 times.
2 if (!seriesKey.isEmpty() && !m_unlimitedSeries.contains(seriesKey)) { m_unlimitedSeries.insert(seriesKey); reload(); }
145 2 }
146 // Weekday names in display order (first day of week first).
147 Q_INVOKABLE QStringList weekdayNames(bool shortNames) const;
148 // ISO 8601 week number of a date (S93.3.4 week-number column).
149 Q_INVOKABLE int weekNumber(const QString &dateIso) const;
150 void reload();
151 5414 QStringList hiddenCalendars() const { return m_hiddenCalendars; }
152 void setHiddenCalendars(const QStringList &keys);
153
154 signals:
155 void monthChanged();
156 void loadingChanged();
157 void dataModeChanged();
158 void searchQueryChanged();
159 // "Today" was chosen (toolbar, sidebar, `t`): a time grid scrolls to
160 // the current time once the period is loaded (S93.3.9, U-02).
161 void todayShown();
162 void hiddenCalendarsChanged();
163 void showTasksChanged();
164 void layoutChanged();
165 void navigatorDaysChanged();
166
167 private:
168 struct DayCell {
169 QDate date;
170 bool inMonth = false;
171 QList<CalendarOccurrence> events;
172 QVariantList monthDays;
173 };
174 struct LoadResult {
175 QString mode;
176 QVector<DayCell> cells;
177 QVariantList weekSpans;
178 QVariantList navigatorDays;
179 };
180 struct LoadRequest {
181 QString databasePath;
182 QString mode;
183 QDate start, end;
184 int gridYear = 1970, gridMonth = 1;
185 int firstDayOfWeek = 1;
186 bool workWeek = false;
187 QStringList hiddenCalendars;
188 QDate navigatorMonth;
189 QStringList searchWords;
190 bool showTasks = false;
191 QString birthdays; // contacts.db path when the birthday layer is on
192 QSet<QString> unlimitedSeries;
193 };
194 static LoadResult load(const LoadRequest &request);
195 static QVector<DayCell> searchCells(const LoadRequest &request);
196 void computeRange();
197 void notifyPeriodChanged();
198 void leaveSearch();
199 QDate gridStartFor(int year, int month) const;
200 void armMidnight();
201 QString m_databasePath;
202 QStringList m_hiddenCalendars;
203 QSet<QString> m_unlimitedSeries; // Sprint 102: "Show all" per series
204 bool m_showTasks = QSettings().value(QStringLiteral("calendar/showTasks"), false).toBool();
205 bool m_showBirthdays = QSettings().value(QStringLiteral("calendar/showBirthdays"), false).toBool();
206 QString m_contactsDatabasePath;
207 QThreadPool m_pool;
208 // S106-289: "today" (grid and navigator marks, the agenda's today cell,
209 // overdue tasks, todayIso) is computed per load; a reload just after
210 // midnight moves it to the new day.
211 QTimer m_midnight;
212 QVector<DayCell> m_days;
213 QVariantList m_weekSpans;
214 QVariantList m_navigatorDays;
215 QDate m_navigatorMonth;
216 QDate m_navigatorAnchor;
217 QDate m_gridStart, m_gridEnd;
218 QDate m_anchorDate;
219 QString m_viewMode = QStringLiteral("month");
220 QString m_dataMode = QStringLiteral("month");
221 QString m_searchQuery;
222 QString m_modeBeforeSearch;
223 int m_year = 1970, m_month = 1;
224 int m_firstDayOfWeek = 1;
225 int m_agendaDays = 30;
226 bool m_workWeek = false;
227 bool m_loading = false;
228 quint64 m_generation = 0;
229 };
230