File indexing completed on 2024-04-28 16:44:42

0001 /*
0002     SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "config-ICU.h"
0008 
0009 #include "alternatecalendarplugin.h"
0010 
0011 #include <QCache>
0012 
0013 #include <KConfigGroup>
0014 #include <KConfigWatcher>
0015 #include <KSharedConfig>
0016 
0017 #include "provider/qtcalendar.h"
0018 #if HAVE_ICU
0019 #include "provider/chinesecalendar.h"
0020 #include "provider/hebrewcalendar.h"
0021 #include "provider/indiancalendar.h"
0022 #endif
0023 
0024 using SubLabel = CalendarEvents::CalendarEventsPlugin::SubLabel;
0025 
0026 class AlternateCalendarPluginPrivate
0027 {
0028 public:
0029     explicit AlternateCalendarPluginPrivate(AlternateCalendarPlugin *parent);
0030     ~AlternateCalendarPluginPrivate();
0031 
0032     void init();
0033     void loadEventsForDateRange(const QDate &startDate, const QDate &endDate);
0034 
0035 private:
0036     std::unique_ptr<AbstractCalendarProvider> m_calendarProvider;
0037 
0038     // Cache lookup data
0039     QCache<QDate, SubLabel> m_subLabelsCache;
0040 
0041     // For updating config
0042     KConfigGroup m_generalConfigGroup;
0043     KConfigWatcher::Ptr m_configWatcher;
0044 
0045     CalendarSystem::System m_calendarSystem;
0046     int m_dateOffset; // For the (tabular) Islamic Civil calendar
0047 
0048     AlternateCalendarPlugin *q;
0049 };
0050 
0051 AlternateCalendarPluginPrivate::AlternateCalendarPluginPrivate(AlternateCalendarPlugin *parent)
0052     : q(parent)
0053 {
0054     m_subLabelsCache.setMaxCost(42 * 3 /*previous, current, next*/);
0055 
0056     auto config = KSharedConfig::openConfig(QStringLiteral("plasma_calendar_alternatecalendar"));
0057     m_generalConfigGroup = config->group("General");
0058     m_configWatcher = KConfigWatcher::create(config);
0059     QObject::connect(m_configWatcher.get(), &KConfigWatcher::configChanged, q, &AlternateCalendarPlugin::updateSettings);
0060     init();
0061 }
0062 
0063 AlternateCalendarPluginPrivate::~AlternateCalendarPluginPrivate()
0064 {
0065 }
0066 
0067 void AlternateCalendarPluginPrivate::init()
0068 {
0069     m_dateOffset = m_generalConfigGroup.readEntry("dateOffset", 0);
0070 
0071     // Find the matched calendar system
0072     const QString system = m_generalConfigGroup.readEntry("calendarSystem", "Julian");
0073     const auto systemIt = s_calendarMap.find(system);
0074 
0075     if (systemIt == s_calendarMap.end()) {
0076         // Invalid config, fall back to Gregorian
0077         m_calendarSystem = CalendarSystem::Gregorian;
0078     } else {
0079         m_calendarSystem = systemIt->second.system;
0080     }
0081 
0082     // Load/Reload the calendar provider
0083     switch (m_calendarSystem) {
0084 #if HAVE_ICU
0085     case CalendarSystem::Chinese:
0086         m_calendarProvider.reset(new ChineseCalendarProvider(q, m_calendarSystem));
0087         break;
0088     case CalendarSystem::Indian:
0089         m_calendarProvider.reset(new IndianCalendarProvider(q, m_calendarSystem));
0090         break;
0091     case CalendarSystem::Hebrew:
0092         m_calendarProvider.reset(new HebrewCalendarProvider(q, m_calendarSystem));
0093         break;
0094 #endif
0095 #ifndef QT_BOOTSTRAPPED
0096     case CalendarSystem::Julian:
0097     case CalendarSystem::Milankovic:
0098 #endif
0099 #if QT_CONFIG(jalalicalendar)
0100     case CalendarSystem::Jalali:
0101 #endif
0102 #if QT_CONFIG(islamiccivilcalendar)
0103     case CalendarSystem::IslamicCivil:
0104 #endif
0105         m_calendarProvider.reset(new QtCalendarProvider(q, m_calendarSystem));
0106         break;
0107     default:
0108         m_calendarProvider.reset(new AbstractCalendarProvider(q, m_calendarSystem));
0109     }
0110 
0111     // Clear the old cache when config is reloaded
0112     m_subLabelsCache.clear();
0113 }
0114 
0115 void AlternateCalendarPluginPrivate::loadEventsForDateRange(const QDate &startDate, const QDate &endDate)
0116 {
0117     if (!endDate.isValid() || m_calendarSystem == CalendarSystem::Gregorian) {
0118         return;
0119     }
0120 
0121     QHash<QDate, QDate> alternateDatesData;
0122     QHash<QDate, CalendarEvents::CalendarEventsPlugin::SubLabel> subLabelsData;
0123 
0124     for (QDate date = startDate; date <= endDate && date.isValid(); date = date.addDays(1)) {
0125         const QDate offsetDate = date.addDays(m_dateOffset);
0126         const QCalendar::YearMonthDay alt = m_calendarProvider->fromGregorian(offsetDate);
0127 
0128         if (alt.day != date.day() || alt.month != date.month() || alt.year != date.year()) {
0129             alternateDatesData.insert(date, QDate(alt.year, alt.month, alt.day));
0130         }
0131 
0132         if (m_subLabelsCache.contains(date)) {
0133             subLabelsData.insert(date, *m_subLabelsCache.object(date));
0134         } else {
0135             const auto it = subLabelsData.insert(date, m_calendarProvider->subLabels(offsetDate));
0136             m_subLabelsCache.insert(date, new SubLabel(*it));
0137         }
0138     }
0139 
0140     if (alternateDatesData.size() > 0) {
0141         Q_EMIT q->alternateDateReady(alternateDatesData);
0142     }
0143     Q_EMIT q->subLabelReady(subLabelsData);
0144 }
0145 
0146 AlternateCalendarPlugin::AlternateCalendarPlugin(QObject *parent)
0147     : CalendarEvents::CalendarEventsPlugin(parent)
0148     , d(std::make_unique<AlternateCalendarPluginPrivate>(this))
0149 {
0150 }
0151 
0152 AlternateCalendarPlugin::~AlternateCalendarPlugin()
0153 {
0154 }
0155 
0156 void AlternateCalendarPlugin::loadEventsForDateRange(const QDate &startDate, const QDate &endDate)
0157 {
0158     m_lastStartDate = startDate;
0159     m_lastEndDate = endDate;
0160 
0161     d->loadEventsForDateRange(startDate, endDate);
0162 }
0163 
0164 void AlternateCalendarPlugin::updateSettings()
0165 {
0166     d->init();
0167     loadEventsForDateRange(m_lastStartDate, m_lastEndDate);
0168 }