File indexing completed on 2025-01-26 05:07:37

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 <QDBusConnection>
0013 #include <QStringMatcher>
0014 #include <QTimeZone>
0015 
0016 TimeZoneFilterProxy::TimeZoneFilterProxy(QObject *parent)
0017     : QSortFilterProxyModel(parent)
0018 {
0019     m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
0020 }
0021 
0022 bool TimeZoneFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0023 {
0024     if (!sourceModel() || (m_filterString.isEmpty() && !m_onlyShowChecked)) {
0025         return true;
0026     }
0027 
0028     const bool checked = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::CheckedRole).toBool();
0029     if (m_onlyShowChecked && !checked) {
0030         return false;
0031     }
0032 
0033     const QString city = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::CityRole).toString();
0034     const QString region = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::RegionRole).toString();
0035     const QString comment = sourceModel()->index(source_row, 0, source_parent).data(TimeZoneModel::CommentRole).toString();
0036 
0037     if (m_stringMatcher.indexIn(city) != -1 || m_stringMatcher.indexIn(region) != -1 || m_stringMatcher.indexIn(comment) != -1) {
0038         return true;
0039     }
0040 
0041     return false;
0042 }
0043 
0044 void TimeZoneFilterProxy::setFilterString(const QString &filterString)
0045 {
0046     m_filterString = filterString;
0047     m_stringMatcher.setPattern(filterString);
0048     Q_EMIT filterStringChanged();
0049     invalidateFilter();
0050 }
0051 
0052 void TimeZoneFilterProxy::setOnlyShowChecked(const bool show)
0053 {
0054     if (m_onlyShowChecked == show) {
0055         return;
0056     }
0057     m_onlyShowChecked = show;
0058     Q_EMIT onlyShowCheckedChanged();
0059     invalidateFilter();
0060 }
0061 
0062 //=============================================================================
0063 
0064 TimeZoneModel::TimeZoneModel(QObject *parent)
0065     : QAbstractListModel(parent)
0066     , m_timezonesI18n(new TimezonesI18n(this))
0067 {
0068     update();
0069 
0070     QDBusConnection::sessionBus().connect(QString(),
0071                                           QStringLiteral("/org/kde/kcmshell_clock"), //
0072                                           QStringLiteral("org.kde.kcmshell_clock"),
0073                                           QStringLiteral("clockUpdated"),
0074                                           this,
0075                                           SLOT(slotUpdate()));
0076 }
0077 
0078 TimeZoneModel::~TimeZoneModel()
0079 {
0080 }
0081 
0082 int TimeZoneModel::rowCount(const QModelIndex &parent) const
0083 {
0084     Q_UNUSED(parent);
0085     return m_data.count();
0086 }
0087 
0088 QVariant TimeZoneModel::data(const QModelIndex &index, int role) const
0089 {
0090     if (index.isValid()) {
0091         TimeZoneData currentData = m_data.at(index.row());
0092 
0093         switch (role) {
0094         case TimeZoneIdRole:
0095             return currentData.id;
0096         case RegionRole:
0097             return currentData.region;
0098         case CityRole:
0099             return currentData.city;
0100         case CommentRole:
0101             return currentData.comment;
0102         case CheckedRole:
0103             return currentData.checked;
0104         case IsLocalTimeZoneRole:
0105             return currentData.isLocalTimeZone;
0106         }
0107     }
0108 
0109     return QVariant();
0110 }
0111 
0112 bool TimeZoneModel::setData(const QModelIndex &index, const QVariant &value, int role)
0113 {
0114     if (!index.isValid() || value.isNull()) {
0115         return false;
0116     }
0117 
0118     if (role == CheckedRole) {
0119         m_data[index.row()].checked = value.toBool();
0120         Q_EMIT dataChanged(index, index);
0121 
0122         if (m_data[index.row()].checked) {
0123             m_selectedTimeZones.append(m_data[index.row()].id);
0124             m_offsetData.insert(m_data[index.row()].id, m_data[index.row()].offsetFromUtc);
0125         } else {
0126             m_selectedTimeZones.removeAll(m_data[index.row()].id);
0127             m_offsetData.remove(m_data[index.row()].id);
0128         }
0129 
0130         sortTimeZones();
0131 
0132         Q_EMIT selectedTimeZonesChanged();
0133         return true;
0134     }
0135 
0136     return false;
0137 }
0138 
0139 void TimeZoneModel::update()
0140 {
0141     beginResetModel();
0142     m_data.clear();
0143 
0144     TimeZoneData local;
0145     local.isLocalTimeZone = true;
0146     local.id = QStringLiteral("Local");
0147     local.region = i18nc("This means \"Local Timezone\"", "Local");
0148     local.city = m_timezonesI18n->i18nCity(QTimeZone::systemTimeZoneId());
0149     local.comment = i18n("System's local time zone");
0150     local.checked = false;
0151 
0152     m_data.append(local);
0153 
0154     const QList<QByteArray> systemTimeZones = QTimeZone::availableTimeZoneIds();
0155 
0156     for (const auto &id : systemTimeZones) {
0157         const QTimeZone zone(id);
0158 
0159         const QString continent = m_timezonesI18n->i18nContinents(QString::fromUtf8(QByteArrayView(id).mid(0, id.indexOf('/'))));
0160 
0161         TimeZoneData newData;
0162         newData.isLocalTimeZone = false;
0163         newData.id = id;
0164         newData.city = m_timezonesI18n->i18nCity(id);
0165         newData.region = zone.country() == QLocale::AnyCountry ? QString() : continent + QLatin1Char('/') + m_timezonesI18n->i18nCountry(zone.country());
0166         newData.comment = zone.comment();
0167         newData.checked = false;
0168         newData.offsetFromUtc = zone.offsetFromUtc(QDateTime::currentDateTimeUtc());
0169         m_data.append(newData);
0170     }
0171 
0172     std::sort(m_data.begin() + 1, m_data.end(), [](const TimeZoneData &left, const TimeZoneData &right) {
0173         return left.city.compare(right.city) < 0;
0174     });
0175 
0176     endResetModel();
0177 }
0178 
0179 void TimeZoneModel::setSelectedTimeZones(const QStringList &selectedTimeZones)
0180 {
0181     m_selectedTimeZones = selectedTimeZones;
0182     for (int i = 0; i < m_data.size(); i++) {
0183         if (m_selectedTimeZones.contains(m_data.at(i).id)) {
0184             m_data[i].checked = true;
0185             m_offsetData.insert(m_data[i].id, m_data[i].offsetFromUtc);
0186 
0187             QModelIndex index = createIndex(i, 0);
0188             Q_EMIT dataChanged(index, index);
0189         }
0190     }
0191 
0192     sortTimeZones();
0193 }
0194 
0195 void TimeZoneModel::selectLocalTimeZone()
0196 {
0197     m_data[0].checked = true;
0198     Q_EMIT dataChanged(index(0, 0), index(0, 0), QList<int>{CheckedRole});
0199 }
0200 
0201 QString TimeZoneModel::localTimeZoneCity()
0202 {
0203     return m_data[0].city;
0204 }
0205 
0206 void TimeZoneModel::slotUpdate()
0207 {
0208     update();
0209     setProperty("selectedTimeZones", m_selectedTimeZones);
0210 }
0211 
0212 QHash<int, QByteArray> TimeZoneModel::roleNames() const
0213 {
0214     return QHash<int, QByteArray>({
0215         {TimeZoneIdRole, "timeZoneId"},
0216         {RegionRole, "region"},
0217         {CityRole, "city"},
0218         {CommentRole, "comment"},
0219         {CheckedRole, "checked"},
0220         {IsLocalTimeZoneRole, "isLocalTimeZone"},
0221     });
0222 }
0223 
0224 void TimeZoneModel::sortTimeZones()
0225 {
0226     std::sort(m_selectedTimeZones.begin(), m_selectedTimeZones.end(), [this](const QString &a, const QString &b) {
0227         return m_offsetData.value(a) < m_offsetData.value(b);
0228     });
0229 }