Merge lp:~larsu/unity-control-center/datetime-use-geonames into lp:unity-control-center

Proposed by Lars Karlitski on 2015-12-11
Status: Merged
Approved by: Sebastien Bacher on 2015-12-11
Approved revision: 12832
Merged at revision: 12841
Proposed branch: lp:~larsu/unity-control-center/datetime-use-geonames
Merge into: lp:unity-control-center
Diff against target: 305 lines (+165/-33)
3 files modified
configure.ac (+1/-1)
debian/control (+1/-0)
panels/datetime/datetime-prefs.c (+163/-32)
To merge this branch: bzr merge lp:~larsu/unity-control-center/datetime-use-geonames
Reviewer Review Type Date Requested Status
Sebastien Bacher 2015-12-11 Approve on 2015-12-11
Review via email: mp+280265@code.launchpad.net

Commit message

datetime: use libgeonames for city completion

This makes looking up cities much faster, doesn't require an internet connection, and insulates us from problems in the web service.

Description of the change

datetime: use libgeonames for city completion

This makes looking up cities much faster, doesn't require an internet connection, and insulates us from problems in the web service.

To post a comment you must log in.
Sebastien Bacher (seb128) wrote :

nice!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2015-11-03 08:25:57 +0000
3+++ configure.ac 2015-12-11 09:23:36 +0000
4@@ -136,7 +136,7 @@
5 PKG_CHECK_MODULES(SHELL, $COMMON_MODULES libgnome-menu-3.0 gio-unix-2.0 x11)
6 PKG_CHECK_MODULES(APPEARANCE_PANEL, $COMMON_MODULES libxml-2.0 gnome-desktop-3.0
7 gdk-pixbuf-2.0 >= $GDKPIXBUF_REQUIRED_VERSION)
8-PKG_CHECK_MODULES(DATETIME_PANEL, $COMMON_MODULES timezonemap
9+PKG_CHECK_MODULES(DATETIME_PANEL, $COMMON_MODULES timezonemap geonames
10 polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION)
11 PKG_CHECK_MODULES(DISPLAY_PANEL, $COMMON_MODULES libunity-settings-daemon x11)
12 PKG_CHECK_MODULES(INFO_PANEL, $COMMON_MODULES libgtop-2.0 gl x11
13
14=== modified file 'debian/control'
15--- debian/control 2015-11-03 08:25:57 +0000
16+++ debian/control 2015-12-11 09:23:36 +0000
17@@ -25,6 +25,7 @@
18 libdbus-1-dev (>= 0.32),
19 libdbus-glib-1-dev (>= 0.32),
20 libgdk-pixbuf2.0-dev (>= 2.23.0),
21+ libgeonames-dev,
22 libglib2.0-dev (>= 2.31.2),
23 libgnome-bluetooth-dev (>= 3.3.4),
24 libgnome-desktop-3-dev (>= 3.5.91),
25
26=== modified file 'panels/datetime/datetime-prefs.c'
27--- panels/datetime/datetime-prefs.c 2014-03-06 21:21:46 +0000
28+++ panels/datetime/datetime-prefs.c 2015-12-11 09:23:36 +0000
29@@ -33,7 +33,7 @@
30 #include <polkit/polkit.h>
31 #include <shell/cc-panel.h>
32 #include <timezonemap/cc-timezone-map.h>
33-#include <timezonemap/timezone-completion.h>
34+#include <geonames.h>
35
36 #include "settings-shared.h"
37 #include "utils.h"
38@@ -41,6 +41,7 @@
39
40 #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/ui/datetime/datetime-dialog.ui"
41
42+GType indicator_datetime_panel_get_type (void);
43 #define INDICATOR_DATETIME_TYPE_PANEL indicator_datetime_panel_get_type()
44
45 typedef struct _IndicatorDatetimePanel IndicatorDatetimePanel;
46@@ -68,7 +69,9 @@
47 gboolean changing_time;
48 GtkWidget * loc_dlg;
49 GSettings * settings;
50- CcTimezoneCompletion * completion;
51+ GtkListStore * cities_model;
52+ guint typing_timeout;
53+ GCancellable * cancellable;
54 };
55
56 struct _IndicatorDatetimePanelClass
57@@ -76,6 +79,14 @@
58 CcPanelClass parent_class;
59 };
60
61+enum
62+{
63+ COL_NAME,
64+ COL_ADMIN1,
65+ COL_COUNTRY,
66+ COL_ZONE
67+};
68+
69 G_DEFINE_DYNAMIC_TYPE (IndicatorDatetimePanel, indicator_datetime_panel, CC_TYPE_PANEL)
70
71 /* Turns the boolean property into a string gsettings */
72@@ -580,30 +591,10 @@
73 const gchar * name, * zone;
74
75 gtk_tree_model_get (model, iter,
76- CC_TIMEZONE_COMPLETION_NAME, &name,
77- CC_TIMEZONE_COMPLETION_ZONE, &zone,
78+ COL_NAME, &name,
79+ COL_ZONE, &zone,
80 -1);
81
82- if (zone == NULL || zone[0] == 0) {
83- const gchar * strlon, * strlat;
84- gdouble lon = 0.0, lat = 0.0;
85-
86- gtk_tree_model_get (model, iter,
87- CC_TIMEZONE_COMPLETION_LONGITUDE, &strlon,
88- CC_TIMEZONE_COMPLETION_LATITUDE, &strlat,
89- -1);
90-
91- if (strlon != NULL && strlon[0] != 0) {
92- lon = g_ascii_strtod(strlon, NULL);
93- }
94-
95- if (strlat != NULL && strlat[0] != 0) {
96- lat = g_ascii_strtod(strlat, NULL);
97- }
98-
99- zone = cc_timezone_map_get_timezone_at_coords (self->priv->tzmap, lon, lat);
100- }
101-
102 gchar * tz_name = g_strdup_printf ("%s %s", zone, name);
103 g_settings_set_string (self->priv->settings, SETTINGS_TIMEZONE_NAME_S, tz_name);
104 g_free (tz_name);
105@@ -614,6 +605,48 @@
106 }
107
108 static gboolean
109+match_city_func (GtkEntryCompletion *completion,
110+ const gchar *key,
111+ GtkTreeIter *iter,
112+ gpointer user_data)
113+{
114+ /* the model only ever contains cities that already match the text in
115+ * the location entry, so it's safe to always accept matches */
116+ return TRUE;
117+}
118+
119+static void
120+cell_data_func (GtkCellLayout *cell_layout,
121+ GtkCellRenderer *cell,
122+ GtkTreeModel *tree_model,
123+ GtkTreeIter *iter,
124+ gpointer user_data)
125+{
126+ gchar *name;
127+ gchar *admin1;
128+ gchar *country;
129+ gchar *text;
130+
131+ gtk_tree_model_get (tree_model, iter,
132+ COL_NAME, &name,
133+ COL_ADMIN1, &admin1,
134+ COL_COUNTRY, &country,
135+ -1);
136+
137+ if (admin1[0] != '\0')
138+ text = g_strdup_printf ("%s <small>(%s, %s)</small>", name, admin1, country);
139+ else
140+ text = g_strdup_printf ("%s <small>(%s)</small>", name, country);
141+
142+ g_object_set (cell, "markup", text, NULL);
143+
144+ g_free (name);
145+ g_free (admin1);
146+ g_free (country);
147+ g_free (text);
148+}
149+
150+static gboolean
151 entry_focus_out (GtkEntry * entry,
152 GdkEventFocus * event G_GNUC_UNUSED,
153 IndicatorDatetimePanel * self)
154@@ -642,10 +675,86 @@
155 }
156
157 static void
158+query_cities_callback (GObject *source_object,
159+ GAsyncResult *result,
160+ gpointer user_data)
161+{
162+ IndicatorDatetimePanel *self = user_data;
163+ gint *indices;
164+ guint len;
165+ guint i;
166+ GtkEntryCompletion *completion;
167+
168+ gtk_list_store_clear (self->priv->cities_model);
169+
170+ indices = geonames_query_cities_finish (result, &len, NULL);
171+ if (indices == NULL)
172+ return;
173+
174+ for (i = 0; i < len; i++)
175+ {
176+ GeonamesCity *city;
177+ GtkTreeIter iter;
178+
179+ city = geonames_get_city (indices[i]);
180+
181+ gtk_list_store_append (self->priv->cities_model, &iter);
182+ gtk_list_store_set (self->priv->cities_model, &iter,
183+ COL_NAME, geonames_city_get_name (city),
184+ COL_ADMIN1, geonames_city_get_state (city),
185+ COL_COUNTRY, geonames_city_get_country (city),
186+ COL_ZONE, geonames_city_get_timezone (city),
187+ -1);
188+
189+ geonames_city_free (city);
190+ }
191+
192+ completion = gtk_entry_get_completion (GTK_ENTRY (self->priv->tz_entry));
193+ gtk_entry_completion_complete (completion);
194+
195+ g_free (indices);
196+}
197+
198+static gboolean
199+typing_timeout (gpointer user_data)
200+{
201+ IndicatorDatetimePanel *self = user_data;
202+ const gchar *query;
203+
204+ if (self->priv->cancellable)
205+ {
206+ g_cancellable_cancel (self->priv->cancellable);
207+ g_object_unref (self->priv->cancellable);
208+ }
209+
210+ self->priv->cancellable = g_cancellable_new ();
211+ query = gtk_entry_get_text (GTK_ENTRY (self->priv->tz_entry));
212+
213+ geonames_query_cities (query, GEONAMES_QUERY_DEFAULT, self->priv->cancellable, query_cities_callback, self);
214+
215+ self->priv->typing_timeout = 0;
216+ return G_SOURCE_REMOVE;
217+}
218+
219+static void
220+entry_changed (GtkEntry *entry,
221+ gpointer user_data)
222+{
223+ IndicatorDatetimePanel *self = user_data;
224+
225+ if (self->priv->typing_timeout > 0)
226+ g_source_remove (self->priv->typing_timeout);
227+
228+ self->priv->typing_timeout = g_timeout_add (100, typing_timeout, self);
229+}
230+
231+static void
232 indicator_datetime_panel_init (IndicatorDatetimePanel * self)
233 {
234 GError * error;
235 GSettings * conf;
236+ GtkEntryCompletion *completion;
237+ GtkCellRenderer *renderer;
238
239 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
240 INDICATOR_DATETIME_TYPE_PANEL,
241@@ -685,9 +794,21 @@
242 cc_timezone_map_set_watermark (self->priv->tzmap, "Geonames.org");
243
244 /* And completion entry */
245- self->priv->completion = cc_timezone_completion_new ();
246- cc_timezone_completion_watch_entry (self->priv->completion, GTK_ENTRY (WIG ("timezoneEntry")));
247- g_signal_connect (self->priv->completion, "match-selected", G_CALLBACK (timezone_selected), self);
248+ self->priv->cities_model = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
249+
250+ completion = gtk_entry_completion_new ();
251+ gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (self->priv->cities_model));
252+ gtk_entry_completion_set_minimum_key_length (completion, 2);
253+ g_object_set (G_OBJECT (completion), "text-column", COL_NAME, NULL); /* use this, because the setter adds a renderer */
254+ gtk_entry_completion_set_match_func (completion, match_city_func, NULL, NULL);
255+ g_signal_connect (completion, "match-selected", G_CALLBACK (timezone_selected), self);
256+
257+ renderer = gtk_cell_renderer_text_new ();
258+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
259+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion), renderer, cell_data_func, NULL, NULL);
260+
261+ gtk_entry_set_completion (GTK_ENTRY (WIG ("timezoneEntry")), completion);
262+ g_signal_connect (WIG ("timezoneEntry"), "changed", G_CALLBACK (entry_changed), self);
263 g_signal_connect (WIG ("timezoneEntry"), "focus-out-event", G_CALLBACK (entry_focus_out), self);
264
265 /* Set up settings bindings */
266@@ -767,6 +888,8 @@
267
268 gtk_widget_show_all (panel);
269 gtk_container_add (GTK_CONTAINER (self), panel);
270+
271+ g_object_unref (completion);
272 }
273
274 static void
275@@ -794,11 +917,6 @@
276 priv->save_time_id = 0;
277 }
278
279- if (priv->completion) {
280- cc_timezone_completion_watch_entry (priv->completion, NULL);
281- g_clear_object (&priv->completion);
282- }
283-
284 if (priv->tz_entry) {
285 gtk_widget_destroy (priv->tz_entry);
286 priv->tz_entry = NULL;
287@@ -814,6 +932,19 @@
288 priv->date_spin = NULL;
289 }
290
291+ g_clear_object (&priv->cities_model);
292+
293+ if (priv->typing_timeout) {
294+ g_source_remove (priv->typing_timeout);
295+ priv->typing_timeout = 0;
296+ }
297+
298+ if (self->priv->cancellable)
299+ {
300+ g_cancellable_cancel (self->priv->cancellable);
301+ g_clear_object (&self->priv->cancellable);
302+ }
303+
304 G_OBJECT_CLASS (indicator_datetime_panel_parent_class)->dispose (object);
305 }
306

Subscribers

People subscribed via source and target branches