[gnome-calendar: 49/49] weather-service: major reorganization and cleanup



commit 23a9de1108a0a7a0f833188230fa671c50705231
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Mon Dec 11 23:10:55 2017 -0200

    weather-service: major reorganization and cleanup
    
    To make it more like the actual code style of GNOME Calendar.
    
    Hope Florian doesn't get (too) mad of me removing and modifying
    so much on his code. I'm sorry dude, my OCD didn't allow me to
    just flood the gate.

 src/gcal-weather-service.c | 1835 ++++++++++++++++++--------------------------
 1 file changed, 739 insertions(+), 1096 deletions(-)
---
diff --git a/src/gcal-weather-service.c b/src/gcal-weather-service.c
index aa39598c..28429f3e 100644
--- a/src/gcal-weather-service.c
+++ b/src/gcal-weather-service.c
@@ -16,7 +16,8 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#define G_LOG_DOMAIN      "Weather"
+#define G_LOG_DOMAIN      "GcalWeatherService"
+
 #define DESKTOP_FILE_NAME "org.gnome.Calendar"
 
 #include <geocode-glib/geocode-glib.h>
@@ -28,8 +29,7 @@
 #include "gcal-timer.h"
 
 
-G_BEGIN_DECLS
-
+#define DAY_SECONDS (24 * 60 * 60)
 
 /**
  * Internal structure used to manage known
@@ -75,10 +75,7 @@ struct _GcalWeatherService
 {
   GObjectClass        parent;
 
-  /* <public> */
-
-  /* <private> */
-  GTimeZone          *time_zone;            /* owned, nullable */
+  GTimeZone          *timezone;            /* owned, nullable */
 
   /* timer: */
   guint               check_interval_new;
@@ -103,101 +100,23 @@ struct _GcalWeatherService
   gboolean            weather_service_running;
 };
 
