aboutsummaryrefslogtreecommitdiff
path: root/src/clock-live.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/clock-live.c')
-rw-r--r--src/clock-live.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/clock-live.c b/src/clock-live.c
new file mode 100644
index 0000000..6e694ed
--- /dev/null
+++ b/src/clock-live.c
@@ -0,0 +1,306 @@
+/*
+ * 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;
+
+ /* cached GTimeZone for use by indicator_datetime_service_get_localtime() */
+ GTimeZone * internal_timezone;
+
+ IndicatorDatetimeTimezone * tz_file;
+ IndicatorDatetimeTimezone * tz_geoclue;
+};
+
+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));
+
+/***
+**** Convenience func
+***/
+
+#if 0
+static void
+emit_changed (IndicatorDatetimeClockLive * self)
+{
+ indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self));
+}
+#endif
+
+/***
+**** Timezones
+***/
+
+static void
+update_internal_timezone (IndicatorDatetimeClockLive * self)
+{
+ priv_t * p = self->priv;
+ const char * id;
+
+ /* find the id from tz_file or tz_geoclue if possible; NULL otherwise */
+ id = NULL;
+ if (p->tz_file != NULL )
+ id = indicator_datetime_timezone_get_timezone (p->tz_file);
+ if (!id && p->tz_geoclue)
+ id = indicator_datetime_timezone_get_timezone (p->tz_geoclue);
+
+ g_clear_pointer (&p->internal_timezone, g_time_zone_unref);
+ p->internal_timezone = g_time_zone_new (id);
+}
+
+static void
+on_current_timezone_changed (IndicatorDatetimeClockLive * self)
+{
+ indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self));
+}
+
+static void
+set_detect_location_enabled (IndicatorDatetimeClockLive * self, gboolean enabled)
+{
+ priv_t * p = self->priv;
+ gboolean changed = FALSE;
+
+ /* geoclue */
+
+ if (!p->tz_geoclue && enabled)
+ {
+ p->tz_geoclue = indicator_datetime_timezone_geoclue_new ();
+ g_signal_connect_swapped (p->tz_geoclue, "notify::timezone",
+ G_CALLBACK(on_current_timezone_changed),
+ self);
+ changed = TRUE;
+ }
+ else if (p->tz_geoclue && !enabled)
+ {
+ g_signal_handlers_disconnect_by_func (p->tz_geoclue,
+ on_current_timezone_changed,
+ self);
+ g_clear_object (&p->tz_geoclue);
+ changed = TRUE;
+ }
+
+ /* timezone file */
+
+ if (!p->tz_file && enabled)
+ {
+ p->tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE);
+ g_signal_connect_swapped (p->tz_file, "notify::timezone",
+ G_CALLBACK(on_current_timezone_changed),
+ self);
+ changed = TRUE;
+ }
+ else if (p->tz_file && !enabled)
+ {
+ g_signal_handlers_disconnect_by_func (p->tz_file,
+ on_current_timezone_changed,
+ self);
+ g_clear_object (&p->tz_file);
+ 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 gchar **
+my_get_timezones (IndicatorDatetimeClock * clock G_GNUC_UNUSED)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+ GHashTable * hash;
+ gchar ** timezones;
+ int i;
+ GHashTableIter iter;
+ gpointer key;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ p = self->priv;
+
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (p->tz_file != NULL)
+ {
+ const gchar * tz = indicator_datetime_timezone_get_timezone (p->tz_file);
+ if (tz && *tz)
+ g_hash_table_add (hash, (gpointer) tz);
+ }
+
+ if (p->tz_geoclue != NULL)
+ {
+ const gchar * tz = indicator_datetime_timezone_get_timezone (p->tz_geoclue);
+ if (tz && *tz)
+ g_hash_table_add (hash, (gpointer) tz);
+ }
+
+ timezones = 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))
+ timezones[i++] = g_strdup (key);
+ g_hash_table_unref (hash);
+ return timezones;
+}
+
+static GDateTime *
+my_get_current_time (IndicatorDatetimeClock * clock)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ p = self->priv;
+
+ if (G_UNLIKELY (p->internal_timezone == NULL))
+ update_internal_timezone (self);
+
+ return g_date_time_new_now (p->internal_timezone);
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+
+ g_clear_pointer (&p->internal_timezone, g_time_zone_unref);
+
+ 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)
+{
+#if 0
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+#endif
+
+ 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_timezones = my_get_timezones;
+ iface->get_current_time = my_get_current_time;
+}
+
+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);
+}