File indexing completed on 2024-06-23 04:42:36

0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.1-or-later
0003 
0004 #include "monthmodel.h"
0005 #include <QCalendar>
0006 
0007 struct MonthModel::Private {
0008     int year;
0009     int month;
0010     QCalendar calendar = QCalendar();
0011     QDate selected;
0012 };
0013 
0014 MonthModel::MonthModel(QObject *parent)
0015     : QAbstractListModel(parent)
0016     , d(new MonthModel::Private())
0017 {
0018     goToday();
0019     d->selected = QDate::currentDate();
0020 }
0021 
0022 MonthModel::~MonthModel() = default;
0023 
0024 int MonthModel::year() const
0025 {
0026     return d->year;
0027 }
0028 
0029 void MonthModel::setYear(int year)
0030 {
0031     if (d->year == year) {
0032         return;
0033     }
0034     d->year = year;
0035     Q_EMIT yearChanged();
0036     Q_EMIT dataChanged(index(0, 0), index(41, 0));
0037     setSelected(QDate(year, d->selected.month(), qMin(d->selected.day(), d->calendar.daysInMonth(d->selected.month(), year))));
0038 }
0039 
0040 int MonthModel::month() const
0041 {
0042     return d->month;
0043 }
0044 
0045 void MonthModel::setMonth(int month)
0046 {
0047     if (d->month == month) {
0048         return;
0049     }
0050     d->month = month;
0051     Q_EMIT monthChanged();
0052     Q_EMIT dataChanged(index(0, 0), index(41, 0));
0053     setSelected(QDate(d->selected.year(), d->month, qMin(d->selected.day(), d->calendar.daysInMonth(d->month, d->selected.year()))));
0054 }
0055 
0056 QDate MonthModel::selected() const
0057 {
0058     return d->selected;
0059 }
0060 
0061 void MonthModel::setSelected(const QDate &selected)
0062 {
0063     if (d->selected == selected) {
0064         return;
0065     }
0066     d->selected = selected;
0067     Q_EMIT selectedChanged();
0068     Q_EMIT dataChanged(index(0, 0), index(41, 0), {Roles::IsSelected});
0069 }
0070 
0071 QStringList MonthModel::weekDays() const
0072 {
0073     QLocale locale;
0074     QStringList daysName;
0075     for (int i = 0; i < 7; i++) {
0076         int day = locale.firstDayOfWeek() + i;
0077         if (day > 7) {
0078             day -= 7;
0079         }
0080         if (day == 7) {
0081             day = 0;
0082         }
0083         daysName.append(locale.standaloneDayName(day == 0 ? Qt::Sunday : day, QLocale::NarrowFormat));
0084     }
0085     return daysName;
0086 }
0087 
0088 void MonthModel::previous()
0089 {
0090     if (d->month == 1) {
0091         setYear(d->year - 1);
0092         setMonth(d->calendar.monthsInYear(d->year) - 1);
0093     } else {
0094         setMonth(d->month - 1);
0095     }
0096 }
0097 
0098 void MonthModel::next()
0099 {
0100     if (d->calendar.monthsInYear(d->year) == d->month) {
0101         setMonth(1);
0102         setYear(d->year + 1);
0103     } else {
0104         setMonth(d->month + 1);
0105     }
0106 }
0107 
0108 void MonthModel::goToday()
0109 {
0110     const auto today = QDate::currentDate();
0111     setMonth(today.month());
0112     setYear(today.year());
0113 }
0114 
0115 QVariant MonthModel::data(const QModelIndex &index, int role) const
0116 {
0117     if (!index.isValid()) {
0118         return {};
0119     }
0120 
0121     const int row = index.row();
0122 
0123     if (!index.parent().isValid()) {
0124         // Fetch days in month
0125         int prefix = d->calendar.dayOfWeek(QDate(d->year, d->month, 1)) - m_locale.firstDayOfWeek();
0126 
0127         if (prefix <= 1) {
0128             prefix += 7;
0129         } else if (prefix > 7) {
0130             prefix -= 7;
0131         }
0132 
0133         switch (role) {
0134         case Qt::DisplayRole:
0135         case DayNumber:
0136         case IsSelected:
0137         case IsToday:
0138         case Date: {
0139             int day = -1;
0140             int month = d->month;
0141             int year = d->year;
0142             const int daysInMonth = d->calendar.daysInMonth(d->month, d->year);
0143             if (row >= prefix && row - prefix < daysInMonth) {
0144                 // This month
0145                 day = row - prefix + 1;
0146             } else if (row - prefix >= daysInMonth) {
0147                 // Next month
0148                 month = d->calendar.monthsInYear(d->year) == d->month ? 1 : d->month + 1;
0149                 year = d->calendar.monthsInYear(d->year) == d->month ? d->year + 1 : d->year;
0150                 day = row - daysInMonth - prefix + 1;
0151             } else {
0152                 // Previous month
0153                 year = d->month > 1 ? d->year : d->year - 1;
0154                 month = d->month > 1 ? d->month - 1 : d->calendar.monthsInYear(year);
0155                 int daysInPreviousMonth = d->calendar.daysInMonth(month, year);
0156                 day = daysInPreviousMonth - prefix + row + 1;
0157             }
0158 
0159             if (role == DayNumber || role == Qt::DisplayRole) {
0160                 return day;
0161             }
0162             const QDate date(year, month, day);
0163             if (role == Date) {
0164                 return date.startOfDay();
0165                 // Ensure the date doesn't get mangled into a different date by QML date conversion
0166             }
0167 
0168             if (role == IsSelected) {
0169                 return d->selected == date;
0170             }
0171             if (role == IsToday) {
0172                 return date == QDate::currentDate();
0173             }
0174             return {};
0175         }
0176         case SameMonth: {
0177             const int daysInMonth = d->calendar.daysInMonth(d->month, d->year);
0178             return row >= prefix && row - prefix < daysInMonth;
0179         }
0180         }
0181     }
0182     return {};
0183 }
0184 
0185 int MonthModel::rowCount(const QModelIndex &parent) const
0186 {
0187     Q_UNUSED(parent);
0188     return 42; // Display 6 weeks with each 7 days
0189 }
0190 
0191 QString MonthModel::monthName(int month)
0192 {
0193     QLocale locale;
0194     return locale.monthName(month);
0195 }
0196 
0197 QHash<int, QByteArray> MonthModel::roleNames() const
0198 {
0199     return {
0200         {Qt::DisplayRole, QByteArrayLiteral("display")},
0201         {Roles::DayNumber, QByteArrayLiteral("dayNumber")},
0202         {Roles::SameMonth, QByteArrayLiteral("sameMonth")},
0203         {Roles::Date, QByteArrayLiteral("date")},
0204         {Roles::IsSelected, QByteArrayLiteral("isSelected")},
0205         {Roles::IsToday, QByteArrayLiteral("isToday")},
0206     };
0207 }