File indexing completed on 2024-04-21 14:55:49

0001 /*
0002     Copyright (C) 2005, S.R.Haque <srhaque@iee.org>.
0003     Copyright (C) 2009, David Faure <faure@kde.org>
0004     This file is part of the KDE project
0005 
0006     This library is free software; you can redistribute it and/or
0007     modify it under the terms of the GNU Library General Public
0008     License version 2, as published by the Free Software Foundation.
0009 
0010     This library is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013     Library General Public License for more details.
0014 
0015     You should have received a copy of the GNU Library General Public License
0016     along with this library; see the file COPYING.LIB.  If not, write to
0017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018     Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "k4timezonewidget.h"
0022 
0023 #include <QDebug>
0024 #include <QFile>
0025 #include <QPixmap>
0026 
0027 #include <klocale.h>
0028 #include <klocalizedstring.h>
0029 #include <ksystemtimezone.h>
0030 #include <ktimezone.h>
0031 
0032 class Q_DECL_HIDDEN K4TimeZoneWidget::Private
0033 {
0034 public:
0035     Private() : itemsCheckable(false), singleSelection(true) {}
0036 
0037     enum Columns {
0038         CityColumn = 0,
0039         RegionColumn,
0040         CommentColumn
0041     };
0042 
0043     enum Roles {
0044         ZoneRole = Qt::UserRole + 0xF3A3CB1
0045     };
0046 
0047     bool itemsCheckable;
0048     bool singleSelection;
0049 };
0050 
0051 static bool localeLessThan(const QString &a, const QString &b)
0052 {
0053     return QString::localeAwareCompare(a, b) < 0;
0054 }
0055 
0056 K4TimeZoneWidget::K4TimeZoneWidget(QWidget *parent, KTimeZones *db)
0057     : QTreeWidget(parent),
0058       d(new K4TimeZoneWidget::Private)
0059 {
0060     // If the user did not provide a timezone database, we'll use the system default.
0061     setRootIsDecorated(false);
0062     setHeaderLabels(QStringList() << i18nc("Define an area in the time zone, like a town area", "Area") << i18nc("Time zone", "Region") << i18n("Comment"));
0063 
0064     // Collect zones by localized city names, so that they can be sorted properly.
0065     QStringList cities;
0066     QHash<QString, KTimeZone> zonesByCity;
0067 
0068     if (!db) {
0069         db = KSystemTimeZones::timeZones();
0070 
0071         // add UTC to the defaults default
0072         KTimeZone utc = KTimeZone::utc();
0073         cities.append(utc.name());
0074         zonesByCity.insert(utc.name(), utc);
0075     }
0076 
0077     const KTimeZones::ZoneMap zones = db->zones();
0078     for (KTimeZones::ZoneMap::ConstIterator it = zones.begin(); it != zones.end(); ++it) {
0079         const KTimeZone zone = it.value();
0080         const QString continentCity = displayName(zone);
0081         const int separator = continentCity.lastIndexOf('/');
0082         // Make up the localized key that will be used for sorting.
0083         // Example: i18n(Asia/Tokyo) -> key = "i18n(Tokyo)|i18n(Asia)|Asia/Tokyo"
0084         // The zone name is appended to ensure unicity even with equal translations (#174918)
0085         const QString key = continentCity.mid(separator + 1) + '|'
0086                             + continentCity.left(separator) + '|' + zone.name();
0087         cities.append(key);
0088         zonesByCity.insert(key, zone);
0089     }
0090     std::sort(cities.begin(), cities.end(), localeLessThan);
0091 
0092     foreach (const QString &key, cities) {
0093         const KTimeZone zone = zonesByCity.value(key);
0094         const QString tzName = zone.name();
0095         QString comment = zone.comment();
0096 
0097         if (!comment.isEmpty()) {
0098             comment = i18n(comment.toUtf8());
0099         }
0100 
0101         // Convert:
0102         //
0103         //  "Europe/London", "GB" -> "London", "Europe/GB".
0104         //  "UTC",           ""   -> "UTC",    "".
0105         QStringList continentCity = displayName(zone).split('/');
0106 
0107         QTreeWidgetItem *listItem = new QTreeWidgetItem(this);
0108         listItem->setText(Private::CityColumn, continentCity[ continentCity.count() - 1 ]);
0109         QString countryName = KLocale::global()->countryCodeToName(zone.countryCode());
0110         if (countryName.isEmpty()) {
0111             continentCity[ continentCity.count() - 1 ] = zone.countryCode();
0112         } else {
0113             continentCity[ continentCity.count() - 1 ] = countryName;
0114         }
0115 
0116         listItem->setText(Private::RegionColumn, continentCity.join(QChar('/')));
0117         listItem->setText(Private::CommentColumn, comment);
0118         listItem->setData(Private::CityColumn, Private::ZoneRole, tzName);   // store complete path in custom role
0119 
0120         // Locate the flag from share/kf5/locale/countries/%1/flag.png
0121         QString flag = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("kf5/locale/countries/%1/flag.png").arg(zone.countryCode().toLower()));
0122         if (QFile::exists(flag)) {
0123             listItem->setIcon(Private::RegionColumn, QPixmap(flag));
0124         }
0125     }
0126 }
0127 
0128 K4TimeZoneWidget::~K4TimeZoneWidget()
0129 {
0130     delete d;
0131 }
0132 
0133 void K4TimeZoneWidget::setItemsCheckable(bool enable)
0134 {
0135     d->itemsCheckable = enable;
0136     const int count = topLevelItemCount();
0137     for (int row = 0; row < count; ++row) {
0138         QTreeWidgetItem *listItem = topLevelItem(row);
0139         listItem->setCheckState(Private::CityColumn, Qt::Unchecked);
0140     }
0141     QTreeWidget::setSelectionMode(QAbstractItemView::NoSelection);
0142 }
0143 
0144 bool K4TimeZoneWidget::itemsCheckable() const
0145 {
0146     return d->itemsCheckable;
0147 }
0148 
0149 QString K4TimeZoneWidget::displayName(const KTimeZone &zone)
0150 {
0151     return i18n(zone.name().toUtf8()).replace('_', ' ');
0152 }
0153 
0154 QStringList K4TimeZoneWidget::selection() const
0155 {
0156     QStringList selection;
0157 
0158     // Loop through all entries.
0159     // Do not use selectedItems() because it skips hidden items, making it
0160     // impossible to use a KTreeWidgetSearchLine.
0161     // There is no QTreeWidgetItemConstIterator, hence the const_cast :/
0162     QTreeWidgetItemIterator it(const_cast<K4TimeZoneWidget *>(this), d->itemsCheckable ? QTreeWidgetItemIterator::Checked : QTreeWidgetItemIterator::Selected);
0163     for (; *it; ++it) {
0164         selection.append((*it)->data(Private::CityColumn, Private::ZoneRole).toString());
0165     }
0166 
0167     return selection;
0168 }
0169 
0170 void K4TimeZoneWidget::setSelected(const QString &zone, bool selected)
0171 {
0172     bool found = false;
0173 
0174     // The code was using findItems( zone, Qt::MatchExactly, Private::ZoneColumn )
0175     // previously, but the underlying model only has 3 columns, the "hidden" column
0176     // wasn't available in there.
0177 
0178     if (!d->itemsCheckable) {
0179         // Runtime compatibility for < 4.3 apps, which don't call the setMultiSelection reimplementation.
0180         d->singleSelection = (QTreeWidget::selectionMode() == QAbstractItemView::SingleSelection);
0181     }
0182 
0183     // Loop through all entries.
0184     const int rowCount = model()->rowCount(QModelIndex());
0185     for (int row = 0; row < rowCount; ++row) {
0186         const QModelIndex index = model()->index(row, Private::CityColumn);
0187         const QString tzName = index.data(Private::ZoneRole).toString();
0188         if (tzName == zone) {
0189 
0190             if (d->singleSelection && selected) {
0191                 clearSelection();
0192             }
0193 
0194             if (d->itemsCheckable) {
0195                 QTreeWidgetItem *listItem = itemFromIndex(index);
0196                 listItem->setCheckState(Private::CityColumn, selected ? Qt::Checked : Qt::Unchecked);
0197             } else {
0198                 selectionModel()->select(index, selected ? (QItemSelectionModel::Select | QItemSelectionModel::Rows) : (QItemSelectionModel::Deselect | QItemSelectionModel::Rows));
0199             }
0200 
0201             // Ensure the selected item is visible as appropriate.
0202             scrollTo(index);
0203 
0204             found = true;
0205 
0206             if (d->singleSelection && selected) {
0207                 break;
0208             }
0209         }
0210     }
0211 
0212     if (!found) {
0213         qDebug() << "No such zone: " << zone;
0214     }
0215 }
0216 
0217 void K4TimeZoneWidget::clearSelection()
0218 {
0219     if (d->itemsCheckable) {
0220         // Un-select all items
0221         const int rowCount = model()->rowCount(QModelIndex());
0222         for (int row = 0; row < rowCount; ++row) {
0223             const QModelIndex index = model()->index(row, 0);
0224             QTreeWidgetItem *listItem = itemFromIndex(index);
0225             listItem->setCheckState(Private::CityColumn, Qt::Unchecked);
0226         }
0227     } else {
0228         QTreeWidget::clearSelection();
0229     }
0230 }
0231 
0232 void K4TimeZoneWidget::setSelectionMode(QAbstractItemView::SelectionMode mode)
0233 {
0234     d->singleSelection = (mode == QAbstractItemView::SingleSelection);
0235     if (!d->itemsCheckable) {
0236         QTreeWidget::setSelectionMode(mode);
0237     }
0238 }
0239 
0240 QAbstractItemView::SelectionMode K4TimeZoneWidget::selectionMode() const
0241 {
0242     if (d->itemsCheckable) {
0243         return d->singleSelection ? QTreeWidget::SingleSelection : QTreeWidget::MultiSelection;
0244     } else {
0245         return QTreeWidget::selectionMode();
0246     }
0247 }
0248 
0249 #include "moc_k4timezonewidget.cpp"