File indexing completed on 2024-05-12 12:41:35

0001 // SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
0002 // SPDX-License-Identifier: LGPL-2.1-or-later
0003 
0004 #include <QDebug>
0005 #include <QMetaEnum>
0006 #include <cmath>
0007 #include "infinitecalendarviewmodel.h"
0008 
0009 InfiniteCalendarViewModel::InfiniteCalendarViewModel(QObject *parent)
0010     : QAbstractListModel(parent)
0011 {
0012     setup();
0013 }
0014 
0015 void InfiniteCalendarViewModel::setup()
0016 {
0017     const QDate today = QDate::currentDate();
0018 
0019     switch (m_scale) {
0020     case WeekScale: {
0021         QDate firstDay = today.addDays(-today.dayOfWeek() + m_locale.firstDayOfWeek());
0022         // We create dates before and after where our view will start from (which is today)
0023         firstDay = firstDay.addDays((-m_datesToAdd * 7) / 2);
0024 
0025         addWeekDates(true, firstDay);
0026         break;
0027     }
0028     case MonthScale: {
0029         QDate firstDay(today.year(), today.month(), 1);
0030         firstDay = firstDay.addMonths(-m_datesToAdd / 2);
0031 
0032         addMonthDates(true, firstDay);
0033         break;
0034     }
0035     case YearScale: {
0036         QDate firstDay(today.year(), today.month(), 1);
0037         firstDay = firstDay.addYears(-m_datesToAdd / 2);
0038 
0039         addYearDates(true, firstDay);
0040         break;
0041     }
0042     case DecadeScale: {
0043         const int firstYear = ((floor(today.year() / 10)) * 10) - 1; // E.g. For 2020 have view start at 2019...
0044         QDate firstDay(firstYear, today.month(), 1);
0045         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
0046 
0047         addDecadeDates(true, firstDay);
0048         break;
0049     }
0050     }
0051 }
0052 
0053 QVariant InfiniteCalendarViewModel::data(const QModelIndex &idx, int role) const
0054 {
0055     if (!hasIndex(idx.row(), idx.column())) {
0056         return {};
0057     }
0058 
0059     const QDate startDate = m_startDates[idx.row()];
0060 
0061     if (m_scale == MonthScale && role != StartDateRole) {
0062         const QDate firstDay = m_firstDayOfMonthDates[idx.row()];
0063 
0064         switch (role) {
0065         case FirstDayOfMonthRole:
0066             return firstDay.startOfDay();
0067         case SelectedMonthRole:
0068             return firstDay.month();
0069         case SelectedYearRole:
0070             return firstDay.year();
0071         default:
0072             qWarning() << "Unknown role for startdate:" << QMetaEnum::fromType<Roles>().valueToKey(role);
0073             return {};
0074         }
0075     }
0076 
0077     switch (role) {
0078     case StartDateRole:
0079         return startDate.startOfDay();
0080     case SelectedMonthRole:
0081         return startDate.month();
0082     case SelectedYearRole:
0083         return startDate.year();
0084     default:
0085         qWarning() << "Unknown role for startdate:" << QMetaEnum::fromType<Roles>().valueToKey(role);
0086         return {};
0087     }
0088 }
0089 
0090 int InfiniteCalendarViewModel::rowCount(const QModelIndex &parent) const
0091 {
0092     Q_UNUSED(parent)
0093     return m_startDates.length();
0094 }
0095 
0096 QHash<int, QByteArray> InfiniteCalendarViewModel::roleNames() const
0097 {
0098     return {
0099         {StartDateRole, QByteArrayLiteral("startDate")},
0100         {FirstDayOfMonthRole, QByteArrayLiteral("firstDay")},
0101         {SelectedMonthRole, QByteArrayLiteral("selectedMonth")},
0102         {SelectedYearRole, QByteArrayLiteral("selectedYear")},
0103     };
0104 }
0105 
0106 void InfiniteCalendarViewModel::addDates(bool atEnd, const QDate startFrom)
0107 {
0108     switch (m_scale) {
0109     case WeekScale:
0110         addWeekDates(atEnd, startFrom);
0111         break;
0112     case MonthScale:
0113         addMonthDates(atEnd, startFrom);
0114         break;
0115     case YearScale:
0116         addYearDates(atEnd, startFrom);
0117         break;
0118     case DecadeScale:
0119         addDecadeDates(atEnd, startFrom);
0120         break;
0121     }
0122 }
0123 
0124 void InfiniteCalendarViewModel::addWeekDates(bool atEnd, const QDate &startFrom)
0125 {
0126     const int newRow = atEnd ? rowCount() : 0;
0127 
0128     beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
0129 
0130     for (int i = 0; i < m_datesToAdd; i++) {
0131         QDate startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addDays(7) : m_startDates[0].addDays(-7);
0132 
0133         if (startDate.dayOfWeek() != m_locale.firstDayOfWeek()) {
0134             startDate = startDate.addDays(-startDate.dayOfWeek() + m_locale.firstDayOfWeek());
0135         }
0136 
0137         if (atEnd) {
0138             m_startDates.append(startDate);
0139         } else {
0140             m_startDates.insert(0, startDate);
0141         }
0142     }
0143 
0144     endInsertRows();
0145 }
0146 
0147 void InfiniteCalendarViewModel::addMonthDates(bool atEnd, const QDate &startFrom)
0148 {
0149     const int newRow = atEnd ? rowCount() : 0;
0150 
0151     beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
0152 
0153     for (int i = 0; i < m_datesToAdd; i++) {
0154         const QDate firstDay = startFrom.isValid() && i == 0 ? startFrom
0155             : atEnd                                          ? m_firstDayOfMonthDates[rowCount() - 1].addMonths(1)
0156                                                              : m_firstDayOfMonthDates[0].addMonths(-1);
0157         QDate startDate = firstDay;
0158 
0159         startDate = startDate.addDays(-startDate.dayOfWeek() + m_locale.firstDayOfWeek());
0160         if (startDate >= firstDay) {
0161             startDate = startDate.addDays(-7);
0162         }
0163 
0164         if (atEnd) {
0165             m_firstDayOfMonthDates.append(firstDay);
0166             m_startDates.append(startDate);
0167         } else {
0168             m_firstDayOfMonthDates.insert(0, firstDay);
0169             m_startDates.insert(0, startDate);
0170         }
0171     }
0172 
0173     endInsertRows();
0174 }
0175 
0176 void InfiniteCalendarViewModel::addYearDates(bool atEnd, const QDate &startFrom)
0177 {
0178     const int newRow = atEnd ? rowCount() : 0;
0179 
0180     beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
0181 
0182     for (int i = 0; i < m_datesToAdd; i++) {
0183         QDate startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addYears(1) : m_startDates[0].addYears(-1);
0184 
0185         if (atEnd) {
0186             m_startDates.append(startDate);
0187         } else {
0188             m_startDates.insert(0, startDate);
0189         }
0190     }
0191 
0192     endInsertRows();
0193 }
0194 
0195 void InfiniteCalendarViewModel::addDecadeDates(bool atEnd, const QDate &startFrom)
0196 {
0197     const int newRow = atEnd ? rowCount() : 0;
0198 
0199     beginInsertRows(QModelIndex(), newRow, newRow + m_datesToAdd - 1);
0200 
0201     for (int i = 0; i < m_datesToAdd; i++) {
0202         QDate startDate = startFrom.isValid() && i == 0 ? startFrom : atEnd ? m_startDates[rowCount() - 1].addYears(10) : m_startDates[0].addYears(-10);
0203 
0204         if (atEnd) {
0205             m_startDates.append(startDate);
0206         } else {
0207             m_startDates.insert(0, startDate);
0208         }
0209     }
0210 
0211     endInsertRows();
0212 }
0213 
0214 int InfiniteCalendarViewModel::datesToAdd() const
0215 {
0216     return m_datesToAdd;
0217 }
0218 
0219 void InfiniteCalendarViewModel::setDatesToAdd(int datesToAdd)
0220 {
0221     m_datesToAdd = datesToAdd;
0222     Q_EMIT datesToAddChanged();
0223 }
0224 
0225 int InfiniteCalendarViewModel::scale()
0226 {
0227     return m_scale;
0228 }
0229 
0230 void InfiniteCalendarViewModel::setScale(int scale)
0231 {
0232     beginResetModel();
0233 
0234     m_startDates.clear();
0235     m_firstDayOfMonthDates.clear();
0236     m_scale = scale;
0237     setup();
0238     Q_EMIT scaleChanged();
0239 
0240     endResetModel();
0241 }
0242 
0243 #include "moc_infinitecalendarviewmodel.cpp"