aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/Makefile.am6
-rw-r--r--src/clock-live.c281
-rw-r--r--src/clock-live.h73
-rw-r--r--src/clock.c110
-rw-r--r--src/clock.h76
-rw-r--r--src/main.c43
-rw-r--r--src/service.c365
-rw-r--r--src/service.h10
-rw-r--r--src/timezone.h2
-rw-r--r--tests/planner-mock.c (renamed from src/planner-mock.c)0
-rw-r--r--tests/planner-mock.h (renamed from src/planner-mock.h)0
11 files changed, 743 insertions, 223 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 640650a..be7eb4d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,10 +18,12 @@ libindicator_datetime_service_a_CFLAGS = \
$(SHARED_CFLAGS)
libindicator_datetime_service_a_SOURCES = \
+ clock.c \
+ clock.h \
+ clock-live.c \
+ clock-live.h \
planner.c \
planner.h \
- planner-mock.c \
- planner-mock.h \
planner-eds.c \
planner-eds.h \
service.c \
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);
+}
diff --git a/src/clock-live.h b/src/clock-live.h
new file mode 100644
index 0000000..4425f5b
--- /dev/null
+++ b/src/clock-live.h
@@ -0,0 +1,73 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __INDICATOR_DATETIME_CLOCK_LIVE__H__
+#define __INDICATOR_DATETIME_CLOCK_LIVE__H__
+
+#include <glib-object.h> /* parent class */
+
+#include "clock.h"
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_CLOCK_LIVE \
+ (indicator_datetime_clock_live_get_type())
+
+#define INDICATOR_DATETIME_CLOCK_LIVE(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \
+ IndicatorDatetimeClockLive))
+
+#define INDICATOR_DATETIME_CLOCK_LIVE_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \
+ IndicatorDatetimeClockLiveClass))
+
+#define INDICATOR_IS_DATETIME_CLOCK_LIVE(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE))
+
+typedef struct _IndicatorDatetimeClockLive
+ IndicatorDatetimeClockLive;
+typedef struct _IndicatorDatetimeClockLivePriv
+ IndicatorDatetimeClockLivePriv;
+typedef struct _IndicatorDatetimeClockLiveClass
+ IndicatorDatetimeClockLiveClass;
+
+/**
+ * An IndicatorDatetimeClock which gives live clock times
+ * from timezones determined by geoclue and /etc/timezone
+ */
+struct _IndicatorDatetimeClockLive
+{
+ GObject parent_instance;
+
+ IndicatorDatetimeClockLivePriv * priv;
+};
+
+struct _IndicatorDatetimeClockLiveClass
+{
+ GObjectClass parent_class;
+};
+
+IndicatorDatetimeClock * indicator_datetime_clock_live_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_CLOCK_LIVE__H__ */
diff --git a/src/clock.c b/src/clock.c
new file mode 100644
index 0000000..2c2fec2
--- /dev/null
+++ b/src/clock.c
@@ -0,0 +1,110 @@
+/*
+ * 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 "clock.h"
+
+enum
+{
+ SIGNAL_CHANGED,
+ SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+G_DEFINE_INTERFACE (IndicatorDatetimeClock,
+ indicator_datetime_clock,
+ G_TYPE_OBJECT);
+
+static void
+indicator_datetime_clock_default_init (IndicatorDatetimeClockInterface * klass)
+{
+ signals[SIGNAL_CHANGED] = g_signal_new (
+ "changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IndicatorDatetimeClockInterface, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/***
+**** PUBLIC API
+***/
+
+/**
+ * Get a strv of timezones.
+ *
+ * Return value: (element-type char*)
+ * (transfer full):
+ * array of timezone strings
+ */
+const gchar **
+indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * self)
+{
+ const gchar ** timezones;
+ IndicatorDatetimeClockInterface * iface;
+
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL);
+ iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self);
+
+ if (iface->get_timezones != NULL)
+ timezones = iface->get_timezones (self);
+ else
+ timezones = NULL;
+
+ return timezones;
+}
+
+/**
+ * Get the current time.
+ *
+ * Return value: (element-type GDateTime*)
+ * (transfer full):
+ * the current time.
+ */
+GDateTime *
+indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * self)
+{
+ GDateTime * now;
+ IndicatorDatetimeClockInterface * iface;
+
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL);
+ iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self);
+
+ if (iface->get_localtime != NULL)
+ now = iface->get_localtime (self);
+ else
+ now = NULL;
+
+ return now;
+}
+
+/**
+ * Emits the "changed" signal.
+ *
+ * This should only be called by subclasses.
+ */
+void
+indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * self)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_CLOCK (self));
+
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+}
diff --git a/src/clock.h b/src/clock.h
new file mode 100644
index 0000000..40cdf1c
--- /dev/null
+++ b/src/clock.h
@@ -0,0 +1,76 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __INDICATOR_DATETIME_CLOCK__H__
+#define __INDICATOR_DATETIME_CLOCK__H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_CLOCK \
+ (indicator_datetime_clock_get_type ())
+
+#define INDICATOR_DATETIME_CLOCK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ INDICATOR_TYPE_DATETIME_CLOCK, \
+ IndicatorDatetimeClock))
+
+#define INDICATOR_IS_DATETIME_CLOCK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DATETIME_CLOCK))
+
+#define INDICATOR_DATETIME_CLOCK_GET_INTERFACE(inst) \
+ (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
+ INDICATOR_TYPE_DATETIME_CLOCK, \
+ IndicatorDatetimeClockInterface))
+
+typedef struct _IndicatorDatetimeClock
+ IndicatorDatetimeClock;
+
+typedef struct _IndicatorDatetimeClockInterface
+ IndicatorDatetimeClockInterface;
+
+struct _IndicatorDatetimeClockInterface
+{
+ GTypeInterface parent_iface;
+
+ /* signals */
+ void (*changed) (IndicatorDatetimeClock * self);
+
+ /* virtual functions */
+ const gchar** (*get_timezones) (IndicatorDatetimeClock * self);
+ GDateTime* (*get_localtime) (IndicatorDatetimeClock * self);
+};
+
+GType indicator_datetime_clock_get_type (void);
+
+/***
+****
+***/
+
+const gchar ** indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * clock);
+
+GDateTime * indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * clock);
+
+void indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * clock);
+
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_CLOCK__H__ */
diff --git a/src/main.c b/src/main.c
index f791683..dc08419 100644
--- a/src/main.c
+++ b/src/main.c
@@ -26,36 +26,26 @@
#include <gio/gio.h>
#include <libnotify/notify.h>
+#include "clock-live.h"
#include "planner-eds.h"
-#include "planner-mock.h"
#include "service.h"
/***
****
***/
-/* When enabled, new alarms will show up every minute to test snap decisions */
-static gboolean test_alarms = FALSE;
-
-static GOptionEntry entries[] = {
- { "test-alarms", '\0', 0, G_OPTION_ARG_NONE, &test_alarms, "Test Alarms", NULL },
- { NULL }
-};
-
static void
on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
{
g_message ("exiting: service couldn't acquire or lost ownership of busname");
- if (!test_alarms)
- g_main_loop_quit ((GMainLoop*)loop);
+ g_main_loop_quit ((GMainLoop*)loop);
}
int
main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
{
- GOptionContext * context;
- GError * error;
+ IndicatorDatetimeClock * clock;
IndicatorDatetimePlanner * planner;
IndicatorDatetimeService * service;
GMainLoop * loop;
@@ -69,36 +59,21 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
if (!notify_init ("indicator-datetime-service"))
g_critical ("libnotify initialization failed");
- /* parse command-line options */
- context = g_option_context_new (NULL);
- g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
- if (!g_option_context_parse (context, &argc, &argv, &error))
- {
- g_print("option parsing failed: %s\n", error->message);
- return EXIT_FAILURE;
- }
-
- /* set up the planner */
- if (test_alarms)
- {
- g_message ("Using fake appointment book for testing alarms.");
- planner = indicator_datetime_planner_mock_new ();
- }
- else
- {
- planner = indicator_datetime_planner_eds_new ();
- }
+ /* create the service */
+ clock = indicator_datetime_clock_live_new ();
+ planner = indicator_datetime_planner_eds_new ();
+ service = indicator_datetime_service_new (clock, planner);
/* run */
- service = indicator_datetime_service_new (planner);
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST,
G_CALLBACK(on_name_lost), loop);
g_main_loop_run (loop);
+ g_main_loop_unref (loop);
/* cleanup */
- g_main_loop_unref (loop);
g_object_unref (service);
g_object_unref (planner);
+ g_object_unref (clock);
return 0;
}
diff --git a/src/service.c b/src/service.c
index 08945bc..5fffc11 100644
--- a/src/service.c
+++ b/src/service.c
@@ -29,8 +29,6 @@
#include <url-dispatcher.h>
#include "dbus-shared.h"
-#include "timezone-file.h"
-#include "timezone-geoclue.h"
#include "service.h"
#include "settings-shared.h"
#include "utils.h"
@@ -54,6 +52,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum
{
PROP_0,
+ PROP_CLOCK,
PROP_PLANNER,
PROP_LAST
};
@@ -101,16 +100,14 @@ struct _IndicatorDatetimeServicePrivate
GSettings * settings;
- IndicatorDatetimeTimezone * tz_file;
- IndicatorDatetimeTimezone * tz_geoclue;
+ IndicatorDatetimeClock * clock;
IndicatorDatetimePlanner * planner;
- /* cached GTimeZone for use by indicator_datetime_service_get_localtime() */
- GTimeZone * internal_timezone;
-
/* the clock app's icon filename */
gchar * clock_app_icon_filename;
+ gchar * header_label_format_string;
+
/* Whether or not we've tried to load the clock app's icon.
This way we don't keep trying to reload it on the desktop */
gboolean clock_app_icon_initialized;
@@ -150,6 +147,13 @@ struct _IndicatorDatetimeServicePrivate
/* appointments over the next few weeks.
Used when building SECTION_APPOINTMENTS */
GSList * upcoming_appointments;
+
+ /* variant cache */
+ GVariant * desktop_title_variant;
+ GVariant * phone_title_variant;
+ GVariant * visible_true_variant;
+ GVariant * visible_false_variant;
+ GVariant * alarm_icon_variant;
};
typedef IndicatorDatetimeServicePrivate priv_t;
@@ -168,6 +172,12 @@ indicator_clear_timer (guint * tag)
}
}
+static inline GDateTime *
+indicator_datetime_service_get_localtime (IndicatorDatetimeService * self)
+{
+ return indicator_datetime_clock_get_localtime (self->priv->clock);
+}
+
/***
****
***/
@@ -178,6 +188,8 @@ static void rebuild_soon (IndicatorDatetimeService * self, int section);
static inline void
rebuild_header_soon (IndicatorDatetimeService * self)
{
+ g_clear_pointer (&self->priv->header_label_format_string, g_free);
+
rebuild_soon (self, SECTION_HEADER);
}
@@ -360,7 +372,7 @@ on_header_timer (gpointer gself)
return G_SOURCE_REMOVE;
}
-static char * get_header_label_format_string (IndicatorDatetimeService *);
+static const char * get_header_label_format_string (IndicatorDatetimeService *);
static void
start_header_timer (IndicatorDatetimeService * self)
@@ -374,11 +386,10 @@ start_header_timer (IndicatorDatetimeService * self)
if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S))
{
- char * fmt = get_header_label_format_string (self);
+ const char * fmt = get_header_label_format_string (self);
header_shows_seconds = fmt && (strstr(fmt,"%s") || strstr(fmt,"%S") ||
strstr(fmt,"%T") || strstr(fmt,"%X") ||
strstr(fmt,"%c"));
- g_free (fmt);
}
if (header_shows_seconds)
@@ -589,23 +600,6 @@ set_alarm_timer (IndicatorDatetimeService * self)
****
***/
-static void
-update_internal_timezone (IndicatorDatetimeService * 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 (!id && p->tz_file)
- 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);
-}
-
/**
* General purpose handler for rebuilding sections and restarting their timers
* when time jumps for whatever reason:
@@ -624,7 +618,6 @@ on_local_time_jumped (IndicatorDatetimeService * self)
1. rebuild the necessary states / menuitems when time jumps
2. restart the timers so their new wait interval is correct */
- update_internal_timezone (self);
on_header_timer (self);
on_timezone_timer (self);
}
@@ -656,38 +649,46 @@ skew_timer_func (gpointer gself)
****
***/
-static gchar *
+static const gchar *
get_header_label_format_string (IndicatorDatetimeService * self)
{
- char * fmt;
- GSettings * s = self->priv->settings;
- const TimeFormatMode mode = g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S);
+ priv_t * p = self->priv;
- if (mode == TIME_FORMAT_MODE_CUSTOM)
+ if (p->header_label_format_string == NULL)
{
- fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S);
- }
- else
- {
- gboolean show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S);
- gboolean show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S);
- fmt = generate_full_format_string (show_day, show_date, s);
+ char * fmt;
+ GSettings * s = p->settings;
+ const TimeFormatMode mode = g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S);
+
+ if (mode == TIME_FORMAT_MODE_CUSTOM)
+ {
+ fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S);
+ }
+ else
+ {
+ gboolean show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S);
+ gboolean show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S);
+ fmt = generate_full_format_string (show_day, show_date, s);
+ }
+
+ p->header_label_format_string = fmt;
}
- return fmt;
+ return p->header_label_format_string;
}
static GVariant *
create_desktop_header_state (IndicatorDatetimeService * self)
{
+ priv_t * p = self->priv;
GVariantBuilder b;
- gchar * fmt;
+ const gchar * fmt;
gchar * str;
gboolean visible;
GDateTime * now;
- const gchar * title = _("Date and Time");
+ GVariant * label_variant;
- visible = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S);
+ visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_CLOCK_S);
/* build the time string for the label & a11y */
fmt = get_header_label_format_string (self);
@@ -699,15 +700,15 @@ create_desktop_header_state (IndicatorDatetimeService * self)
g_warning ("%s", str);
}
+ label_variant = g_variant_new_take_string (str);
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
- g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (str));
- g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (str));
- g_variant_builder_add (&b, "{sv}", "title", g_variant_new_string (title));
- g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (visible));
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", label_variant);
+ g_variant_builder_add (&b, "{sv}", "label", label_variant);
+ g_variant_builder_add_value (&b, p->desktop_title_variant);
+ g_variant_builder_add_value (&b, visible ? p->visible_true_variant : p->visible_false_variant);
/* cleanup */
g_date_time_unref (now);
- g_free (fmt);
return g_variant_builder_end (&b);
}
@@ -717,43 +718,37 @@ service_has_alarms (IndicatorDatetimeService * self);
static GVariant *
create_phone_header_state (IndicatorDatetimeService * self)
{
+ priv_t * p = self->priv;
+ const gboolean has_alarms = service_has_alarms (self);
GVariantBuilder b;
GDateTime * now;
const gchar * fmt;
- gchar * label;
- gboolean has_alarms;
- gchar * a11y;
- const gchar * title = _("Upcoming");
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add_value (&b, p->phone_title_variant);
+ g_variant_builder_add_value (&b, p->visible_true_variant);
+
+ /* icon */
+ if (has_alarms)
+ g_variant_builder_add_value (&b, p->alarm_icon_variant);
- /* label */
+ /* label, a11y */
now = indicator_datetime_service_get_localtime (self);
fmt = get_terse_header_time_format_string ();
- label = g_date_time_format (now, fmt);
-
- /* icon */
- if ((has_alarms = service_has_alarms (self)))
+ if (has_alarms)
{
- GIcon * icon;
- icon = g_themed_icon_new_with_default_fallbacks (ALARM_CLOCK_ICON_NAME);
- g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon));
- g_object_unref (icon);
+ gchar * label = g_date_time_format (now, fmt);
+ gchar * a11y = g_strdup_printf (_("%s (has alarms)"), label);
+ g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (label));
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_take_string (a11y));
}
-
- /* a11y */
- if (has_alarms)
- a11y = g_strdup_printf (_("%s (has alarms)"), label);
else
- a11y = g_strdup (label);
- g_variant_builder_add (&b, "{sv}", "accessible-desc",
- g_variant_new_take_string (a11y));
-
- g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE));
- g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (label));
- g_variant_builder_add (&b, "{sv}", "title", g_variant_new_string (title));
+ {
+ GVariant * v = g_variant_new_take_string (g_date_time_format (now, fmt));
+ g_variant_builder_add (&b, "{sv}", "label", v);
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", v);
+ }
- /* cleanup */
g_date_time_unref (now);
return g_variant_builder_end (&b);
}
@@ -1100,61 +1095,6 @@ create_desktop_appointments_section (IndicatorDatetimeService * self)
****
***/
-static void
-on_current_timezone_changed (IndicatorDatetimeService * self)
-{
- on_local_time_jumped (self);
-}
-
-/* When the 'auto-detect timezone' boolean setting changes,
- start or stop watching geoclue and /etc/timezone */
-static void
-set_detect_location_enabled (IndicatorDatetimeService * self, gboolean enabled)
-{
- gboolean changed = FALSE;
- priv_t * p = self->priv;
-
- /* 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);
-}
/* A temp struct used by create_locations_section()
for pruning duplicates and sorting. */
@@ -1240,44 +1180,30 @@ create_locations_section (IndicatorDatetimeService * self)
GSList * l;
GSList * locations = NULL;
gchar ** user_locations;
- gboolean visible;
- IndicatorDatetimeTimezone * detected_timezones[2];
+ const gchar ** detected_timezones;
priv_t * p = self->priv;
GDateTime * now = indicator_datetime_service_get_localtime (self);
- set_detect_location_enabled (self,
- g_settings_get_boolean (p->settings, SETTINGS_SHOW_DETECTED_S));
-
menu = g_menu_new ();
/***
- **** Build a list of locations to add: use geo_timezone,
- **** current_timezone, and SETTINGS_LOCATIONS_S, but omit duplicates.
+ **** Build a list of locations to add, omitting duplicates
***/
- /* maybe add the auto-detected timezones */
- detected_timezones[0] = p->tz_geoclue;
- detected_timezones[1] = p->tz_file;
- visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_DETECTED_S);
- for (i=0; i<G_N_ELEMENTS(detected_timezones); i++)
+ detected_timezones = indicator_datetime_clock_get_timezones (p->clock);
+ for (i=0; detected_timezones && detected_timezones[i]; i++)
{
- if (detected_timezones[i] != NULL)
- {
- const char * tz = indicator_datetime_timezone_get_timezone (detected_timezones[i]);
- if (tz && *tz)
- {
- gchar * name = get_current_zone_name (tz, p->settings);
- locations = locations_add (locations, tz, name, visible);
- g_free (name);
- }
- }
+ const char * tz = detected_timezones[i];
+ gchar * name = get_current_zone_name (tz, p->settings);
+ locations = locations_add (locations, tz, name, TRUE);
+ g_free (name);
}
/* maybe add the user-specified locations */
user_locations = g_settings_get_strv (p->settings, SETTINGS_LOCATIONS_S);
if (user_locations != NULL)
{
- visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_LOCATIONS_S);
+ const gboolean visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_LOCATIONS_S);
for (i=0; user_locations[i] != NULL; i++)
{
@@ -1735,6 +1661,9 @@ rebuild_now (IndicatorDatetimeService * self, int sections)
struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
+ if (p->actions == NULL)
+ return;
+
if (sections & SECTION_HEADER)
{
g_simple_action_set_state (p->desktop_header_action,
@@ -2088,6 +2017,10 @@ my_get_property (GObject * o,
switch (property_id)
{
+ case PROP_CLOCK:
+ g_value_set_object (value, self->priv->clock);
+ break;
+
case PROP_PLANNER:
g_value_set_object (value, self->priv->planner);
break;
@@ -2107,6 +2040,10 @@ my_set_property (GObject * o,
switch (property_id)
{
+ case PROP_CLOCK:
+ indicator_datetime_service_set_clock (self, g_value_get_object (value));
+ break;
+
case PROP_PLANNER:
indicator_datetime_service_set_planner (self, g_value_get_object (value));
break;
@@ -2138,8 +2075,7 @@ my_dispose (GObject * o)
g_clear_object (&p->cancellable);
}
- set_detect_location_enabled (self, FALSE);
-
+ indicator_datetime_service_set_clock (self, NULL);
indicator_datetime_service_set_planner (self, NULL);
if (p->login1_manager != NULL)
@@ -2165,12 +2101,17 @@ my_dispose (GObject * o)
for (i=0; i<N_PROFILES; ++i)
g_clear_object (&p->menus[i].menu);
- g_clear_pointer (&p->internal_timezone, g_time_zone_unref);
g_clear_object (&p->calendar_action);
g_clear_object (&p->desktop_header_action);
g_clear_object (&p->phone_header_action);
g_clear_object (&p->conn);
+ g_clear_pointer (&p->desktop_title_variant, g_variant_unref);
+ g_clear_pointer (&p->phone_title_variant, g_variant_unref);
+ g_clear_pointer (&p->visible_true_variant, g_variant_unref);
+ g_clear_pointer (&p->visible_false_variant, g_variant_unref);
+ g_clear_pointer (&p->alarm_icon_variant, g_variant_unref);
+
G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o);
}
@@ -2181,6 +2122,7 @@ my_finalize (GObject * o)
priv_t * p = self->priv;
g_free (p->clock_app_icon_filename);
+ g_free (p->header_label_format_string);
g_clear_pointer (&p->skew_time, g_date_time_unref);
g_clear_pointer (&p->calendar_date, g_date_time_unref);
@@ -2194,9 +2136,45 @@ my_finalize (GObject * o)
static void
indicator_datetime_service_init (IndicatorDatetimeService * self)
{
- guint i, n;
+ GIcon * icon;
priv_t * p;
+
+ /* init the priv pointer */
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_SERVICE,
+ IndicatorDatetimeServicePrivate);
+ self->priv = p;
+
+ p->cancellable = g_cancellable_new ();
+
+ p->settings = g_settings_new (SETTINGS_INTERFACE);
+
+ p->desktop_title_variant = g_variant_new ("{sv}", "title", g_variant_new_string (_("Date and Time")));
+ g_variant_ref_sink (p->desktop_title_variant);
+
+ p->phone_title_variant = g_variant_new ("{sv}", "title", g_variant_new_string (_("Upcoming")));
+ g_variant_ref_sink (p->phone_title_variant);
+
+ p->visible_true_variant = g_variant_new ("{sv}", "visible", g_variant_new_boolean (TRUE));
+ g_variant_ref_sink (p->visible_true_variant);
+
+ p->visible_false_variant = g_variant_new ("{sv}", "visible", g_variant_new_boolean (FALSE));
+ g_variant_ref_sink (p->visible_false_variant);
+
+ icon = g_themed_icon_new_with_default_fallbacks (ALARM_CLOCK_ICON_NAME);
+ p->alarm_icon_variant = g_variant_new ("{sv}", "icon", g_icon_serialize (icon));
+ g_variant_ref_sink (p->alarm_icon_variant);
+ g_object_unref (icon);
+}
+
+static void
+my_constructed (GObject * gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ priv_t * p = self->priv;
GString * gstr = g_string_new (NULL);
+ guint i, n;
/* these are the settings that affect the
contents of the respective sections */
@@ -2233,20 +2211,10 @@ indicator_datetime_service_init (IndicatorDatetimeService * self)
};
- /* init the priv pointer */
-
- p = G_TYPE_INSTANCE_GET_PRIVATE (self,
- INDICATOR_TYPE_DATETIME_SERVICE,
- IndicatorDatetimeServicePrivate);
- self->priv = p;
-
- p->cancellable = g_cancellable_new ();
-
/***
- **** Create the settings object and listen for changes
+ **** Listen for settings changes
***/
- p->settings = g_settings_new (SETTINGS_INTERFACE);
for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++)
{
g_string_printf (gstr, "changed::%s", header_settings[i]);
@@ -2327,6 +2295,7 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
object_class->dispose = my_dispose;
object_class->finalize = my_finalize;
+ object_class->constructed = my_constructed;
object_class->get_property = my_get_property;
object_class->set_property = my_set_property;
@@ -2345,6 +2314,12 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
properties[PROP_0] = NULL;
+ properties[PROP_CLOCK] = g_param_spec_object ("clock",
+ "Clock",
+ "The clock",
+ INDICATOR_TYPE_DATETIME_CLOCK,
+ flags);
+
properties[PROP_PLANNER] = g_param_spec_object ("planner",
"Planner",
"The appointment provider",
@@ -2359,28 +2334,17 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
***/
IndicatorDatetimeService *
-indicator_datetime_service_new (IndicatorDatetimePlanner * planner)
+indicator_datetime_service_new (IndicatorDatetimeClock * clock,
+ IndicatorDatetimePlanner * planner)
{
GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE,
+ "clock", clock,
"planner", planner,
NULL);
return INDICATOR_DATETIME_SERVICE (o);
}
-/* This currently just returns the system time,
- As we add test coverage, we'll need this to bypass the system time. */
-GDateTime *
-indicator_datetime_service_get_localtime (IndicatorDatetimeService * self)
-{
- priv_t * p = self->priv;
-
- if (G_UNLIKELY (p->internal_timezone == NULL))
- update_internal_timezone (self);
-
- return g_date_time_new_now (p->internal_timezone);
-}
-
void
indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
GDateTime * date)
@@ -2400,6 +2364,43 @@ indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
update_appointment_lists (self);
}
+static void
+on_clock_changed (IndicatorDatetimeService * self)
+{
+ on_local_time_jumped (self);
+}
+
+void
+indicator_datetime_service_set_clock (IndicatorDatetimeService * self,
+ IndicatorDatetimeClock * clock)
+{
+ priv_t * p;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
+ g_return_if_fail ((clock == NULL) || INDICATOR_IS_DATETIME_CLOCK (clock));
+
+ p = self->priv;
+
+ /* clear the old clock */
+
+ if (p->clock != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->clock, self);
+ g_clear_object (&p->clock);
+ }
+
+ /* set the new clock */
+
+ if (clock != NULL)
+ {
+ p->clock = g_object_ref (clock);
+
+ g_signal_connect_swapped (p->clock, "changed",
+ G_CALLBACK(on_clock_changed), self);
+ on_clock_changed (self);
+ }
+}
+
void
indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
IndicatorDatetimePlanner * planner)
@@ -2407,7 +2408,7 @@ indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
priv_t * p;
g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
- g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (planner));
+ g_return_if_fail ((planner == NULL) || INDICATOR_IS_DATETIME_PLANNER (planner));
p = self->priv;
diff --git a/src/service.h b/src/service.h
index 25bb59a..d38db72 100644
--- a/src/service.h
+++ b/src/service.h
@@ -22,6 +22,8 @@
#include <glib.h>
#include <glib-object.h>
+
+#include "clock.h"
#include "planner.h"
G_BEGIN_DECLS
@@ -63,9 +65,8 @@ struct _IndicatorDatetimeServiceClass
GType indicator_datetime_service_get_type (void);
-IndicatorDatetimeService * indicator_datetime_service_new (IndicatorDatetimePlanner * planner);
-
-GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service);
+IndicatorDatetimeService * indicator_datetime_service_new (IndicatorDatetimeClock * clock,
+ IndicatorDatetimePlanner * planner);
void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
GDateTime * date);
@@ -74,6 +75,9 @@ void indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
IndicatorDatetimePlanner * planner);
+void indicator_datetime_service_set_clock (IndicatorDatetimeService * self,
+ IndicatorDatetimeClock * clock);
+
G_END_DECLS
diff --git a/src/timezone.h b/src/timezone.h
index aaaa296..fa6593d 100644
--- a/src/timezone.h
+++ b/src/timezone.h
@@ -37,8 +37,6 @@ typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass;
GType indicator_datetime_timezone_get_type (void);
-#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone"
-
/**
* Abstract Base Class for objects that provide a timezone.
*
diff --git a/src/planner-mock.c b/tests/planner-mock.c
index e67ad7e..e67ad7e 100644
--- a/src/planner-mock.c
+++ b/tests/planner-mock.c
diff --git a/src/planner-mock.h b/tests/planner-mock.h
index 8d7d7c2..8d7d7c2 100644
--- a/src/planner-mock.h
+++ b/tests/planner-mock.h