File indexing completed on 2024-05-12 17:08:52

0001 /*
0002     SPDX-FileCopyrightText: 2013 Mark Gaiser <markg85@gmail.com>
0003     SPDX-FileCopyrightText: 2016 Martin Klapetek <mklapetek@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <QDebug>
0009 
0010 #include "calendar.h"
0011 
0012 class CalendarPrivate
0013 {
0014 public:
0015     explicit CalendarPrivate(Calendar *q);
0016 
0017     QDate displayedDate;
0018     QDate today;
0019     Calendar::Types types;
0020     QList<DayData> dayList;
0021     DaysModel *daysModel;
0022     QJsonArray weekList;
0023 
0024     int days = 0;
0025     int weeks = 0;
0026     int firstDayOfWeek;
0027     QString errorMessage;
0028 
0029 private:
0030     Q_DISABLE_COPY(CalendarPrivate)
0031 };
0032 
0033 CalendarPrivate::CalendarPrivate(Calendar *q)
0034     : types(Calendar::Holiday | Calendar::Event | Calendar::Todo | Calendar::Journal)
0035     , daysModel(new DaysModel(q))
0036     , firstDayOfWeek(QLocale::system().firstDayOfWeek())
0037 {
0038     daysModel->setSourceData(&dayList);
0039 }
0040 
0041 Calendar::Calendar(QObject *parent)
0042     : QObject(parent)
0043     , d(new CalendarPrivate(this))
0044 {
0045     //  m_dayHelper = new CalendarDayHelper(this);
0046     //   connect(m_dayHelper, SIGNAL(calendarChanged()), this, SLOT(updateData()));
0047     connect(this, &Calendar::monthNameChanged, this, &Calendar::monthChanged);
0048 }
0049 
0050 Calendar::~Calendar()
0051 {
0052     delete d;
0053 }
0054 
0055 QDateTime Calendar::displayedDate() const
0056 {
0057     return d->displayedDate.startOfDay();
0058 }
0059 
0060 void Calendar::setDisplayedDate(const QDate &dateTime)
0061 {
0062     if (d->displayedDate == dateTime) {
0063         return;
0064     }
0065 
0066     const int oldMonth = d->displayedDate.month();
0067     const int oldYear = d->displayedDate.year();
0068 
0069     d->displayedDate = dateTime;
0070 
0071     //  m_dayHelper->setDate(m_displayedDate.year(), m_displayedDate.month());
0072 
0073     updateData();
0074     Q_EMIT displayedDateChanged();
0075     if (oldMonth != d->displayedDate.month()) {
0076         Q_EMIT monthNameChanged();
0077     }
0078     if (oldYear != d->displayedDate.year()) {
0079         Q_EMIT yearChanged();
0080     }
0081 }
0082 
0083 void Calendar::setDisplayedDate(const QDateTime &dateTime)
0084 {
0085     setDisplayedDate(dateTime.date());
0086 }
0087 
0088 QDateTime Calendar::today() const
0089 {
0090     return d->today.startOfDay();
0091 }
0092 
0093 void Calendar::setToday(const QDateTime &dateTime)
0094 {
0095     QDate date = dateTime.date();
0096     if (date == d->today) {
0097         return;
0098     }
0099 
0100     d->today = date;
0101     if (d->displayedDate.isNull()) {
0102         resetToToday();
0103     } else {
0104         // the else is to prevent calling updateData() twice
0105         // if the resetToToday() was called
0106         updateData();
0107     }
0108     Q_EMIT todayChanged();
0109 }
0110 
0111 void Calendar::resetToToday()
0112 {
0113     setDisplayedDate(d->today);
0114     updateData();
0115 }
0116 
0117 int Calendar::types() const
0118 {
0119     return d->types;
0120 }
0121 
0122 void Calendar::setTypes(int types)
0123 {
0124     if (d->types == static_cast<Types>(types)) {
0125         return;
0126     }
0127 
0128     //    d->m_types = static_cast<Types>(types);
0129     //    updateTypes();
0130 
0131     Q_EMIT typesChanged();
0132 }
0133 
0134 int Calendar::days()
0135 {
0136     return d->days;
0137 }
0138 
0139 void Calendar::setDays(int days)
0140 {
0141     if (d->days != days) {
0142         d->days = days;
0143         updateData();
0144         Q_EMIT daysChanged();
0145     }
0146 }
0147 
0148 int Calendar::weeks() const
0149 {
0150     return d->weeks;
0151 }
0152 
0153 void Calendar::setWeeks(int weeks)
0154 {
0155     if (d->weeks != weeks) {
0156         d->weeks = weeks;
0157         updateData();
0158         Q_EMIT weeksChanged();
0159     }
0160 }
0161 
0162 int Calendar::firstDayOfWeek() const
0163 {
0164     // QML has Sunday as 0, so we need to accommodate here
0165     return d->firstDayOfWeek == 7 ? 0 : d->firstDayOfWeek;
0166 }
0167 
0168 void Calendar::setFirstDayOfWeek(int day)
0169 {
0170     if (day > 7) {
0171         return;
0172     }
0173 
0174     if (d->firstDayOfWeek != day) {
0175         // QML has Sunday as 0, so we need to accommodate here
0176         // for QDate functions which have Sunday as 7
0177         if (day == 0) {
0178             d->firstDayOfWeek = 7;
0179         } else {
0180             d->firstDayOfWeek = day;
0181         }
0182 
0183         updateData();
0184         Q_EMIT firstDayOfWeekChanged();
0185     }
0186 }
0187 
0188 QString Calendar::errorMessage() const
0189 {
0190     return d->errorMessage;
0191 }
0192 
0193 int Calendar::currentWeek() const
0194 {
0195     QDate date(QDate::currentDate());
0196     return date.weekNumber();
0197 }
0198 
0199 QString Calendar::dayName(int weekday) const
0200 {
0201     return QLocale::system().dayName(weekday, QLocale::ShortFormat);
0202 }
0203 
0204 QString Calendar::monthName() const
0205 {
0206     // Simple QDate::longMonthName won't do the job as it
0207     // will return the month name using LC_DATE locale which is used
0208     // for date formatting etc. So for example, in en_US locale
0209     // and cs_CZ LC_DATE, it would return Czech month names while
0210     // it should return English ones. So here we force the LANG
0211     // locale and take the month name from that.
0212     //
0213     // See https://bugs.kde.org/show_bug.cgi?id=353715
0214 
0215     QLocale langLocale;
0216 
0217     if (QLocale().uiLanguages().length() > 0) {
0218         langLocale = QLocale(QLocale().uiLanguages().at(0));
0219     }
0220     return langLocale.standaloneMonthName(d->displayedDate.month());
0221 }
0222 
0223 int Calendar::year() const
0224 {
0225     return d->displayedDate.year();
0226 }
0227 
0228 int Calendar::month() const
0229 {
0230     return d->displayedDate.month();
0231 }
0232 
0233 QAbstractItemModel *Calendar::daysModel() const
0234 {
0235     return d->daysModel;
0236 }
0237 QJsonArray Calendar::weeksModel() const
0238 {
0239     return d->weekList;
0240 }
0241 
0242 void Calendar::updateData()
0243 {
0244     if (d->days == 0 || d->weeks == 0) {
0245         return;
0246     }
0247 
0248     d->dayList.clear();
0249     d->weekList = QJsonArray();
0250 
0251     int totalDays = d->days * d->weeks;
0252 
0253     int daysBeforeCurrentMonth = 0;
0254     int daysAfterCurrentMonth = 0;
0255 
0256     QDate firstDay(d->displayedDate.year(), d->displayedDate.month(), 1);
0257 
0258     // If the first day is the same as the starting day then we add a complete row before it.
0259     if (d->firstDayOfWeek < firstDay.dayOfWeek()) {
0260         daysBeforeCurrentMonth = firstDay.dayOfWeek() - d->firstDayOfWeek;
0261     } else {
0262         daysBeforeCurrentMonth = days() - (d->firstDayOfWeek - firstDay.dayOfWeek());
0263     }
0264 
0265     int daysThusFar = daysBeforeCurrentMonth + d->displayedDate.daysInMonth();
0266     if (daysThusFar < totalDays) {
0267         daysAfterCurrentMonth = totalDays - daysThusFar;
0268     }
0269 
0270     if (daysBeforeCurrentMonth > 0) {
0271         QDate previousMonth = d->displayedDate.addMonths(-1);
0272         // QDate previousMonth(d->m_displayedDate.year(), d->m_displayedDate.month() - 1, 1);
0273         for (int i = 0; i < daysBeforeCurrentMonth; i++) {
0274             DayData day;
0275             day.isCurrent = false;
0276             day.dayNumber = previousMonth.daysInMonth() - (daysBeforeCurrentMonth - (i + 1));
0277             day.monthNumber = previousMonth.month();
0278             day.yearNumber = previousMonth.year();
0279             //      day.containsEventItems = false;
0280             d->dayList << day;
0281         }
0282     }
0283 
0284     for (int i = 0; i < d->displayedDate.daysInMonth(); i++) {
0285         DayData day;
0286         day.isCurrent = true;
0287         day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates
0288         //  day.containsEventItems = m_dayHelper->containsEventItems(i + 1);
0289         day.monthNumber = d->displayedDate.month();
0290         day.yearNumber = d->displayedDate.year();
0291         d->dayList << day;
0292     }
0293 
0294     if (daysAfterCurrentMonth > 0) {
0295         for (int i = 0; i < daysAfterCurrentMonth; i++) {
0296             DayData day;
0297             day.isCurrent = false;
0298             day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates
0299             //   day.containsEventItems = false;
0300             day.monthNumber = d->displayedDate.addMonths(1).month();
0301             day.yearNumber = d->displayedDate.addMonths(1).year();
0302             d->dayList << day;
0303         }
0304     }
0305     const int numOfDaysInCalendar = d->dayList.count();
0306 
0307     // Week numbers are always counted from Mondays
0308     // so find which index is Monday
0309     int mondayOffset = 0;
0310     if (!d->dayList.isEmpty()) {
0311         const DayData &data = d->dayList.at(0);
0312         QDate firstDay(data.yearNumber, data.monthNumber, data.dayNumber);
0313         // If the first day is not already Monday, get offset for Monday
0314         if (firstDay.dayOfWeek() != 1) {
0315             mondayOffset = 8 - firstDay.dayOfWeek();
0316         }
0317     }
0318 
0319     // Fill weeksModel with the week numbers
0320     for (int i = mondayOffset; i < numOfDaysInCalendar; i += 7) {
0321         const DayData &data = d->dayList.at(i);
0322         d->weekList.append(QDate(data.yearNumber, data.monthNumber, data.dayNumber).weekNumber());
0323     }
0324     Q_EMIT weeksModelChanged();
0325     d->daysModel->update();
0326 
0327     //    qDebug() << "---------------------------------------------------------------";
0328     //    qDebug() << "Date obj: " << d->m_displayedDate;
0329     //    qDebug() << "Month: " << d->m_displayedDate.month();
0330     //    qDebug() << "m_days: " << d->m_days;
0331     //    qDebug() << "m_weeks: " << d->m_weeks;
0332     //    qDebug() << "Days before this month: " << daysBeforeCurrentMonth;
0333     //    qDebug() << "Days after this month: " << daysAfterCurrentMonth;
0334     //    qDebug() << "Days in current month: " << d->m_displayedDate.daysInMonth();
0335     //    qDebug() << "d->m_dayList size: " << d->m_dayList.count();
0336     //    qDebug() << "---------------------------------------------------------------";
0337 }
0338 
0339 void Calendar::nextDecade()
0340 {
0341     setDisplayedDate(d->displayedDate.addYears(10));
0342 }
0343 
0344 void Calendar::previousDecade()
0345 {
0346     // Negative years don't make sense
0347     if (d->displayedDate.year() >= 10) {
0348         setDisplayedDate(d->displayedDate.addYears(-10));
0349     }
0350 }
0351 
0352 void Calendar::nextYear()
0353 {
0354     setDisplayedDate(d->displayedDate.addYears(1));
0355 }
0356 
0357 void Calendar::previousYear()
0358 {
0359     // Negative years don't make sense
0360     if (d->displayedDate.year() >= 1) {
0361         setDisplayedDate(d->displayedDate.addYears(-1));
0362     }
0363 }
0364 
0365 void Calendar::nextMonth()
0366 {
0367     setDisplayedDate(d->displayedDate.addMonths(1));
0368 }
0369 
0370 void Calendar::previousMonth()
0371 {
0372     setDisplayedDate(d->displayedDate.addMonths(-1));
0373 }
0374 
0375 void Calendar::goToMonth(int month)
0376 {
0377     setDisplayedDate(QDate(d->displayedDate.year(), month, 1));
0378 }
0379 
0380 void Calendar::goToYear(int year)
0381 {
0382     setDisplayedDate(QDate(year, d->displayedDate.month(), 1));
0383 }