@@ -14,94 +14,141 @@
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
18#include <QDebug>
17#include <QDebug>
20+#include <glib-object.h>
21+#include <timezonemap/tz.h>
20#include "LocalePlugin.h"
23#include "LocalePlugin.h"
21#include "timezonemodel.h"
24#include "timezonemodel.h"
24−TimeZoneModel::TimeZoneModel(QObject *parent)
27+TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
25− : QAbstractListModel(parent)
28+ QAbstractListModel(parent),
29+ m_workerThread(new TimeZonePopulateWorker())
31+ qRegisterMetaType<TzLocation>();
27− m_roleNames = {{IdRole, "id"},
33+ m_roleNames[Qt::DisplayRole] = "displayName";
28− {Abbreviation, "abbreviation"},
34+ m_roleNames[TimeZoneRole] = "timeZone";
29− {Country, "country"},
35+ m_roleNames[CityRole] = "city";
30− {CountryCode, "countryCode"},
36+ m_roleNames[CountryRole] = "country";
32− {Comment, "comment"},
38+ QObject::connect(m_workerThread,
33− {Time, "time"}};
39+ &TimeZonePopulateWorker::resultReady,
41+ &TimeZoneLocationModel::processModelResult);
42+ QObject::connect(m_workerThread,
43+ &TimeZonePopulateWorker::finished,
45+ &TimeZoneLocationModel::store);
46+ QObject::connect(m_workerThread,
47+ &TimeZonePopulateWorker::finished,
49+ &QObject::deleteLater);
37−QString TimeZoneModel::selectedZoneId() const
54+void TimeZoneLocationModel::init()
39− return m_selectedZoneId;
56+ beginResetModel();
57+ m_workerThread->start();
42−void TimeZoneModel::setSelectedZoneId(const QString &selectedZoneId)
60+void TimeZoneLocationModel::store()
44− if (m_selectedZoneId == selectedZoneId)
62+ m_workerThread = nullptr;
45− return;
63+ endResetModel();
47− m_selectedZoneId = selectedZoneId;
48− Q_EMIT selectedZoneIdChanged(selectedZoneId);
51−int TimeZoneModel::rowCount(const QModelIndex &parent) const
66+void TimeZoneLocationModel::processModelResult(const TzLocation &location)
53− Q_UNUSED(parent)
68+ m_locations.append(location);
54− return m_zoneIds.count();
57−QVariant TimeZoneModel::data(const QModelIndex &index, int role) const
71+int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
59− if (index.isValid()) {
73+ if (parent.isValid())
60− const QByteArray tzid = m_zoneIds.at(index.row());
74+ return 0;
61− QTimeZone tz(tzid);
75+ return m_locations.count();
64− qWarning() << Q_FUNC_INFO << "Invalid timezone" << tzid;
68− switch (role) {
78+QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
70− return QString(tz.id()); // to let QML compare it effortlessly with a QString
80+ if (index.row() >= m_locations.count() || index.row() < 0)
71− case Abbreviation:
81+ return QVariant();
72− return tz.abbreviation(QDateTime::currentDateTime());
82+
73− case Country:
83+ const TzLocation tz = m_locations.at(index.row());
74− return LocaleAttached::countryToString(tz.country());
84+
75− case CountryCode: {
85+ const QString country(tz.full_country.isEmpty() ? tz.country : tz.full_country);
76− return LocaleAttached::qlocToCountryCode(tz.country());
86+
78− case City: {
88+ case Qt::DisplayRole:
79− const QString cityName = QString::fromUtf8(tzid.split('/').last().replace('_', ' ')); // take the last part, replace _ by a space
89+ if (!tz.state.isEmpty())
80− return cityName;
90+ return QStringLiteral("%1, %2, %3").arg(tz.city).arg(tz.state).arg(country);
82− case Comment:
92+ return QStringLiteral("%1, %2").arg(tz.city).arg(country);
83− return tz.comment();
93+ case SimpleRole:
84− case Time:
94+ return QStringLiteral("%1, %2").arg(tz.city).arg(country);
85− return QDateTime::currentDateTime().toTimeZone(tz).toString("h:mm");
95+ case TimeZoneRole:
86− default:
101+ default:
87− qWarning() << Q_FUNC_INFO << "Unsupported data role" << role;
102+ qWarning() << Q_FUNC_INFO << "Unknown role";
88− break;
103+ return QVariant();
95−QHash<int, QByteArray> TimeZoneModel::roleNames() const
107+QHash<int, QByteArray> TimeZoneLocationModel::roleNames() const
97 return m_roleNames;
109 return m_roleNames;
100−void TimeZoneModel::init()
112+void TimeZonePopulateWorker::run()
102− beginResetModel();
114+ buildCityMap();
103− m_zoneIds = QTimeZone::availableTimeZoneIds();
115+}
104− endResetModel();
116+
117+void TimeZonePopulateWorker::buildCityMap()
119+ TzDB *tzdb = tz_load_db();
120+ GPtrArray *tz_locations = tz_get_locations(tzdb);
122+ TimeZoneLocationModel::TzLocation tmpTz;
124+ for (guint i = 0; i < tz_locations->len; ++i) {
125+ auto tmp = static_cast<CcTimezoneLocation *>(g_ptr_array_index(tz_locations, i));
126+ gchar *en_name, *country, *zone, *state, *full_country;
127+ g_object_get (tmp, "en_name", &en_name,
128+ "country", &country,
131+ "full_country", &full_country,
133+ // There are empty entries in the DB
134+ if (g_strcmp0(en_name, "") != 0) {
135+ tmpTz.city = en_name;
136+ tmpTz.country = country;
137+ tmpTz.timezone = zone;
138+ tmpTz.state = state;
139+ tmpTz.full_country = full_country;
145+ g_free (full_country);
147+ Q_EMIT (resultReady(tmpTz));
150+ g_ptr_array_free (tz_locations, TRUE);
@@ -110,7 +157,7 @@ TimeZoneFilterModel::TimeZoneFilterModel(QObject *parent)
111 setDynamicSortFilter(false);
158 setDynamicSortFilter(false);
112 setSortLocaleAware(true);
159 setSortLocaleAware(true);
113− setSortRole(TimeZoneModel::City);
160+ setSortRole(TimeZoneLocationModel::CityRole);
114 m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
161 m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
@@ -122,14 +169,14 @@ bool TimeZoneFilterModel::filterAcceptsRow(int row, const QModelIndex &parentInd
124 if (!m_filter.isEmpty()) { // filtering by freeform text input, cf setFilter(QString)
171 if (!m_filter.isEmpty()) { // filtering by freeform text input, cf setFilter(QString)
125− const QString city = sourceModel()->index(row, 0, parentIndex).data(TimeZoneModel::City).toString();
172+ const QString city = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CityRole).toString();
126− //const QString country = sourceModel()->index(row, 0, parentIndex).data(TimeZoneModel::Country).toString();
173+ //const QString country = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CountryRole).toString();
128 if (m_stringMatcher.indexIn(city) == 0 /*|| m_stringMatcher.indexIn(country) == 0*/) { // match at the beginning of the city name
175 if (m_stringMatcher.indexIn(city) == 0 /*|| m_stringMatcher.indexIn(country) == 0*/) { // match at the beginning of the city name
129 return true;
176 return true;
131 } else if (!m_country.isEmpty()) { // filter by country code
178 } else if (!m_country.isEmpty()) { // filter by country code
132− const QString countryCode = sourceModel()->index(row, 0, parentIndex).data(TimeZoneModel::CountryCode).toString();
179+ const QString countryCode = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CountryRole).toString();
133 return m_country.compare(countryCode, Qt::CaseInsensitive) == 0;
180 return m_country.compare(countryCode, Qt::CaseInsensitive) == 0;