~mterry/unity8/oobe-revert-geonames

« back to all changes in this revision

Viewing changes to plugins/Wizard/timezonemodel.cpp

  • Committer: Michael Terry
  • Date: 2016-03-22 02:34:59 UTC
  • Revision ID: michael.terry@canonical.com-20160322023459-edeyku0j6jw315yb
Revert use of geonames

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
#include <glib.h>
20
20
#include <glib-object.h>
 
21
#include <timezonemap/tz.h>
21
22
 
22
23
#include "LocalePlugin.h"
23
24
#include "timezonemodel.h"
24
25
 
25
26
TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
26
27
    QAbstractListModel(parent),
27
 
    m_listUpdating(false),
28
 
    m_cancellable(nullptr)
 
28
    m_workerThread(new TimeZonePopulateWorker())
29
29
{
 
30
    qRegisterMetaType<TzLocationWizard>();
 
31
 
30
32
    m_roleNames[Qt::DisplayRole] = "displayName";
31
33
    m_roleNames[TimeZoneRole] = "timeZone";
32
34
    m_roleNames[CityRole] = "city";
34
36
    m_roleNames[OffsetRole] = "offset";
35
37
    m_roleNames[LatitudeRole] = "latitude";
36
38
    m_roleNames[LongitudeRole] = "longitude";
 
39
 
 
40
    QObject::connect(m_workerThread,
 
41
                     &TimeZonePopulateWorker::resultReady,
 
42
                     this,
 
43
                     &TimeZoneLocationModel::processModelResult);
 
44
    QObject::connect(m_workerThread,
 
45
                     &TimeZonePopulateWorker::finished,
 
46
                     this,
 
47
                     &TimeZoneLocationModel::store);
 
48
    QObject::connect(m_workerThread,
 
49
                     &TimeZonePopulateWorker::finished,
 
50
                     m_workerThread,
 
51
                     &QObject::deleteLater);
 
52
 
 
53
    init();
 
54
}
 
55
 
 
56
void TimeZoneLocationModel::init()
 
57
{
 
58
    beginResetModel();
 
59
    m_workerThread->start();
 
60
}
 
61
 
 
62
void TimeZoneLocationModel::store()
 
63
{
 
64
    m_workerThread = nullptr;
 
65
    endResetModel();
 
66
}
 
67
 
 
68
void TimeZoneLocationModel::processModelResult(const TzLocationWizard &location)
 
69
{
 
70
    m_locations.append(location);
37
71
}
38
72
 
39
73
int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
40
74
{
41
 
    if (parent.isValid()) {
 
75
    if (parent.isValid())
42
76
        return 0;
43
 
    } else if (m_filter.isEmpty()) {
44
 
        return m_countryLocations.count();
45
 
    } else {
46
 
        return m_locations.count();
47
 
    }
 
77
    return m_locations.count();
48
78
}
49
79
 
