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 ¤tDate) 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"