[gnome-initial-setup/shell/4765: 15/362] location: add automatic timezone discovery



commit f9ff42b2d3d5312ae3bb5e34d7e02f42ce1ad19e
Author: Cosimo Cecchi <cosimo endlessm com>
Date:   Fri Apr 18 12:24:14 2014 -0700

    location: add automatic timezone discovery

 configure.ac                                       |    8 +
 gnome-initial-setup/pages/location/Makefile.am     |   10 +
 .../pages/location/cc-timezone-monitor.c           |  442 ++++++++++++++++++++
 .../pages/location/cc-timezone-monitor.h           |   56 +++
 .../pages/location/gis-location-page.c             |   27 ++
 gnome-initial-setup/pages/location/tz.c            |    2 +-
 gnome-initial-setup/pages/location/tz.h            |    1 +
 gnome-initial-setup/pages/location/weather-tz.c    |  136 ++++++
 gnome-initial-setup/pages/location/weather-tz.h    |   31 ++
 9 files changed, 712 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e21edf7..ac6145d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,8 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
                   gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION
                   gstreamer-1.0
                   fontconfig
+                  geoclue-2.0
+                  geocode-glib-1.0
                   gweather-3.0
                   goa-1.0
                   goa-backend-1.0
@@ -54,6 +56,12 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
 
 PKG_CHECK_MODULES(COPY_WORKER, gio-2.0)
 
