File indexing completed on 2024-05-05 05:36:41

0001 /*
0002     SPDX-FileCopyrightText: 2014 Kai Uwe Broulik <kde@privat.broulik.de>
0003     SPDX-FileCopyrightText: 2014 Martin Klapetek <mklapetek@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "timezonemodel.h"
0009 #include "timezonesi18n.h"
0010 
0011 #include <KLocalizedString>
0012 #include <QStringMatcher>
0013 #include <QTimeZone>
0014 
0015 TimeZoneFilterProxy::TimeZoneFilterProxy(QObject *parent)
0016     : QSortFilterProxyModel(parent)
0017 {
0018     m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
0019 }
0020 
0021 bool TimeZoneFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0022 {
0023     if (!sourceModel() || m_filterString.isEmpty()) {
0024         return true;
0025     }
0026 
0027     const QString city = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::CityRole).toString();
0028     const QString region = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::RegionRole).toString();
0029     const QString comment = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::CommentRole).toString();
0030 
0031     if (m_stringMatcher.indexIn(city) != -1 || m_stringMatcher.indexIn(region) != -1 || m_stringMatcher.indexIn(comment) != -1) {
0032         return true;
0033     }
0034 
0035     return false;
0036 }
0037 
0038 void TimeZoneFilterProxy::setFilterString(const QString &filterString)
0039 {
0040     m_filterString = filterString;
0041     m_stringMatcher.setPattern(filterString);
0042     emit filterStringChanged();
0043     invalidateFilter();
0044 }
0045 
0046 //=============================================================================
0047 
0048 TimeZoneModel::TimeZoneModel(QObject *parent)
0049     : QAbstractListModel(parent)
0050     , m_timezonesI18n(new TimezonesI18n(this))
0051 {
0052     update();
0053 }
0054 
0055 TimeZoneModel::~TimeZoneModel()
0056 {
0057 }
0058 
0059 int TimeZoneModel::rowCount(const QModelIndex &parent) const
0060 {
0061     return parent.isValid() ? 0 : m_data.count();
0062 }
0063 
0064 QVariant TimeZoneModel::data(const QModelIndex &index, int role) const
0065 {
0066     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0067 
0068     const TimeZoneData &currentData = m_data.at(index.row());
0069 
0070     switch (role) {
0071     case TimeZoneIdRole:
0072         return currentData.id;
0073     case RegionRole: {
0074         return currentData.region;
0075     case CityRole:
0076         if (currentData.city.isEmpty())
0077             return currentData.id;
0078     }
0079         return currentData.city;
0080     case CommentRole:
0081         return currentData.comment;
0082     case CheckedRole:
0083         return currentData.checked;
0084     }
0085 
0086     return {};
0087 }
0088 
0089 bool TimeZoneModel::setData(const QModelIndex &index, const QVariant &value, int role)
0090 {
0091     if (!index.isValid() || value.isNull()) {
0092         return false;
0093     }
0094 
0095     if (role == CheckedRole) {
0096         m_data[index.row()].checked = value.toBool();
0097         emit dataChanged(index, index);
0098 
0099         if (m_data[index.row()].checked) {
0100             m_selectedTimeZones.append(m_data[index.row()].id);
0101             m_offsetData.insert(m_data[index.row()].id, m_data[index.row()].offsetFromUtc);
0102         } else {
0103             m_selectedTimeZones.removeAll(m_data[index.row()].id);
0104             m_offsetData.remove(m_data[index.row()].id);
0105         }
0106 
0107         sortTimeZones();
0108 
0109         emit selectedTimeZonesChanged();
0110         return true;
0111     }
0112 
0113     return false;
0114 }
0115 
0116 void TimeZoneModel::update()
0117 {
0118     beginResetModel();
0119     m_data.clear();
0120 
0121     QTimeZone localZone = QTimeZone(QTimeZone::systemTimeZoneId());
0122     const QStringList data = QString::fromUtf8(localZone.id()).split(QLatin1Char('/'));
0123 
0124     TimeZoneData local;
0125     local.id = "Local";
0126     local.region = i18nc("This means \"Local Timezone\"", "Local");
0127     local.city = m_timezonesI18n->i18nCity(data.last());
0128     local.comment = i18n("Your system time zone");
0129     local.checked = false;
0130 
0131     m_data.append(local);
0132 
0133     QStringList cities;
0134     QHash<QString, QTimeZone> zonesByCity;
0135 
0136     const auto systemTimeZones = QTimeZone::availableTimeZoneIds();
0137 
0138     for (const auto &id : systemTimeZones) {
0139         const QTimeZone zone(id);
0140 
0141         const QStringList splitted = QString::fromUtf8(id).split(QStringLiteral("/"));
0142 
0143         // CITY | COUNTRY | CONTINENT
0144         const QString key = QStringLiteral("%1|%2|%3").arg(splitted.last(), QLocale::territoryToString(zone.territory()), splitted.first());
0145 
0146         cities.append(key);
0147         zonesByCity.insert(key, zone);
0148     }
0149     cities.sort(Qt::CaseInsensitive);
0150 
0151     for (const QString &key : std::as_const(cities)) {
0152         const QTimeZone timeZone = zonesByCity.value(key);
0153         QString comment = timeZone.comment();
0154 
0155         // FIXME - this was in the old code but makes no sense
0156         //         if (!comment.isEmpty()) {
0157         //             comment = i18n(comment.toUtf8());
0158         //         }
0159 
0160         const QStringList cityCountryContinent = key.split(QLatin1Char('|'));
0161 
0162         TimeZoneData newData;
0163         newData.id = timeZone.id();
0164         newData.region = timeZone.territory() == QLocale::AnyCountry
0165             ? QString()
0166             : m_timezonesI18n->i18nContinents(cityCountryContinent.at(2)) + QLatin1Char('/') + m_timezonesI18n->i18nCountry(timeZone.territory());
0167         newData.city = m_timezonesI18n->i18nCity(cityCountryContinent.at(0));
0168         newData.comment = comment;
0169         newData.checked = false;
0170         newData.offsetFromUtc = timeZone.offsetFromUtc(QDateTime::currentDateTimeUtc());
0171         m_data.append(newData);
0172     }
0173 
0174     endResetModel();
0175 }
0176 
0177 void TimeZoneModel::setSelectedTimeZones(const QStringList &selectedTimeZones)
0178 {
0179     m_selectedTimeZones = selectedTimeZones;
0180     for (int i = 0; i < m_data.size(); i++) {
0181         if (m_selectedTimeZones.contains(m_data.at(i).id)) {
0182             m_data[i].checked = true;
0183             m_offsetData.insert(m_data[i].id, m_data[i].offsetFromUtc);
0184 
0185             QModelIndex index = createIndex(i, 0);
0186             emit dataChanged(index, index);
0187         }
0188     }
0189 
0190     sortTimeZones();
0191 }
0192 
0193 void TimeZoneModel::selectLocalTimeZone()
0194 {
0195     m_data[0].checked = true;
0196 
0197     QModelIndex index = createIndex(0, 0);
0198     emit dataChanged(index, index);
0199 
0200     m_selectedTimeZones << m_data[0].id;
0201     emit selectedTimeZonesChanged();
0202 }
0203 
0204 QHash<int, QByteArray> TimeZoneModel::roleNames() const
0205 {
0206     return QHash<int, QByteArray>({
0207         {TimeZoneIdRole, "timeZoneId"},
0208         {RegionRole, "region"},
0209         {CityRole, "city"},
0210         {CommentRole, "comment"},
0211         {CheckedRole, "checked"},
0212     });
0213 }
0214 
0215 void TimeZoneModel::sortTimeZones()
0216 {
0217     std::sort(m_selectedTimeZones.begin(), m_selectedTimeZones.end(), [this](const QString &a, const QString &b) {
0218         return m_offsetData.value(a) < m_offsetData.value(b);
0219     });
0220 }