File indexing completed on 2024-04-28 04:40:43

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