+GEOCLUE_DBUS_INTERFACE_XML=`pkg-config --variable=dbus_interface geoclue-2.0`
+if test "x$GEOCLUE_DBUS_INTERFACE_XML" = "x"; then
+   AC_MSG_ERROR([Cannot find dbus_interface variable in geoclue-2.0.pc])
+fi
+AC_SUBST(GEOCLUE_DBUS_INTERFACE_XML)
+
 AC_ARG_ENABLE(ibus,
         AS_HELP_STRING([--disable-ibus],
                        [Disable IBus support]),
diff --git a/gnome-initial-setup/pages/location/Makefile.am b/gnome-initial-setup/pages/location/Makefile.am
index 02fe6c1..badf8a0 100644
--- a/gnome-initial-setup/pages/location/Makefile.am
+++ b/gnome-initial-setup/pages/location/Makefile.am
@@ -7,6 +7,14 @@ BUILT_SOURCES =
 AM_CPPFLAGS = \
        -DGNOMECC_DATA_DIR="\"$(datadir)/gnome-control-center\""
 
+geoclue.c: geoclue.h
+geoclue.h: $(GEOCLUE_DBUS_INTERFACE_XML)
+       $(AM_V_GEN) gdbus-codegen \
+               --interface-prefix org.freedesktop.GeoClue2 \
+               --generate-c-code geoclue \
+               --c-namespace Geoclue $<
+BUILT_SOURCES += geoclue.c geoclue.h
+
 timedated.c: timedated.h
 timedated.h: $(srcdir)/timedated1-interface.xml
        $(AM_V_GEN) gdbus-codegen \
@@ -30,7 +38,9 @@ BUILT_SOURCES += location-resources.c location-resources.h
 
 libgislocation_la_SOURCES =    \
        tz.c tz.h \
+       weather-tz.c weather-tz.h \
        cc-timezone-map.c cc-timezone-map.h \
+       cc-timezone-monitor.c cc-timezone-monitor.h \
        gis-location-page.c gis-location-page.h \
        date-endian.c date-endian.h \
        $(BUILT_SOURCES)
diff --git a/gnome-initial-setup/pages/location/cc-timezone-monitor.c 
b/gnome-initial-setup/pages/location/cc-timezone-monitor.c
new file mode 100644
index 0000000..996063d
--- /dev/null
+++ b/gnome-initial-setup/pages/location/cc-timezone-monitor.c
@@ -0,0 +1,442 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember <kalevlember gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "cc-timezone-monitor.h"
+
+#include "geoclue.h"
+#include "timedated.h"
+#include "tz.h"
+#include "weather-tz.h"
+
+#include <geocode-glib/geocode-glib.h>
+#include <polkit/polkit.h>
+
+#define DESKTOP_ID "gnome-datetime-panel"
+#define SET_TIMEZONE_PERMISSION "org.freedesktop.timedate1.set-timezone"
+
+/* Defines from geoclue private header src/public-api/gclue-enums.h */
+#define GCLUE_ACCURACY_LEVEL_CITY 4
+
+enum {
+        TIMEZONE_CHANGED,
+        LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+        GCancellable *cancellable;
+        GeoclueClient *geoclue_client;
+        GeoclueManager *geoclue_manager;
+
+        TzDB *tzdb;
+        WeatherTzDB *weather_tzdb;
+} CcTimezoneMonitorPrivate;
+
+#define GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE((object), CC_TYPE_TIMEZONE_MONITOR, 
CcTimezoneMonitorPrivate))
+G_DEFINE_TYPE (CcTimezoneMonitor, cc_timezone_monitor, G_TYPE_OBJECT)
+
+static gint
+compare_locations (TzLocation *a,
+                   TzLocation *b)
+{
+        if (a->dist > b->dist)
+                return 1;
+
+        if (a->dist < b->dist)
+                return -1;
+
+        return 0;
+}
+
+static GList *
+sort_by_closest_to (GList           *locations,
+                    GeocodeLocation *location)
+{
+        GList *l;
+
+        for (l = locations; l; l = l->next) {
+                GeocodeLocation *loc;
+                TzLocation *tz_location = l->data;
+
+                loc = geocode_location_new (tz_location->latitude,
+                                            tz_location->longitude,
+                                            GEOCODE_LOCATION_ACCURACY_UNKNOWN);
+                tz_location->dist = geocode_location_get_distance_from (loc, location);
+                g_object_unref (loc);
+        }
+
+        return g_list_sort (locations, (GCompareFunc) compare_locations);
+}
+
+static GList *
+ptr_array_to_list (GPtrArray *array)
+{
+        GList *l = NULL;
+        gint i;
+
+        for (i = 0; i < array->len; i++)
+                l = g_list_prepend (l, g_ptr_array_index (array, i));
+
+        return l;
+}
+
+static GList *
+find_by_country (GList       *locations,
+                 const gchar *country_code)
+{
+        GList *l, *found = NULL;
+        gchar *c1;
+        gchar *c2;
+
+        c1 = g_ascii_strdown (country_code, -1);
+
+        for (l = locations; l; l = l->next) {
+                TzLocation *loc = l->data;
+
+                c2 = g_ascii_strdown (loc->country, -1);
+                if (g_strcmp0 (c1, c2) == 0)
+                        found = g_list_prepend (found, loc);
+                g_free (c2);
+        }
+        g_free (c1);
+
+        return found;
+}
+
+static TzLocation *
+find_tzlocation (CcTimezoneMonitor  *self,
+                 GeocodeLocation    *location,
+                 const gchar        *country_code)
+{
+        GList *filtered;
+        GList *locations;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+        TzLocation *closest_tz_location;
+
+        /* First load locations from Olson DB */
+        locations = ptr_array_to_list (tz_get_locations (priv->tzdb));
+        g_return_val_if_fail (locations != NULL, NULL);
+
+        /* ... and then add libgweather's locations as well */
+        locations = g_list_concat (locations,
+                                   weather_tz_db_get_locations (priv->weather_tzdb));
+
+        /* Filter tz locations by country */
+        filtered = find_by_country (locations, country_code);
+        if (filtered != NULL) {
+                g_list_free (locations);
+                locations = filtered;
+        } else {
+                g_debug ("No match for country code '%s' in tzdb", country_code);
+        }
+
+        /* Find the closest tz location */
+        locations = sort_by_closest_to (locations, location);
+        closest_tz_location = (TzLocation *) locations->data;
+
+        g_list_free (locations);
+
+        return closest_tz_location;
+}
+
+static void
+process_location (CcTimezoneMonitor *self,
+                  GeocodePlace       *place)
+{
+        GeocodeLocation *location;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+        TzLocation *new_tzlocation;
+        const gchar *country_code;
+        const gchar *new_timezone;
+
+        country_code = geocode_place_get_country_code (place);
+        location = geocode_place_get_location (place);
+
+        new_tzlocation = find_tzlocation (self, location, country_code);
+
+        g_signal_emit (G_OBJECT (self),
+                       signals[TIMEZONE_CHANGED],
+                       0, new_tzlocation);
+}
+
+static void
+on_reverse_geocoding_ready (GObject      *source_object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+        GeocodePlace *place;
+        GError *error = NULL;
+        CcTimezoneMonitor *self = user_data;
+
+        place = geocode_reverse_resolve_finish (GEOCODE_REVERSE (source_object),
+                                                res,
+                                                &error);
+        if (error != NULL) {
+                g_debug ("Reverse geocoding failed: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+        g_debug ("Geocode lookup resolved country to '%s'",
+                 geocode_place_get_country (place));
+
+        process_location (self, place);
+        g_object_unref (place);
+}
+
+static void
+start_reverse_geocoding (CcTimezoneMonitor *self,
+                         gdouble             latitude,
+                         gdouble             longitude)
+{
+        GeocodeLocation *location;
+        GeocodeReverse *reverse;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        location = geocode_location_new (latitude,
+                                         longitude,
+                                         GEOCODE_LOCATION_ACCURACY_CITY);
+
+        reverse = geocode_reverse_new_for_location (location);
+        geocode_reverse_resolve_async (reverse,
+                                       priv->cancellable,
+                                       on_reverse_geocoding_ready,
+                                       self);
+
+        g_object_unref (location);
+        g_object_unref (reverse);
+}
+
+static void
+on_location_proxy_ready (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+        GeoclueLocation *location;
+        gdouble latitude, longitude;
+        GError *error = NULL;
+        CcTimezoneMonitor *self = user_data;
+
+        location = geoclue_location_proxy_new_for_bus_finish (res, &error);
+        if (error != NULL) {
+                g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        latitude = geoclue_location_get_latitude (location);
+        longitude = geoclue_location_get_longitude (location);
+
+        start_reverse_geocoding (self, latitude, longitude);
+
+        g_object_unref (location);
+}
+
+static void
+on_location_updated (GDBusProxy *client,
+                     gchar      *location_path_old,
+                     gchar      *location_path_new,
+                     gpointer    user_data)
+{
+        CcTimezoneMonitor *self = user_data;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        geoclue_location_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+                                            G_DBUS_PROXY_FLAGS_NONE,
+                                            "org.freedesktop.GeoClue2",
+                                            location_path_new,
+                                            priv->cancellable,
+                                            on_location_proxy_ready,
+                                            self);
+}
+
+static void
+on_start_ready (GObject      *source_object,
+                GAsyncResult *res,
+                gpointer      user_data)
+{
+        GError *error = NULL;
+
+        if (!geoclue_client_call_start_finish (GEOCLUE_CLIENT (source_object),
+                                               res,
+                                               &error)) {
+                g_critical ("Failed to start GeoClue2 client: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+}
+
+static void
+on_client_proxy_ready (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+        GError *error = NULL;
+        CcTimezoneMonitor *self = user_data;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        priv->geoclue_client = geoclue_client_proxy_new_for_bus_finish (res, &error);
+        if (error != NULL) {
+                g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        //geoclue_client_set_desktop_id (priv->geoclue_client, DESKTOP_ID);
+        geoclue_client_set_distance_threshold (priv->geoclue_client,
+                                               GEOCODE_LOCATION_ACCURACY_CITY);
+        //geoclue_client_set_requested_accuracy_level (priv->geoclue_client,
+        //                                             GCLUE_ACCURACY_LEVEL_CITY);
+
+        g_signal_connect (priv->geoclue_client, "location-updated",
+                          G_CALLBACK (on_location_updated), self);
+
+        geoclue_client_call_start (priv->geoclue_client,
+                                   priv->cancellable,
+                                   on_start_ready,
+                                   self);
+}
+
+static void
+on_get_client_ready (GObject      *source_object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+        gchar *client_path;
+        GError *error = NULL;
+        CcTimezoneMonitor *self = user_data;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        if (!geoclue_manager_call_get_client_finish (GEOCLUE_MANAGER (source_object),
+                                                     &client_path,
+                                                     res,
+                                                     &error)) {
+                g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        geoclue_client_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+                                          G_DBUS_PROXY_FLAGS_NONE,
+                                          "org.freedesktop.GeoClue2",
+                                          client_path,
+                                          priv->cancellable,
+                                          on_client_proxy_ready,
+                                          self);
+}
+
+static void
+on_manager_proxy_ready (GObject      *source_object,
+                        GAsyncResult *res,
+                        gpointer      user_data)
+{
+
+        GError *error = NULL;
+        CcTimezoneMonitor *self = user_data;
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        priv->geoclue_manager = geoclue_manager_proxy_new_for_bus_finish (res, &error);
+        if (error != NULL) {
+                g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        geoclue_manager_call_get_client (priv->geoclue_manager,
+                                         priv->cancellable,
+                                         on_get_client_ready,
+                                         self);
+}
+
+static void
+register_geoclue (CcTimezoneMonitor *self)
+{
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        geoclue_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+                                           G_DBUS_PROXY_FLAGS_NONE,
+                                           "org.freedesktop.GeoClue2",
+                                           "/org/freedesktop/GeoClue2/Manager",
+                                           priv->cancellable,
+                                           on_manager_proxy_ready,
+                                           self);
+}
+
+CcTimezoneMonitor *
+cc_timezone_monitor_new (void)
+{
+        return g_object_new (CC_TYPE_TIMEZONE_MONITOR, NULL);
+}
+
+static void
+cc_timezone_monitor_finalize (GObject *obj)
+{
+        CcTimezoneMonitor *monitor = CC_TIMEZONE_MONITOR (obj);
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (monitor);
+
+        g_debug ("Stopping timezone monitor");
+
+        if (priv->cancellable) {
+                g_cancellable_cancel (priv->cancellable);
+                g_clear_object (&priv->cancellable);
+        }
+
+        g_clear_object (&priv->geoclue_client);
+        g_clear_object (&priv->geoclue_manager);
+        g_clear_pointer (&priv->tzdb, tz_db_free);
+        g_clear_pointer (&priv->weather_tzdb, weather_tz_db_free);
+
+        G_OBJECT_CLASS (cc_timezone_monitor_parent_class)->finalize (obj);
+}
+
+static void
+cc_timezone_monitor_class_init (CcTimezoneMonitorClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = cc_timezone_monitor_finalize;
+
+        signals[TIMEZONE_CHANGED] =
+                g_signal_new ("timezone-changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (CcTimezoneMonitorClass, timezone_changed),
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+       g_type_class_add_private (object_class, sizeof(CcTimezoneMonitorPrivate));
+}
+
+static void
+cc_timezone_monitor_init (CcTimezoneMonitor *self)
+{
+        CcTimezoneMonitorPrivate *priv = GET_PRIVATE (self);
+
+        g_debug ("Starting timezone monitor");
+
+        priv->cancellable = g_cancellable_new ();
+
+        priv->tzdb = tz_load_db ();
+        priv->weather_tzdb = weather_tz_db_new ();
+
+        register_geoclue (self);
+}
diff --git a/gnome-initial-setup/pages/location/cc-timezone-monitor.h 
b/gnome-initial-setup/pages/location/cc-timezone-monitor.h
new file mode 100644
index 0000000..10c8bb3
--- /dev/null
+++ b/gnome-initial-setup/pages/location/cc-timezone-monitor.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember <kalevlember gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __CC_TIMEZONE_MONITOR_H
+#define __CC_TIMEZONE_MONITOR_H
+
+#include <glib-object.h>
+#include "tz.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_TIMEZONE_MONITOR                  (cc_timezone_monitor_get_type ())
+#define CC_TIMEZONE_MONITOR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
CC_TYPE_TIMEZONE_MONITOR, CcTimezoneMonitor))
+#define CC_IS_TIMEZONE_MONITOR(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
CC_TYPE_TIMEZONE_MONITOR))
+#define CC_TIMEZONE_MONITOR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), 
CC_TYPE_TIMEZONE_MONITOR, CcTimezoneMonitorClass))
+#define CC_IS_TIMEZONE_MONITOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), 
CC_TYPE_TIMEZONE_MONITOR))
+#define CC_TIMEZONE_MONITOR_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), 
CC_TYPE_TIMEZONE_MONITOR, CcTimezoneMonitorClass))
+
+typedef struct _CcTimezoneMonitor        CcTimezoneMonitor;
+typedef struct _CcTimezoneMonitorClass   CcTimezoneMonitorClass;
+
+struct _CcTimezoneMonitor
+{
+       GObject parent_instance;
+};
+
+struct _CcTimezoneMonitorClass
+{
+       GObjectClass parent_class;
+
+       void (*timezone_changed) (CcTimezoneMonitor *monitor, TzLocation *new_tzlocation);
+};
+
+GType cc_timezone_monitor_get_type (void) G_GNUC_CONST;
+
+CcTimezoneMonitor *cc_timezone_monitor_new (void);
+
+G_END_DECLS
+
+#endif /* __CC_TIMEZONE_MONITOR_H */
diff --git a/gnome-initial-setup/pages/location/gis-location-page.c 
b/gnome-initial-setup/pages/location/gis-location-page.c
index ee22b89..9876469 100644
--- a/gnome-initial-setup/pages/location/gis-location-page.c
+++ b/gnome-initial-setup/pages/location/gis-location-page.c
@@ -41,6 +41,7 @@
 #include <libgweather/location-entry.h>
 
 #include "cc-timezone-map.h"