50
80
QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
51
81
{
52
 
    GeonamesCity *city;
53
 
    if (m_filter.isEmpty()) {
54
 
        city = m_countryLocations.value(index.row());
55
 
    } else {
56
 
        city = m_locations.value(index.row());
57
 
    }
58
 
    if (!city)
 
82
    if (index.row() >= m_locations.count() || index.row() < 0)
59
83
        return QVariant();
60
84
 
 
85
    const TzLocationWizard tz = m_locations.at(index.row());
 
86
 
 
87
    const QString country(tz.full_country.isEmpty() ? tz.country : tz.full_country);
 
88
 
61
89
    switch (role) {
62
90
    case Qt::DisplayRole:
63
 
        return QStringLiteral("%1, %2, %3").arg(geonames_city_get_name(city))
64
 
                                           .arg(geonames_city_get_state(city))
65
 
                                           .arg(geonames_city_get_country(city));
 
91
        if (!tz.state.isEmpty())
 
92
            return QStringLiteral("%1, %2, %3").arg(tz.city).arg(tz.state).arg(country);
 
93
        else
 
94
            return QStringLiteral("%1, %2").arg(tz.city).arg(country);
66
95
    case SimpleRole:
67
 
        return QStringLiteral("%1, %2").arg(geonames_city_get_name(city))
68
 
                                       .arg(geonames_city_get_country(city));
 
96
        return QStringLiteral("%1, %2").arg(tz.city).arg(country);
69
97
    case TimeZoneRole:
70
 
        return geonames_city_get_timezone(city);
 
98
        return tz.timezone;
71
99
    case CountryRole:
72
 
        return geonames_city_get_country(city);
 
100
        return tz.country;
73
101
    case CityRole:
74
 
        return geonames_city_get_name(city);
 
102
        return tz.city;
75
103
    case OffsetRole: {
76
 
        QTimeZone tmp(geonames_city_get_timezone(city));
 
104
        QTimeZone tmp(tz.timezone.toLatin1());
77
105
        return static_cast<double>(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
78
106
    }
79
107
    case LatitudeRole:
80
 
        return geonames_city_get_latitude(city);
 
108
        return tz.latitude;
81
109
    case LongitudeRole:
82
 
        return geonames_city_get_longitude(city);
 
110
        return tz.longitude;
83
111
    default:
84
112
        qWarning() << Q_FUNC_INFO << "Unknown role";
85
113
        return QVariant();
91
119
    return m_roleNames;
92
120
}
93
121
 
94
 
void TimeZoneLocationModel::setModel(const QList<GeonamesCity *> &locations)
95
 
{
96
 
    beginResetModel();
97
 
 
98
 
    Q_FOREACH(GeonamesCity *city, m_locations) {
99
 
        geonames_city_free(city);
100
 
    }
101
 
 
102
 
    m_locations = locations;
103
 
    endResetModel();
104
 
}
105
 
 
106
 
void TimeZoneLocationModel::filterFinished(GObject      *source_object,
107
 
                                           GAsyncResult *res,
108
 
                                           gpointer      user_data)
109
 
{
110
 
    Q_UNUSED(source_object);
111
 
 
112
 
    g_autofree gint *cities = nullptr;
113
 
    guint cities_len = 0;
114
 
    g_autoptr(GError) error = nullptr;
115
 
 
116
 
    cities = geonames_query_cities_finish(res, &cities_len, &error);
117
 
    if (error) {
118
 
        if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
119
 
            TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
120
 
            g_clear_object(&model->m_cancellable);
121
 
            model->setListUpdating(false);
122
 
            qWarning() << "Could not filter timezones:" << error->message;
123
 
        }
124
 
        return;
125
 
    }
126
 
 
127
 
    QList<GeonamesCity *> locations;
128
 
 
129
 
    for (guint i = 0; i < cities_len; ++i) {
130
 
        GeonamesCity *city = geonames_get_city(cities[i]);
131
 
        if (city) {
132
 
            locations.append(city);
133
 
        }
134
 
    }
135
 
 
136
 
    TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
137
 
 
138
 
    g_clear_object(&model->m_cancellable);
139
 
 
140
 
    model->setModel(locations);
141
 
    model->setListUpdating(false);
142
 
}
143
 
 
144
 
bool TimeZoneLocationModel::listUpdating() const
145
 
{
146
 
    return m_listUpdating;
147
 
}
148
 
 
149
 
void TimeZoneLocationModel::setListUpdating(bool listUpdating)
150
 
{
151
 
    if (m_listUpdating != listUpdating) {
152
 
        m_listUpdating = listUpdating;
153
 
        Q_EMIT listUpdatingChanged();
154
 
    }
155
 
}
156
 
 
157
 
QString TimeZoneLocationModel::filter() const
 
122
void TimeZonePopulateWorker::run()
 
123
{
 
124
    buildCityMap();
 
125
}
 
126
 
 
127
void TimeZonePopulateWorker::buildCityMap()
 
128
{
 
129
    TzDB *tzdb = tz_load_db();
 
130
    GPtrArray *tz_locations = tz_get_locations(tzdb);
 
131
 
 
132
    TimeZoneLocationModel::TzLocationWizard tmpTz;
 
133
 
 
134
    for (guint i = 0; i < tz_locations->len; ++i) {
 
135
        auto tmp = static_cast<CcTimezoneLocation *>(g_ptr_array_index(tz_locations, i));
 
136
        gchar *en_name, *country, *zone, *state, *full_country;
 
137
        gdouble latitude;
 
138
        gdouble longitude;
 
139
        g_object_get (tmp, "en_name", &en_name,
 
140
                      "country", &country,
 
141
                      "zone", &zone,
 
142
                      "state", &state,
 
143
                      "full_country", &full_country,
 
144
                      "latitude", &latitude,
 
145
                      "longitude", &longitude,
 
146
                      nullptr);
 
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;
 
156
 
 
157
            Q_EMIT (resultReady(tmpTz));
 
158
        }
 
159
        g_free (en_name);
 
160
        g_free (country);
 
161
        g_free (zone);
 
162
        g_free (state);
 
163
        g_free (full_country);
 
164
    }
 
165
 
 
166
    g_ptr_array_free (tz_locations, TRUE);
 
167
    tz_db_free(tzdb);
 
168
}
 
