1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Controls module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickcalendarmodel_p.h"
41
42namespace {
43 static const int daysInAWeek = 7;
44
45 /*
46 Not the number of weeks per month, but the number of weeks that are
47 shown on a typical calendar.
48 */
49 static const int weeksOnACalendarMonth = 6;
50
51 /*
52 The amount of days to populate the calendar with.
53 */
54 static const int daysOnACalendarMonth = daysInAWeek * weeksOnACalendarMonth;
55}
56
57QT_BEGIN_NAMESPACE
58
59/*!
60 QQuickCalendarModel provides a model for the Calendar control.
61 It is responsible for populating itself with dates based on a given month
62 and year.
63
64 The model stores a list of dates whose indices map directly to the Calendar.
65 For example, the model would store the following dates when any day in
66 January 2015 is selected on the Calendar:
67
68 [30/12/2014][31/12/2014][01/01/2015]...[31/01/2015][01/02/2015]...[09/02/2015]
69
70 The Calendar would then display the dates in the same order within a grid:
71
72 January 2015
73
74 [30][31][01][02][03][04][05]
75 [06][07][08][09][10][11][12]
76 [13][14][15][16][17][18][19]
77 [20][21][22][23][24][25][26]
78 [27][28][29][30][31][01][02]
79 [03][04][05][06][07][08][09]
80*/
81
82QQuickCalendarModel1::QQuickCalendarModel1(QObject *parent) :
83 QAbstractListModel(parent)
84{
85}
86
87/*!
88 The date that determines which dates are stored.
89
90 We store all of the days in the month of visibleDate, as well as any days
91 in the previous or following month if there is enough space.
92*/
93QDate QQuickCalendarModel1::visibleDate() const
94{
95 return mVisibleDate;
96}
97
98/*!
99 Sets the visible date to \a visibleDate.
100
101 If \a visibleDate is a valid date and is different than the previously
102 visible date, the visible date is changed and
103 populateFromVisibleDate() called.
104*/
105void QQuickCalendarModel1::setVisibleDate(const QDate &date)
106{
107 if (date != mVisibleDate && date.isValid()) {
108 const QDate previousDate = mVisibleDate;
109 mVisibleDate = date;
110 populateFromVisibleDate(previousDate);
111 emit visibleDateChanged(visibleDate: date);
112 }
113}
114
115/*!
116 The locale set on the Calendar.
117
118 This affects which dates are stored for visibleDate(). For example, if
119 the locale is en_US, the first day of the week is Sunday. Therefore, if
120 visibleDate() is some day in January 2014, there will be three days
121 displayed before the 1st of January:
122
123
124 January 2014
125
126 [SO][MO][TU][WE][TH][FR][SA]
127 [29][30][31][01][02][03][04]
128 ...
129
130 If the locale is then changed to en_GB (with the same visibleDate()),
131 there will be 2 days before the 1st of January, because Monday is the
132 first day of the week for that locale:
133
134 January 2014
135
136 [MO][TU][WE][TH][FR][SA][SO]
137 [30][31][01][02][03][04][05]
138 ...
139*/
140QLocale QQuickCalendarModel1::locale() const
141{
142 return mLocale;
143}
144
145/*!
146 Sets the locale to \a locale.
147*/
148void QQuickCalendarModel1::setLocale(const QLocale &locale)
149{
150 if (locale != mLocale) {
151 Qt::DayOfWeek previousFirstDayOfWeek = mLocale.firstDayOfWeek();
152 mLocale = locale;
153 emit localeChanged(locale: mLocale);
154 if (mLocale.firstDayOfWeek() != previousFirstDayOfWeek) {
155 // We don't have a previousDate, so just use our current one...
156 // it's ignored anyway, since we're forcing the repopulation.
157 populateFromVisibleDate(previousDate: mVisibleDate, force: true);
158 }
159 }
160}
161
162QVariant QQuickCalendarModel1::data(const QModelIndex &index, int role) const
163{
164 if (role == DateRole)
165 return QDateTime(mVisibleDates.at(i: index.row()), QTime(12, 0));
166 return QVariant();
167}
168
169int QQuickCalendarModel1::rowCount(const QModelIndex &) const
170{
171 return mVisibleDates.isEmpty() ? 0 : weeksOnACalendarMonth * daysInAWeek;
172}
173
174QHash<int, QByteArray> QQuickCalendarModel1::roleNames() const
175{
176 QHash<int, QByteArray> names;
177 names[DateRole] = QByteArrayLiteral("date");
178 return names;
179}
180
181/*!
182 Returns the date at \a index, or an invalid date if \a index is invalid.
183*/
184QDateTime QQuickCalendarModel1::dateAt(int index) const
185{
186 return index >= 0 && index < mVisibleDates.size() ? QDateTime(mVisibleDates.at(i: index), QTime(12, 0)) : QDateTime();
187}
188
189/*!
190 Returns the index for \a date, or -1 if \a date is outside of our range.
191*/
192int QQuickCalendarModel1::indexAt(const QDate &date)
193{
194 if (mVisibleDates.size() == 0 || date < mFirstVisibleDate || date > mLastVisibleDate)
195 return -1;
196
197 // The index of the visible date will be the days from the
198 // previous month that we had to display before it, plus the
199 // day of the visible date itself.
200 return qMax(a: qint64(0), b: mFirstVisibleDate.daysTo(date));
201}
202
203/*!
204 Returns the week number for the first day of the week corresponding to \a row,
205 or -1 if \a row is outside of our range.
206*/
207int QQuickCalendarModel1::weekNumberAt(int row) const
208{
209 const int index = row * daysInAWeek;
210 const QDate date = dateAt(index).date();
211 if (date.isValid())
212 return date.weekNumber();
213 return -1;
214}
215
216/*!
217 Called before visibleDateChanged() is emitted.
218
219 This function is called even when just the day has changed, in which case
220 it does nothing.
221
222 The \a force parameter is used when the locale has changed; the month
223 shown doesn't change, but the days displayed do.
224 The \a previousDate parameter is ignored when \a force is true.
225*/
226void QQuickCalendarModel1::populateFromVisibleDate(const QDate &previousDate, bool force)
227{
228 // We don't need to populate if the year and month haven't changed.
229 if (!force && mVisibleDate.year() == previousDate.year() && mVisibleDate.month() == previousDate.month())
230 return;
231
232 // Since our model is of a fixed size, we fill it once and assign values each time
233 // the month changes, instead of clearing and appending each time.
234 bool isEmpty = mVisibleDates.isEmpty();
235 if (isEmpty) {
236 beginResetModel();
237 mVisibleDates.fill(from: QDate(), asize: daysOnACalendarMonth);
238 }
239
240 // The actual first (1st) day of the month.
241 QDate firstDayOfMonthDate(mVisibleDate.year(), mVisibleDate.month(), 1);
242 int difference = ((firstDayOfMonthDate.dayOfWeek() - mLocale.firstDayOfWeek()) + 7) % 7;
243 // The first day to display should never be the 1st of the month, as we want some days from
244 // the previous month to be visible.
245 if (difference == 0)
246 difference += daysInAWeek;
247 QDate firstDateToDisplay = firstDayOfMonthDate.addDays(days: -difference);
248 for (int i = 0; i < daysOnACalendarMonth; ++i)
249 mVisibleDates[i] = firstDateToDisplay.addDays(days: i);
250
251 mFirstVisibleDate = mVisibleDates.at(i: 0);
252 mLastVisibleDate = mVisibleDates.at(i: mVisibleDates.size() - 1);
253
254 if (!isEmpty) {
255 emit dataChanged(topLeft: index(row: 0, column: 0), bottomRight: index(row: rowCount() - 1, column: 0));
256 } else {
257 endResetModel();
258 emit countChanged(count: rowCount());
259 }
260}
261
262QT_END_NAMESPACE
263

source code of qtquickcontrols/src/controls/Private/qquickcalendarmodel.cpp