on xenial help sign in
Bring back custom implementation, so we can use lat/long and country codes
379afa2Michael Terry
CMakeLists.txt+1
@@ -58,6 +58,7 @@ find_package(Qt5Concurrent 5.4 REQUIRED)
58find_package(Qt5Sql 5.4 REQUIRED)
58find_package(Qt5Sql 5.4 REQUIRED)
59
59
60pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13)
60pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13)
61+pkg_search_module(GEONAMES REQUIRED geonames)
61pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
62pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
62pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
63pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
63pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
64pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
debian/control+1
@@ -15,6 +15,7 @@ Build-Depends: cmake,
15 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
15 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
16 libconnectivity-qt1-dev,
16 libconnectivity-qt1-dev,
17 libevdev-dev,
17 libevdev-dev,
18+ libgeonames-dev,
18 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
19 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
19 libgl1-mesa-dri,
20 libgl1-mesa-dri,
20 libgles2-mesa-dev[armhf],
21 libgles2-mesa-dev[armhf],
plugins/Wizard/CMakeLists.txt+3−2
@@ -1,15 +1,16 @@
1−include_directories(${GLIB_INCLUDE_DIRS})
1+include_directories(${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS})
2
2
3add_library(Wizard-qml MODULE
3add_library(Wizard-qml MODULE
4 plugin.cpp
4 plugin.cpp
5 PageList.cpp
5 PageList.cpp
6 System.cpp
6 System.cpp
7 LocalePlugin.cpp
7 LocalePlugin.cpp
8+ timezonemodel.cpp
8 Status.cpp
9 Status.cpp
9)
10)
10
11
11qt5_use_modules(Wizard-qml DBus Qml Concurrent)
12qt5_use_modules(Wizard-qml DBus Qml Concurrent)
12−target_link_libraries(Wizard-qml ${GLIB_LDFLAGS})
13+target_link_libraries(Wizard-qml ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS})
13add_unity8_plugin(Wizard 0.1 Wizard TARGETS Wizard-qml)
14add_unity8_plugin(Wizard 0.1 Wizard TARGETS Wizard-qml)
14
15
15set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
16set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
plugins/Wizard/plugin.cpp+2
@@ -17,6 +17,7 @@
17#include "plugin.h"
17#include "plugin.h"
18#include "PageList.h"
18#include "PageList.h"
19#include "System.h"
19#include "System.h"
20+#include "timezonemodel.h"
20#include "LocalePlugin.h"
21#include "LocalePlugin.h"
21#include "Status.h"
22#include "Status.h"
22
23
@@ -28,5 +29,6 @@ void WizardPlugin::registerTypes(const char *uri)
28 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
29 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
29 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
30 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
30 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
31 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
32+ qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
31 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
33 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
32}
34}
plugins/Wizard/timezonemodel.cppnew file+194
@@ -0,0 +1,194 @@
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+ return m_locations.count();
44+}
45+
46+QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
47+{
48+ if (index.row() >= m_locations.count() || index.row() < 0)
49+ return QVariant();
50+
51+ GeonamesCity *city = m_locations[index.row()];
52+
53+ switch (role) {
54+ case Qt::DisplayRole:
55+ return QVariant(QString("%1, %2, %3").arg(geonames_city_get_name(city))
56+ .arg(geonames_city_get_state(city))
57+ .arg(geonames_city_get_country(city)));
58+ case SimpleRole:
59+ return QVariant(QString("%1, %2").arg(geonames_city_get_name(city))
60+ .arg(geonames_city_get_country(city)));
61+ case TimeZoneRole:
62+ return geonames_city_get_timezone(city);
63+ case CountryRole:
64+ return geonames_city_get_country(city);
65+ case CityRole:
66+ return geonames_city_get_name(city);
67+ case OffsetRole: {
68+ QTimeZone tmp(geonames_city_get_timezone(city));
69+ return static_cast<double>(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
70+ }
71+ case LatitudeRole:
72+ return geonames_city_get_latitude(city);
73+ case LongitudeRole:
74+ return geonames_city_get_longitude(city);
75+ default:
76+ qWarning() << Q_FUNC_INFO << "Unknown role";
77+ return QVariant();
78+ }
79+}
80+
81+QHash<int, QByteArray> TimeZoneLocationModel::roleNames() const
82+{
83+ return m_roleNames;
84+}
85+
86+void TimeZoneLocationModel::filterFinished(GObject *source_object,
87+ GAsyncResult *res,
88+ gpointer user_data)
89+{
90+ Q_UNUSED(source_object);
91+
92+ g_autofree gint *cities = nullptr;
93+ guint cities_len = 0;
94+ g_autoptr(GError) error = nullptr;
95+
96+ cities = geonames_query_cities_finish(res, &cities_len, &error);
97+ if (error) {
98+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
99+ TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
100+ g_clear_object(&model->m_cancellable);
101+ qWarning() << "Could not filter timezones:" << error->message;
102+ }
103+ return;
104+ }
105+
106+ QList<GeonamesCity *> locations;
107+
108+ for (guint i = 0; i < cities_len; ++i) {
109+ GeonamesCity *city = geonames_get_city(cities[i]);
110+ if (city) {
111+ locations.append(city);
112+ }
113+ }
114+
115+ TimeZoneLocationModel *model = static_cast<TimeZoneLocationModel *>(user_data);
116+
117+ g_clear_object(&model->m_cancellable);
118+
119+ model->setModel(locations);
120+ model->setListUpdating(false);
121+}
122+
123+bool TimeZoneLocationModel::listUpdating() const
124+{
125+ return m_listUpdating;
126+}
127+
128+void TimeZoneLocationModel::setListUpdating(bool listUpdating)
129+{
130+ if (m_listUpdating != listUpdating) {
131+ m_listUpdating = listUpdating;
132+ Q_EMIT listUpdatingChanged();
133+ }
134+}
135+
136+QString TimeZoneLocationModel::filter() const
137+{
138+ return m_filter;
139+}
140+
141+void TimeZoneLocationModel::setFilter(const QString &filter)
142+{
143+ if (filter != m_filter) {
144+ m_filter = filter;
145+ Q_EMIT filterChanged();
146+ }
147+
148+ setListUpdating(true);
149+
150+ if (m_cancellable) {
151+ g_cancellable_cancel(m_cancellable);
152+ g_clear_object(&m_cancellable);
153+ }
154+
155+ setModel(QList<GeonamesCity *>());
156+
157+ if (filter.isEmpty()) {
158+ setListUpdating(false);
159+ return;
160+ }
161+
162+ m_cancellable = g_cancellable_new();
163+ geonames_query_cities(filter.toUtf8().data(),
164+ GEONAMES_QUERY_DEFAULT,
165+ m_cancellable,
166+ filterFinished,
167+ this);
168+}
169+
170+QString TimeZoneLocationModel::country() const
171+{
172+ return m_country;
173+}
174+
175+void TimeZoneLocationModel::setCountry(const QString &country)
176+{
177+ if (m_country == country)
178+ return;
179+
180+ m_country = country;
181+ Q_EMIT countryChanged(country);
182+}
183+
184+TimeZoneLocationModel::~TimeZoneLocationModel()
185+{
186+ if (m_cancellable) {
187+ g_cancellable_cancel(m_cancellable);
188+ g_clear_object(&m_cancellable);
189+ }
190+
191+ Q_FOREACH(GeonamesCity *city, m_locations) {
192+ geonames_city_free(city);
193+ }
194+}
plugins/Wizard/timezonemodel.hnew file+79
@@ -0,0 +1,79 @@
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+#ifndef TIMEZONEMODEL_H
18+#define TIMEZONEMODEL_H
19+
20+#include <geonames.h>
21+#include <glib.h>
22+#include <QAbstractListModel>
23+
24+class TimeZoneLocationModel: public QAbstractListModel
25+{
26+ Q_OBJECT
27+ Q_PROPERTY(QString listUpdating READ listUpdating NOTIFY listUpdatingChanged)
28+ Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
29+ Q_PROPERTY(QString country READ country WRITE setCountry NOTIFY countryChanged)
30+ Q_ENUMS(Roles)
31+
32+public:
33+ explicit TimeZoneLocationModel(QObject *parent = nullptr);
34+ ~TimeZoneLocationModel();
35+
36+ enum Roles {
37+ TimeZoneRole = Qt::UserRole + 1,
38+ CityRole,
39+ CountryRole,
40+ SimpleRole,
41+ OffsetRole,
42+ LatitudeRole,
43+ LongitudeRole
44+ };
45+
46+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
47+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
48+ QHash<int, QByteArray> roleNames() const override;
49+
50+ bool listUpdating() const;
51+
52+ QString filter() const;
53+ void setFilter(const QString &filter);
54+
55+ QString country() const;
56+ void setCountry(const QString &country);
57+
58+Q_SIGNALS:
59+ void listUpdatingChanged();
60+ void filterChanged();
61+ void countryChanged(const QString &country);
62+
63+private:
64+ void setModel(QList<GeonamesCity *> locations);
65+ void setListUpdating(bool listUpdating);
66+ static void filterFinished(GObject *source_object,
67+ GAsyncResult *res,
68+ gpointer user_data);
69+
70+
71+ bool m_listUpdating;
72+ QString m_filter;
73+ QString m_country;
74+ GCancellable *m_cancellable;
75+ QHash<int, QByteArray> m_roleNames;
76+ QList<GeonamesCity *> m_locations;
77+};
78+
79+#endif
qml/Wizard/Pages/50-timezone.qml+6−3
@@ -87,7 +87,6 @@ LocalComponents.Page {
87 }
87 }
88
88
89 resetViews();
89 resetViews();
90− timeDatePanel.filter = Qt.binding(function() { return searchField.text; });
91 theme.palette.normal.backgroundText = "#cdcdcd";
90 theme.palette.normal.backgroundText = "#cdcdcd";
92 searchField.forceActiveFocus();
91 searchField.forceActiveFocus();
93 }
92 }
@@ -204,7 +203,11 @@ LocalComponents.Page {
204 objectName: "tzList"
203 objectName: "tzList"
205 clip: true
204 clip: true
206 currentIndex: -1
205 currentIndex: -1
206+ model: TimeZoneModel {
207− model: timeDatePanel.timeZoneModel
207+ id: timeZoneModel
208+ filter: searchField.text
209+ country: i18n.language.split('_')[1].split('.')[0]
210+ }
208 delegate: tzComponent
211 delegate: tzComponent
209 }
212 }
210
213
@@ -212,7 +215,7 @@ LocalComponents.Page {
212 anchors.centerIn: tzList
215 anchors.centerIn: tzList
213 running: tzList.count == 0 &&
216 running: tzList.count == 0 &&
214 searchField.length > 0 &&
217 searchField.length > 0 &&
215− timeDatePanel.listUpdating
218+ timeZoneModel.listUpdating
216 visible: running
219 visible: running
217 }
220 }
218 }
221 }
tests/mocks/Wizard/CMakeLists.txt+3−1
@@ -1,4 +1,5 @@
1include_directories(
1include_directories(
2+ ${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS}
2 ${CMAKE_SOURCE_DIR}/plugins/Wizard
3 ${CMAKE_SOURCE_DIR}/plugins/Wizard
3)
4)
4
5
@@ -6,10 +7,11 @@ add_library(MockWizard-qml MODULE
6 mockplugin.cpp
7 mockplugin.cpp
7 MockSystem.cpp
8 MockSystem.cpp
8 ${CMAKE_SOURCE_DIR}/plugins/Wizard/PageList.cpp
9 ${CMAKE_SOURCE_DIR}/plugins/Wizard/PageList.cpp
10+ ${CMAKE_SOURCE_DIR}/plugins/Wizard/timezonemodel.cpp
9 ${CMAKE_SOURCE_DIR}/plugins/Wizard/LocalePlugin.cpp
11 ${CMAKE_SOURCE_DIR}/plugins/Wizard/LocalePlugin.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/Wizard/Status.cpp
12 ${CMAKE_SOURCE_DIR}/plugins/Wizard/Status.cpp
11)
13)
12
14
13qt5_use_modules(MockWizard-qml DBus Qml Concurrent)
15qt5_use_modules(MockWizard-qml DBus Qml Concurrent)
14−target_link_libraries(MockWizard-qml ${GLIB_LDFLAGS})
16+target_link_libraries(MockWizard-qml ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS})
15add_unity8_mock(Wizard 0.1 Wizard TARGETS MockWizard-qml)
17add_unity8_mock(Wizard 0.1 Wizard TARGETS MockWizard-qml)
tests/mocks/Wizard/mockplugin.cpp+2
@@ -17,6 +17,7 @@
17#include "mockplugin.h"
17#include "mockplugin.h"
18#include "MockSystem.h"
18#include "MockSystem.h"
19#include "PageList.h"
19#include "PageList.h"
20+#include "timezonemodel.h"
20#include "LocalePlugin.h"
21#include "LocalePlugin.h"
21#include "Status.h"
22#include "Status.h"
22
23
@@ -28,5 +29,6 @@ void MockWizardPlugin::registerTypes(const char *uri)
28 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
29 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
29 qmlRegisterSingletonType<MockSystem>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new MockSystem; });
30 qmlRegisterSingletonType<MockSystem>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new MockSystem; });
30 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
31 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
32+ qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
31 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
33 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
32}
34}