aboutsummaryrefslogtreecommitdiff
path: root/src/service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service.c')
-rw-r--r--src/service.c1357
1 files changed, 1289 insertions, 68 deletions
diff --git a/src/service.c b/src/service.c
index f5e16a0..7287336 100644
--- a/src/service.c
+++ b/src/service.c
@@ -3,6 +3,7 @@
*
* Authors:
* Charles Kerr <charles.kerr@canonical.com>
+ * Ted Gould <ted@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
@@ -17,25 +18,33 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <locale.h>
+#include "config.h"
+
+#include <string.h> /* strstr() */
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include "planner-eds.h"
+#include "timezone-file.h"
+#include "timezone-geoclue.h"
#include "service.h"
+#include "settings-shared.h"
+#include "utils.h"
-/* FIXME: remove -test */
-#define BUS_NAME "com.canonical.indicator.datetime-test"
+#define BUS_NAME "datetime.indicator"
#define BUS_PATH "/com/canonical/indicator/datetime"
+#define SKEW_CHECK_INTERVAL_SEC 10
+#define SKEW_DIFF_THRESHOLD_USEC ((SKEW_CHECK_INTERVAL_SEC+5) * G_USEC_PER_SEC)
+
G_DEFINE_TYPE (IndicatorDatetimeService,
indicator_datetime_service,
G_TYPE_OBJECT)
-/* signals enum */
enum
{
- NAME_LOST,
+ SIGNAL_NAME_LOST,
LAST_SIGNAL
};
@@ -69,7 +78,7 @@ enum
static const char * const menu_names[N_PROFILES] =
{
"desktop",
- "desktop_greeter"
+ "greeter"
};
struct ProfileMenuInfo
@@ -85,15 +94,38 @@ struct ProfileMenuInfo
struct _IndicatorDatetimeServicePrivate
{
+ GCancellable * cancellable;
+
+ GSettings * settings;
+
+ IndicatorDatetimeTimezone * tz_file;
+ IndicatorDatetimeTimezone * tz_geoclue;
+ IndicatorDatetimePlanner * planner;
+
guint own_id;
- GSimpleActionGroup * actions;
guint actions_export_id;
- struct ProfileMenuInfo menus[N_PROFILES];
+ GDBusConnection * conn;
+
guint rebuild_id;
int rebuild_flags;
- GDBusConnection * conn;
- GCancellable * cancellable;
+ struct ProfileMenuInfo menus[N_PROFILES];
+
+ GDateTime * skew_time;
+ guint skew_timer;
+
+ guint header_timer;
+ guint timezone_timer;
+
+ /* Which year/month to show in the calendar,
+ and which day should get the cursor.
+ This value is reflected in the calendar action's state */
+ time_t calendar_date;
+
+ GSimpleActionGroup * actions;
GSimpleAction * header_action;
+ GSimpleAction * calendar_action;
+
+ GDBusProxy * login1_manager;
gboolean replace;
};
@@ -104,6 +136,20 @@ typedef IndicatorDatetimeServicePrivate priv_t;
****
***/
+static void
+indicator_clear_timer (guint * tag)
+{
+ if (*tag)
+ {
+ g_source_remove (*tag);
+ *tag = 0;
+ }
+}
+
+/***
+****
+***/
+
static void rebuild_now (IndicatorDatetimeService * self, int section);
static void rebuild_soon (IndicatorDatetimeService * self, int section);
@@ -112,21 +158,25 @@ rebuild_header_soon (IndicatorDatetimeService * self)
{
rebuild_soon (self, SECTION_HEADER);
}
+
static inline void
-rebuild_calendar_soon (IndicatorDatetimeService * self)
+rebuild_calendar_section_soon (IndicatorDatetimeService * self)
{
rebuild_soon (self, SECTION_CALENDAR);
}
+
static inline void
rebuild_appointments_section_soon (IndicatorDatetimeService * self)
{
rebuild_soon (self, SECTION_APPOINTMENTS);
}
+
static inline void
rebuild_locations_section_soon (IndicatorDatetimeService * self)
{
rebuild_soon (self, SECTION_LOCATIONS);
}
+
static inline void
rebuild_settings_section_soon (IndicatorDatetimeService * self)
{
@@ -134,68 +184,976 @@ rebuild_settings_section_soon (IndicatorDatetimeService * self)
}
/***
-****
+**** TIMEZONE TIMER
***/
+/*
+ * Periodically rebuild the sections that have time format strings
+ * that are dependent on the current time:
+ *
+ * 1. appointment menuitems' time format strings depend on the
+ * current time; for example, they don't show the day of week
+ * if the appointment is today.
+ *
+ * 2. location menuitems' time format strings depend on the
+ * current time; for example, they don't show the day of the week
+ * if the local date and location date are the same.
+ *
+ * 3. the "local date" menuitem in the calendar section is,
+ * obviously, dependent on the local time.
+ *
+ * In short, we want to update whenever the number of days between two zone
+ * might have changed. We do that by updating when the day changes in either zone.
+ *
+ * Since not all UTC offsets are evenly divisible by hours
+ * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour
+ * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45.
+ */
+
+static guint
+calculate_seconds_until_next_fifteen_minutes (void)
+{
+ char * str;
+ gint minute;
+ guint seconds;
+ GTimeSpan diff;
+ GDateTime * now;
+ GDateTime * next;
+ GDateTime * start_of_next;
+
+ now = g_date_time_new_now_local ();
+
+ minute = g_date_time_get_minute (now);
+ minute = 15 - (minute % 15);
+ next = g_date_time_add_minutes (now, minute);
+ start_of_next = g_date_time_new_local (g_date_time_get_year (next),
+ g_date_time_get_month (next),
+ g_date_time_get_day_of_month (next),
+ g_date_time_get_hour (next),
+ g_date_time_get_minute (next),
+ 1);
+
+ str = g_date_time_format (start_of_next, "%F %T");
+ g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str);
+ g_free (str);
+
+ diff = g_date_time_difference (start_of_next, now);
+ seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND;
+
+ g_date_time_unref (start_of_next);
+ g_date_time_unref (next);
+ g_date_time_unref (now);
+
+ return seconds;
+}
+
+static void start_timezone_timer (IndicatorDatetimeService * self);
+
+static gboolean
+on_timezone_timer (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ rebuild_soon (self, SECTION_CALENDAR |
+ SECTION_APPOINTMENTS |
+ SECTION_LOCATIONS);
+
+ /* Restarting the timer to recalculate the interval. This helps us to hit
+ our marks despite clock skew, suspend+resume, leap seconds, etc */
+ start_timezone_timer (self);
+ return G_SOURCE_REMOVE;
+}
+
static void
-update_header_action (IndicatorDatetimeService * self)
+start_timezone_timer (IndicatorDatetimeService * self)
{
- GVariant * v;
- gchar * a11y = g_strdup ("a11y");
- const gchar * label = "Hello World";
- const gchar * iconstr = "icon";
- const priv_t * const p = self->priv;
+ priv_t * p = self->priv;
- g_return_if_fail (p->header_action != NULL);
+ indicator_clear_timer (&p->timezone_timer);
- v = g_variant_new ("(sssb)", label, iconstr, a11y, TRUE);
- g_simple_action_set_state (p->header_action, v);
- g_free (a11y);
+ p->timezone_timer = g_timeout_add_seconds (calculate_seconds_until_next_fifteen_minutes(),
+ on_timezone_timer,
+ self);
}
/***
+**** HEADER TIMER
+***/
+
+/*
+ * This is to periodically rebuild the header's action's state.
+ *
+ * If the label shows seconds, update when we reach the next second.
+ * Otherwise, update when we reach the next minute.
+ */
+
+static guint
+calculate_milliseconds_until_next_minute (void)
+{
+ GDateTime * now;
+ GDateTime * next;
+ GDateTime * start_of_next;
+ GTimeSpan interval_usec;
+ guint interval_msec;
+
+ now = g_date_time_new_now_local ();
+ next = g_date_time_add_minutes (now, 1);
+ start_of_next = g_date_time_new_local (g_date_time_get_year (next),
+ g_date_time_get_month (next),
+ g_date_time_get_day_of_month (next),
+ g_date_time_get_hour (next),
+ g_date_time_get_minute (next),
+ 0);
+
+ interval_usec = g_date_time_difference (start_of_next, now);
+ interval_msec = (interval_usec + 999) / 1000;
+
+ g_date_time_unref (start_of_next);
+ g_date_time_unref (next);
+ g_date_time_unref (now);
+
+ return interval_msec;
+}
+
+static gint
+calculate_milliseconds_until_next_second (void)
+{
+ GDateTime * now;
+ gint interval_usec;
+ guint interval_msec;
+
+ now = g_date_time_new_now_local ();
+
+ interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now);
+ interval_msec = (interval_usec + 999) / 1000;
+
+ g_date_time_unref (now);
+
+ return interval_msec;
+}
+
+static void start_header_timer (IndicatorDatetimeService * self);
+
+static gboolean
+on_header_timer (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ rebuild_now (self, SECTION_HEADER);
+
+ /* Restarting the timer to recalculate the interval. This helps us to hit
+ our marks despite clock skew, suspend+resume, leap seconds, etc */
+ start_header_timer (self);
+ return G_SOURCE_REMOVE;
+}
+
+static char * get_header_label_format_string (IndicatorDatetimeService *);
+
+static void
+start_header_timer (IndicatorDatetimeService * self)
+{
+ guint interval_msec;
+ gboolean header_shows_seconds = FALSE;
+ priv_t * p = self->priv;
+
+ indicator_clear_timer (&p->header_timer);
+
+ if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S))
+ {
+ 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)
+ interval_msec = calculate_milliseconds_until_next_second ();
+ else
+ interval_msec = calculate_milliseconds_until_next_minute ();
+
+ interval_msec += 50; /* add a small margin to ensure the callback
+ fires /after/ next is reached */
+
+ p->header_timer = g_timeout_add_full (G_PRIORITY_HIGH,
+ interval_msec,
+ on_header_timer,
+ self,
+ NULL);
+}
+
+/**
+ * General purpose handler for rebuilding sections and restarting their timers
+ * when time jumps for whatever reason:
+ *
+ * - clock skew
+ * - laptop suspend + resume
+ * - geoclue detects that we've changed timezones
+ * - Unity is running inside a TARDIS
+ */
+static void
+on_local_time_jumped (IndicatorDatetimeService * self)
+{
+ g_debug ("%s %s", G_STRLOC, G_STRFUNC);
+
+ /* these calls accomplish two things:
+ 1. rebuild the necessary states / menuitems when time jumps
+ 2. restart the timers so their new wait interval is correct */
+
+ on_header_timer (self);
+ on_timezone_timer (self);
+}
+
+static gboolean
+skew_timer_func (gpointer gself)
+{
+ GDateTime * now = g_date_time_new_now_local ();
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ priv_t * p = self->priv;
+
+ /* check for clock skew: has too much time passed since the last check? */
+ if (p->skew_time != NULL)
+ {
+ const GTimeSpan diff = g_date_time_difference (now, p->skew_time);
+
+ if (diff > SKEW_DIFF_THRESHOLD_USEC)
+ on_local_time_jumped (self);
+ }
+
+ g_clear_pointer (&p->skew_time, g_date_time_unref);
+ p->skew_time = now;
+ return G_SOURCE_CONTINUE;
+}
+
+/***
+****
+**** HEADER SECTION
****
***/
-static GMenuModel *
-create_calendar_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+typedef enum
{
- GMenu * menu;
+ TIME_FORMAT_MODE_LOCALE_DEFAULT,
+ TIME_FORMAT_MODE_12_HOUR,
+ TIME_FORMAT_MODE_24_HOUR,
+ TIME_FORMAT_MODE_CUSTOM
+}
+TimeFormatMode;
- menu = g_menu_new ();
+/* gets the user's time-format from GSettings */
+static TimeFormatMode
+get_time_format_mode (IndicatorDatetimeService * self)
+{
+ char * str;
+ TimeFormatMode mode;
+
+ str = g_settings_get_string (self->priv->settings, SETTINGS_TIME_FORMAT_S);
+
+ if (!g_strcmp0 ("12-hour", str))
+ mode = TIME_FORMAT_MODE_12_HOUR;
+ else if (!g_strcmp0 ("24-hour", str))
+ mode = TIME_FORMAT_MODE_24_HOUR;
+ else if (!g_strcmp0 ("custom", str))
+ mode = TIME_FORMAT_MODE_CUSTOM;
+ else
+ mode = TIME_FORMAT_MODE_LOCALE_DEFAULT;
+
+ g_free (str);
+ return mode;
+}
+
+static gchar *
+get_header_label_format_string (IndicatorDatetimeService * self)
+{
+ char * fmt;
+ const TimeFormatMode mode = get_time_format_mode (self);
+ GSettings * s = self->priv->settings;
+
+ 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_format_string_full (show_day, show_date);
+ }
+
+ return fmt;
+}
+
+static GVariant *
+create_header_state (IndicatorDatetimeService * self)
+{
+ GVariantBuilder b;
+ gchar * fmt;
+ gchar * str;
+ gboolean visible;
+ GDateTime * now_local;
+ priv_t * p = self->priv;
+
+ 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);
+ now_local = g_date_time_new_now_local ();
+ str = g_date_time_format (now_local, fmt);
+ if (str == NULL)
+ {
+ str = g_strdup (_("Unsupported date format"));
+ g_warning ("%s", str);
+ }
+
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (str));
+ g_variant_builder_add (&b, "{sv}", "label", g_variant_new_string (str));
+ g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (visible));
+
+ /* cleanup */
+ g_date_time_unref (now_local);
+ g_free (str);
+ g_free (fmt);
+ return g_variant_builder_end (&b);
+}
+
+
+/***
+****
+**** CALENDAR SECTION
+****
+***/
+
+static GDateTime *
+get_calendar_date (IndicatorDatetimeService * self)
+{
+ GDateTime * date;
+ priv_t * p = self->priv;
+
+ if (p->calendar_date == 0)
+ date = g_date_time_new_now_local ();
+ else
+ date = g_date_time_new_from_unix_local ((gint64)p->calendar_date);
+
+ return date;
+}
+
+static GSList *
+get_all_appointments_this_month (IndicatorDatetimeService * self)
+{
+ GSList * appointments = NULL;
+ priv_t * p = self->priv;
+
+ if (p->planner != NULL)
+ {
+ GDateTime * calendar_date;
+ GDateTime * begin;
+ GDateTime * end;
+ int y, m, d;
+
+ calendar_date = get_calendar_date (self);
+ g_date_time_get_ymd (calendar_date, &y, &m, &d);
+ begin = g_date_time_new_local (y, m, 0,
+ 0, 0, 0);
+ end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y),
+ 23, 59, 0);
+
+ appointments = indicator_datetime_planner_get_appointments (p->planner,
+ begin,
+ end);
+
+ g_date_time_unref (end);
+ g_date_time_unref (begin);
+ g_date_time_unref (calendar_date);
+ }
+
+ return appointments;
+}
+
+static GVariant *
+create_calendar_state (IndicatorDatetimeService * self)
+{
+ guint i;
+ const char * key;
+ gboolean days[32] = { 0 };
+ GVariantBuilder dict_builder;
+ GVariantBuilder day_builder;
+ GDateTime * date;
+ GSList * l;
+ GSList * appts;
+ gboolean b;
+ priv_t * p = self->priv;
+
+ g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_DICTIONARY);
+
+ key = "appointment-days";
+ appts = get_all_appointments_this_month (self);
+ for (l=appts; l!=NULL; l=l->next)
+ {
+ const struct IndicatorDatetimeAppt * appt = l->data;
+ days[g_date_time_get_day_of_month (appt->begin)] = TRUE;
+ }
+ g_variant_builder_init (&day_builder, G_VARIANT_TYPE("ai"));
+ for (i=0; i<G_N_ELEMENTS(days); i++)
+ if (days[i])
+ g_variant_builder_add (&day_builder, "i", i);
+ g_variant_builder_add (&dict_builder, "{sv}", key,
+ g_variant_builder_end (&day_builder));
+ g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
+
+ key = "calendar-day";
+ date = get_calendar_date (self);
+ g_variant_builder_add (&dict_builder, "{sv}", key,
+ g_variant_new_int64 (g_date_time_to_unix (date)));
+ g_date_time_unref (date);
+
+ key = "show-week-numbers";
+ b = g_settings_get_boolean (p->settings, SETTINGS_SHOW_WEEK_NUMBERS_S);
+ g_variant_builder_add (&dict_builder, "{sv}", key, g_variant_new_boolean (b));
+
+ return g_variant_builder_end (&dict_builder);
+}
+
+static void
+update_calendar_action_state (IndicatorDatetimeService * self)
+{
+ g_simple_action_set_state (self->priv->calendar_action,
+ create_calendar_state (self));
+}
+
+static GMenuModel *
+create_calendar_section (IndicatorDatetimeService * self)
+{
+ char * label;
+ GMenuItem * menu_item;
+ GDateTime * date_time;
+ GMenu * menu = g_menu_new ();
+
+ /* create the local date menuitem */
+ date_time = g_date_time_new_now_local ();
+ if (date_time == NULL)
+ {
+ label = g_strdup (_("Error getting time"));
+ }
+ else
+ {
+ label = g_date_time_format (date_time, _("%A, %e %B %Y"));
+ g_date_time_unref (date_time);
+ }
+ menu_item = g_menu_item_new (label, NULL);
+ g_menu_item_set_action_and_target_value (menu_item, "indicator.activate-planner",
+ g_variant_new_int64(0));
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+ g_free (label);
+
+ /* create the calendar menuitem */
+ if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CALENDAR_S))
+ {
+ label = g_strdup ("[calendar]");
+ menu_item = g_menu_item_new ("[calendar]", NULL);
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.calendar",
+ g_variant_new_int64(0));
+ g_menu_item_set_attribute (menu_item, "x-canonical-type",
+ "s", "com.canonical.indicator.calendar");
+ g_menu_item_set_attribute (menu_item, "activation-action",
+ "s", "indicator.activate-planner");
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+ g_free (label);
+ }
return G_MENU_MODEL (menu);
}
+/***
+****
+**** APPOINTMENTS SECTION
+****
+***/
+
+/* gets the next MAX_APPTS appointments */
+static GSList *
+get_upcoming_appointments (IndicatorDatetimeService * self)
+{
+ const int MAX_APPTS = 5;
+ GSList * l;
+ GSList * appts = NULL;
+ priv_t * p = self->priv;
+
+ if (p->planner != NULL)
+ {
+ GDateTime * begin = get_calendar_date (self);
+ GDateTime * end = g_date_time_add_months (begin, 1);
+
+ appts = indicator_datetime_planner_get_appointments (p->planner,
+ begin,
+ end);
+
+ g_date_time_unref (end);
+ g_date_time_unref (begin);
+ }
+
+ /* truncate at MAX_APPTS */
+ if ((l = g_slist_nth (appts, MAX_APPTS-1)))
+ {
+ g_slist_free_full (l->next, (GDestroyNotify)indicator_datetime_appt_free);
+ l->next = NULL;
+ }
+
+ return appts;
+}
+
+static char *
+get_appointment_time_format (struct IndicatorDatetimeAppt * appt)
+{
+ char * fmt;
+ gboolean full_day = g_date_time_difference (appt->end, appt->begin) == G_TIME_SPAN_DAY;
+
+ if (full_day)
+ {
+ /* TRANSLATORS: This is a strftime string for the day for full day events
+ in the menu. It should most likely be either '%A' for a full text day
+ (Wednesday) or '%a' for a shortened one (Wed). You should only need to
+ change for '%a' in the case of langauges with very long day names. */
+ fmt = g_strdup (_("%A"));
+ }
+ else
+ {
+ fmt = generate_format_string_at_time (appt->begin);
+ }
+
+ return fmt;
+}
+
static GMenuModel *
-create_appointments_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+create_appointments_section (IndicatorDatetimeService * self)
{
- GMenu * menu;
+ priv_t * p = self->priv;
+ GMenu * menu = g_menu_new ();
- menu = g_menu_new ();
+ if (g_settings_get_boolean (p->settings, SETTINGS_SHOW_EVENTS_S))
+ {
+ GSList * l;
+ GSList * appts;
+ GMenuItem * menu_item;
+
+ /* build appointment menuitems */
+ appts = get_upcoming_appointments (self);
+ for (l=appts; l!=NULL; l=l->next)
+ {
+ struct IndicatorDatetimeAppt * appt = l->data;
+ char * fmt = get_appointment_time_format (appt);
+ const gint64 unix_time = g_date_time_to_unix (appt->begin);
+
+ menu_item = g_menu_item_new (appt->summary, NULL);
+ g_menu_item_set_attribute (menu_item, "x-canonical-color",
+ "s", appt->color);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time",
+ "x", unix_time);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time-format",
+ "s", fmt);
+ g_menu_item_set_attribute (menu_item, "x-canonical-type",
+ "s", "com.canonical.indicator.appointment");
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.activate-planner",
+ g_variant_new_int64 (unix_time));
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+ g_free (fmt);
+ }
+
+ /* build 'add event' menuitem */
+ menu_item = g_menu_item_new (_("Add Event…"), NULL);
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.activate-planner",
+ g_variant_new_int64 (0));
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+
+ /* cleanup */
+ g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
+ }
return G_MENU_MODEL (menu);
}
+
+/***
+****
+**** LOCATIONS SECTION
+****
+***/
+
+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. */
+struct TimeLocation
+{
+ gint32 offset;
+ gchar * zone;
+ gchar * name;
+ gboolean visible;
+ GDateTime * local_time;
+};
+
+static void
+time_location_free (struct TimeLocation * loc)
+{
+ g_date_time_unref (loc->local_time);
+ g_free (loc->name);
+ g_free (loc->zone);
+ g_free (loc);
+}
+
+static struct TimeLocation*
+time_location_new (const char * zone,
+ const char * name,
+ gboolean visible,
+ time_t now)
+{
+ struct TimeLocation * loc = g_new (struct TimeLocation, 1);
+ GTimeZone * tz = g_time_zone_new (zone);
+ gint interval = g_time_zone_find_interval (tz, G_TIME_TYPE_UNIVERSAL, now);
+ loc->offset = g_time_zone_get_offset (tz, interval);
+ loc->zone = g_strdup (zone);
+ loc->name = g_strdup (name);
+ loc->visible = visible;
+ loc->local_time = g_date_time_new_now (tz);
+ g_time_zone_unref (tz);
+ return loc;
+}
+
+static int
+time_location_compare (const struct TimeLocation * a,
+ const struct TimeLocation * b)
+{
+ int ret = a->offset - b->offset; /* primary key */
+
+ if (!ret)
+ ret = g_strcmp0 (a->name, b->name); /* secondary key */
+
+ if (!ret)
+ ret = a->visible - b->visible; /* tertiary key */
+
+ return ret;
+}
+
+static GSList*
+locations_add (GSList * locations,
+ const char * zone,
+ const char * name,
+ gboolean visible,
+ time_t now)
+{
+ struct TimeLocation * loc = time_location_new (zone, name, visible, now);
+
+ if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare))
+ {
+ g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name);
+ time_location_free (loc);
+ }
+ else
+ {
+ g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name);
+ locations = g_slist_append (locations, loc);
+ }
+
+ return locations;
+}
+
static GMenuModel *
-create_locations_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+create_locations_section (IndicatorDatetimeService * self)
{
+ guint i;
GMenu * menu;
+ GSList * l;
+ GSList * locations = NULL;
+ gchar ** user_locations;
+ gboolean visible;
+ const time_t now = time (NULL);
+ priv_t * p = self->priv;
+ IndicatorDatetimeTimezone * detected_timezones[2];
+
+ 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.
+ ***/
+
+ /* 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++)
+ {
+ 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);
+ locations = locations_add (locations, tz, name, visible, now);
+ 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);
+
+ for (i=0; user_locations[i] != NULL; i++)
+ {
+ gchar * zone;
+ gchar * name;
+ split_settings_location (user_locations[i], &zone, &name);
+ locations = locations_add (locations, zone, name, visible, now);
+ g_free (name);
+ g_free (zone);
+ }
+
+ g_strfreev (user_locations);
+ user_locations = NULL;
+ }
+
+ /* now build menuitems for all the locations */
+ for (l=locations; l!=NULL; l=l->next)
+ {
+ struct TimeLocation * loc = l->data;
+ if (loc->visible)
+ {
+ char * label;
+ char * detailed_action;
+ char * fmt;
+ GMenuItem * menu_item;
+
+ label = g_strdup (loc->name);
+ detailed_action = g_strdup_printf ("indicator.set-location::%s %s",
+ loc->zone,
+ loc->name);
+ fmt = generate_format_string_at_time (loc->local_time);
+
+ menu_item = g_menu_item_new (label, detailed_action);
+ g_menu_item_set_attribute (menu_item, "x-canonical-type",
+ "s", "com.canonical.indicator.location");
+ g_menu_item_set_attribute (menu_item, "x-canonical-timezone",
+ "s", loc->zone);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time-format",
+ "s", fmt);
+ g_menu_append_item (menu, menu_item);
+
+ g_object_unref (menu_item);
+ g_free (fmt);
+ g_free (detailed_action);
+ g_free (label);
+ }
+ }
+
+ g_slist_free_full (locations, (GDestroyNotify)time_location_free);
return G_MENU_MODEL (menu);
}
-static GMenuModel *
-create_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+/***
+****
+***/
+
+struct settimezone_data
{
- GMenu * menu;
+ IndicatorDatetimeService * service;
+ char * timezone_id;
+ char * name;
+};
- menu = g_menu_new ();
+static void
+settimezone_data_free (struct settimezone_data * data)
+{
+ g_free (data->timezone_id);
+ g_free (data->name);
+ g_free (data);
+}
- g_menu_append (menu, _("Date and Time Settings\342\200\246"), "indicator.activateSettings");
+static void
+on_datetime1_set_timezone_response (GObject * object,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ GError * err;
+ GVariant * answers;
+ struct settimezone_data * data = gdata;
+
+ err = NULL;
+ answers = g_dbus_proxy_call_finish (G_DBUS_PROXY(object), res, &err);
+ if (err != NULL)
+ {
+ g_warning ("Could not set new timezone: %s", err->message);
+ g_error_free (err);
+ }
+ else
+ {
+ char * timezone_name = g_strdup_printf ("%s %s",
+ data->timezone_id,
+ data->name);
+
+ g_settings_set_string (data->service->priv->settings,
+ SETTINGS_TIMEZONE_NAME_S,
+ timezone_name);
+
+ g_free (timezone_name);
+ g_variant_unref (answers);
+ }
+
+ settimezone_data_free (data);
+}
+
+static void
+on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ GError * err;
+ GDBusProxy * proxy;
+ struct settimezone_data * data = gdata;
+
+ err = NULL;
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
+ if (err != NULL)
+ {
+ g_warning ("Could not grab DBus proxy for timedated: %s", err->message);
+ g_error_free (err);
+ settimezone_data_free (data);
+ }
+ else
+ {
+ g_dbus_proxy_call (proxy,
+ "SetTimezone",
+ g_variant_new ("(sb)", data->timezone_id, TRUE),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ data->service->priv->cancellable,
+ on_datetime1_set_timezone_response,
+ data);
+
+ g_object_unref (proxy);
+ }
+}
+
+static void
+indicator_datetime_service_set_location (IndicatorDatetimeService * self,
+ const char * timezone_id,
+ const char * name)
+{
+ priv_t * p = self->priv;
+ struct settimezone_data * data;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
+ g_return_if_fail (name && *name);
+ g_return_if_fail (timezone_id && *timezone_id);
+
+ data = g_new0 (struct settimezone_data, 1);
+ data->timezone_id = g_strdup (timezone_id);
+ data->name = g_strdup (name);
+ data->service = self;
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ p->cancellable,
+ on_datetime1_proxy_ready,
+ data);
+}
+
+static void
+on_set_location (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param,
+ gpointer gself)
+{
+ char * zone;
+ char * name;
+ IndicatorDatetimeService * self;
+
+ self = INDICATOR_DATETIME_SERVICE (gself);
+ split_settings_location (g_variant_get_string (param, NULL), &zone, &name);
+ indicator_datetime_service_set_location (self, zone, name);
+
+ g_free (name);
+ g_free (zone);
+}
+/***
+****
+***/
+
+static GMenuModel *
+create_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+{
+ GMenu * menu = g_menu_new ();
+ g_menu_append (menu, _("Date and Time Settings\342\200\246"), "indicator.activate-settings");
return G_MENU_MODEL (menu);
}
@@ -221,11 +1179,13 @@ create_menu (IndicatorDatetimeService * self, int profile)
}
else if (profile == PROFILE_GREETER)
{
- /* FIXME: what goes here? */
+ sections[n++] = create_calendar_section (self);
}
/* add sections to the submenu */
+
submenu = g_menu_new ();
+
for (i=0; i<n; ++i)
{
g_menu_append_section (submenu, NULL, sections[i]);
@@ -234,7 +1194,8 @@ create_menu (IndicatorDatetimeService * self, int profile)
/* add submenu to the header */
header = g_menu_item_new (NULL, "indicator._header");
- g_menu_item_set_attribute (header, "x-canonical-type", "s", "com.canonical.indicator.root");
+ g_menu_item_set_attribute (header, "x-canonical-type",
+ "s", "com.canonical.indicator.root");
g_menu_item_set_submenu (header, G_MENU_MODEL (submenu));
g_object_unref (submenu);
@@ -251,23 +1212,87 @@ create_menu (IndicatorDatetimeService * self, int profile)
**** GActions
***/
+/* Run a particular program based on an activation */
+static void
+execute_command (const gchar * cmd)
+{
+ GError * err = NULL;
+
+ g_debug ("Issuing command '%s'", cmd);
+
+ if (!g_spawn_command_line_async (cmd, &err))
+ {
+ g_warning ("Unable to start %s: %s", cmd, err->message);
+ g_error_free (err);
+ }
+}
+
+#ifdef HAVE_CCPANEL
+ #define SETTINGS_APP_INVOCATION "gnome-control-center indicator-datetime"
+#else
+ #define SETTINGS_APP_INVOCATION "gnome-control-center datetime"
+#endif
+
static void
on_settings_activated (GSimpleAction * a G_GNUC_UNUSED,
GVariant * param G_GNUC_UNUSED,
gpointer gself G_GNUC_UNUSED)
{
- g_message ("settings activated");
+ execute_command (SETTINGS_APP_INVOCATION);
}
static void
+on_activate_planner (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param,
+ gpointer gself)
+{
+ priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv;
+
+ if (p->planner != NULL)
+ {
+ const time_t t = g_variant_get_int64 (param);
+
+ if (t)
+ {
+ GDateTime * date_time = g_date_time_new_from_unix_local (t);
+ indicator_datetime_planner_activate_time (p->planner, date_time);
+ g_date_time_unref (date_time);
+ }
+ else /* no time specified... */
+ {
+ indicator_datetime_planner_activate (p->planner);
+ }
+ }
+}
+
+static void
+on_calendar_action_activated (GSimpleAction * action G_GNUC_UNUSED,
+ GVariant * state,
+ gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ priv_t * p = self->priv;
+ const time_t calendar_date = (time_t) g_variant_get_int64 (state);
+
+ if (p->calendar_date != calendar_date)
+ {
+ p->calendar_date = (time_t) g_variant_get_int64 (state);
+ update_calendar_action_state (self);
+ rebuild_appointments_section_soon (self);
+ }
+}
+
+
+static void
init_gactions (IndicatorDatetimeService * self)
{
- GVariant * v;
GSimpleAction * a;
priv_t * p = self->priv;
GActionEntry entries[] = {
- { "activateSettings", on_settings_activated, NULL, NULL, NULL },
+ { "activate-settings", on_settings_activated },
+ { "activate-planner", on_activate_planner, "x", NULL },
+ { "set-location", on_set_location, "s" }
};
p->actions = g_simple_action_group_new ();
@@ -278,11 +1303,19 @@ init_gactions (IndicatorDatetimeService * self)
self);
/* add the header action */
- v = g_variant_new ("(sssb)", "Hello World", "icon", "a11y", TRUE);
- a = g_simple_action_new_stateful ("_header", NULL, v);
+ a = g_simple_action_new_stateful ("_header", NULL, create_header_state (self));
g_simple_action_group_insert (p->actions, G_ACTION(a));
p->header_action = a;
+ /* add the calendar action */
+ a = g_simple_action_new_stateful ("calendar",
+ G_VARIANT_TYPE_INT64,
+ create_calendar_state (self));
+ g_simple_action_group_insert (p->actions, G_ACTION(a));
+ g_signal_connect (a, "activate",
+ G_CALLBACK(on_calendar_action_activated), self);
+ p->calendar_action = a;
+
rebuild_now (self, SECTION_HEADER);
}
@@ -308,16 +1341,17 @@ rebuild_now (IndicatorDatetimeService * self, int sections)
{
priv_t * p = self->priv;
struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
- //struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
+ struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
if (sections & SECTION_HEADER)
{
- update_header_action (self);
+ g_simple_action_set_state (p->header_action, create_header_state (self));
}
if (sections & SECTION_CALENDAR)
{
rebuild_section (desktop->submenu, 0, create_calendar_section (self));
+ rebuild_section (greeter->submenu, 0, create_calendar_section (self));
}
if (sections & SECTION_APPOINTMENTS)
@@ -333,7 +1367,6 @@ rebuild_now (IndicatorDatetimeService * self, int sections)
if (sections & SECTION_SETTINGS)
{
rebuild_section (desktop->submenu, 3, create_settings_section (self));
- //rebuild_section (greeter->submenu, 0, create_datetime_section(self));
}
}
@@ -369,7 +1402,52 @@ rebuild_soon (IndicatorDatetimeService * self, int section)
}
/***
-**** GDBus
+**** org.freedesktop.login1.Manager
+***/
+
+static void
+on_login1_manager_signal (GDBusProxy * proxy G_GNUC_UNUSED,
+ gchar * sender_name G_GNUC_UNUSED,
+ gchar * signal_name,
+ GVariant * parameters,
+ gpointer gself)
+{
+ if (!g_strcmp0 (signal_name, "PrepareForSleep"))
+ {
+ gboolean sleeping = FALSE;
+ g_variant_get (parameters, "(b)", &sleeping);
+ if (!sleeping)
+ on_local_time_jumped (INDICATOR_DATETIME_SERVICE (gself));
+ }
+}
+
+static void
+on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * err;
+ GDBusProxy * proxy;
+
+ err = NULL;
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
+
+ if (err != NULL)
+ {
+ g_warning ("Could not grab DBus proxy for logind: %s", err->message);
+ g_error_free (err);
+ }
+ else
+ {
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ self->priv->login1_manager = proxy;
+ g_signal_connect (proxy, "g-signal",
+ G_CALLBACK(on_login1_manager_signal), self);
+ }
+}
+
+/***
+**** GDBus
***/
static void
@@ -464,9 +1542,10 @@ on_name_lost (GDBusConnection * connection G_GNUC_UNUSED,
unexport (self);
- g_signal_emit (self, signals[NAME_LOST], 0, NULL);
+ g_signal_emit (self, signals[SIGNAL_NAME_LOST], 0, NULL);
}
+
/***
**** GObject virtual functions
***/
@@ -476,21 +1555,22 @@ my_constructed (GObject * o)
{
GBusNameOwnerFlags owner_flags;
IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o);
+ priv_t * p = self->priv;
/* own the name in constructed() instead of init() so that
we'll know the value of the 'replace' property */
owner_flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
- if (self->priv->replace)
+ if (p->replace)
owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
- self->priv->own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
- BUS_NAME,
- owner_flags,
- on_bus_acquired,
- NULL,
- on_name_lost,
- self,
- NULL);
+ p->own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ BUS_NAME,
+ owner_flags,
+ on_bus_acquired,
+ NULL,
+ on_name_lost,
+ self,
+ NULL);
}
static void
@@ -552,10 +1632,29 @@ my_dispose (GObject * o)
g_clear_object (&p->cancellable);
}
- if (p->rebuild_id)
+ set_detect_location_enabled (self, FALSE);
+
+ if (p->planner != NULL)
{
- g_source_remove (p->rebuild_id);
- p->rebuild_id = 0;
+ g_signal_handlers_disconnect_by_data (p->planner, self);
+ g_clear_object (&p->planner);
+ }
+
+ if (p->login1_manager != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_manager, self);
+ g_clear_object (&p->login1_manager);
+ }
+
+ indicator_clear_timer (&p->skew_timer);
+ indicator_clear_timer (&p->rebuild_id);
+ indicator_clear_timer (&p->timezone_timer);
+ indicator_clear_timer (&p->header_timer);
+
+ if (p->settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->settings, self);
+ g_clear_object (&p->settings);
}
g_clear_object (&p->actions);
@@ -563,12 +1662,25 @@ my_dispose (GObject * o)
for (i=0; i<N_PROFILES; ++i)
g_clear_object (&p->menus[i].menu);
+ g_clear_object (&p->planner);
+ g_clear_object (&p->calendar_action);
g_clear_object (&p->header_action);
g_clear_object (&p->conn);
G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o);
}
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o);
+ priv_t * p = self->priv;
+
+ g_clear_pointer (&p->skew_time, g_date_time_unref);
+
+ G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o);
+}
+
/***
**** Instantiation
***/
@@ -576,18 +1688,125 @@ my_dispose (GObject * o)
static void
indicator_datetime_service_init (IndicatorDatetimeService * self)
{
+ guint i, n;
priv_t * p;
+ GString * gstr = g_string_new (NULL);
+
+ /* these are the settings that affect the
+ contents of the respective sections */
+ const char * const header_settings[] = {
+ SETTINGS_SHOW_CLOCK_S,
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_SHOW_DAY_S,
+ SETTINGS_SHOW_DATE_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S
+ };
+ const char * const calendar_settings[] = {
+ SETTINGS_SHOW_CALENDAR_S,
+ SETTINGS_SHOW_WEEK_NUMBERS_S
+ };
+ const char * const appointment_settings[] = {
+ SETTINGS_SHOW_EVENTS_S,
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S
+ };
+ const char * const location_settings[] = {
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S,
+ SETTINGS_SHOW_LOCATIONS_S,
+ SETTINGS_LOCATIONS_S,
+ SETTINGS_SHOW_DETECTED_S,
+ SETTINGS_TIMEZONE_NAME_S
+ };
+ const char * const time_format_string_settings[] = {
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S
+ };
+
+
+ /* init the priv pointer */
- /* init our priv pointer */
p = G_TYPE_INSTANCE_GET_PRIVATE (self,
INDICATOR_TYPE_DATETIME_SERVICE,
IndicatorDatetimeServicePrivate);
self->priv = p;
- /* init the backend objects */
p->cancellable = g_cancellable_new ();
+ /***
+ **** Create the planner and listen for changes
+ ***/
+
+ p->planner = indicator_datetime_planner_eds_new ();
+
+ g_signal_connect_swapped (p->planner, "appointments-changed",
+ G_CALLBACK(rebuild_calendar_section_soon), self);
+
+
+ /***
+ **** Create the settings object and listen for 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]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_header_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(calendar_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", calendar_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_calendar_section_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(appointment_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", appointment_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_appointments_section_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(location_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", location_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_locations_section_soon), self);
+ }
+
+ /* The keys in time_format_string_settings affect the time format strings we build.
+ When these change, we need to rebuild everything that has a time format string. */
+ for (i=0, n=G_N_ELEMENTS(time_format_string_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", time_format_string_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(on_local_time_jumped), self);
+ }
+
init_gactions (self);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ p->cancellable,
+ on_login1_manager_proxy_ready,
+ self);
+
+ p->skew_timer = g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC,
+ skew_timer_func,
+ self);
+
+ on_local_time_jumped (self);
+
+ g_string_free (gstr, TRUE);
}
static void
@@ -596,19 +1815,21 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
GObjectClass * object_class = G_OBJECT_CLASS (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;
g_type_class_add_private (klass, sizeof (IndicatorDatetimeServicePrivate));
- signals[NAME_LOST] = g_signal_new (INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IndicatorDatetimeServiceClass, name_lost),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ signals[SIGNAL_NAME_LOST] = g_signal_new (
+ INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IndicatorDatetimeServiceClass, name_lost),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
properties[PROP_0] = NULL;