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