+#include "cc-timezone-monitor.h"
 #include "timedated.h"
 
 #define GNOME_DESKTOP_USE_UNSTABLE_API
@@ -58,6 +59,8 @@ struct _GisLocationPagePrivate
 
   Timedate1 *dtm;
   GCancellable *cancellable;
+
+  CcTimezoneMonitor *timezone_monitor;
 };
 typedef struct _GisLocationPagePrivate GisLocationPagePrivate;
 
@@ -162,6 +165,24 @@ location_changed_cb (CcTimezoneMap   *map,
 }
 
 static void
+timezone_changed_cb (CcTimezoneMonitor *timezone_monitor,
+                     TzLocation        *location,
+                     GisLocationPage   *page)
+{
+  GisLocationPagePrivate *priv = gis_location_page_get_instance_private (page);
+
+  g_debug ("timezone changed to %s/%s", location->country, location->zone);
+
+  priv->current_location = location;
+
+  cc_timezone_map_set_timezone (priv->map, location->zone);
+
+  update_timezone (page);
+
+  queue_set_timezone (page);
+}
+
+static void
 set_location_from_gweather_location (GisLocationPage  *page,
                                      GWeatherLocation *gloc)
 {
@@ -708,6 +729,11 @@ gis_location_page_constructed (GObject *object)
   g_signal_connect (priv->clock_tracker, "notify::clock",
                     G_CALLBACK (clock_changed), page);
 
+  /* add automatic timezone */
+  priv->timezone_monitor = cc_timezone_monitor_new ();
+  g_signal_connect (priv->timezone_monitor, "timezone-changed",
+                    G_CALLBACK (timezone_changed_cb), page);
+
   update_time (page);
 
   gis_page_set_complete (GIS_PAGE (page), TRUE);
@@ -723,6 +749,7 @@ gis_location_page_dispose (GObject *object)
 
   g_clear_object (&priv->dtm);
   g_clear_object (&priv->clock_tracker);
+  g_clear_object (&priv->timezone_monitor);
   g_clear_pointer (&priv->date, g_date_time_unref);
   if (priv->cancellable) {
     g_cancellable_cancel (priv->cancellable);
diff --git a/gnome-initial-setup/pages/location/tz.c b/gnome-initial-setup/pages/location/tz.c
index c539d59..5465d32 100644
--- a/gnome-initial-setup/pages/location/tz.c
+++ b/gnome-initial-setup/pages/location/tz.c
@@ -131,7 +131,7 @@ tz_load_db (void)
        return tz_db;
 }
 
-static void
+void
 tz_location_free (TzLocation *loc)
 {
        g_free (loc->country);
diff --git a/gnome-initial-setup/pages/location/tz.h b/gnome-initial-setup/pages/location/tz.h
index 71c1c23..7329233 100644
--- a/gnome-initial-setup/pages/location/tz.h
+++ b/gnome-initial-setup/pages/location/tz.h
@@ -78,6 +78,7 @@ char *     tz_info_get_clean_name     (TzDB *tz_db,
 GPtrArray *tz_get_locations           (TzDB *db);
 void       tz_location_get_position   (TzLocation *loc,
                                       double *longitude, double *latitude);
+void       tz_location_free           (TzLocation *loc);
 char      *tz_location_get_country    (TzLocation *loc);
 gchar     *tz_location_get_zone       (TzLocation *loc);
 gchar     *tz_location_get_comment    (TzLocation *loc);
diff --git a/gnome-initial-setup/pages/location/weather-tz.c b/gnome-initial-setup/pages/location/weather-tz.c
new file mode 100644
index 0000000..2666846
--- /dev/null
+++ b/gnome-initial-setup/pages/location/weather-tz.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember <kalevlember gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "weather-tz.h"
+#include "tz.h"
+
+#define GWEATHER_I_KNOW_THIS_IS_UNSTABLE
+#include <libgweather/gweather.h>
+
+struct _WeatherTzDB
+{
+        GList *tz_locations;
+};
+
+static GList *
+location_get_cities (GWeatherLocation *parent_location)
+{
+        GList *cities = NULL;
+        GWeatherLocation **children;
+        gint i;
+
+        children = gweather_location_get_children (parent_location);
+        for (i = 0; children[i]; i++) {
+                if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_CITY) {
+                        cities = g_list_prepend (cities,
+                                                 children[i]);
+                } else {
+                        cities = g_list_concat (cities,
+                                                location_get_cities (children[i]));
+                }
+        }
+
+        return cities;
+}
+
+static gboolean
+weather_location_has_timezone (GWeatherLocation *loc)
+{
+        return gweather_location_get_timezone (loc) != NULL;
+}
+
+/**
+ * load_timezones:
+ * @cities: a list of #GWeatherLocation
+ *
+ * Returns: a list of #TzLocation
+ */
+static GList *
+load_timezones (GList *cities)
+{
+        GList *l;
+        GList *tz_locations = NULL;
+
+        for (l = cities; l; l = l->next) {
+                TzLocation *loc;
+                const gchar *country;
+                const gchar *timezone_id;
+                gdouble latitude;
+                gdouble longitude;
+
+                if (!gweather_location_has_coords (l->data) ||
+                    !weather_location_has_timezone (l->data)) {
+                        g_debug ("Incomplete GWeather location entry: (%s) %s",
+                                 gweather_location_get_country (l->data),
+                                 gweather_location_get_city_name (l->data));
+                        continue;
+                }
+
+                country = gweather_location_get_country (l->data);
+                timezone_id = gweather_timezone_get_tzid (gweather_location_get_timezone (l->data));
+                gweather_location_get_coords (l->data,
+                                              &latitude,
+                                              &longitude);
+
+                loc = g_new0 (TzLocation, 1);
+                loc->country = g_strdup (country);
+                loc->latitude = latitude;
+                loc->longitude = longitude;
+                loc->zone = g_strdup (timezone_id);
+                loc->comment = NULL;
+
+                tz_locations = g_list_prepend (tz_locations, loc);
+        }
+
+        return tz_locations;
+}
+
+GList *
+weather_tz_db_get_locations (WeatherTzDB *tzdb)
+{
+        return g_list_copy (tzdb->tz_locations);
+}
+
+WeatherTzDB *
+weather_tz_db_new (void)
+{
+        GList *cities;
+        GWeatherLocation *world;
+        WeatherTzDB *tzdb;
+
+        world = gweather_location_get_world ();
+        cities = location_get_cities (world);
+
+        tzdb = g_new0 (WeatherTzDB, 1);
+        tzdb->tz_locations = load_timezones (cities);
+
+        g_list_free (cities);
+
+        return tzdb;
+}
+
+void
+weather_tz_db_free (WeatherTzDB *tzdb)
+{
+        g_list_free_full (tzdb->tz_locations, (GDestroyNotify) tz_location_free);
+
+        g_free (tzdb);
+}
diff --git a/gnome-initial-setup/pages/location/weather-tz.h b/gnome-initial-setup/pages/location/weather-tz.h
new file mode 100644
index 0000000..14de449
--- /dev/null
+++ b/gnome-initial-setup/pages/location/weather-tz.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember <kalevlember gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __WEATHER_TZ_H
+#define __WEATHER_TZ_H
+
+#include <glib.h>
+
+typedef struct _WeatherTzDB WeatherTzDB;
+
+WeatherTzDB     *weather_tz_db_new              (void);
+GList           *weather_tz_db_get_locations    (WeatherTzDB *db);
+void             weather_tz_db_free             (WeatherTzDB *db);
+
+#endif /* __WEATHER_TZ_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]