aboutsummaryrefslogtreecommitdiff
path: root/src/clock-live.c
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-10-24 04:15:20 +0000
committerTarmac <Unknown>2013-10-24 04:15:20 +0000
commit9e2f24172c1fa388e2be2938998b20f5bf3a8241 (patch)
tree3cf94298d909590579d5171a7bc95b5a0608efd3 /src/clock-live.c
parente38b6293bb37557d27efd052c82ee44d70996077 (diff)
parent6da162acd4a293a1cfe1933c54f6b06935deebbf (diff)
downloadayatana-indicator-datetime-9e2f24172c1fa388e2be2938998b20f5bf3a8241.tar.gz
ayatana-indicator-datetime-9e2f24172c1fa388e2be2938998b20f5bf3a8241.tar.bz2
ayatana-indicator-datetime-9e2f24172c1fa388e2be2938998b20f5bf3a8241.zip
Make timezone detection and localtime mockable. Fixes: https://bugs.launchpad.net/bugs/1237509.
Approved by PS Jenkins bot, Ted Gould.
Diffstat (limited to 'src/clock-live.c')
-rw-r--r--src/clock-live.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/src/clock-live.c b/src/clock-live.c
new file mode 100644
index 0000000..4153747
--- /dev/null
+++ b/src/clock-live.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <glib.h>
+#include <gio/gio.h>
+
+#include "config.h"
+
+#include "clock-live.h"
+#include "settings-shared.h"
+#include "timezone-file.h"
+#include "timezone-geoclue.h"
+
+/***
+**** private struct
+***/
+
+struct _IndicatorDatetimeClockLivePriv
+{
+ GSettings * settings;
+
+ GSList * timezones; /* IndicatorDatetimeTimezone */
+ gchar ** timezones_strv;
+ GTimeZone * localtime_zone;
+};
+
+typedef IndicatorDatetimeClockLivePriv priv_t;
+
+/***
+**** GObject boilerplate
+***/
+
+static void indicator_datetime_clock_interface_init (
+ IndicatorDatetimeClockInterface * iface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ IndicatorDatetimeClockLive,
+ indicator_datetime_clock_live,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_DATETIME_CLOCK,
+ indicator_datetime_clock_interface_init));
+
+/***
+**** Timezones
+***/
+
+static void
+on_current_timezone_changed (IndicatorDatetimeClockLive * self)
+{
+ priv_t * p = self->priv;
+
+ /* Invalidate the timezone information.
+ These fields will be lazily regenerated by rebuild_timezones() */
+ g_clear_pointer (&p->timezones_strv, g_strfreev);
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+
+ indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self));
+}
+
+static void
+set_detect_location_enabled (IndicatorDatetimeClockLive * self, gboolean enabled)
+{
+ GSList * l;
+ priv_t * p = self->priv;
+ gboolean changed = FALSE;
+
+ /* clear out the old timezone objects */
+ if (p->timezones != NULL)
+ {
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ g_signal_handlers_disconnect_by_func (l->data, on_current_timezone_changed, self);
+ g_object_unref (l->data);
+ }
+
+ g_slist_free (p->timezones);
+ p->timezones = NULL;
+ changed = TRUE;
+ }
+
+ /* maybe add new timezone objects */
+ if (enabled)
+ {
+ p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_geoclue_new ());
+ p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_file_new (TIMEZONE_FILE));
+
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ g_signal_connect_swapped (l->data, "notify::timezone",
+ G_CALLBACK(on_current_timezone_changed), self);
+ }
+
+ changed = TRUE;
+ }
+
+ if (changed)
+ on_current_timezone_changed (self);
+}
+
+/* When the 'auto-detect timezone' boolean setting changes,
+ start or stop watching geoclue and /etc/timezone */
+static void
+on_detect_location_changed (IndicatorDatetimeClockLive * self)
+{
+ const gboolean enabled = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_DETECTED_S);
+ set_detect_location_enabled (self, enabled);
+}
+
+/***
+**** IndicatorDatetimeClock virtual functions
+***/
+
+static void
+rebuild_timezones (IndicatorDatetimeClockLive * self)
+{
+ priv_t * p;
+ GHashTable * hash;
+ GSList * l;
+ int i;
+ GHashTableIter iter;
+ gpointer key;
+
+ p = self->priv;
+
+ /* Build a hashtable of timezone strings.
+ This will weed out duplicates. */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ const gchar * tz = indicator_datetime_timezone_get_timezone (l->data);
+ if (tz && *tz)
+ g_hash_table_add (hash, (gpointer) tz);
+ }
+
+ /* rebuild p->timezone_strv */
+ g_strfreev (p->timezones_strv);
+ p->timezones_strv = g_new0 (gchar*, g_hash_table_size(hash) + 1);
+ i = 0;
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ p->timezones_strv[i++] = g_strdup (key);
+
+ /* rebuild localtime_zone */
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+ p->localtime_zone = g_time_zone_new (p->timezones_strv ? p->timezones_strv[0] : NULL);
+
+ /* cleanup */
+ g_hash_table_unref (hash);
+}
+
+static const gchar **
+my_get_timezones (IndicatorDatetimeClock * clock)
+{
+ IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ priv_t * p = self->priv;
+
+ if (G_UNLIKELY (p->timezones_strv == NULL))
+ rebuild_timezones (self);
+
+ return (const gchar **) p->timezones_strv;
+}
+
+static GDateTime *
+my_get_localtime (IndicatorDatetimeClock * clock)
+{
+ IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ priv_t * p = self->priv;
+
+ if (G_UNLIKELY (p->localtime_zone == NULL))
+ rebuild_timezones (self);
+
+ return g_date_time_new_now (p->localtime_zone);
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+
+ if (p->settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->settings, self);
+ g_clear_object (&p->settings);
+ }
+
+ set_detect_location_enabled (self, FALSE);
+
+ G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+ g_strfreev (p->timezones_strv);
+
+ G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_datetime_clock_live_class_init (IndicatorDatetimeClockLiveClass * klass)
+{
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+
+ g_type_class_add_private (klass,
+ sizeof (IndicatorDatetimeClockLivePriv));
+}
+
+static void
+indicator_datetime_clock_interface_init (IndicatorDatetimeClockInterface * iface)
+{
+ iface->get_localtime = my_get_localtime;
+ iface->get_timezones = my_get_timezones;
+}
+
+static void
+indicator_datetime_clock_live_init (IndicatorDatetimeClockLive * self)
+{
+ IndicatorDatetimeClockLivePriv * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE,
+ IndicatorDatetimeClockLivePriv);
+ self->priv = p;
+
+ p->settings = g_settings_new (SETTINGS_INTERFACE);
+ g_signal_connect (p->settings, "changed::" SETTINGS_SHOW_DETECTED_S,
+ G_CALLBACK(on_detect_location_changed), self);
+
+
+ on_detect_location_changed (self);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorDatetimeClock *
+indicator_datetime_clock_live_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_CLOCK_LIVE, NULL);
+
+ return INDICATOR_DATETIME_CLOCK (o);
+}