MauiKit Calendar

monthmodel.cpp
1// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#include "monthmodel.h"
5#include <QCalendar>
6
7struct MonthModel::Private {
8 int year;
9 int month;
10 QCalendar calendar = QCalendar();
11 QDate selected;
12};
13
14MonthModel::MonthModel(QObject *parent)
15 : QAbstractListModel(parent)
16 , d(new MonthModel::Private())
17{
18 goToday();
19 d->selected = QDate::currentDate();
20}
21
22MonthModel::~MonthModel() = default;
23
24int MonthModel::year() const
25{
26 return d->year;
27}
28
29void MonthModel::setYear(int year)
30{
31 if (d->year == year) {
32 return;
33 }
34 d->year = year;
35 Q_EMIT yearChanged();
36 Q_EMIT dataChanged(index(0, 0), index(41, 0));
37 setSelected(QDate(year, d->selected.month(), qMin(d->selected.day(), d->calendar.daysInMonth(d->selected.month(), year))));
38}
39
40int MonthModel::month() const
41{
42 return d->month;
43}
44
45void MonthModel::setMonth(int month)
46{
47 if (d->month == month) {
48 return;
49 }
50 d->month = month;
51 Q_EMIT monthChanged();
52 Q_EMIT dataChanged(index(0, 0), index(41, 0));
53 setSelected(QDate(d->selected.year(), d->month, qMin(d->selected.day(), d->calendar.daysInMonth(d->month, d->selected.year()))));
54}
55
57{
58 return d->selected;
59}
60
61void MonthModel::setSelected(const QDate &selected)
62{
63 if (d->selected == selected) {
64 return;
65 }
66 d->selected = selected;
67 Q_EMIT selectedChanged();
68 Q_EMIT dataChanged(index(0, 0), index(41, 0), {Roles::IsSelected});
69}
70
72{
73 QLocale locale;
74 QStringList daysName;
75 for (int i = 0; i < 7; i++) {
76 int day = locale.firstDayOfWeek() + i;
77 if (day > 7) {
78 day -= 7;
79 }
80 if (day == 7) {
81 day = 0;
82 }
83 daysName.append(locale.standaloneDayName(day == 0 ? Qt::Sunday : day, QLocale::NarrowFormat));
84 }
85 return daysName;
86}
87
89{
90 if (d->month == 1) {
91 setYear(d->year - 1);
92 setMonth(d->calendar.monthsInYear(d->year) - 1);
93 } else {
94 setMonth(d->month - 1);
95 }
96}
97
99{
100 if (d->calendar.monthsInYear(d->year) == d->month) {
101 setMonth(1);
102 setYear(d->year + 1);
103 } else {
104 setMonth(d->month + 1);
105 }
106}
107
109{
110 const auto today = QDate::currentDate();
111 setMonth(today.month());
112 setYear(today.year());
113}
114
115QVariant MonthModel::data(const QModelIndex &index, int role) const
116{
117 if (!index.isValid()) {
118 return {};
119 }
120
121 const int row = index.row();
122
123 if (!index.parent().isValid()) {
124 // Fetch days in month
125 int prefix = d->calendar.dayOfWeek(QDate(d->year, d->month, 1)) - m_locale.firstDayOfWeek();
126
127 if (prefix <= 1) {
128 prefix += 7;
129 } else if (prefix > 7) {
130 prefix -= 7;
131 }
132
133 switch (role) {
134 case Qt::DisplayRole:
135 case DayNumber:
136 case IsSelected:
137 case IsToday:
138 case Date: {
139 int day = -1;
140 int month = d->month;
141 int year = d->year;
142 const int daysInMonth = d->calendar.daysInMonth(d->month, d->year);
143 if (row >= prefix && row - prefix < daysInMonth) {
144 // This month
145 day = row - prefix + 1;
146 } else if (row - prefix >= daysInMonth) {
147 // Next month
148 month = d->calendar.monthsInYear(d->year) == d->month ? 1 : d->month + 1;
149 year = d->calendar.monthsInYear(d->year) == d->month ? d->year + 1 : d->year;
150 day = row - daysInMonth - prefix + 1;
151 } else {
152 // Previous month
153 year = d->month > 1 ? d->year : d->year - 1;
154 month = d->month > 1 ? d->month - 1 : d->calendar.monthsInYear(year);
155 int daysInPreviousMonth = d->calendar.daysInMonth(month, year);
156 day = daysInPreviousMonth - prefix + row + 1;
157 }
158
159 if (role == DayNumber || role == Qt::DisplayRole) {
160 return day;
161 }
162 const QDate date(year, month, day);
163 if (role == Date) {
164 return date.startOfDay();
165 // Ensure the date doesn't get mangled into a different date by QML date conversion
166 }
167
168 if (role == IsSelected) {
169 return d->selected == date;
170 }
171 if (role == IsToday) {
172 return date == QDate::currentDate();
173 }
174 return {};
175 }
176 case SameMonth: {
177 const int daysInMonth = d->calendar.daysInMonth(d->month, d->year);
178 return row >= prefix && row - prefix < daysInMonth;
179 }
180 }
181 }
182 return {};
183}
184
185int MonthModel::rowCount(const QModelIndex &parent) const
186{
187 Q_UNUSED(parent);
188 return 42; // Display 6 weeks with each 7 days
189}
190
191QString MonthModel::monthName(int month)
192{
193 QLocale locale;
194 return locale.monthName(month);
195}
196
197QHash<int, QByteArray> MonthModel::roleNames() const
198{
199 return {
200 {Qt::DisplayRole, QByteArrayLiteral("display")},
201 {Roles::DayNumber, QByteArrayLiteral("dayNumber")},
202 {Roles::SameMonth, QByteArrayLiteral("sameMonth")},
203 {Roles::Date, QByteArrayLiteral("date")},
204 {Roles::IsSelected, QByteArrayLiteral("isSelected")},
205 {Roles::IsToday, QByteArrayLiteral("isToday")},
206 };
207}
Q_INVOKABLE void next()
Q_INVOKABLE void previous()
QDate selected
Definition monthmodel.h:25
QStringList weekDays
Definition monthmodel.h:23
Q_INVOKABLE void goToday()
QML_ELEMENTint year
Definition monthmodel.h:19
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QDate currentDate()
void append(QList< T > &&value)
Qt::DayOfWeek firstDayOfWeek() const const
QString monthName(int month, FormatType type) const const
QString standaloneDayName(int day, FormatType type) const const
bool isValid() const const
QModelIndex parent() const const
int row() const const
Q_EMITQ_EMIT
QObject * parent() const const
DisplayRole
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Sep 27 2024 11:56:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.