Kirigami-addons

infinitecalendarviewmodel.cpp
1// SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#include <QMetaEnum>
5#include <cmath>
6#include "infinitecalendarviewmodel.h"
7
8InfiniteCalendarViewModel::InfiniteCalendarViewModel(QObject *parent)
9 : QAbstractListModel(parent)
10{
11}
12
13void InfiniteCalendarViewModel::classBegin()
14{
15
16}
17
18void InfiniteCalendarViewModel::componentComplete()
19{
20 m_isCompleted = true;
21 setup();
22}
23
24void InfiniteCalendarViewModel::setup()
25{
26 if (!m_isCompleted) {
27 return;
28 }
29
30 if (!m_currentDate.isValid()) {
31 return;
32 }
33
34 switch (m_scale) {
35 case WeekScale: {
36 QDateTime firstDay = m_currentDate.addDays(-m_currentDate.date().dayOfWeek() + m_locale.firstDayOfWeek());
37 // We create dates before and after where our view will start from (which is m_currentDate)
38 firstDay = firstDay.addDays((-m_datesToAdd * 7) / 2);
39
40 addWeekDates(true, firstDay);
41 break;
42 }
43 case MonthScale: {
44 QDateTime firstDay(QDate(m_currentDate.date().year(), m_currentDate.date().month(), 1), {});
45 firstDay = firstDay.addMonths(-m_datesToAdd / 2);
46
47 addMonthDates(true, firstDay);
48 break;
49 }
50 case YearScale: {
51 QDateTime firstDay(QDate(m_currentDate.date().year(), m_currentDate.date().month(), 1), {});
52 firstDay = firstDay.addYears(-m_datesToAdd / 2);
53
54 addYearDates(true, firstDay);
55 break;
56 }
57 case DecadeScale: {
58 const int firstYear = ((floor(m_currentDate.date().year() / 10)) * 10) - 1; // E.g. For 2020 have view start at 2019...
59 QDateTime firstDay(QDate(firstYear, m_currentDate.date().month(), 1), {});
60 firstDay = firstDay.addYears(((-m_datesToAdd * 12) / 2) + 10); // 3 * 4 grid so 12 years, end at 2030, and align for mid index to be current decade
61
62 addDecadeDates(true, firstDay);
63 break;
64 }
65 }
66}
67
68QVariant InfiniteCalendarViewModel::data(const QModelIndex &idx, int role) const
69{
70 if (!hasIndex(idx.row(), idx.column())) {
71 return {};
72 }
73
74 if (m_scale == MonthScale && role != StartDateRole) {
75 const auto firstDay = m_firstDayOfMonthDates[idx.row()].date();
76
77 switch (role) {
78 case FirstDayOfMonthRole:
79 return firstDay.startOfDay();
80 case SelectedMonthRole:
81 return firstDay.month();
82 case SelectedYearRole:
83 return firstDay.year();
84 default:
85 qWarning() << "Unknown role for firstDay:" << QMetaEnum::fromType<Roles>().valueToKey(role);
86 return {};
87 }
88 }
89
90 const auto startDate = m_startDates[idx.row()].date();
91
92 switch (role) {
93 case FirstDayOfMonthRole:
94 return QDate(startDate.year(), startDate.month(), 1).startOfDay();
95 case StartDateRole:
96 return startDate.startOfDay();
97 case SelectedMonthRole:
98 return startDate.month();
99 case SelectedYearRole:
100 return startDate.year();
101 default:
102 qWarning() << "Unknown role for startdate:" << QMetaEnum::fromType<Roles>().valueToKey(role);
103 return {};
104 }
105}
106
107int InfiniteCalendarViewModel::rowCount(const QModelIndex &parent) const
108{
109 Q_UNUSED(parent)
110 return m_startDates.length();
111}
112
113QHash<int, QByteArray> InfiniteCalendarViewModel::roleNames() const
114{
115 return {
116 {StartDateRole, QByteArrayLiteral("startDate")},
117 {FirstDayOfMonthRole, QByteArrayLiteral("firstDay")},
118 {SelectedMonthRole, QByteArrayLiteral("selectedMonth")},
119 {SelectedYearRole, QByteArrayLiteral("selectedYear")},
120 };
121}
122
123QDateTime InfiniteCalendarViewModel::currentDate() const
124{
125 return m_currentDate;
126}
127
128void InfiniteCalendarViewModel::setCurrentDate(const QDateTime &currentDate)
129{
130 m_currentDate = currentDate;
131}
132
133QDateTime InfiniteCalendarViewModel::minimumDate() const
134{
135 return m_minimumDate;
136}
137
138void InfiniteCalendarViewModel::setMinimumDate(const QDateTime &minimumDate)
139{
140 if (m_minimumDate == minimumDate) {
141 return;
142 }
143 m_minimumDate = minimumDate;
144 Q_EMIT minimumDateChanged();
145}
146
147QDateTime InfiniteCalendarViewModel::maximumDate() const
148{
149 return m_maximumDate;
150}
151
152void InfiniteCalendarViewModel::setMaximumDate(const QDateTime &maximumDate)
153{
154 if (m_maximumDate == maximumDate) {
155 return;
156 }
157 m_maximumDate = maximumDate;
158 Q_EMIT maximumDateChanged();
159}
160
161void InfiniteCalendarViewModel::addDates(bool atEnd, const QDateTime &startFrom)
162{
163 switch (m_scale) {
164 case WeekScale:
165 addWeekDates(atEnd, startFrom);
166 break;
167 case MonthScale:
168 addMonthDates(atEnd, startFrom);
169 break;
170 case YearScale:
171 addYearDates(atEnd, startFrom);
172 break;
173 case DecadeScale:
174 addDecadeDates(atEnd, startFrom);
175 break;
176 }
177}
178
179void InfiniteCalendarViewModel::addWeekDates(bool atEnd, const QDateTime &startFrom)
180{
181 const int newRow = atEnd ? rowCount() : 0;
182
183 beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
184
185 for (int i = 0; i < m_datesToAdd; i++) {
186 QDateTime startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addDays(7) : m_startDates[0].addDays(-7);
187
188 if (startDate.date().dayOfWeek() != m_locale.firstDayOfWeek()) {
189 startDate = startDate.addDays(-startDate.date().dayOfWeek() + m_locale.firstDayOfWeek());
190 }
191
192 if (atEnd) {
193 m_startDates.append(startDate);
194 } else {
195 m_startDates.insert(0, startDate);
196 }
197 }
198
200}
201
202void InfiniteCalendarViewModel::addMonthDates(bool atEnd, const QDateTime &startFrom)
203{
204 QVector<QDateTime> startDates;
205
206 const int newRow = atEnd ? rowCount() : 0;
207
208 for (int i = 0; i < m_datesToAdd; i++) {
209 QDateTime firstDay;
210
211 if (startFrom.isValid() && i == 0) {
212 firstDay = startFrom;
213 } else if (atEnd) {
214 firstDay = m_firstDayOfMonthDates[newRow + startDates.length() - 1].addMonths(1);
215 } else {
216 firstDay = m_firstDayOfMonthDates[0].addMonths(-1);
217 }
218
219 QDateTime startDate = firstDay;
220
221 startDate = startDate.addDays(-startDate.date().dayOfWeek() + m_locale.firstDayOfWeek());
222 if (startDate >= firstDay) {
223 startDate = startDate.addDays(-7);
224 }
225
226 if (atEnd) {
227 if (m_maximumDate.isValid() && startDate > m_maximumDate) {
228 break;
229 }
230 m_firstDayOfMonthDates.append(firstDay);
231 startDates.append(startDate);
232 } else {
233 m_firstDayOfMonthDates.insert(0, firstDay);
234 startDates.insert(0, startDate);
235 }
236 }
237
238 beginInsertRows({}, newRow, newRow + startDates.length() - 1);
239
240 if (atEnd) {
241 m_startDates = m_startDates + startDates;
242 } else {
243 m_startDates = startDates + m_startDates;
244 }
245
247}
248
249void InfiniteCalendarViewModel::addYearDates(bool atEnd, const QDateTime &startFrom)
250{
251 const int newRow = atEnd ? rowCount() : 0;
252
253 beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
254
255 for (int i = 0; i < m_datesToAdd; i++) {
256 QDateTime startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addYears(1) : m_startDates[0].addYears(-1);
257
258 if (atEnd) {
259 m_startDates.append(startDate);
260 } else {
261 m_startDates.insert(0, startDate);
262 }
263 }
264
266}
267
268void InfiniteCalendarViewModel::addDecadeDates(bool atEnd, const QDateTime &startFrom)
269{
270 const int newRow = atEnd ? rowCount() : 0;
271
272 beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
273
274 for (int i = 0; i < m_datesToAdd; i++) {
275 QDateTime startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addYears(10) : m_startDates[0].addYears(-10);
276
277 if (atEnd) {
278 m_startDates.append(startDate);
279 } else {
280 m_startDates.insert(0, startDate);
281 }
282 }
283
285}
286
287int InfiniteCalendarViewModel::datesToAdd() const
288{
289 return m_datesToAdd;
290}
291
292void InfiniteCalendarViewModel::setDatesToAdd(int datesToAdd)
293{
294 m_datesToAdd = datesToAdd;
295 Q_EMIT datesToAddChanged();
296}
297
298int InfiniteCalendarViewModel::scale()
299{
300 return m_scale;
301}
302
303void InfiniteCalendarViewModel::setScale(int scale)
304{
306
307 m_startDates.clear();
308 m_firstDayOfMonthDates.clear();
309 m_scale = scale;
310 setup();
311 Q_EMIT scaleChanged();
312
314}
315
316#include "moc_infinitecalendarviewmodel.cpp"
void beginInsertRows(const QModelIndex &parent, int first, int last)
bool hasIndex(int row, int column, const QModelIndex &parent) const const
virtual QModelIndex parent(const QModelIndex &index) const const=0
int dayOfWeek() const const
QDateTime addDays(qint64 ndays) const const
QDateTime addMonths(int nmonths) const const
QDateTime addYears(int nyears) const const
QDate date() const const
bool isValid() const const
void append(QList< T > &&value)
iterator insert(const_iterator before, parameter_type value)
qsizetype length() const const
QMetaEnum fromType()
const char * valueToKey(int value) const const
int column() const const
int row() const const
Q_EMITQ_EMIT
QTestData & newRow(const char *dataTag)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:49:11 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.