-
-/* Auxiliary methods: */
-static void          on_network_change                           (GNetworkMonitor    *monitor,
-                                                                  gboolean            available,
-                                                                  GcalWeatherService *self);
-
-static void          on_gclue_simple_creation                    (GClueSimple        *source,
-                                                                  GAsyncResult       *result,
-                                                                  GcalWeatherService *data);
-
-static void          on_gclue_location_changed                   (GClueLocation      *location,
-                                                                  GcalWeatherService *self);
-
-static void          on_gclue_client_activity_changed            (GClueClient        *client,
-                                                                  GcalWeatherService *self);
-
-static void          on_gclue_client_stop                        (GClueClient        *client,
-                                                                  GAsyncResult       *res,
-                                                                  GClueSimple        *simple);
-
-static void          on_gweather_update                          (GWeatherInfo       *info,
-                                                                  GcalWeatherService *self);
-
-static void          on_duration_timer_timeout                   (GcalTimer          *timer,
-                                                                  GcalWeatherService *self);
-
-static void          on_midnight_timer_timeout                   (GcalTimer          *timer,
+static void          on_gweather_update_cb                       (GWeatherInfo       *info,
                                                                   GcalWeatherService *self);
 
-
-/* Timer Helpers: */
-static void          update_timeout_interval                     (GcalWeatherService *self);
-
 static void          schedule_midnight                           (GcalWeatherService *self);
 
 static void          start_timer                                 (GcalWeatherService *self);
 
 static void          stop_timer                                  (GcalWeatherService *self);
 
-
-/* Internal location API and callbacks: */
-
-static void          update_location                             (GcalWeatherService *self,
-                                                                  GWeatherLocation   *location);
-
-static void          update_gclue_location                       (GcalWeatherService *self,
-                                                                  GClueLocation      *location);
-
-
-/* Internal Weather API */
-static void          set_max_days                                (GcalWeatherService *self,
-                                                                  guint               days);
-
-static void          set_valid_timespan                          (GcalWeatherService *self,
-                                                                  gint64              timespan);
-
 static gboolean      has_valid_weather_infos                     (GcalWeatherService *self);
 
 static void          update_weather                              (GcalWeatherService *self,
                                                                   GWeatherInfo       *info,
                                                                   gboolean            reuse_old_on_error);
 
-
-/* Internal weather update API and callbacks */
-static void          set_check_interval_new                      (GcalWeatherService *self,
-                                                                  guint               check_interval);
-
-static void          set_check_interval_renew                    (GcalWeatherService *self,
-                                                                  guint               check_interval);
-
 static gssize        get_normalized_icon_name_len                (const gchar        *str);
 
-static gchar*        get_normalized_icon_name                    (GWeatherInfo       *gwi,
-                                                                  gboolean            is_night_icon);
-
-static gint          get_icon_name_sortkey                       (const gchar        *icon_name,
-                                                                  gboolean           *supports_night_icon);
-
-static gboolean      get_time_day_start                          (GcalWeatherService *self,
-                                                                  GDate              *ret_date,
-                                                                  gint64             *ret_unix,
-                                                                  gint64             *ret_unix_exact);
-
-static inline gboolean get_gweather_temperature                  (GWeatherInfo       *gwi,
-                                                                  gdouble            *temp);
-
-static gboolean      compute_weather_info_data                   (GSList             *samples,
-                                                                  gboolean            is_today,
-                                                                  gchar             **icon_name,
-                                                                  gchar             **temperature);
-
-static GSList*       preprocess_gweather_reports                 (GcalWeatherService *self,
-                                                                  GSList             *samples);
-
-
 G_DEFINE_TYPE (GcalWeatherService, gcal_weather_service, G_TYPE_OBJECT)
 
 enum
@@ -214,253 +133,16 @@ enum
 enum
 {
   SIG_WEATHER_CHANGED,
-  SIG_NUM,
+  N_SIGNALS,
 };
 
-static guint gcal_weather_service_signals[SIG_NUM] = { 0 };
-
-G_END_DECLS
-
-
-/********************
- * < gobject setup >
- *******************/
-
-static void
-gcal_weather_service_finalize (GObject *object)
-{
-  GcalWeatherService *self; /* unowned */
-
-  self = (GcalWeatherService *) object;
-
-  gcal_timer_free (self->duration_timer);
-  self->duration_timer = NULL;
-
-  gcal_timer_free (self->midnight_timer);
-  self->midnight_timer = NULL;
-
-  if (self->time_zone != NULL)
-    {
-      g_time_zone_unref (self->time_zone);
-      self->time_zone = NULL;
-    }
-
-  if (self->location_service != NULL)
-    g_clear_object (&self->location_service);
-
-  g_cancellable_cancel (self->location_cancellable);
-  g_clear_object (&self->location_cancellable);
-
-  g_slist_free_full (self->weather_infos, g_object_unref);
-
-  if (self->gweather_info != NULL)
-    g_clear_object (&self->gweather_info);
-
-  if (self->network_changed_sid > 0)
-    {
-      GNetworkMonitor *monitor; /* unowned */
-
-      monitor = g_network_monitor_get_default ();
-      g_signal_handler_disconnect (monitor, self->network_changed_sid);
-    }
-
-  G_OBJECT_CLASS (gcal_weather_service_parent_class)->finalize (object);
-}
-
-static void
-gcal_weather_service_get_property (GObject    *object,
-                                   guint       prop_id,
-                                   GValue     *value,
-                                   GParamSpec *pspec)
-{
-  GcalWeatherService* self; /* unowned */
-
-  self = GCAL_WEATHER_SERVICE (object);
-  switch (prop_id)
-  {
-  case PROP_MAX_DAYS:
-    g_value_set_uint (value, gcal_weather_service_get_max_days (self));
-    break;
-  case PROP_TIME_ZONE:
-    g_value_set_pointer (value, gcal_weather_service_get_time_zone (self));
-    break;
-  case PROP_CHECK_INTERVAL_NEW:
-    g_value_set_uint (value, gcal_weather_service_get_check_interval_new (self));
-    break;
-  case PROP_CHECK_INTERVAL_RENEW:
-    g_value_set_uint (value, gcal_weather_service_get_check_interval_renew (self));
-    break;
-  case PROP_VALID_TIMESPAN:
-    g_value_set_int64 (value, gcal_weather_service_get_valid_timespan (self));
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    break;
-  }
-}
-
-static void
-gcal_weather_service_set_property (GObject      *object,
-                                   guint         prop_id,
-                                   const GValue *value,
-                                   GParamSpec   *pspec)
-{
-  GcalWeatherService* self; /* unowned */
-
-  self = GCAL_WEATHER_SERVICE (object);
-  switch (prop_id)
-  {
-  case PROP_MAX_DAYS:
-    set_max_days (self, g_value_get_uint (value));
-    break;
-  case PROP_TIME_ZONE:
-    gcal_weather_service_set_time_zone (self, g_value_get_pointer (value));
-    break;
-  case PROP_CHECK_INTERVAL_NEW:
-    set_check_interval_new (self, g_value_get_uint (value));
-    break;
-  case PROP_CHECK_INTERVAL_RENEW:
-    set_check_interval_renew (self, g_value_get_uint (value));
-    break;
-  case PROP_VALID_TIMESPAN:
-    set_valid_timespan (self, g_value_get_int64 (value));
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    break;
-  }
-}
-
-static void
-gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
-{
-  GObjectClass *object_class; /* unowned */
-
-  object_class = G_OBJECT_CLASS (klass);
-  object_class->finalize = gcal_weather_service_finalize;
-  object_class->get_property = gcal_weather_service_get_property;
-  object_class->set_property = gcal_weather_service_set_property;
-
-  /**
-   * GcalWeatherService:max-days:
-   *
-   * Maximal number of days to fetch forecasts for.
-   */
-  g_object_class_install_property
-      (G_OBJECT_CLASS (klass),
-       PROP_MAX_DAYS,
-       g_param_spec_uint ("max-days", "max-days", "max-days",
-                          1, G_MAXUINT, 3,
-                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-  /**
-   * GcalWeatherService:time-zone:
-   *
-   * The time zone to use.
-   */
-  g_object_class_install_property
-      (G_OBJECT_CLASS (klass),
-       PROP_TIME_ZONE,
-       g_param_spec_pointer ("time-zone", "time-zone", "time-zone",
-                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
-
-  /**
-   * GcalWeatherService:check-interval-new:
-   *
-   * Amount of seconds to wait before re-fetching weather infos.
-   * This interval is used when no valid weather reports are available.
-   */
-  g_object_class_install_property
-      (G_OBJECT_CLASS (klass),
-       PROP_CHECK_INTERVAL_NEW,
-       g_param_spec_uint ("check-interval-new", "check-interval-new", "check-interval-new",
-                          0, G_MAXUINT, GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT,
-                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-  /**
-   * GcalWeatherService:check-interval-renew:
-   *
-   * Amount of seconds to wait before re-fetching weather information.
-   * This interval is used when valid weather reports are available.
-   */
-  g_object_class_install_property
-      (G_OBJECT_CLASS (klass),
-       PROP_CHECK_INTERVAL_RENEW,
-       g_param_spec_uint ("check-interval-renew", "check-interval-renew", "check-interval-renew",
-                          0, G_MAXUINT, GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT,
-                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-  /**
-   * GcalWeatherService:valid_timespan:
-   *
-   * Amount of seconds fetched weather information are considered as valid.
-   */
-  g_object_class_install_property
-      (G_OBJECT_CLASS (klass),
-       PROP_VALID_TIMESPAN,
-       g_param_spec_int64 ("valid-timespan", "valid-timespan", "valid-timespan",
-                           0, G_MAXINT64, GCAL_WEATHER_VALID_TIMESPAN_DFLT,
-                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-
-  /**
-   * GcalWeatherService::weather-changed:
-   * @sender: The #GcalWeatherService
-   * @self:   data pointer.
-   *
-   * Triggered on weather changes. Call
-   * gcal_weather_service_get_weather_infos() to
-   * retrieve predictions.
-   */
-  gcal_weather_service_signals[SIG_WEATHER_CHANGED]
-    = g_signal_new ("weather-changed",
-                    GCAL_TYPE_WEATHER_SERVICE,
-                    G_SIGNAL_RUN_LAST,
-                    0,
-                    NULL,
-                    NULL,
-                    g_cclosure_marshal_VOID__VOID,
-                    G_TYPE_NONE,
-                    0);
-}
-
-static void
-gcal_weather_service_init (GcalWeatherService *self)
-{
-  GNetworkMonitor *monitor; /* unowned */
-
-  self->duration_timer = gcal_timer_new (GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT);
-  gcal_timer_set_callback (self->duration_timer, (GCalTimerFunc) on_duration_timer_timeout, self, NULL);
-  self->midnight_timer = gcal_timer_new (24*60*60);
-  gcal_timer_set_callback (self->midnight_timer, (GCalTimerFunc) on_midnight_timer_timeout, self, NULL);
-  self->time_zone = NULL;
-  self->check_interval_new = GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT;
-  self->check_interval_renew = GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT;
-  self->location_cancellable = g_cancellable_new ();
-  self->location_service_running = FALSE;
-  self->location_service = NULL;
-  self->weather_infos = NULL;
-  self->weather_infos_upated = -1;
-  self->valid_timespan = -1;
-  self->gweather_info = NULL;
-  self->weather_service_running = FALSE;
-  self->max_days = 0;
+static guint signals[N_SIGNALS] = { 0 };
 
-  monitor = g_network_monitor_get_default ();
-  self->network_changed_sid = g_signal_connect (monitor,
-                                                "network-changed",
-                                                (GCallback) on_network_change,
-                                                self);
-}
 
-/* get_normalized_icon_name:
- * @str: A icon name to normalize.
- *
- * Translates a given weather icon name to the
- * one to display for folded weather reports.
- *
- * Returns: (transfer full): A normalized icon name.
+/*
+ * Auxiliary methods
  */
+
 static gchar*
 get_normalized_icon_name (GWeatherInfo* wi,
                           gboolean      is_night_icon)
@@ -472,21 +154,21 @@ get_normalized_icon_name (GWeatherInfo* wi,
   const gsize sym_pfx_size = G_N_ELEMENTS (sym_pfx) - 1;
 
   const gchar *str; /* unowned */
-  gssize       normalized_size;
-  gchar       *buffer = NULL; /* owned */
-  gchar       *bufpos = NULL; /* unowned */
-  gsize        buffer_size;
+  gssize normalized_size;
+  gchar *buffer = NULL; /* owned */
+  gchar *bufpos = NULL; /* unowned */
+  gsize buffer_size;
 
   g_return_val_if_fail (wi != NULL, NULL);
-  
+
   str = gweather_info_get_icon_name (wi);
-  if (str == NULL)
+  if (!str)
     return NULL;
-  
+
   normalized_size = get_normalized_icon_name_len (str);
   g_return_val_if_fail (normalized_size >= 0, NULL);
 
-  if (is_night_icon) 
+  if (is_night_icon)
     buffer_size = normalized_size + night_pfx_size + sym_pfx_size + 1;
   else
     buffer_size = normalized_size + sym_pfx_size + 1;
@@ -504,350 +186,311 @@ get_normalized_icon_name (GWeatherInfo* wi,
     }
 
   memcpy (bufpos, sym_pfx, sym_pfx_size);
-  buffer[buffer_size - 1] = '\0';  
+  buffer[buffer_size - 1] = '\0';
 
   return buffer;
 }
 
+static void
+update_timeout_interval (GcalWeatherService *self)
+{
+  guint interval;
 
-/**************
- * < private >
- **************/
+  if (has_valid_weather_infos (self))
+    interval = self->check_interval_renew;
+  else
+    interval = self->check_interval_new;
 
-/*
- * Auxiliary methods:
- */
+  gcal_timer_set_default_duration (self->duration_timer, interval);
+}
 
-/* on_network_change:
- * @monitor:   The emitting #GNetworkMonitor
- * @available: The current value of “network-available”
- * @self:      The #GcalWeatherService instance.
- *
- * Starts and stops timer based on monitored network
- * changes.
- */
 static void
-on_network_change (GNetworkMonitor    *monitor,
-                   gboolean            available,
-                   GcalWeatherService *self)
+schedule_midnight (GcalWeatherService  *self)
 {
-  gboolean is_running;
+  g_autoptr (GTimeZone) zone = NULL;
+  g_autoptr (GDateTime) now = NULL;
+  g_autoptr (GDateTime) tom = NULL;
+  g_autoptr (GDateTime) mid = NULL;
+  gint64 real_now;
+  gint64 real_mid;
 
-  g_return_if_fail (G_IS_NETWORK_MONITOR (monitor));
   g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
 
-  g_debug ("network changed, available = %d", available);
+  zone = !self->timezone ? g_time_zone_new_local () : g_time_zone_ref (self->timezone);
 
-  is_running = gcal_timer_is_running (self->duration_timer);
-  if (available && !is_running)
-    {
-      if (self->gweather_info != NULL)
-        gweather_info_update (self->gweather_info);
+  now = g_date_time_new_now (zone);
+  tom = g_date_time_add_days (now, 1);
+  mid = g_date_time_new (zone,
+                         g_date_time_get_year (tom),
+                         g_date_time_get_month (tom),
+                         g_date_time_get_day_of_month (tom),
+                         0, 0, 0);
 
-      start_timer (self);
-    }
-  else if (!available && is_running)
-    {
-      stop_timer (self);
-    }
+  real_mid = g_date_time_to_unix (mid);
+  real_now = g_date_time_to_unix (now);
+
+  gcal_timer_set_default_duration (self->midnight_timer,
+                                   real_mid - real_now);
 }
 
-/* on_gclue_simple_creation:
- * @source:
- * @result:                Result of gclue_simple_new().
- * @self: (transfer full): A GcalWeatherService reference.
- *
- * Callback used in gcal_weather_service_run().
- */
 static void
-on_gclue_simple_creation (GClueSimple        *_source,
-                          GAsyncResult       *result,
-                          GcalWeatherService *self)
+start_timer (GcalWeatherService  *self)
 {
-  GClueSimple *location_service;   /* owned */
-  GClueLocation *location;         /* unowned */
-  GClueClient *client;             /* unowned */
-  g_autoptr (GError) error = NULL;
-
-  g_return_if_fail (G_IS_ASYNC_RESULT (result));
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  GNetworkMonitor *monitor; /* unowned */
 
-  /* make sure we do not touch self->location_service
-   * if the current operation was cancelled.
-   */
-  location_service = gclue_simple_new_finish (result, &error);
-  if (error != NULL)
+  monitor = g_network_monitor_get_default ();
+  if (g_network_monitor_get_network_available (monitor))
     {
-      g_assert (location_service == NULL);
-
-      if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
-        /* Cancelled during creation. Silently fail. */;
-      else
-        g_warning ("Could not create GCLueSimple: %s", error->message);
-
-      g_object_unref (self);
-      return;
-    }
-
-  g_assert (self->location_service == NULL);
-  g_assert (location_service != NULL);
-
-  self->location_service = g_steal_pointer (&location_service);
-
-  location = gclue_simple_get_location (self->location_service);
-  client = gclue_simple_get_client (self->location_service);
-
-  if (location != NULL)
-    {
-      update_gclue_location (self, location);
+      update_timeout_interval (self);
+      gcal_timer_start (self->duration_timer);
 
-      g_signal_connect_object (location,
-                               "notify::location",
-                               G_CALLBACK (on_gclue_location_changed),
-                               self,
-                               0);
+      schedule_midnight (self);
+      gcal_timer_start (self->midnight_timer);
     }
-
-  g_signal_connect_object (client,
-                           "notify::active",
-                           G_CALLBACK (on_gclue_client_activity_changed),
-                           self,
-                           0);
-
-  g_object_unref (self);
 }
 
-/* on_gclue_location_changed:
- * @location: #GClueLocation owned by @self
- * @self: The #GcalWeatherService
- *
- * Handles location changes.
- */
 static void
-on_gclue_location_changed (GClueLocation       *location,
-                           GcalWeatherService  *self)
+stop_timer (GcalWeatherService  *self)
 {
-  g_return_if_fail (GCLUE_IS_LOCATION (location));
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-
-  update_gclue_location (self, location);
+  gcal_timer_stop (self->duration_timer);
+  gcal_timer_stop (self->midnight_timer);
 }
 
-/* on_gclue_client_activity_changed:
- * @client: The #GClueclient ownd by @self
- * @self: The #GcalWeatherService
- *
- * Handles location client activity changes.
- */
 static void
-on_gclue_client_activity_changed (GClueClient         *client,
-                                  GcalWeatherService  *self)
+set_check_interval_new (GcalWeatherService *self,
+                        guint               interval)
 {
-  g_return_if_fail (GCLUE_IS_CLIENT (client));
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  self->check_interval_new = interval;
+  update_timeout_interval (self);
 
-  /* Notify listeners about unknown locations: */
-  update_location (self, NULL);
+  g_object_notify ((GObject*) self, "check-interval-new");
 }
 
-/* on_gclue_client_stop:
- * @source_object: A #GClueClient.
- * @res:           Result of gclue_client_call_stop().
- * @simple:        (transfer full): A #GClueSimple.
- *
- * Helper-callback used in gcal_weather_service_stop().
- */
 static void
-on_gclue_client_stop (GClueClient  *client,
-                      GAsyncResult *res,
-                      GClueSimple  *simple)
+set_check_interval_renew (GcalWeatherService *self,
+                          guint               interval)
 {
-  g_autoptr(GError) error = NULL; /* owned */
-  gboolean stopped;
-
-  g_return_if_fail (GCLUE_IS_CLIENT (client));
-  g_return_if_fail (G_IS_ASYNC_RESULT (res));
-  g_return_if_fail (GCLUE_IS_SIMPLE (simple));
-
-  stopped = gclue_client_call_stop_finish (client,
-                                           res,
-                                           &error);
-  if (error != NULL)
-      g_warning ("Could not stop location service: %s", error->message);
-  else if (!stopped)
-      g_warning ("Could not stop location service");
+  self->check_interval_renew = interval;
+  update_timeout_interval (self);
 
-  g_object_unref (simple);
+  g_object_notify ((GObject*) self, "check-interval-renew");
 }
 
-/* on_gweather_update:
- * @self: A #GcalWeatherService instance.
- * @timespan: Amount of seconds we consider weather information as valid.
- *
- * Triggered on weather updates with previously handled or no
- * location changes.
- */
-static void
-on_gweather_update (GWeatherInfo       *info,
-                    GcalWeatherService *self)
+static gssize
+get_normalized_icon_name_len (const gchar *str)
 {
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
-
-  update_weather (self, info, TRUE);
-}
+  const gchar *suffix1 = "-symbolic";
+  const gssize suffix1_len = strlen (suffix1);
 
-/* on_duration_timer_timeout
- * @self: A #GcalWeatherService.
- *
- * Handles scheduled weather report updates.
- */
-static void
-on_duration_timer_timeout (GcalTimer          *timer,
-                           GcalWeatherService *self)
-{
-  g_return_if_fail (timer != NULL);
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  const gchar *suffix2 = "-night";
+  const gssize suffix2_len = strlen (suffix2);
 
-  if (self->gweather_info != NULL)
-    gweather_info_update (self->gweather_info);
-}
+  gssize clean_len;
+  gssize str_len;
 
-/* on_midnight_timer_timeout
- * @self: A #GcalWeatherService.
- *
- * Handles scheduled weather report updates.
- */
-static void
-on_midnight_timer_timeout (GcalTimer          *timer,
-                           GcalWeatherService *self)
-{
-  g_return_if_fail (timer != NULL);
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  str_len = strlen (str);
 
-  if (self->gweather_info != NULL)
-    gweather_info_update (self->gweather_info);
+  clean_len = str_len - suffix1_len;
+  if (clean_len >= 0 && memcmp (suffix1, str + clean_len, suffix1_len) == 0)
+    str_len = clean_len;
 
-  if (gcal_timer_is_running (self->duration_timer))
-    gcal_timer_reset (self->duration_timer);
+  clean_len = str_len - suffix2_len;
+  if (clean_len >= 0 && memcmp (suffix2, str + clean_len, suffix2_len) == 0)
+    str_len = clean_len;
 
-  schedule_midnight (self);
+  return str_len;
 }
 
+static gint
+get_icon_name_sortkey (const gchar *icon_name,
+                       gboolean    *supports_night_icon)
+{
+  /* Note that we can't use gweathers condition
+   * field as it is not necessarily holds valid values.
+   * libgweather uses its own heuristic to determine
+   * the icon to use. String matching is still better
+   * than copying their algorithm, I guess.
+   */
+
+  gssize normalized_name_len;
 
+  const GcalWeatherIconInfo icons[] =
+    { {"weather-clear",             TRUE},
+      {"weather-few-clouds",        TRUE},
+      {"weather-overcast",          FALSE},
+      {"weather-fog",               FALSE},
+      {"weather-showers-scattered", FALSE},
+      {"weather-showers",           FALSE},
+      {"weather-snow",              FALSE},
+      {"weather-storm",             FALSE},
+      {"weather-severe-alert",      FALSE}
+    };
 
-/*
- * Intenral timer Helpers:
- */
+  g_return_val_if_fail (icon_name != NULL, -1);
+  g_return_val_if_fail (supports_night_icon != NULL, -1);
 
-/* update_timeout_interval:
- * @self: The #GcalWeatherService instance.
- *
- * Selects the right duration timer timeout based
- * on locally-stored weather information.
- */
-static void
-update_timeout_interval (GcalWeatherService *self)
-{
-  guint interval;
+  *supports_night_icon = FALSE;
 
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  normalized_name_len = get_normalized_icon_name_len (icon_name);
+  g_return_val_if_fail (normalized_name_len >= 0, -1);
 
-  if (has_valid_weather_infos (self))
-    interval = self->check_interval_renew;
-  else
-    interval = self->check_interval_new;
+  for (int i = 0; i < G_N_ELEMENTS (icons); i++)
+    {
+      if (icons[i].name[normalized_name_len] == '\0' && strncmp (icon_name, icons[i].name, 
normalized_name_len) == 0)
+        {
+          *supports_night_icon = icons[i].night_support;
+          return i;
+        }
+    }
 
-  gcal_timer_set_default_duration (self->duration_timer, interval);
+  g_warning ("Unknown weather icon '%s'", icon_name);
+
+  return -1;
 }
 
-/* schedule_midnight:
- * @self: The #GcalWeatherService instance.
- *
- * Sets the midnight timer timeout to midnight.
- * The timer needs to be reset when it
- * emits.
- */
-static void
-schedule_midnight (GcalWeatherService  *self)
+static gboolean
+get_time_day_start (GcalWeatherService *self,
+                    GDate              *ret_date,
+                    gint64             *ret_unix,
+                    gint64             *ret_unix_exact)
 {
   g_autoptr (GTimeZone) zone = NULL;
   g_autoptr (GDateTime) now = NULL;
-  g_autoptr (GDateTime) tom = NULL;
-  g_autoptr (GDateTime) mid = NULL;
-  gint64 real_now;
-  gint64 real_mid;
+  g_autoptr (GDateTime) day = NULL;
 
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (ret_date != NULL, FALSE);
+  g_return_val_if_fail (ret_unix != NULL, FALSE);
+  g_return_val_if_fail (ret_unix_exact != NULL, FALSE);
 
-  zone = (self->time_zone == NULL)
-           ? g_time_zone_new_local ()
-           : g_time_zone_ref (self->time_zone);
+  zone = !self->timezone ? g_time_zone_new_local () : g_time_zone_ref (self->timezone);
 
   now = g_date_time_new_now (zone);
-  tom = g_date_time_add_days (now, 1);
-  mid = g_date_time_new (zone,
-                         g_date_time_get_year (tom),
-                         g_date_time_get_month (tom),
-                         g_date_time_get_day_of_month (tom),
+  day = g_date_time_new (zone,
+                         g_date_time_get_year (now),
+                         g_date_time_get_month (now),
+                         g_date_time_get_day_of_month (now),
                          0, 0, 0);
 
-  real_mid = g_date_time_to_unix (mid);
-  real_now = g_date_time_to_unix (now);
+  g_date_set_dmy (ret_date,
+                  g_date_time_get_day_of_month (day),
+                  g_date_time_get_month (day),
+                  g_date_time_get_year (day));
 
-  gcal_timer_set_default_duration (self->midnight_timer,
-                                   real_mid - real_now);
+  *ret_unix = g_date_time_to_unix (day);
+  *ret_unix_exact = g_date_time_to_unix (now);
+  return TRUE;
 }
 
-/* start_timer:
- * @self: The #GcalWeatherService instance.
- *
- * Starts weather timer in case it makes sense.
- */
-static void
-start_timer (GcalWeatherService  *self)
+static inline gboolean
+get_gweather_temperature (GWeatherInfo *gwi,
+                          gdouble      *temp)
 {
-  GNetworkMonitor *monitor; /* unowned */
+  gboolean valid;
+  gdouble  value;
 
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  *temp = NAN;
 
-  monitor = g_network_monitor_get_default ();
-  if (g_network_monitor_get_network_available (monitor))
-    {
-      update_timeout_interval (self);
-      gcal_timer_start (self->duration_timer);
+  g_return_val_if_fail (gwi != NULL, FALSE);
+  g_return_val_if_fail (temp != NULL, FALSE);
 
-      schedule_midnight (self);
-      gcal_timer_start (self->midnight_timer);
+  valid = gweather_info_get_value_temp (gwi,
+                                        GWEATHER_TEMP_UNIT_DEFAULT,
+                                        &value);
+
+  /* TODO: Extract temperatures in Celsius and catch implausible cases */
+  if (valid)
+    {
+      *temp = value;
+      return TRUE;
+    }
+  else
+    {
+      *temp = NAN;
+      return FALSE;
     }
 }
 
-/* stop_timer:
- * @self: The #GcalWeatherService instance.
- *
- * Stops the timer.
- */
-static void
-stop_timer (GcalWeatherService  *self)
+static gboolean
+compute_weather_info_data (GSList    *samples,
+                           gboolean   is_today,
+                           gchar    **icon_name,
+                           gchar    **temperature)
 {
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  GWeatherInfo *phenomenon_gwi = NULL;
+  GWeatherInfo *temp_gwi = NULL;
+  GSList *iter;
+  gboolean phenomenon_supports_night_icon;
+  gboolean has_daytime;
+  gdouble temp_val;
+  gint phenomenon_val;
+
+  temp_val = NAN;
+  has_daytime = FALSE;
+  phenomenon_val = -1;
+  phenomenon_supports_night_icon = FALSE;
 
-  gcal_timer_stop (self->duration_timer);
-  gcal_timer_stop (self->midnight_timer);
-}
+  /* Note: I checked three different gweather consumers
+   *   and they all pick different values. So here is my
+   *   take: I pick up the worst weather for icons and
+   *   the highest temperature. I basically want to know
+   *   whether I need my umbrella for my appointment.
+   *   Not sure about the right temperature. It is probably
+   *   better to pick-up the median of all predictions
+   *   during daytime.
+   */
+
+  for (iter = samples; iter; iter = iter->next)
+    {
+      GWeatherInfo  *gwi;       /* unowned */
+      const gchar   *icon_name; /* unowned */
+      gboolean supports_night_icon;
+      gboolean valid_temp;
+      gdouble temp;
+      gint phenomenon;
 
+      gwi = GWEATHER_INFO (iter->data);
+      phenomenon = -1;
+
+      icon_name = gweather_info_get_icon_name (gwi);
+      if (icon_name)
+        phenomenon = get_icon_name_sortkey (icon_name, &supports_night_icon);
+
+      valid_temp = get_gweather_temperature (gwi, &temp);
+
+      if (phenomenon >= 0 && (phenomenon_gwi == NULL || phenomenon > phenomenon_val))
+        {
+          phenomenon_supports_night_icon = supports_night_icon;
+          phenomenon_val = phenomenon;
+          phenomenon_gwi = gwi;
+        }
+
+      if (valid_temp && (!temp_gwi || temp > temp_val))
+        {
+          temp_val = temp;
+          temp_gwi = gwi;
+        }
+
+      if (gweather_info_is_daytime (gwi))
+        has_daytime = TRUE;
+    }
+
+  if (phenomenon_gwi && temp_gwi)
+    {
+      *icon_name = get_normalized_icon_name (phenomenon_gwi, is_today && !has_daytime && 
phenomenon_supports_night_icon);
+      *temperature = gweather_info_get_temp (temp_gwi);
+      return TRUE;
+    }
+  else
+    {
+      /* empty list */
+      *icon_name = NULL;
+      *temperature = NULL;
+      return FALSE;
+    }
+}
 
-/*
- * Internal location API:
- */
 
-/**
- * update_location:
- * @self:     The #GcalWeatherService instance.
- * @location: (nullable): The location we want weather information for.
- *
- * Registers the location to retrieve weather information from.
- */
 static void
 update_location (GcalWeatherService  *self,
                  GWeatherLocation    *location)
@@ -877,7 +520,7 @@ update_location (GcalWeatherService  *self,
        * what is going on.
        */
       gweather_info_set_enabled_providers (self->gweather_info, GWEATHER_PROVIDER_METAR | 
GWEATHER_PROVIDER_OWM | GWEATHER_PROVIDER_YR_NO);
-      g_signal_connect (self->gweather_info, "updated", (GCallback) on_gweather_update, self);
+      g_signal_connect (self->gweather_info, "updated", (GCallback) on_gweather_update_cb, self);
 
       /* gweather_info_update might or might not trigger a
        * GWeatherInfo::update() signal. Therefore, we have to
@@ -893,23 +536,13 @@ update_location (GcalWeatherService  *self,
     }
 }
 
-/**
- * update_gclue_location:
- * @self:     The #GcalWeatherService instance.
- * @location: (nullable): The location we want weather information for.
- *
- * Registers the location to retrieve weather information from.
- */
 static void
 update_gclue_location (GcalWeatherService  *self,
                        GClueLocation       *location)
 {
   GWeatherLocation *wlocation = NULL; /* owned */
 
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (location == NULL || GCLUE_IS_LOCATION (location));
-
-  if (location != NULL)
+  if (location)
     {
       GWeatherLocation *wworld; /* unowned */
       gdouble latitude;
@@ -923,593 +556,609 @@ update_gclue_location (GcalWeatherService  *self,
       wlocation = gweather_location_find_nearest_city (wworld, latitude, longitude);
     }
 
-
   update_location (self, wlocation);
 
-  if (wlocation != NULL)
-    gweather_location_unref (wlocation);
+  g_clear_pointer (&wlocation, gweather_location_unref);
 }
 
+static GSList*
+preprocess_gweather_reports (GcalWeatherService *self,
+                             GSList             *samples)
+{
+  GWeatherInfo *first_tomorrow = NULL; /* unowned */
+  GSList **days = NULL;   /* owned[owned[unowned]] */
+  GSList *result = NULL;  /* owned[owned] */
+  GSList *iter = NULL;    /* unowned */
+  GDate cur_gdate;
+  glong first_tomorrow_dtime = -1;
+  glong today_unix;
+  glong unix_now;
 
+  /* This function basically separates samples by date and calls compute_weather_info_data
+   * for every bucket to build weather infos. Each bucket represents a single day.
+   *
+   * All gweather consumers I reviewed presume sorted samples. However, there is no documented
+   * order. Lets assume the worst.
+   */
 
-/*
- * Internal Weather API
- */
+  if (self->max_days <= 0)
+    return NULL;
 
-#if PRINT_WEATHER_DATA
-static gchar*
-gwc2str (GWeatherInfo *gwi)
-{
-    g_autoptr (GDateTime) date = NULL;
-    g_autofree gchar     *date_str = NULL;
-    glong      update;
+  if (!get_time_day_start (self, &cur_gdate, &today_unix, &unix_now))
+    return NULL;
 
-    gchar     *icon_name; /* unowned */
-    gdouble    temp;
+  days = g_malloc0 (sizeof (GSList*) * self->max_days);
 
-    if (!gweather_info_get_value_update (gwi, &update))
-        return g_strdup ("<null>");
+  /* Split samples to max_days buckets: */
+  for (iter = samples; iter != NULL; iter = iter->next)
+    {
+      GWeatherInfo *gwi; /* unowned */
+      gboolean valid_date;
+      glong gwi_dtime;
+      gsize bucket;
 
-    date = g_date_time_new_from_unix_local (update);
-    date_str = g_date_time_format (date, "%F %T"),
+      gwi = GWEATHER_INFO (iter->data);
+      valid_date = gweather_info_get_value_update (gwi, &gwi_dtime);
+      if (!valid_date)
+        continue;
 
-    get_gweather_temperature (gwi, &temp);
-    icon_name = gweather_info_get_symbolic_icon_name (gwi);
+      #if PRINT_WEATHER_DATA
+      {
+        g_autofree gchar* dbg_str = gwc2str (gwi);
+        g_message ("WEATHER READING POINT: %s", dbg_str);
+      }
+      #endif
 
-    return g_strdup_printf ("(%s: t:%f, w:%s)",
-                            date_str,
-                            temp,
-                            icon_name);
-}
-#endif
+      if (gwi_dtime >= 0 && gwi_dtime >= today_unix)
+        {
+          bucket = (gwi_dtime - today_unix) / DAY_SECONDS;
+          if (bucket < self->max_days)
+            days[bucket] = g_slist_prepend (days[bucket], gwi);
 
-/* set_max_days:
- * @self: The #GcalWeatherService instance.
- * @days: Number of days.
- *
- * Setter for #GcalWeatherInfos:max-days.
- */
-static void
-set_max_days (GcalWeatherService *self,
-              guint               days)
-{
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (days >= 1);
+          if (bucket == 1 && (first_tomorrow == NULL || first_tomorrow_dtime > gwi_dtime))
+            {
+              first_tomorrow_dtime = gwi_dtime;
+              first_tomorrow = gwi;
+            }
+        }
+      else
+        {
+          g_debug ("Encountered historic weather information");
+        }
+    }
 
-  self->max_days = days;
+  if (!days[0] && first_tomorrow)
+    {
 
-  g_object_notify ((GObject*) self, "max-days");
-}
+      glong secs_left_today;
+      glong secs_between;
 
-/* set_valid_timespan:
- * @self: A #GcalWeatherService instance.
- * @timespan: Amount of seconds we consider weather information as valid.
- */
-static void
-set_valid_timespan (GcalWeatherService *self,
-                    gint64              timespan)
-{
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (timespan >= 0);
+      /* There is no data point left for today. Lets borrow one. */
+      secs_left_today = DAY_SECONDS - (unix_now - today_unix);
+      secs_between = first_tomorrow_dtime - unix_now;
 
-  self->valid_timespan = timespan;
+      if (secs_left_today < 90*60 && secs_between <= 180*60)
+        days[0] = g_slist_prepend (days[0], first_tomorrow);
+    }
 
-  g_object_notify ((GObject*) self, "valid-timespan");
-}
+  /* Produce GcalWeatherInfo for each bucket: */
+  for (int i = 0; i < self->max_days; i++)
+    {
+      g_autofree gchar *icon_name;
+      g_autofree gchar *temperature;
 
-/* has_valid_weather_infos
- * @self: A #GcalWeatherService instance.
- *
- * Checks whether weather information are available
- * and up-to-date.
- */
-static gboolean
-has_valid_weather_infos (GcalWeatherService *self)
-{
-  gint64 now;
+      if (compute_weather_info_data (days[i], i == 0, &icon_name, &temperature))
+        {
+          GcalWeatherInfo* gcwi;
 
-  g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), FALSE);
+          gcwi = gcal_weather_info_new (&cur_gdate, icon_name, temperature);
+          result = g_slist_prepend (result, g_steal_pointer (&gcwi));
+        }
 
-  if (self->gweather_info == NULL || self->weather_infos_upated < 0)
-    return FALSE;
+      g_date_add_days (&cur_gdate, 1);
+    }
 
-  now = g_get_monotonic_time ();
-  return (now - self->weather_infos_upated) / 1000000 <= self->valid_timespan;
+  /* Cleanup */
+  for (int i = 0; i < self->max_days; i++)
+    g_slist_free (days[i]);
+  g_free (days);
+
+  return result;
 }
 
-/* update_weather:
- * @self: A #GcalWeatherService instance.
- * @info: (nullable): Newly received weather information or %NULL.
- * @reuse_old_on_error: Whether to re-use old but not outdated weather
- *                      information in case we could not fetch new data.
- *
- * Retrieves weather information for @location and triggers
- * #GcalWeatherService::weather-changed.
+
+/*
+ * Callbacks
  */
+
 static void
-update_weather (GcalWeatherService *self,
-                GWeatherInfo       *info,
-                gboolean            reuse_old_on_error)
+on_network_changed_cb (GNetworkMonitor    *monitor,
+                       gboolean            available,
+                       GcalWeatherService *self)
 {
-  GSList *gwforecast = NULL; /* unowned */
-
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
-
+  gboolean is_running;
 
-  /* Compute a list of newly received weather infos. */
-  if (info == NULL)
-    {
-      g_debug ("Could not retrieve valid weather");
-    }
-  else if (gweather_info_is_valid (info))
-    {
-      g_debug ("Received valid weather information");
-      gwforecast = gweather_info_get_forecast_list (info);
-    }
-  else
-    {
-      g_autofree gchar* location_name = gweather_info_get_location_name (info);
-      g_debug ("Could not retrieve valid weather for location '%s'", location_name);
-    }
+  is_running = gcal_timer_is_running (self->duration_timer);
 
-  if (gwforecast == NULL && self->weather_infos_upated >= 0)
+  if (available && !is_running)
     {
-      if (!reuse_old_on_error || !has_valid_weather_infos (self))
-        {
-          g_slist_free_full (self->weather_infos, g_object_unref);
-          self->weather_infos = NULL;
-          self->weather_infos_upated = -1;
+      if (self->gweather_info != NULL)
+        gweather_info_update (self->gweather_info);
 
-          g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0);
-        }
+      start_timer (self);
     }
-  else if (gwforecast != NULL)
+  else if (!available && is_running)
     {
-      g_slist_free_full (self->weather_infos, g_object_unref);
-      self->weather_infos = preprocess_gweather_reports (self, gwforecast);
-      self->weather_infos_upated = g_get_monotonic_time ();
-
-      g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0);
+      stop_timer (self);
     }
 }
 
-
-
-/*
- * Internal weather update API and callbacks
- */
-
-/* set_check_interval_new:
- * @self: The #GcalWeatherService instance.
- * @days: Number of days.
- *
- * Setter for GcalWeatherInfos:check-interval-new.
- */
 static void
-set_check_interval_new (GcalWeatherService *self,
-                        guint               interval)
+on_gclue_location_changed_cb (GClueLocation      *location,
+                              GcalWeatherService *self)
 {
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (interval > 0);
-
-  self->check_interval_new = interval;
-  update_timeout_interval (self);
+  update_gclue_location (self, location);
+}
 
-  g_object_notify ((GObject*) self, "check-interval-new");
+static void
+on_gclue_client_activity_changed_cb (GClueClient        *client,
+                                     GcalWeatherService *self)
+{
+  /* Notify listeners about unknown locations: */
+  update_location (self, NULL);
 }
 
-/* set_check_interval_renew:
- * @self: The #GcalWeatherService instance.
- * @days: Number of days.
- *
- * Setter for GcalWeatherInfos:check-interval-renew.
- */
 static void
-set_check_interval_renew (GcalWeatherService *self,
-                          guint               interval)
+on_gclue_simple_creation_cb (GClueSimple        *_source,
+                             GAsyncResult       *result,
+                             GcalWeatherService *self)
 {
-  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
-  g_return_if_fail (interval > 0);
+  g_autoptr (GError) error = NULL;
+  GClueLocation *location;         /* unowned */
+  GClueSimple *location_service;   /* owned */
+  GClueClient *client;             /* unowned */
 
-  self->check_interval_renew = interval;
-  update_timeout_interval (self);
+  /* make sure we do not touch self->location_service
+   * if the current operation was cancelled.
+   */
+  location_service = gclue_simple_new_finish (result, &error);
 
-  g_object_notify ((GObject*) self, "check-interval-renew");
-}
+  if (error)
+    {
+      g_assert_null (location_service);
 
-/* drop_suffix:
- * @str: A icon name to normalize.
- *
- * Translates a given weather icon name to the
- * one to display for folded weather reports.
- *
- * Returns: Number of initial characters that
- *          belong to the normalized name.
- */
-static gssize
-get_normalized_icon_name_len (const gchar *str)
-{
-  const gchar suffix1[] = "-symbolic";
-  const gssize suffix1_len = G_N_ELEMENTS (suffix1) - 1;
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        /* Cancelled during creation. Silently fail. */;
+      else
+        g_warning ("Could not create GCLueSimple: %s", error->message);
 
-  const gchar suffix2[] = "-night";
-  const gssize suffix2_len = G_N_ELEMENTS (suffix2) - 1;
+      g_object_unref (self);
+      return;
+    }
 
-  gssize clean_len;
-  gssize str_len;
+  self->location_service = g_steal_pointer (&location_service);
 
-  g_return_val_if_fail (str != NULL, -1);
+  location = gclue_simple_get_location (self->location_service);
+  client = gclue_simple_get_client (self->location_service);
 
-  str_len = strlen (str);
+  if (location)
+    {
+      update_gclue_location (self, location);
 
-  clean_len = str_len - suffix1_len;
-  if (clean_len >= 0 && memcmp (suffix1, str + clean_len, suffix1_len) == 0)
-    str_len = clean_len;
+      g_signal_connect_object (location,
+                               "notify::location",
+                               G_CALLBACK (on_gclue_location_changed_cb),
+                               self,
+                               0);
+    }
 
-  clean_len = str_len - suffix2_len;
-  if (clean_len >= 0 && memcmp (suffix2, str + clean_len, suffix2_len) == 0)
-    str_len = clean_len;
+  g_signal_connect_object (client,
+                           "notify::active",
+                           G_CALLBACK (on_gclue_client_activity_changed_cb),
+                           self,
+                           0);
 
-  return str_len;
+  g_object_unref (self);
 }
 
-/* get_icon_name_sortkey:
- *
- * Returns a sort key for a given weather
- * icon name and -1 for unknown ones.
+/* on_gclue_client_stop:
+ * @source_object: A #GClueClient.
+ * @res:           Result of gclue_client_call_stop().
+ * @simple:        (transfer full): A #GClueSimple.
  *
- * The lower the key, the better the weather.
+ * Helper-callback used in gcal_weather_service_stop().
  */
-static gint
-get_icon_name_sortkey (const gchar *icon_name,
-                       gboolean    *supports_night_icon)
+static void
+on_gclue_client_stop (GClueClient  *client,
+                      GAsyncResult *res,
+                      GClueSimple  *simple)
 {
-  /* Note that we can't use gweathers condition
-   * field as it is not necessarily holds valid values.
-   * libgweather uses its own heuristic to determine
-   * the icon to use. String matching is still better
-   * than copying their algorithm, I guess.
-   */
-
-  gssize normalized_name_len;
+  g_autoptr(GError) error = NULL; /* owned */
+  gboolean stopped;
 
-  const GcalWeatherIconInfo icons[] =
-    { {"weather-clear",             TRUE},
-      {"weather-few-clouds",        TRUE},
-      {"weather-overcast",          FALSE},
-      {"weather-fog",               FALSE},
-      {"weather-showers-scattered", FALSE},
-      {"weather-showers",           FALSE},
-      {"weather-snow",              FALSE},
-      {"weather-storm",             FALSE},
-      {"weather-severe-alert",      FALSE}
-    };
+  stopped = gclue_client_call_stop_finish (client,
+                                           res,
+                                           &error);
+  if (error != NULL)
+    g_warning ("Could not stop location service: %s", error->message);
+  else if (!stopped)
+    g_warning ("Could not stop location service");
 
-  g_return_val_if_fail (icon_name != NULL, -1);
-  g_return_val_if_fail (supports_night_icon != NULL, -1);
+  g_object_unref (simple);
+}
 
-  *supports_night_icon = FALSE;
+static void
+on_gweather_update_cb (GWeatherInfo       *info,
+                       GcalWeatherService *self)
+{
+  update_weather (self, info, TRUE);
+}
 
-  normalized_name_len = get_normalized_icon_name_len (icon_name);
-  g_return_val_if_fail (normalized_name_len >= 0, -1);
+/* on_duration_timer_timeout
+ * @self: A #GcalWeatherService.
+ *
+ * Handles scheduled weather report updates.
+ */
+static void
+on_duration_timer_timeout (GcalTimer          *timer,
+                           GcalWeatherService *self)
+{
+  if (self->gweather_info)
+    gweather_info_update (self->gweather_info);
+}
 
-  for (int i = 0; i < G_N_ELEMENTS (icons); i++)
-    {
-      if (icons[i].name[normalized_name_len] == '\0' && strncmp (icon_name, icons[i].name, 
normalized_name_len) == 0)
-        {
-          *supports_night_icon = icons[i].night_support;
-          return i;
-        }
-    }
+/* on_midnight_timer_timeout
+ * @self: A #GcalWeatherService.
+ *
+ * Handles scheduled weather report updates.
+ */
+static void
+on_midnight_timer_timeout (GcalTimer          *timer,
+                           GcalWeatherService *self)
+{
+  if (self->gweather_info)
+    gweather_info_update (self->gweather_info);
 
-  g_warning ("Unknown weather icon '%s'", icon_name);
+  if (gcal_timer_is_running (self->duration_timer))
+    gcal_timer_reset (self->duration_timer);
 
-  return -1;
+  schedule_midnight (self);
 }
 
-/* get_time_day_start:
- * @self: The #GcalWeatherService instance.
- * @date: (out) (not nullable): A #GDate that should be set to today.
- * @unix: (out) (not nullable): A UNIX time stamp that should be set to today.
- * @ret_unix_exact (out) (not nullable): The exact date time this data point predicts.
- *
- * Provides current date in two different forms.
- *
- * Returns: %TRUE on success.
- */
-static gboolean
-get_time_day_start (GcalWeatherService *self,
-                    GDate              *ret_date,
-                    gint64             *ret_unix,
-                    gint64             *ret_unix_exact)
+#if PRINT_WEATHER_DATA
+static gchar*
+gwc2str (GWeatherInfo *gwi)
 {
-  g_autoptr (GTimeZone) zone = NULL;
-  g_autoptr (GDateTime) now = NULL;
-  g_autoptr (GDateTime) day = NULL;
+    g_autoptr (GDateTime) date = NULL;
+    g_autofree gchar     *date_str = NULL;
+    glong      update;
 
-  g_return_val_if_fail (self != NULL, FALSE);
-  g_return_val_if_fail (ret_date != NULL, FALSE);
-  g_return_val_if_fail (ret_unix != NULL, FALSE);
-  g_return_val_if_fail (ret_unix_exact != NULL, FALSE);
+    gchar     *icon_name; /* unowned */
+    gdouble    temp;
 
-  zone = (self->time_zone == NULL)
-          ? g_time_zone_new_local ()
-          : g_time_zone_ref (self->time_zone);
+    if (!gweather_info_get_value_update (gwi, &update))
+      return g_strdup ("<null>");
 
-  now = g_date_time_new_now (zone);
-  day = g_date_time_new (zone,
-                         g_date_time_get_year (now),
-                         g_date_time_get_month (now),
-                         g_date_time_get_day_of_month (now),
-                         0, 0, 0);
+    date = g_date_time_new_from_unix_local (update);
+    date_str = g_date_time_format (date, "%F %T"),
 
-  g_date_set_dmy (ret_date,
-                  g_date_time_get_day_of_month (day),
-                  g_date_time_get_month (day),
-                  g_date_time_get_year (day));
+    get_gweather_temperature (gwi, &temp);
+    icon_name = gweather_info_get_symbolic_icon_name (gwi);
 
-  *ret_unix = g_date_time_to_unix (day);
-  *ret_unix_exact = g_date_time_to_unix (now);
-  return TRUE;
+    return g_strdup_printf ("(%s: t:%f, w:%s)",
+                            date_str,
+                            temp,
+                            icon_name);
 }
+#endif
 
-/* get_gweather_temperature:
- * @gwi: #GWeatherInfo to extract temperatures from.
- * @temp: (out): extracted temperature or %NAN.
- *
- * Returns sanitized temperatures. Returned values should only
- * be used as sort key.
- *
- * Returns: %TRUE for valid temperatures.
- */
-static inline gboolean
-get_gweather_temperature (GWeatherInfo *gwi,
-                          gdouble      *temp)
+static void
+set_max_days (GcalWeatherService *self,
+              guint               days)
 {
-  gboolean valid;
-  gdouble  value;
+  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (days >= 1);
 
-  *temp = NAN;
+  self->max_days = days;
 
-  g_return_val_if_fail (gwi != NULL, FALSE);
-  g_return_val_if_fail (temp != NULL, FALSE);
+  g_object_notify ((GObject*) self, "max-days");
+}
 
-  valid = gweather_info_get_value_temp (gwi,
-                                        GWEATHER_TEMP_UNIT_DEFAULT,
-                                        &value);
+static void
+set_valid_timespan (GcalWeatherService *self,
+                    gint64              timespan)
+{
+  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (timespan >= 0);
 
-  /* TODO: Extract temperatures in Celsius and catch
-   *       implausible cases.
-   */
-  if (valid)
-    {
-      *temp = value;
-      return TRUE;
-    }
-  else
-    {
-      *temp = NAN;
-      return FALSE;
-    }
+  self->valid_timespan = timespan;
+
+  g_object_notify ((GObject*) self, "valid-timespan");
 }
 
-/* compute_weather_info_data:
- * @samples: List of received #GWeatherInfos.
- * @icon_name: (out): (transfer full): weather icon name or %NULL.
- * @temperature: (out): (transfer full): temperature and unit or %NULL.
- *
- * Computes a icon name and temperature representing @samples.
- *
- * Returns: %TRUE if there is a valid icon name and temperature.
- */
 static gboolean
-compute_weather_info_data (GSList    *samples,
-                           gboolean   is_today,
-                           gchar    **icon_name,
-                           gchar    **temperature)
+has_valid_weather_infos (GcalWeatherService *self)
 {
-  gboolean      phenomenon_supports_night_icon = FALSE;
-  gint          phenomenon_val = -1;
-  GWeatherInfo *phenomenon_gwi = NULL; /* unowned */
-  gdouble       temp_val = NAN;
-  GWeatherInfo *temp_gwi = NULL; /* unowned */
-  gboolean      has_daytime = FALSE;
-  GSList       *iter;            /* unowned */
+  gint64 now;
 
-  /* Note: I checked three different gweather consumers
-   *   and they all pick different values. So here is my
-   *   take: I pick up the worst weather for icons and
-   *   the highest temperature. I basically want to know
-   *   whether I need my umbrella for my appointment.
-   *   Not sure about the right temperature. It is probably
-   *   better to pick-up the median of all predictions
-   *   during daytime.
-   */
+  g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), FALSE);
+
+  if (self->gweather_info == NULL || self->weather_infos_upated < 0)
+    return FALSE;
 
-  g_return_val_if_fail (icon_name != NULL, FALSE);
-  g_return_val_if_fail (temperature != NULL, FALSE);
+  now = g_get_monotonic_time ();
+  return (now - self->weather_infos_upated) / 1000000 <= self->valid_timespan;
+}
 
-  for (iter = samples; iter != NULL; iter = iter->next)
-    {
-      GWeatherInfo  *gwi;       /* unowned */
-      const gchar   *icon_name; /* unowned */
-      gint           phenomenon = -1;
-      gboolean       supports_night_icon;
-      gdouble  temp;
-      gboolean valid_temp;
+/* update_weather:
+ * @self: A #GcalWeatherService instance.
+ * @info: (nullable): Newly received weather information or %NULL.
+ * @reuse_old_on_error: Whether to re-use old but not outdated weather
+ *                      information in case we could not fetch new data.
+ *
+ * Retrieves weather information for @location and triggers
+ * #GcalWeatherService::weather-changed.
+ */
+static void
+update_weather (GcalWeatherService *self,
+                GWeatherInfo       *info,
+                gboolean            reuse_old_on_error)
+{
+  GSList *gwforecast = NULL; /* unowned */
 
-      gwi = GWEATHER_INFO (iter->data);
+  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
 
-      icon_name = gweather_info_get_icon_name (gwi);
-      if (icon_name != NULL)
-        phenomenon  = get_icon_name_sortkey (icon_name, &supports_night_icon);
 
-      valid_temp = get_gweather_temperature (gwi, &temp);
+  /* Compute a list of newly received weather infos. */
+  if (!info)
+    {
+      g_debug ("Could not retrieve valid weather");
+    }
+  else if (gweather_info_is_valid (info))
+    {
+      g_debug ("Received valid weather information");
+      gwforecast = gweather_info_get_forecast_list (info);
+    }
+  else
+    {
+      g_autofree gchar* location_name = gweather_info_get_location_name (info);
+      g_debug ("Could not retrieve valid weather for location '%s'", location_name);
+    }
 
-      if (phenomenon >= 0 && (phenomenon_gwi == NULL || phenomenon > phenomenon_val))
+  if (!gwforecast && self->weather_infos_upated >= 0)
+    {
+      if (!reuse_old_on_error || !has_valid_weather_infos (self))
         {
-          phenomenon_supports_night_icon = supports_night_icon;
-          phenomenon_val = phenomenon;
-          phenomenon_gwi = gwi;
-        }
+          g_slist_free_full (self->weather_infos, g_object_unref);
+          self->weather_infos = NULL;
+          self->weather_infos_upated = -1;
 
-      if (valid_temp && (temp_gwi == NULL || temp > temp_val))
-        {
-          temp_val = temp;
-          temp_gwi = gwi;
+          g_signal_emit (self, signals[SIG_WEATHER_CHANGED], 0);
         }
-
-      if (gweather_info_is_daytime (gwi))
-        has_daytime = TRUE;
-    }
-
-  if (phenomenon_gwi != NULL && temp_gwi != NULL)
-    {
-      *icon_name = get_normalized_icon_name (phenomenon_gwi, is_today && !has_daytime && 
phenomenon_supports_night_icon);
-      *temperature = gweather_info_get_temp (temp_gwi);
-      return TRUE;
     }
-  else
+  else if (gwforecast)
     {
-      /* empty list */
-      *icon_name = NULL;
-      *temperature = NULL;
-      return FALSE;
+      g_slist_free_full (self->weather_infos, g_object_unref);
+      self->weather_infos = preprocess_gweather_reports (self, gwforecast);
+      self->weather_infos_upated = g_get_monotonic_time ();
+
+      g_signal_emit (self, signals[SIG_WEATHER_CHANGED], 0);
     }
 }
 
-/* preprocess_gweather_reports:
- * @self:     The #GcalWeatherService instance.
- * @samples:  Received list of #GWeatherInfos
- *
- * Computes weather info objects representing specific days
- * by combining given #GWeatherInfos.
- *
- * Returns: (transfer full): List of up to $self->max_days #GcalWeatherInfos.
+
+/*
+ * GObject overrides
  */
-static GSList*
-preprocess_gweather_reports (GcalWeatherService *self,
-                             GSList             *samples)
+
+static void
+gcal_weather_service_finalize (GObject *object)
 {
-  const glong DAY_SECONDS = 24*60*60;
-  GSList  *result = NULL; /* owned[owned] */
-  GSList **days;          /* owned[owned[unowned]] */
-  GSList *iter;           /* unowned */
-  GDate cur_gdate;
-  glong today_unix;
-  glong unix_now;
+  GcalWeatherService *self = (GcalWeatherService *) object;
 
-  GWeatherInfo *first_tomorrow = NULL; /* unowned */
-  glong         first_tomorrow_dtime = -1;
+  g_cancellable_cancel (self->location_cancellable);
 
-  g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), NULL);
+  g_clear_pointer (&self->duration_timer, gcal_timer_free);
+  g_clear_pointer (&self->midnight_timer, gcal_timer_free);
+  g_clear_pointer (&self->timezone, g_time_zone_unref);
 
-  /* This function basically separates samples by
-   * date and calls compute_weather_info_data for
-   * every bucket to build weather infos.
-   * Each bucket represents a single day.
-   */
+  g_clear_object (&self->gweather_info);
+  g_clear_object (&self->location_service);
+  g_clear_object (&self->location_cancellable);
 
-  /* All gweather consumers I reviewed presume
-   * sorted samples. However, there is no documented
-   * order. Lets assume the worst.
-   */
+  g_slist_free_full (self->weather_infos, g_object_unref);
 
-  if (self->max_days <= 0)
-    return NULL;
+  if (self->network_changed_sid > 0)
+    g_signal_handler_disconnect (g_network_monitor_get_default (), self->network_changed_sid);
 
-  if (!get_time_day_start (self, &cur_gdate, &today_unix, &unix_now))
-    return NULL;
+  G_OBJECT_CLASS (gcal_weather_service_parent_class)->finalize (object);
+}
 
-  days = g_malloc0 (sizeof (GSList*) * self->max_days);
+static void
+gcal_weather_service_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  GcalWeatherService *self = (GcalWeatherService *) object;
 
-  /* Split samples to max_days buckets: */
-  for (iter = samples; iter != NULL; iter = iter->next)
+  switch (prop_id)
     {
-      GWeatherInfo *gwi; /* unowned */
-      gboolean valid_date;
-      glong gwi_dtime;
-      gsize bucket;
+    case PROP_MAX_DAYS:
+      g_value_set_uint (value, gcal_weather_service_get_max_days (self));
+      break;
 
-      gwi = GWEATHER_INFO (iter->data);
-      valid_date = gweather_info_get_value_update (gwi, &gwi_dtime);
-      if (!valid_date)
-        continue;
+    case PROP_TIME_ZONE:
+      g_value_set_pointer (value, gcal_weather_service_get_time_zone (self));
+      break;
 
-      #if PRINT_WEATHER_DATA
-      {
-        g_autofree gchar* dbg_str = gwc2str (gwi);
-        g_message ("WEATHER READING POINT: %s", dbg_str);
-      }
-      #endif
+    case PROP_CHECK_INTERVAL_NEW:
+      g_value_set_uint (value, gcal_weather_service_get_check_interval_new (self));
+      break;
 
-      if (gwi_dtime >= 0 && gwi_dtime >= today_unix)
-        {
-          bucket = (gwi_dtime - today_unix) / DAY_SECONDS;
-          if (bucket >= 0 && bucket < self->max_days)
-            days[bucket] = g_slist_prepend (days[bucket], gwi);
+    case PROP_CHECK_INTERVAL_RENEW:
+      g_value_set_uint (value, gcal_weather_service_get_check_interval_renew (self));
+      break;
 
-          if (bucket == 1 && (first_tomorrow == NULL || first_tomorrow_dtime > gwi_dtime))
-            {
-              first_tomorrow_dtime = gwi_dtime;
-              first_tomorrow = gwi;
-            }
-        }
-      else
-        {
-          g_debug ("Encountered historic weather information");
-        }
+    case PROP_VALID_TIMESPAN:
+      g_value_set_int64 (value, gcal_weather_service_get_valid_timespan (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
     }
+}
+
+static void
+gcal_weather_service_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  GcalWeatherService *self = (GcalWeatherService *) object;
 
-  if (days[0] == NULL && first_tomorrow != NULL)
+  switch (prop_id)
     {
-        /* There is no data point left for today.
-         * Lets borrow one.
-         */
-       glong secs_left_today;
-       glong secs_between;
+    case PROP_MAX_DAYS:
+      set_max_days (self, g_value_get_uint (value));
+      break;
+
+    case PROP_TIME_ZONE:
+      gcal_weather_service_set_time_zone (self, g_value_get_pointer (value));
+      break;
+
+    case PROP_CHECK_INTERVAL_NEW:
+      set_check_interval_new (self, g_value_get_uint (value));
+      break;
 
-       secs_left_today = DAY_SECONDS - (unix_now - today_unix);
-       secs_between = first_tomorrow_dtime - unix_now;
+    case PROP_CHECK_INTERVAL_RENEW:
+      set_check_interval_renew (self, g_value_get_uint (value));
+      break;
 
-       if (secs_left_today < 90*60 && secs_between <= 180*60)
-         days[0] = g_slist_prepend (days[0], first_tomorrow);
+    case PROP_VALID_TIMESPAN:
+      set_valid_timespan (self, g_value_get_int64 (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
     }
+}
 
-  /* Produce GcalWeatherInfo for each bucket: */
-  for (int i = 0; i < self->max_days; i++)
-    {
-      g_autofree gchar *icon_name;
-      g_autofree gchar *temperature;
+static void
+gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
+{
+  GObjectClass *object_class;
 
-      if (compute_weather_info_data (days[i], i == 0, &icon_name, &temperature))
-        {
-          GcalWeatherInfo* gcwi; /* owned */
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = gcal_weather_service_finalize;
+  object_class->get_property = gcal_weather_service_get_property;
+  object_class->set_property = gcal_weather_service_set_property;
 
-          gcwi = gcal_weather_info_new (&cur_gdate,
-                                        icon_name,
-                                        temperature);
+  /**
+   * GcalWeatherService:max-days:
+   *
+   * Maximal number of days to fetch forecasts for.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_MAX_DAYS,
+       g_param_spec_uint ("max-days", "max-days", "max-days",
+                          1, G_MAXUINT, 3,
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
-          result = g_slist_prepend (result,
-                                    g_steal_pointer (&gcwi));
-        }
+  /**
+   * GcalWeatherService:time-zone:
+   *
+   * The time zone to use.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_TIME_ZONE,
+       g_param_spec_pointer ("time-zone", "time-zone", "time-zone",
+                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
 
-      g_date_add_days (&cur_gdate, 1);
-    }
+  /**
+   * GcalWeatherService:check-interval-new:
+   *
+   * Amount of seconds to wait before re-fetching weather infos.
+   * This interval is used when no valid weather reports are available.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_CHECK_INTERVAL_NEW,
+       g_param_spec_uint ("check-interval-new", "check-interval-new", "check-interval-new",
+                          0, G_MAXUINT, GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT,
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
-  /* Cleanup: */
-  for (int i = 0; i < self->max_days; i++)
-    g_slist_free (days[i]);
-  g_free (days);
+  /**
+   * GcalWeatherService:check-interval-renew:
+   *
+   * Amount of seconds to wait before re-fetching weather information.
+   * This interval is used when valid weather reports are available.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_CHECK_INTERVAL_RENEW,
+       g_param_spec_uint ("check-interval-renew", "check-interval-renew", "check-interval-renew",
+                          0, G_MAXUINT, GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT,
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
-  return result;
+  /**
+   * GcalWeatherService:valid_timespan:
+   *
+   * Amount of seconds fetched weather information are considered as valid.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_VALID_TIMESPAN,
+       g_param_spec_int64 ("valid-timespan", "valid-timespan", "valid-timespan",
+                           0, G_MAXINT64, GCAL_WEATHER_VALID_TIMESPAN_DFLT,
+                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+
+  /**
+   * GcalWeatherService::weather-changed:
+   * @sender: The #GcalWeatherService
+   * @self:   data pointer.
+   *
+   * Triggered on weather changes. Call
+   * gcal_weather_service_get_weather_infos() to
+   * retrieve predictions.
+   */
+  signals[SIG_WEATHER_CHANGED] = g_signal_new ("weather-changed",
+                                               GCAL_TYPE_WEATHER_SERVICE,
+                                               G_SIGNAL_RUN_LAST,
+                                               0,
+                                               NULL,
+                                               NULL,
+                                               g_cclosure_marshal_VOID__VOID,
+                                               G_TYPE_NONE,
+                                               0);
 }
 
+static void
+gcal_weather_service_init (GcalWeatherService *self)
+{
+  self->check_interval_new = GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT;
+  self->check_interval_renew = GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT;
+  self->location_cancellable = g_cancellable_new ();
+  self->weather_infos_upated = -1;
+  self->valid_timespan = -1;
+
+  self->duration_timer = gcal_timer_new (GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT);
+  gcal_timer_set_callback (self->duration_timer, (GCalTimerFunc) on_duration_timer_timeout, self, NULL);
+
+  self->midnight_timer = gcal_timer_new (24 * 60 * 60);
+  gcal_timer_set_callback (self->midnight_timer, (GCalTimerFunc) on_midnight_timer_timeout, self, NULL);
 
-/*************
- * < public >
- *************/
+  self->network_changed_sid = g_signal_connect (g_network_monitor_get_default (),
+                                                "network-changed",
+                                                G_CALLBACK (on_network_changed_cb),
+                                                self);
+}
 
 /**
  * gcal_weather_service_new:
@@ -1548,7 +1197,8 @@ GTimeZone*
 gcal_weather_service_get_time_zone (GcalWeatherService *self)
 {
   g_return_val_if_fail (self != NULL, NULL);
-  return self->time_zone;
+
+  return self->timezone;
 }
 
 /**
@@ -1564,25 +1214,19 @@ gcal_weather_service_set_time_zone (GcalWeatherService *self,
 {
   g_return_if_fail (self != NULL);
 
-  if (self->time_zone != value)
-    {
-      if (self->time_zone != NULL)
-        {
-          g_time_zone_unref (self->time_zone);
-          self->time_zone = NULL;
-        }
+  if (self->timezone == value)
+    return;
 
-      if (value != NULL)
-        self->time_zone = g_time_zone_ref (value);
+  g_clear_pointer (&self->timezone, g_time_zone_unref);
+  self->timezone = value ? g_time_zone_ref (value) : NULL;
 
-      /* make sure we provide correct weather infos */
-      gweather_info_update (self->gweather_info);
+  /* make sure we provide correct weather infos */
+  gweather_info_update (self->gweather_info);
 
-      /* make sure midnight is timed correctly: */
-      schedule_midnight (self);
+  /* make sure midnight is timed correctly: */
+  schedule_midnight (self);
 
-      g_object_notify ((GObject*) self, "time-zone");
-    }
+  g_object_notify ((GObject*) self, "time-zone");
 }
 
 /**
@@ -1688,6 +1332,7 @@ gcal_weather_service_update (GcalWeatherService *self)
     {
       gweather_info_update (self->gweather_info);
       update_timeout_interval (self);
+
       if (gcal_timer_is_running (self->duration_timer))
         gcal_timer_reset (self->duration_timer);
     }
@@ -1707,22 +1352,24 @@ gcal_weather_service_run (GcalWeatherService *self,
 {
   g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
 
-  g_debug ("Start weather service");
+  g_debug ("Starting weather service");
 
   if (self->location_service_running || self->weather_service_running)
     gcal_weather_service_stop (self);
 
-  if (location == NULL)
+  if (!location)
     {
       /* Start location and weather service: */
       self->location_service_running = TRUE;
       self->weather_service_running = TRUE;
+
       g_cancellable_cancel (self->location_cancellable);
       g_cancellable_reset (self->location_cancellable);
+
       gclue_simple_new (DESKTOP_FILE_NAME,
                         GCLUE_ACCURACY_LEVEL_EXACT,
                         self->location_cancellable,
-                        (GAsyncReadyCallback) on_gclue_simple_creation,
+                        (GAsyncReadyCallback) on_gclue_simple_creation_cb,
                         g_object_ref (self));
     }
   else
@@ -1756,21 +1403,17 @@ gcal_weather_service_stop (GcalWeatherService *self)
   self->location_service_running = FALSE;
   self->weather_service_running = FALSE;
 
-  /* Notify all listeners about unknown location: */
+  /* Notify all listeners about unknown location */
   update_location (self, NULL);
 
-  if (self->location_service == NULL)
+  if (!self->location_service)
     {
       /* location service is under construction. Cancel creation. */
       g_cancellable_cancel (self->location_cancellable);
     }
   else
     {
-      GClueClient *client; /* unowned */
-
-      client = gclue_simple_get_client (self->location_service);
-
-      gclue_client_call_stop (client,
+      gclue_client_call_stop (gclue_simple_get_client (self->location_service),
                               NULL,
                               (GAsyncReadyCallback) on_gclue_client_stop,
                               g_steal_pointer (&self->location_service));


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