169
 
 
170
 
 
171
TimeZoneFilterModel::TimeZoneFilterModel(QObject *parent)
 
172
    : QSortFilterProxyModel(parent)
 
173
{
 
174
    setDynamicSortFilter(false);
 
175
    setSortLocaleAware(true);
 
176
    setSortRole(TimeZoneLocationModel::CityRole);
 
177
    m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
 
178
    sort(0);
 
179
}
 
180
 
 
181
bool TimeZoneFilterModel::filterAcceptsRow(int row, const QModelIndex &parentIndex) const
 
182
{
 
183
    if (!sourceModel()) {
 
184
        return true;
 
185
    }
 
186
 
 
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();
 
189
 
 
190
        if (m_stringMatcher.indexIn(city) == 0) { // match at the beginning of the city name
 
191
            return true;
 
192
        }
 
193
    } else if (!m_country.isEmpty()) { // filter by country code
 
194
        const QString countryCode = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CountryRole).toString();
 
195
        return m_country.compare(countryCode, Qt::CaseInsensitive) == 0;
 
196
    }
 
197
 
 
198
    return false;
 
199
}
 
200
 
 
201
QString TimeZoneFilterModel::filter() const
158
202
{
159
203
    return m_filter;
160
204
}
161
205
 
162
 
void TimeZoneLocationModel::setFilter(const QString &filter)
 
206
void TimeZoneFilterModel::setFilter(const QString &filter)
163
207
{
164
208
    if (filter != m_filter) {
165
209
        m_filter = filter;
 
210
        m_stringMatcher.setPattern(m_filter);
166
211
        Q_EMIT filterChanged();
167
 
    }
168
 
 
169
 
    setListUpdating(true);
170
 
 
171
 
    if (m_cancellable) {
172
 
        g_cancellable_cancel(m_cancellable);
173
 
        g_clear_object(&m_cancellable);
174
 
    }
175
 
 
176
 
    setModel(QList<GeonamesCity *>());
177
 
 
178
 
    if (filter.isEmpty()) {
179
 
        setListUpdating(false);
180
 
        return;
181
 
    }
182
 
 
183
 
    m_cancellable = g_cancellable_new();
184
 
    geonames_query_cities(filter.toUtf8().data(),
185
 
                          GEONAMES_QUERY_DEFAULT,
186
 
                          m_cancellable,
187
 
                          filterFinished,
188
 
                          this);
 
212
        invalidate();
 
213
    }
189
214
}
190
215
 
191
 
QString TimeZoneLocationModel::country() const
 
216
QString TimeZoneFilterModel::country() const
192
217
{
193
218
    return m_country;
194
219
}
195
220
 
196
 
static bool citycmp(GeonamesCity *a, GeonamesCity *b)
197
 
{
198
 
    return geonames_city_get_population(b) < geonames_city_get_population(a);
199
 
}
200
 
 
201
 
void TimeZoneLocationModel::setCountry(const QString &country)
 
221
void TimeZoneFilterModel::setCountry(const QString &country)
202
222
{
203
223
    if (m_country == country)
204
224
        return;
205
225
 
206
 
    beginResetModel();
207
 
 
208
226
    m_country = country;
209
 
 
210
 
    Q_FOREACH(GeonamesCity *city, m_countryLocations) {
211
 
        geonames_city_free(city);
212
 
    }
213
 
    m_countryLocations.clear();
214
 
 
215
 
    gint num_cities = geonames_get_n_cities();
216
 
    for (gint i = 0; i < num_cities; i++) {
217
 
        GeonamesCity *city = geonames_get_city(i);
218
 
        if (city && m_country == geonames_city_get_country_code(city)) {
219
 
            m_countryLocations.append(city);
220
 
        }
221
 
    }
222
 
 
223
 
    std::sort(m_countryLocations.begin(), m_countryLocations.end(), citycmp);
224
 
 
225
 
    endResetModel();
226
 
 
227
227
    Q_EMIT countryChanged(country);
228
228
}
229
 
 
230
 
TimeZoneLocationModel::~TimeZoneLocationModel()
231
 
{
232
 
    if (m_cancellable) {
233
 
        g_cancellable_cancel(m_cancellable);
234
 
        g_clear_object(&m_cancellable);
235
 
    }
236
 
 
237
 
    Q_FOREACH(GeonamesCity *city, m_countryLocations) {
238
 
        geonames_city_free(city);
239
 
    }
240
 
 
241
 
    Q_FOREACH(GeonamesCity *city, m_locations) {
242
 
        geonames_city_free(city);
243
 
    }
244
 
}