Lomiri
timezonemodel.cpp
1 /*
2  * Copyright (C) 2016 Canonical Ltd.
3  *
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
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranties of
10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11  * PURPOSE. See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <QDebug>
18 
19 #include <glib.h>
20 #include <glib-object.h>
21 
22 #include "LocalePlugin.h"
23 #include "timezonemodel.h"
24 
25 TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
26  QAbstractListModel(parent),
27  m_listUpdating(false),
28  m_cancellable(nullptr)
29 {
30  m_roleNames[Qt::DisplayRole] = "displayName";
31  m_roleNames[TimeZoneRole] = "timeZone";
32  m_roleNames[CityRole] = "city";
33  m_roleNames[CountryRole] = "country";
34  m_roleNames[OffsetRole] = "offset";
35  m_roleNames[LatitudeRole] = "latitude";
36  m_roleNames[LongitudeRole] = "longitude";
37 }
38 
39 int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
40 {
41  if (parent.isValid()) {
42  return 0;
43  } else if (m_filter.isEmpty()) {
44  return m_countryLocations.count();
45  } else {
46  return m_locations.count();
47  }
48 }
49 
50 QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
51 {
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)
59  return QVariant();
60 
61  switch (role) {
62  case Qt::DisplayRole:
63  return QStringLiteral("%1, %2, %3").arg(geonames_city_get_name(city),
64  geonames_city_get_state(city),
65  geonames_city_get_country(city));
66  case SimpleRole:
67  return QStringLiteral("%1, %2").arg(geonames_city_get_name(city),
68  geonames_city_get_country(city));
69  case TimeZoneRole:
70  return geonames_city_get_timezone(city);
71  case CountryRole:
72  return geonames_city_get_country(city);
73  case CityRole:
74  return geonames_city_get_name(city);
75  case OffsetRole: {
76  QTimeZone tmp(geonames_city_get_timezone(city));
77  return static_cast<double>(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
78  }
79  case LatitudeRole:
80  return geonames_city_get_latitude(city);
81  case LongitudeRole:
82  return geonames_city_get_longitude(city);
83  default:
84  qWarning() << Q_FUNC_INFO << "Unknown role";
85  return QVariant();
86  }
87 }
88 
89 QHash<int, QByteArray> TimeZoneLocationModel::roleNames() const
90 {
91  return m_roleNames;
92 }
93 
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
158 {
159  return m_filter;
160 }
161 
162 void TimeZoneLocationModel::setFilter(const QString &filter)
163 {
164  if (filter != m_filter) {
165  m_filter = filter;
166  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);
189 }
190 
191 QString TimeZoneLocationModel::country() const
192 {
193  return m_country;
194 }
195 
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)
202 {
203  if (m_country == country)
204  return;
205 
206  beginResetModel();
207 
208  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  Q_EMIT countryChanged(country);
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 }