diff options
Diffstat (limited to 'src/service.c')
-rw-r--r-- | src/service.c | 1090 |
1 files changed, 774 insertions, 316 deletions
diff --git a/src/service.c b/src/service.c index fec2bb1..5fffc11 100644 --- a/src/service.c +++ b/src/service.c @@ -24,17 +24,18 @@ #include <glib/gi18n.h> #include <gio/gio.h> +#include <libnotify/notify.h> +#include <json-glib/json-glib.h> +#include <url-dispatcher.h> #include "dbus-shared.h" -#include "planner-eds.h" -#include "timezone-file.h" -#include "timezone-geoclue.h" #include "service.h" #include "settings-shared.h" #include "utils.h" #define SKEW_CHECK_INTERVAL_SEC 10 #define SKEW_DIFF_THRESHOLD_USEC ((SKEW_CHECK_INTERVAL_SEC+5) * G_USEC_PER_SEC) +#define ALARM_CLOCK_ICON_NAME "alarm-clock" G_DEFINE_TYPE (IndicatorDatetimeService, indicator_datetime_service, @@ -50,6 +51,16 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { + PROP_0, + PROP_CLOCK, + PROP_PLANNER, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +enum +{ SECTION_HEADER = (1<<0), SECTION_CALENDAR = (1<<1), SECTION_APPOINTMENTS = (1<<2), @@ -89,10 +100,18 @@ struct _IndicatorDatetimeServicePrivate GSettings * settings; - IndicatorDatetimeTimezone * tz_file; - IndicatorDatetimeTimezone * tz_geoclue; + IndicatorDatetimeClock * clock; IndicatorDatetimePlanner * planner; + /* 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; + guint own_id; guint actions_export_id; GDBusConnection * conn; @@ -106,6 +125,7 @@ struct _IndicatorDatetimeServicePrivate guint header_timer; guint timezone_timer; + guint alarm_timer; /* Which year/month to show in the calendar, and which day should get the cursor. @@ -118,6 +138,22 @@ struct _IndicatorDatetimeServicePrivate GSimpleAction * calendar_action; GDBusProxy * login1_manager; + + /* all the appointments in the selected calendar_date's month. + Used when populating the 'appointment-days' entry in + create_calendar_state() */ + GSList * calendar_appointments; + + /* 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; @@ -136,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); +} + /*** **** ***/ @@ -146,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); } @@ -218,7 +262,7 @@ calculate_seconds_until_next_fifteen_minutes (GDateTime * now) g_date_time_get_day_of_month (next), g_date_time_get_hour (next), g_date_time_get_minute (next), - 1); + 0.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); @@ -290,7 +334,7 @@ calculate_milliseconds_until_next_minute (GDateTime * now) g_date_time_get_day_of_month (next), g_date_time_get_hour (next), g_date_time_get_minute (next), - 0); + 0.1); interval_usec = g_date_time_difference (start_of_next, now); interval_msec = (interval_usec + 999) / 1000; @@ -328,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) @@ -342,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) @@ -366,6 +409,197 @@ start_header_timer (IndicatorDatetimeService * self) g_date_time_unref (now); } +/*** +**** ALARMS +***/ + +static void set_alarm_timer (IndicatorDatetimeService * self); + +static gboolean +appointment_has_alarm_url (const struct IndicatorDatetimeAppt * appt) +{ + return (appt->has_alarms) && + (appt->url != NULL) && + (g_str_has_prefix (appt->url, "alarm:///")); +} + +static gboolean +datetimes_have_the_same_minute (GDateTime * a G_GNUC_UNUSED, GDateTime * b G_GNUC_UNUSED) +{ + int ay, am, ad; + int by, bm, bd; + + g_date_time_get_ymd (a, &ay, &am, &ad); + g_date_time_get_ymd (b, &by, &bm, &bd); + + return (ay == by) && + (am == bm) && + (ad == bd) && + (g_date_time_get_hour (a) == g_date_time_get_hour (b)) && + (g_date_time_get_minute (a) == g_date_time_get_minute (b)); +} + +static void +dispatch_alarm_url (const struct IndicatorDatetimeAppt * appt) +{ + gchar * str; + + g_return_if_fail (appt != NULL); + g_return_if_fail (appointment_has_alarm_url (appt)); + + str = g_date_time_format (appt->begin, "%F %T"); + g_debug ("dispatching url \"%s\" for appointment \"%s\", which begins at %s", + appt->url, appt->summary, str); + g_free (str); + + url_dispatch_send (appt->url, NULL, NULL); +} + +static void +on_snap_decided (NotifyNotification * notification G_GNUC_UNUSED, + char * action, + gpointer gurl) +{ + g_debug ("%s: %s", G_STRFUNC, action); + + if (!g_strcmp0 (action, "show")) + { + const gchar * url = gurl; + g_debug ("dispatching url '%s'", url); + url_dispatch_send (url, NULL, NULL); + } +} + +static void +show_snap_decision_for_alarm (const struct IndicatorDatetimeAppt * appt) +{ + gchar * title; + const gchar * body; + const gchar * icon_name; + NotifyNotification * nn; + GError * error; + + title = g_date_time_format (appt->begin, + get_terse_time_format_string (appt->begin)); + body = appt->summary; + icon_name = ALARM_CLOCK_ICON_NAME; + g_debug ("creating a snap decision with title '%s', body '%s', icon '%s'", + title, body, icon_name); + + nn = notify_notification_new (title, body, icon_name); + notify_notification_set_hint_string (nn, + "x-canonical-snap-decisions", + "true"); + notify_notification_set_hint_string (nn, + "x-canonical-private-button-tint", + "true"); + notify_notification_add_action (nn, "show", _("Show"), + on_snap_decided, g_strdup(appt->url), g_free); + notify_notification_add_action (nn, "dismiss", _("Dismiss"), + on_snap_decided, NULL, NULL); + + error = NULL; + notify_notification_show (nn, &error); + if (error != NULL) + { + g_warning ("Unable to show alarm '%s' popup: %s", body, error->message); + g_error_free (error); + dispatch_alarm_url (appt); + } + + g_free (title); +} + +static void update_appointment_lists (IndicatorDatetimeService * self); + +static gboolean +on_alarm_timer (gpointer gself) +{ + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); + GDateTime * now; + GSList * l; + + /* If there are any alarms at the current time, show a snap decision */ + now = indicator_datetime_service_get_localtime (self); + for (l=self->priv->upcoming_appointments; l!=NULL; l=l->next) + { + const struct IndicatorDatetimeAppt * appt = l->data; + + if (appointment_has_alarm_url (appt)) + if (datetimes_have_the_same_minute (now, appt->begin)) + show_snap_decision_for_alarm (appt); + } + g_date_time_unref (now); + + /* rebuild the alarm list asynchronously. + set_upcoming_appointments() will update the alarm timer when this + async call is done, so no need to restart the timer here... */ + update_appointment_lists (self); + + return G_SOURCE_REMOVE; +} + +/* if there are upcoming alarms, set the alarm timer to the nearest one. + otherwise, unset the alarm timer. */ +static void +set_alarm_timer (IndicatorDatetimeService * self) +{ + priv_t * p; + GDateTime * now; + GDateTime * alarm_time; + GSList * l; + + p = self->priv; + indicator_clear_timer (&p->alarm_timer); + + now = indicator_datetime_service_get_localtime (self); + + /* find the time of the next alarm on our calendar */ + alarm_time = NULL; + for (l=p->upcoming_appointments; l!=NULL; l=l->next) + { + const struct IndicatorDatetimeAppt * appt = l->data; + + if (appointment_has_alarm_url (appt)) + if (g_date_time_compare (appt->begin, now) > 0) + if (!alarm_time || g_date_time_compare (alarm_time, appt->begin) > 0) + alarm_time = appt->begin; + } + + /* if there's an upcoming alarm, set a timer to wake up at that time */ + if (alarm_time != NULL) + { + GTimeSpan interval_msec; + gchar * str; + GDateTime * then; + + interval_msec = g_date_time_difference (alarm_time, now); + interval_msec += G_USEC_PER_SEC; /* fire a moment after alarm_time */ + interval_msec /= 1000; /* convert from usec to msec */ + + str = g_date_time_format (alarm_time, "%F %T"); + g_debug ("%s is the next alarm time", str); + g_free (str); + then = g_date_time_add_seconds (now, interval_msec/1000); + str = g_date_time_format (then, "%F %T"); + g_debug ("%s is when we'll wake up for it", str); + g_free (str); + g_date_time_unref (then); + + p->alarm_timer = g_timeout_add_full (G_PRIORITY_HIGH, + (guint) interval_msec, + on_alarm_timer, + self, + NULL); + } + + g_date_time_unref (now); +} + +/*** +**** +***/ + /** * General purpose handler for rebuilding sections and restarting their timers * when time jumps for whatever reason: @@ -415,68 +649,46 @@ skew_timer_func (gpointer gself) **** ***/ -typedef enum -{ - TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM -} -TimeFormatMode; - -/* 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 * +static const gchar * get_header_label_format_string (IndicatorDatetimeService * self) { - char * fmt; - const TimeFormatMode mode = get_time_format_mode (self); - GSettings * s = self->priv->settings; + priv_t * p = self->priv; - if (mode == TIME_FORMAT_MODE_CUSTOM) - { - fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S); - } - else + if (p->header_label_format_string == NULL) { - 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); + 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; + 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); @@ -484,19 +696,19 @@ create_desktop_header_state (IndicatorDatetimeService * self) str = g_date_time_format (now, fmt); if (str == NULL) { - str = g_strdup (_("Unsupported date format")); + str = g_strdup_printf (_("Unsupported date format ā%sā"), fmt); 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)); + 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", 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 (str); - g_free (fmt); return g_variant_builder_end (&b); } @@ -506,44 +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; - g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}")); - - /* label */ - now = indicator_datetime_service_get_localtime (self); - fmt = get_terse_time_format_string (now); - label = g_date_time_format (now, fmt); - g_variant_builder_add (&b, "{sv}", "label", g_variant_new_string (label)); + 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 = service_has_alarms (self))) - { - GIcon * icon; - icon = g_themed_icon_new_with_default_fallbacks ("alarm-symbolic"); - g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon)); - g_object_unref (icon); - } + if (has_alarms) + g_variant_builder_add_value (&b, p->alarm_icon_variant); - /* a11y */ + /* label, a11y */ + now = indicator_datetime_service_get_localtime (self); + fmt = get_terse_header_time_format_string (); if (has_alarms) - a11y = g_strdup_printf (_("%s (has alarms)"), label); + { + 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)); + } else - a11y = g_strdup (label); - g_variant_builder_add (&b, "{sv}", "accessible-desc", - g_variant_new_string (a11y)); - - /* visible */ - g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); + { + 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_free (a11y); - g_free (label); g_date_time_unref (now); return g_variant_builder_end (&b); } @@ -569,38 +774,6 @@ get_calendar_date (IndicatorDatetimeService * self) 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, 1, - 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) { @@ -611,15 +784,13 @@ create_calendar_state (IndicatorDatetimeService * self) 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) + for (l=p->calendar_appointments; l!=NULL; l=l->next) { const struct IndicatorDatetimeAppt * appt = l->data; days[g_date_time_get_day_of_month (appt->begin)] = TRUE; @@ -630,7 +801,6 @@ create_calendar_state (IndicatorDatetimeService * self) 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); @@ -678,10 +848,8 @@ add_localtime_menuitem (GMenu * menu, static void add_calendar_menuitem (GMenu * menu) { - char * label; GMenuItem * menu_item; - 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"); @@ -689,7 +857,6 @@ add_calendar_menuitem (GMenu * menu) g_menu_append_item (menu, menu_item); g_object_unref (menu_item); - g_free (label); } static GMenuModel * @@ -711,11 +878,8 @@ create_phone_calendar_section (IndicatorDatetimeService * self) { GMenu * menu = g_menu_new (); - /* strftime(3) format string to show day of week */ - add_localtime_menuitem (menu, self, _("%A"), NULL); - /* strftime(3) format string to show date */ - add_localtime_menuitem (menu, self, _("%e %B %Y"), "calendar"); + add_localtime_menuitem (menu, self, _("%A, %e %B %Y"), "calendar"); return G_MENU_MODEL (menu); } @@ -726,38 +890,6 @@ create_phone_calendar_section (IndicatorDatetimeService * self) **** ***/ -/* 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 gboolean service_has_alarms (IndicatorDatetimeService * self) { @@ -765,7 +897,7 @@ service_has_alarms (IndicatorDatetimeService * self) GSList * appts; GSList * l; - appts = get_upcoming_appointments (self); + appts = self->priv->upcoming_appointments; for (l=appts; l!=NULL; l=l->next) { struct IndicatorDatetimeAppt * appt = l->data; @@ -773,13 +905,13 @@ service_has_alarms (IndicatorDatetimeService * self) break; } - g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free); return has_alarms; } static char * get_appointment_time_format (struct IndicatorDatetimeAppt * appt, GDateTime * now, + GSettings * settings, gboolean terse) { char * fmt; @@ -788,7 +920,7 @@ get_appointment_time_format (struct IndicatorDatetimeAppt * appt, if (appt->is_daily) { const char * time_fmt = terse ? get_terse_time_format_string (appt->begin) - : get_full_time_format_string (); + : get_full_time_format_string (settings); fmt = join_date_and_time_format_strings (_("Daily"), time_fmt); } else if (full_day) @@ -800,31 +932,49 @@ get_appointment_time_format (struct IndicatorDatetimeAppt * appt, else { fmt = terse ? generate_terse_format_string_at_time (now, appt->begin) - : generate_full_format_string_at_time (now, appt->begin); + : generate_full_format_string_at_time (now, appt->begin, settings); } return fmt; } static void -add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse) +add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean phone) { - GDateTime * now = indicator_datetime_service_get_localtime (self); + const int MAX_APPTS = 5; + GDateTime * now; + GHashTable * added; GSList * appts; GSList * l; + int i; + + now = indicator_datetime_service_get_localtime (self); + + added = g_hash_table_new (g_str_hash, g_str_equal); /* build appointment menuitems */ - appts = get_upcoming_appointments (self); - for (l=appts; l!=NULL; l=l->next) + appts = self->priv->upcoming_appointments; + for (l=appts, i=0; l!=NULL && i<MAX_APPTS; l=l->next, i++) { struct IndicatorDatetimeAppt * appt = l->data; - char * fmt = get_appointment_time_format (appt, now, terse); - const gint64 unix_time = g_date_time_to_unix (appt->begin); + char * fmt; + gint64 unix_time; GMenuItem * menu_item; + if (g_hash_table_contains (added, appt->uid)) + continue; + + g_hash_table_add (added, appt->uid); + + fmt = get_appointment_time_format (appt, now, self->priv->settings, phone); + unix_time = g_date_time_to_unix (appt->begin); + menu_item = g_menu_item_new (appt->summary, NULL); - if (!appt->has_alarms) + if (appt->has_alarms) + g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON, + "s", ALARM_CLOCK_ICON_NAME); + else if (appt->color != NULL) g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt->color); @@ -835,27 +985,79 @@ add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse) g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", appt->has_alarms ? "com.canonical.indicator.alarm" : "com.canonical.indicator.appointment"); - g_menu_item_set_action_and_target_value (menu_item, - "indicator.activate-planner", - g_variant_new_int64 (unix_time)); + + if (phone) + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-appointment", + g_variant_new_string (appt->uid)); + else + 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); } /* cleanup */ + g_hash_table_unref (added); g_date_time_unref (now); - g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free); +} + + +/* try to extract the clock app's filename from click. (/$pkgdir/$icon) */ +static gchar * +get_clock_app_icon_filename (void) +{ + gchar * icon_filename = NULL; + gchar * pkgdir; + + pkgdir = NULL; + g_spawn_command_line_sync ("click pkgdir com.ubuntu.clock", &pkgdir, NULL, NULL, NULL); + if (pkgdir != NULL) + { + gchar * manifest = NULL; + g_strstrip (pkgdir); + g_spawn_command_line_sync ("click info com.ubuntu.clock", &manifest, NULL, NULL, NULL); + if (manifest != NULL) + { + JsonParser * parser = json_parser_new (); + if (json_parser_load_from_data (parser, manifest, -1, NULL)) + { + JsonNode * root = json_parser_get_root (parser); /* transfer-none */ + if ((root != NULL) && (JSON_NODE_TYPE(root) == JSON_NODE_OBJECT)) + { + JsonObject * o = json_node_get_object (root); /* transfer-none */ + const gchar * icon_name = json_object_get_string_member (o, "icon"); + if (icon_name != NULL) + icon_filename = g_build_filename (pkgdir, icon_name, NULL); + } + } + g_object_unref (parser); + g_free (manifest); + } + g_free (pkgdir); + } + + return icon_filename; } static GMenuModel * create_phone_appointments_section (IndicatorDatetimeService * self) { + priv_t * p = self->priv; GMenu * menu = g_menu_new (); GMenuItem * menu_item; - menu_item = g_menu_item_new (_("Clock"), NULL); - g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON, "s", "clock"); + if (G_UNLIKELY (!p->clock_app_icon_initialized)) + { + p->clock_app_icon_initialized = TRUE; + p->clock_app_icon_filename = get_clock_app_icon_filename (); + } + + menu_item = g_menu_item_new (_("Clock"), "indicator.activate-phone-clock-app"); + if (p->clock_app_icon_filename != NULL) + g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON, "s", p->clock_app_icon_filename); g_menu_append_item (menu, menu_item); g_object_unref (menu_item); @@ -893,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. */ @@ -966,7 +1113,7 @@ time_location_free (struct TimeLocation * loc) g_date_time_unref (loc->local_time); g_free (loc->name); g_free (loc->zone); - g_free (loc); + g_slice_free (struct TimeLocation, loc); } static struct TimeLocation* @@ -974,7 +1121,7 @@ time_location_new (const char * zone, const char * name, gboolean visible) { - struct TimeLocation * loc = g_new (struct TimeLocation, 1); + struct TimeLocation * loc = g_slice_new (struct TimeLocation); GTimeZone * tz = g_time_zone_new (zone); loc->zone = g_strdup (zone); loc->name = g_strdup (name); @@ -1033,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); - 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++) { @@ -1092,18 +1225,16 @@ create_locations_section (IndicatorDatetimeService * self) 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_full_format_string_at_time (now, loc->local_time); + fmt = generate_full_format_string_at_time (now, loc->local_time, p->settings); - menu_item = g_menu_item_new (label, detailed_action); + menu_item = g_menu_item_new (loc->name, 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", @@ -1115,7 +1246,6 @@ create_locations_section (IndicatorDatetimeService * self) g_object_unref (menu_item); g_free (fmt); g_free (detailed_action); - g_free (label); } } @@ -1140,7 +1270,7 @@ setlocation_data_free (struct setlocation_data * data) { g_free (data->timezone_id); g_free (data->name); - g_free (data); + g_slice_free (struct setlocation_data, data); } static void @@ -1224,7 +1354,7 @@ indicator_datetime_service_set_location (IndicatorDatetimeService * self, g_return_if_fail (name && *name); g_return_if_fail (timezone_id && *timezone_id); - data = g_new0 (struct setlocation_data, 1); + data = g_slice_new0 (struct setlocation_data); data->timezone_id = g_strdup (timezone_id); data->name = g_strdup (name); data->service = self; @@ -1376,7 +1506,44 @@ on_phone_settings_activated (GSimpleAction * a G_GNUC_UNUSED, GVariant * param G_GNUC_UNUSED, gpointer gself G_GNUC_UNUSED) { - execute_command ("system-settings time-date"); + url_dispatch_send ("settings:///system/time-date", NULL, NULL); +} + +static void +on_activate_appointment (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param, + gpointer gself) +{ + priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv; + const gchar * uid = g_variant_get_string (param, NULL); + + if (uid != NULL) + { + const struct IndicatorDatetimeAppt * appt; + GSList * l; + + /* find the appointment that matches that uid */ + for (l=p->upcoming_appointments, appt=NULL; l && !appt; l=l->next) + { + const struct IndicatorDatetimeAppt * tmp = l->data; + if (!g_strcmp0 (uid, tmp->uid)) + appt = tmp; + } + + /* if that appointment's an alarm, dispatch its url */ + g_debug ("%s: uri '%s'; matching appt is %p", G_STRFUNC, uid, appt); + if (appt && appointment_has_alarm_url (appt)) + dispatch_alarm_url (appt); + } +} + +static void +on_phone_clock_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself G_GNUC_UNUSED) +{ + const char * url = "appid://com.ubuntu.clock/clock/current-user-version"; + url_dispatch_send (url, NULL, NULL); } static void @@ -1432,7 +1599,9 @@ init_gactions (IndicatorDatetimeService * self) GActionEntry entries[] = { { "activate-desktop-settings", on_desktop_settings_activated }, { "activate-phone-settings", on_phone_settings_activated }, + { "activate-phone-clock-app", on_phone_clock_activated }, { "activate-planner", on_activate_planner, "x", NULL }, + { "activate-appointment", on_activate_appointment, "s", NULL }, { "set-location", on_set_location, "s" } }; @@ -1492,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, @@ -1604,6 +1776,137 @@ on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED, } /*** +**** Appointments +***/ + +static void +set_calendar_appointments (IndicatorDatetimeService * self, + GSList * appointments) +{ + priv_t * p = self->priv; + + /* repopulate the list */ + indicator_datetime_planner_free_appointments (p->calendar_appointments); + p->calendar_appointments = appointments; + + /* sync the menus/actions */ + update_calendar_action_state (self); + rebuild_calendar_section_soon (self); +} + +static void +on_calendar_appointments_ready (GObject * source, + GAsyncResult * res, + gpointer self) +{ + IndicatorDatetimePlanner * planner; + GError * error; + GSList * appointments; + + planner = INDICATOR_DATETIME_PLANNER (source); + error = NULL; + appointments = indicator_datetime_planner_get_appointments_finish (planner, + res, + &error); + + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("can't get this month's appointments: %s", error->message); + + g_error_free (error); + } + else + { + set_calendar_appointments (INDICATOR_DATETIME_SERVICE (self), + appointments); + } +} + +static void +set_upcoming_appointments (IndicatorDatetimeService * self, + GSList * appointments) +{ + priv_t * p = self->priv; + + /* repopulate the list */ + indicator_datetime_planner_free_appointments (p->upcoming_appointments); + p->upcoming_appointments = appointments; + + /* sync the menus/actions */ + rebuild_appointments_section_soon (self); + + /* alarm timer is keyed off of the next alarm time, + so it needs to be rebuilt when the appointment list changes */ + set_alarm_timer (self); +} + +static void +on_upcoming_appointments_ready (GObject * source, + GAsyncResult * res, + gpointer self) +{ + IndicatorDatetimePlanner * planner; + GError * error; + GSList * appointments; + + planner = INDICATOR_DATETIME_PLANNER (source); + error = NULL; + appointments = indicator_datetime_planner_get_appointments_finish (planner, + res, + &error); + + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("can't get upcoming appointments: %s", error->message); + + g_error_free (error); + } + else + { + set_upcoming_appointments (INDICATOR_DATETIME_SERVICE (self), + appointments); + } +} + +static void +update_appointment_lists (IndicatorDatetimeService * self) +{ + IndicatorDatetimePlanner * planner; + GDateTime * calendar_date; + GDateTime * begin; + GDateTime * end; + int y, m, d; + + planner = self->priv->planner; + calendar_date = get_calendar_date (self); + + /* get all the appointments in the calendar month */ + g_date_time_get_ymd (calendar_date, &y, &m, &d); + begin = g_date_time_new_local (y, m, 1, 0, 0, 0.1); + end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y), 23, 59, 59.9); + if (begin && end) + indicator_datetime_planner_get_appointments (planner, begin, end, + on_calendar_appointments_ready, + self); + g_clear_pointer (&begin, g_date_time_unref); + g_clear_pointer (&end, g_date_time_unref); + + /* get the upcoming appointments */ + begin = g_date_time_ref (calendar_date); + end = g_date_time_add_months (begin, 1); + if (begin && end) + indicator_datetime_planner_get_appointments (planner, begin, end, + on_upcoming_appointments_ready, + self); + g_clear_pointer (&begin, g_date_time_unref); + g_clear_pointer (&end, g_date_time_unref); + g_clear_pointer (&calendar_date, g_date_time_unref); +} + + +/*** **** GDBus ***/ @@ -1642,9 +1945,6 @@ on_bus_acquired (GDBusConnection * connection, char * path = g_strdup_printf ("%s/%s", BUS_PATH, menu_names[i]); struct ProfileMenuInfo * menu = &p->menus[i]; - if (menu->menu == NULL) - create_menu (self, i); - if ((id = g_dbus_connection_export_menu_model (connection, path, G_MENU_MODEL (menu->menu), @@ -1708,6 +2008,53 @@ on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, ***/ static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (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; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_set_property (GObject * o, + guint property_id, + const GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (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; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + + +static void my_dispose (GObject * o) { int i; @@ -1728,13 +2075,8 @@ my_dispose (GObject * o) g_clear_object (&p->cancellable); } - set_detect_location_enabled (self, FALSE); - - if (p->planner != NULL) - { - g_signal_handlers_disconnect_by_data (p->planner, self); - g_clear_object (&p->planner); - } + indicator_datetime_service_set_clock (self, NULL); + indicator_datetime_service_set_planner (self, NULL); if (p->login1_manager != NULL) { @@ -1746,6 +2088,7 @@ my_dispose (GObject * o) indicator_clear_timer (&p->rebuild_id); indicator_clear_timer (&p->timezone_timer); indicator_clear_timer (&p->header_timer); + indicator_clear_timer (&p->alarm_timer); if (p->settings != NULL) { @@ -1758,12 +2101,17 @@ 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->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); } @@ -1773,6 +2121,8 @@ my_finalize (GObject * o) IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(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); @@ -1786,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 */ @@ -1825,30 +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 planner and listen for changes + **** Listen for settings 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]); @@ -1913,6 +2279,11 @@ indicator_datetime_service_init (IndicatorDatetimeService * self) on_local_time_jumped (self); + set_alarm_timer (self); + + for (i=0; i<N_PROFILES; ++i) + create_menu (self, i); + g_string_free (gstr, TRUE); } @@ -1920,9 +2291,13 @@ static void indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) { GObjectClass * object_class = G_OBJECT_CLASS (klass); + const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; 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)); @@ -1934,6 +2309,24 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + /* install properties */ + + 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", + INDICATOR_TYPE_DATETIME_PLANNER, + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); } /*** @@ -1941,21 +2334,17 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) ***/ IndicatorDatetimeService * -indicator_datetime_service_new (void) +indicator_datetime_service_new (IndicatorDatetimeClock * clock, + IndicatorDatetimePlanner * planner) { - GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, NULL); + 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 G_GNUC_UNUSED) -{ - return g_date_time_new_now_local (); -} - void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, GDateTime * date) @@ -1972,8 +2361,77 @@ indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, /* sync the menuitems and action states */ if (dirty) + 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) +{ + priv_t * p; + + g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self)); + g_return_if_fail ((planner == NULL) || INDICATOR_IS_DATETIME_PLANNER (planner)); + + p = self->priv; + + /* clear the old planner & appointments */ + + if (p->planner != NULL) { - update_calendar_action_state (self); - rebuild_appointments_section_soon (self); + g_signal_handlers_disconnect_by_data (p->planner, self); + g_clear_object (&p->planner); + } + + g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments); + g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments); + + /* set the new planner & begin fetching appointments from it */ + + if (planner != NULL) + { + p->planner = g_object_ref (planner); + + g_signal_connect_swapped (p->planner, "appointments-changed", + G_CALLBACK(update_appointment_lists), self); + + update_appointment_lists (self); } } |