@@ -1,5 +1,5 @@
2− * Copyright (C) 2015 Canonical Ltd.
2+ * Copyright (C) 2016 Canonical Ltd.
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
5 * under the terms of the GNU General Public License version 3, as published
@@ -18,17 +18,15 @@
19#include <glib.h>
19#include <glib.h>
20#include <glib-object.h>
20#include <glib-object.h>
21−#include <timezonemap/tz.h>
23#include "LocalePlugin.h"
22#include "LocalePlugin.h"
24#include "timezonemodel.h"
23#include "timezonemodel.h"
26TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
25TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
27 QAbstractListModel(parent),
26 QAbstractListModel(parent),
28− m_workerThread(new TimeZonePopulateWorker())
27+ m_listUpdating(false),
28+ m_cancellable(nullptr)
30− qRegisterMetaType<TzLocationWizard>();
32 m_roleNames[Qt::DisplayRole] = "displayName";
30 m_roleNames[Qt::DisplayRole] = "displayName";
33 m_roleNames[TimeZoneRole] = "timeZone";
31 m_roleNames[TimeZoneRole] = "timeZone";
34 m_roleNames[CityRole] = "city";
32 m_roleNames[CityRole] = "city";
@@ -36,78 +34,52 @@ TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
36 m_roleNames[OffsetRole] = "offset";
34 m_roleNames[OffsetRole] = "offset";
37 m_roleNames[LatitudeRole] = "latitude";
35 m_roleNames[LatitudeRole] = "latitude";
38 m_roleNames[LongitudeRole] = "longitude";
36 m_roleNames[LongitudeRole] = "longitude";
40− QObject::connect(m_workerThread,
41− &TimeZonePopulateWorker::resultReady,
43− &TimeZoneLocationModel::processModelResult);
44− QObject::connect(m_workerThread,
45− &TimeZonePopulateWorker::finished,
47− &TimeZoneLocationModel::store);
48− QObject::connect(m_workerThread,
49− &TimeZonePopulateWorker::finished,
51− &QObject::deleteLater);
56−void TimeZoneLocationModel::init()
59− m_workerThread->start();
62−void TimeZoneLocationModel::store()
64− m_workerThread = nullptr;
68−void TimeZoneLocationModel::processModelResult(const TzLocationWizard &location)
70− m_locations.append(location);
73int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
39int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
75− if (parent.isValid())
41+ if (parent.isValid()) {
43+ } else if (m_filter.isEmpty()) {
44+ return m_countryLocations.count();
77− return m_locations.count();
46+ return m_locations.count();
80QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
50QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
53+ if (m_filter.isEmpty()) {
54+ city = m_countryLocations.value(index.row());
82− if (index.row() >= m_locations.count() || index.row() < 0)
56+ city = m_locations.value(index.row());
83 return QVariant();
59 return QVariant();
85− const TzLocationWizard tz = m_locations.at(index.row());
87− const QString country(tz.full_country.isEmpty() ? tz.country : tz.full_country);
89 switch (role) {
61 switch (role) {
90 case Qt::DisplayRole:
62 case Qt::DisplayRole:
91− if (!tz.state.isEmpty())
92− return QStringLiteral("%1, %2, %3").arg(tz.city).arg(tz.state).arg(country);
63+ return QStringLiteral("%1, %2, %3").arg(geonames_city_get_name(city))
93− else
64+ .arg(geonames_city_get_state(city))
94− return QStringLiteral("%1, %2").arg(tz.city).arg(country);
65+ .arg(geonames_city_get_country(city));
95 case SimpleRole:
66 case SimpleRole:
96− return QStringLiteral("%1, %2").arg(tz.city).arg(country);
67+ return QStringLiteral("%1, %2").arg(geonames_city_get_name(city))
68+ .arg(geonames_city_get_country(city));
97 case TimeZoneRole:
69 case TimeZoneRole:
98− return tz.timezone;
70+ return geonames_city_get_timezone(city);
99 case CountryRole:
71 case CountryRole:
100− return tz.country;
72+ return geonames_city_get_country(city);
101 case CityRole:
73 case CityRole:
102− return tz.city;
74+ return geonames_city_get_name(city);
103 case OffsetRole: {
75 case OffsetRole: {
104− QTimeZone tmp(tz.timezone.toLatin1());
76+ QTimeZone tmp(geonames_city_get_timezone(city));
105 return static_cast<double>(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
77 return static_cast<double>(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
107 case LatitudeRole:
79 case LatitudeRole:
108− return tz.latitude;
80+ return geonames_city_get_latitude(city);
109 case LongitudeRole:
81 case LongitudeRole:
110− return tz.longitude;
82+ return geonames_city_get_longitude(city);
112 qWarning() << Q_FUNC_INFO << "Unknown role";
84 qWarning() << Q_FUNC_INFO << "Unknown role";
113 return QVariant();
85 return QVariant();
@@ -119,110 +91,149 @@ QHash<int, QByteArray> TimeZoneLocationModel::roleNames() const
119 return m_roleNames;
91 return m_roleNames;
122−void TimeZonePopulateWorker::run()
94+void TimeZoneLocationModel::setModel(const QList<GeonamesCity *> &locations)
124− buildCityMap();
96+ beginResetModel();
98+ Q_FOREACH(GeonamesCity *city, m_locations) {
99+ geonames_city_free(city);
102+ m_locations = locations;
127−void TimeZonePopulateWorker::buildCityMap()
106+void TimeZoneLocationModel::filterFinished(GObject *source_object,
129− TzDB *tzdb = tz_load_db();
110+ Q_UNUSED(source_object);
130− GPtrArray *tz_locations = tz_get_locations(tzdb);
111+
131−
112+ g_autofree gint *cities = nullptr;
132− TimeZoneLocationModel::TzLocationWizard tmpTz;
113+ guint cities_len = 0;
133−
114+ g_autoptr(GError) error = nullptr;
134− for (guint i = 0; i < tz_locations->len; ++i) {
115+
135− auto tmp = static_cast<CcTimezoneLocation *>(g_ptr_array_index(tz_locations, i));
116+ cities = geonames_query_cities_finish(res, &cities_len, &error);
136− gchar *en_name, *country, *zone, *state, *full_country;
117+ if (error) {
137− gdouble latitude;
118+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
138− gdouble longitude;
119+ TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
139− g_object_get (tmp, "en_name", &en_name,
120+ g_clear_object(&model->m_cancellable);
140− "country", &country,
121+ model->setListUpdating(false);
141− "zone", &zone,
122+ qWarning() << "Could not filter timezones:" << error->message;
143− "full_country", &full_country,
144− "latitude", &latitude,
145− "longitude", &longitude,
147− // There are empty entries in the DB
148− if (g_strcmp0(en_name, "") != 0) {
149− tmpTz.city = en_name;
150− tmpTz.country = country;
151− tmpTz.timezone = zone;
152− tmpTz.state = state;
153− tmpTz.full_country = full_country;
154− tmpTz.latitude = latitude;
155− tmpTz.longitude = longitude;
157− Q_EMIT (resultReady(tmpTz));
159− g_free (en_name);
124+ return;
163− g_free (full_country);
166− g_ptr_array_free (tz_locations, TRUE);
127+ QList<GeonamesCity *> locations;
129+ for (guint i = 0; i < cities_len; ++i) {
130+ GeonamesCity *city = geonames_get_city(cities[i]);
132+ locations.append(city);
171−TimeZoneFilterModel::TimeZoneFilterModel(QObject *parent)
136+ TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
172− : QSortFilterProxyModel(parent)
137+
173−{
138+ g_clear_object(&model->m_cancellable);
174− setDynamicSortFilter(false);
139+
175− setSortLocaleAware(true);
140+ model->setModel(locations);
176− setSortRole(TimeZoneLocationModel::CityRole);
141+ model->setListUpdating(false);
177− m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
181−bool TimeZoneFilterModel::filterAcceptsRow(int row, const QModelIndex &parentIndex) const
144+bool TimeZoneLocationModel::listUpdating() const
183− if (!sourceModel()) {
184− return true;
146+ return m_listUpdating;
187− if (!m_filter.isEmpty()) { // filtering by freeform text input, cf setFilter(QString)
188− const QString city = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CityRole).toString();
190− if (m_stringMatcher.indexIn(city) == 0) { // match at the beginning of the city name
149+void TimeZoneLocationModel::setListUpdating(bool listUpdating)
192− }
151+ if (m_listUpdating != listUpdating) {
193− } else if (!m_country.isEmpty()) { // filter by country code
152+ m_listUpdating = listUpdating;
194− const QString countryCode = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CountryRole).toString();
153+ Q_EMIT listUpdatingChanged();
195− return m_country.compare(countryCode, Qt::CaseInsensitive) == 0;
201−QString TimeZoneFilterModel::filter() const
157+QString TimeZoneLocationModel::filter() const
203 return m_filter;
159 return m_filter;
206−void TimeZoneFilterModel::setFilter(const QString &filter)
162+void TimeZoneLocationModel::setFilter(const QString &filter)
208 if (filter != m_filter) {
164 if (filter != m_filter) {
209 m_filter = filter;
165 m_filter = filter;
210− m_stringMatcher.setPattern(m_filter);
211 Q_EMIT filterChanged();
166 Q_EMIT filterChanged();
169+ setListUpdating(true);
171+ if (m_cancellable) {
172+ g_cancellable_cancel(m_cancellable);
173+ g_clear_object(&m_cancellable);
176+ setModel(QList<GeonamesCity *>());
178+ if (filter.isEmpty()) {
179+ setListUpdating(false);
183+ m_cancellable = g_cancellable_new();
184+ geonames_query_cities(filter.toUtf8().data(),
185+ GEONAMES_QUERY_DEFAULT,
216−QString TimeZoneFilterModel::country() const
191+QString TimeZoneLocationModel::country() const
218 return m_country;
193 return m_country;
196+static bool citycmp(GeonamesCity *a, GeonamesCity *b)
198+ return geonames_city_get_population(b) < geonames_city_get_population(a);
221−void TimeZoneFilterModel::setCountry(const QString &country)
201+void TimeZoneLocationModel::setCountry(const QString &country)
223 if (m_country == country)
203 if (m_country == country)
226 m_country = country;
206 m_country = country;
208+ Q_FOREACH(GeonamesCity *city, m_countryLocations) {
209+ geonames_city_free(city);
212+ gint num_cities = geonames_get_n_cities();
213+ for (gint i = 0; i < num_cities; i++) {
214+ GeonamesCity *city = geonames_get_city(i);
215+ if (city && m_country == geonames_city_get_country_code(city)) {
216+ m_countryLocations.append(city);
220+ std::sort(m_countryLocations.begin(), m_countryLocations.end(), citycmp);
227 Q_EMIT countryChanged(country);
222 Q_EMIT countryChanged(country);
225+TimeZoneLocationModel::~TimeZoneLocationModel()
227+ if (m_cancellable) {
228+ g_cancellable_cancel(m_cancellable);
229+ g_clear_object(&m_cancellable);
232+ Q_FOREACH(GeonamesCity *city, m_countryLocations) {
233+ geonames_city_free(city);
236+ Q_FOREACH(GeonamesCity *city, m_locations) {
237+ geonames_city_free(city);