From 5b500193b3eb561260cc31e714e2b0c35148cfff Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 3 May 2013 09:53:41 -0700 Subject: refactor the geoclue code into its own class. --- src/Makefile.am | 4 + src/datetime-service.c | 214 +++++++--------------------------------- src/location-geoclue.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++ src/location-geoclue.h | 61 ++++++++++++ src/location.c | 108 ++++++++++++++++++++ src/location.h | 68 +++++++++++++ 6 files changed, 536 insertions(+), 180 deletions(-) create mode 100644 src/location-geoclue.c create mode 100644 src/location-geoclue.h create mode 100644 src/location.c create mode 100644 src/location.h diff --git a/src/Makefile.am b/src/Makefile.am index a31cb4b..f9b8562 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,10 @@ indicator_datetime_service_SOURCES = \ datetime-interface.h \ gen-datetime-service.xml.c \ datetime-service.c \ + location.c \ + location.h \ + location-geoclue.c \ + location-geoclue.h \ utils.c \ utils.h \ dbus-shared.h \ diff --git a/src/datetime-service.c b/src/datetime-service.c index 56ea51c..3738084 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -35,9 +35,6 @@ with this program. If not, see . #include #include -#include -#include - #include #include #include @@ -48,6 +45,7 @@ with this program. If not, see . #include "datetime-interface.h" #include "dbus-shared.h" +#include "location-geoclue.h" #include "settings-shared.h" #include "utils.h" @@ -92,11 +90,11 @@ static time_t start_time_appointments = (time_t) 0; static GSettings * conf = NULL; static ESourceRegistry * source_registry = NULL; static GList * appointment_sources = NULL; +static IndicatorDatetimeLocation * geo_location = NULL; /* Our 2 important timezones */ static gchar * current_timezone = NULL; -static gchar * geo_timezone = NULL; struct comp_instance { ECalComponent *comp; @@ -187,11 +185,14 @@ update_location_menu_items (void) const time_t now = time(NULL); /* maybe add geo_timezone */ - if (geo_timezone != NULL) { - const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); - gchar * name = get_current_zone_name (geo_timezone); - locations = locations_add (locations, geo_timezone, name, visible, now); - g_free (name); + if (geo_location != NULL) { + const char * geo_timezone = indicator_datetime_location_get_timezone (geo_location); + if (geo_timezone && *geo_timezone) { + const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); + gchar * name = get_current_zone_name (geo_timezone); + locations = locations_add (locations, geo_timezone, name, visible, now); + g_free (name); + } } /* maybe add current_timezone */ @@ -1182,174 +1183,6 @@ system_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data) g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), user_data); } - -/**** -***** GEOCLUE -****/ - -static void geo_start (void); -static void geo_stop (void); -static void geo_create_client (GeoclueMaster * master, GeoclueMasterClient * client, gchar * path, GError * error, gpointer user_data); -static void geo_client_invalid (GeoclueMasterClient * client, gpointer user_data); - -static GeoclueMaster * geo_master = NULL; -static GeoclueMasterClient * geo_client = NULL; -static GeoclueAddress * geo_address = NULL; - -static void -geo_set_timezone (const gchar * timezone) -{ - if (geo_timezone != timezone) { - g_clear_pointer (&geo_timezone, g_free); - geo_timezone = g_strdup (timezone); - g_debug("Geoclue timezone is: %s", timezone ? timezone : "(Null)"); - update_location_menu_items(); - } -} - -/* Callback from getting the address */ -static void -geo_address_cb (GeoclueAddress * address, int timestamp, GHashTable * addy_data, GeoclueAccuracy * accuracy, GError * error, gpointer user_data) -{ - if (error == NULL) { - geo_set_timezone (g_hash_table_lookup (addy_data, "timezone")); - } else { - g_warning("Unable to get Geoclue address: %s", error->message); - g_clear_error (&error); - } -} - -/* Clean up the reference we kept to the address and make sure to - drop the signals incase someone else has one. */ -static void -geo_address_clean (void) -{ - if (geo_address != NULL) { - g_signal_handlers_disconnect_by_func (geo_address, geo_address_cb, NULL); - g_clear_object (&geo_address); - } -} - -/* Clean up and remove all signal handlers from the client as we - unreference it as well. */ -static void -geo_client_clean (void) -{ - if (geo_client != NULL) { - g_signal_handlers_disconnect_by_func (geo_client, geo_client_invalid, NULL); - g_clear_object (&geo_client); - } -} - -/* Callback from creating the address */ -static void -geo_create_address (GeoclueMasterClient * master, GeoclueAddress * address, GError * error, gpointer user_data) -{ - if (error != NULL) { - g_warning("Unable to create GeoClue address: %s", error->message); - g_clear_error (&error); - return; - } - - /* We shouldn't have created a new address if we already had one - so this is a warning. But, it really is only a mem-leak so we - don't need to error out. */ - g_warn_if_fail(geo_address == NULL); - geo_address_clean(); - - g_debug("Created Geoclue Address"); - geo_address = g_object_ref (address); - - geoclue_address_get_address_async (geo_address, geo_address_cb, NULL); - - g_signal_connect (address, "address-changed", G_CALLBACK(geo_address_cb), NULL); -} - -/* Callback from setting requirements */ -static void -geo_req_set (GeoclueMasterClient * master, GError * error, gpointer user_data) -{ - if (error != NULL) { - g_warning("Unable to set Geoclue requirements: %s", error->message); - g_clear_error (&error); - } -} - -/* Client is killing itself rather oddly */ -static void -geo_client_invalid (GeoclueMasterClient * client, gpointer user_data) -{ - g_warning("Master client invalid, rebuilding."); - geo_stop (); - geo_start (); -} - -static void -geo_stop (void) -{ - geo_set_timezone (NULL); - - geo_address_clean (); - geo_client_clean (); - g_clear_object (&geo_master); -} - -static void -geo_start (void) -{ - g_warn_if_fail (geo_master == NULL); - - g_clear_object (&geo_master); - geo_master = geoclue_master_get_default(); - geoclue_master_create_client_async (geo_master, geo_create_client, NULL); -} - -/* Callback from creating the client */ -static void -geo_create_client (GeoclueMaster * master, GeoclueMasterClient * client, gchar * path, GError * error, gpointer user_data) -{ - g_debug("Created Geoclue client at: %s", path); - - geo_client = client; - - if (error != NULL) { - g_warning("Unable to get a GeoClue client! '%s' Geolocation based timezone support will not be available.", error->message); - g_clear_error (&error); - return; - } - - if (client == NULL) { - g_warning(_("Unable to get a GeoClue client! Geolocation based timezone support will not be available.")); - return; - } - - g_object_ref (geo_client); - - /* New client, make sure we don't have an address hanging on */ - geo_address_clean(); - - geoclue_master_client_set_requirements_async(geo_client, - GEOCLUE_ACCURACY_LEVEL_REGION, - 0, - FALSE, - GEOCLUE_RESOURCE_ALL, - geo_req_set, - NULL); - - geoclue_master_client_create_address_async(geo_client, geo_create_address, NULL); - - g_signal_connect(client, "invalidated", G_CALLBACK(geo_client_invalid), NULL); -} - -static void -on_use_geoclue_changed_cb (GSettings * settings, gchar * key, gpointer unused G_GNUC_UNUSED) -{ - geo_stop (); - - if (g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S)) - geo_start (); -} - /**** ***** ****/ @@ -1417,6 +1250,28 @@ source_registry_changed_cb (ESourceRegistry *registry __attribute__ ((unused)), update_appointment_menu_items (user_data); } +static void +on_use_geoclue_changed_cb (GSettings *settings, + gchar *key G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + const gboolean using = geo_location != NULL; + const gboolean should_use = g_settings_get_boolean (conf, "show-auto-detected-location"); + + if (using && !should_use) + { + g_signal_handlers_disconnect_by_func (geo_location, update_location_menu_items, 0); + g_clear_object (&geo_location); + update_location_menu_items (); + } + else if (should_use && !using) + { + geo_location = indicator_datetime_location_geoclue_new (); + g_signal_connect (geo_location, "notify::timezone", + G_CALLBACK(update_location_menu_items), NULL); + } +} + /* Function to build everything up. Entry point from asm. */ int main (int argc, char ** argv) @@ -1461,8 +1316,7 @@ main (int argc, char ** argv) update_current_timezone(); /* Setup geoclue */ - if (g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S)) - geo_start (); + on_use_geoclue_changed_cb (conf, NULL, NULL); /* Setup dbus interface */ dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL); @@ -1501,7 +1355,7 @@ main (int argc, char ** argv) icaltimezone_free_builtin_timezones(); - geo_stop (); + g_clear_object (&geo_location); return 0; } diff --git a/src/location-geoclue.c b/src/location-geoclue.c new file mode 100644 index 0000000..2e57f39 --- /dev/null +++ b/src/location-geoclue.c @@ -0,0 +1,261 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "location-geoclue.h" + +struct _IndicatorDatetimeLocationGeocluePriv +{ + GeoclueMaster * master; + GeoclueMasterClient * client; + GeoclueAddress * address; + gchar * timezone; +}; + +typedef IndicatorDatetimeLocationGeocluePriv priv_t; + +G_DEFINE_TYPE (IndicatorDatetimeLocationGeoclue, + indicator_datetime_location_geoclue, + INDICATOR_TYPE_DATETIME_LOCATION) + +static void geo_restart (IndicatorDatetimeLocationGeoclue * self); + +/*** +**** +***/ + +static void +set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) +{ + priv_t * p = self->priv; + + if (p->timezone != timezone) + { + g_free (p->timezone); + p->timezone = g_strdup (timezone); + indicator_datetime_location_notify_timezone (INDICATOR_DATETIME_LOCATION(self)); + } +} + +static void +on_address_changed (GeoclueAddress * address, + int timestamp, + GHashTable * addy_data, + GeoclueAccuracy * accuracy, + GError * error, + gpointer gself) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else + { + IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); + const char * timezone = g_hash_table_lookup (addy_data, "timezone"); + set_timezone (self, timezone); + } +} + +static void +on_address_created (GeoclueMasterClient * master, + GeoclueAddress * address, + GError * error, + gpointer gself) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else + { + priv_t * p = INDICATOR_DATETIME_LOCATION_GEOCLUE(gself)->priv; + + g_assert (p->address == NULL); + p->address = g_object_ref (address); + + geoclue_address_get_address_async (address, on_address_changed, gself); + g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed), gself); + } +} + +static void +on_requirements_set (GeoclueMasterClient * master, GError * error, gpointer user_data) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +static void +on_client_created (GeoclueMaster * master, + GeoclueMasterClient * client, + gchar * path, + GError * error, + gpointer gself) +{ + g_debug ("Created Geoclue client at: %s", path); + + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else if (client == NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + } + else + { + IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); + priv_t * p = self->priv; + + g_clear_object (&p->client); + p->client = g_object_ref (client); + g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself); + + geoclue_master_client_set_requirements_async (p->client, + GEOCLUE_ACCURACY_LEVEL_REGION, + 0, + FALSE, + GEOCLUE_RESOURCE_ALL, + on_requirements_set, + NULL); + + geoclue_master_client_create_address_async (p->client, on_address_created, gself); + } +} + +static void +geo_start (IndicatorDatetimeLocationGeoclue * self) +{ + priv_t * p = self->priv; + + g_assert (p->master == NULL); + p->master = geoclue_master_get_default (); + geoclue_master_create_client_async (p->master, on_client_created, self); +} + +static void +geo_stop (IndicatorDatetimeLocationGeoclue * self) +{ + priv_t * p = self->priv; + + if (p->address != NULL) + { + g_signal_handlers_disconnect_by_func (p->address, on_address_changed, self); + g_clear_object (&p->address); + } + + if (p->client != NULL) + { + g_signal_handlers_disconnect_by_func (p->client, geo_restart, self); + g_clear_object (&p->client); + } + + g_clear_object (&p->master); +} + +static void +geo_restart (IndicatorDatetimeLocationGeoclue * self) +{ + geo_stop (self); + geo_start (self); +} + +/*** +**** +***/ + +static const char * +my_get_timezone (IndicatorDatetimeLocation * self) +{ + return INDICATOR_DATETIME_LOCATION_GEOCLUE(self)->priv->timezone; +} + +static void +my_dispose (GObject * o) +{ + geo_stop (INDICATOR_DATETIME_LOCATION_GEOCLUE (o)); + + G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (o); + priv_t * p = self->priv; + + g_free (p->timezone); + + G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->finalize (o); +} + +static void +indicator_datetime_location_geoclue_class_init (IndicatorDatetimeLocationGeoclueClass * klass) +{ + GObjectClass * object_class; + IndicatorDatetimeLocationClass * location_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + location_class = INDICATOR_DATETIME_LOCATION_CLASS (klass); + location_class->get_timezone = my_get_timezone; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimeLocationGeocluePriv)); +} + +static void +indicator_datetime_location_geoclue_init (IndicatorDatetimeLocationGeoclue * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, + IndicatorDatetimeLocationGeocluePriv); + self->priv = p; + + geo_start (self); +} + +/*** +**** Public +***/ + +IndicatorDatetimeLocation * +indicator_datetime_location_geoclue_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, NULL); + + return INDICATOR_DATETIME_LOCATION (o); +} diff --git a/src/location-geoclue.h b/src/location-geoclue.h new file mode 100644 index 0000000..7b65917 --- /dev/null +++ b/src/location-geoclue.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ +#define __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ + +#include +#include + +#include "location.h" /* parent class */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE (indicator_datetime_location_geoclue_get_type()) +#define INDICATOR_DATETIME_LOCATION_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, IndicatorDatetimeLocationGeoclue)) +#define INDICATOR_DATETIME_LOCATION_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, IndicatorDatetimeLocationGeoclueClass)) +#define INDICATOR_IS_DATETIME_LOCATION_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE)) + +typedef struct _IndicatorDatetimeLocationGeoclue IndicatorDatetimeLocationGeoclue; +typedef struct _IndicatorDatetimeLocationGeocluePriv IndicatorDatetimeLocationGeocluePriv; +typedef struct _IndicatorDatetimeLocationGeoclueClass IndicatorDatetimeLocationGeoclueClass; + +GType indicator_datetime_location_geoclue_get_type (void); + +/** + * An implementation of IndicatorDatetimeLocation that gets its user information + * from org.freedesktop.ConsoleKit and org.freedesktop.Accounts over DBus. + */ +struct _IndicatorDatetimeLocationGeoclue +{ + /*< private >*/ + IndicatorDatetimeLocation parent; + IndicatorDatetimeLocationGeocluePriv * priv; +}; + +struct _IndicatorDatetimeLocationGeoclueClass +{ + IndicatorDatetimeLocationClass parent_class; +}; + +IndicatorDatetimeLocation * indicator_datetime_location_geoclue_new (void); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ */ diff --git a/src/location.c b/src/location.c new file mode 100644 index 0000000..12e25c3 --- /dev/null +++ b/src/location.c @@ -0,0 +1,108 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "location.h" + +G_DEFINE_TYPE (IndicatorDatetimeLocation, + indicator_datetime_location, + G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_TIMEZONE, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeLocation * self = INDICATOR_DATETIME_LOCATION (o); + + switch (property_id) + { + case PROP_TIMEZONE: + g_value_set_string (value, indicator_datetime_location_get_timezone (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * object) +{ + G_OBJECT_CLASS (indicator_datetime_location_parent_class)->dispose (object); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_datetime_location_class_init (IndicatorDatetimeLocationClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = my_get_property; + object_class->dispose = my_dispose; + + klass->get_timezone = NULL; + + properties[PROP_0] = NULL; + + properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", + "Timezone", + "Timezone", + "", + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +indicator_datetime_location_init (IndicatorDatetimeLocation * self G_GNUC_UNUSED) +{ +} + +/*** +**** +***/ + +const char * +indicator_datetime_location_get_timezone (IndicatorDatetimeLocation * self) +{ + g_return_val_if_fail (INDICATOR_IS_DATETIME_LOCATION (self), NULL); + + return INDICATOR_DATETIME_LOCATION_GET_CLASS (self)->get_timezone (self); +} + +void +indicator_datetime_location_notify_timezone (IndicatorDatetimeLocation * self) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_LOCATION (self)); + + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); +} + diff --git a/src/location.h b/src/location.h new file mode 100644 index 0000000..f9fd2ce --- /dev/null +++ b/src/location.h @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_LOCATION__H__ +#define __INDICATOR_DATETIME_LOCATION__H__ + +#include +#include + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_LOCATION (indicator_datetime_location_get_type()) +#define INDICATOR_DATETIME_LOCATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocation)) +#define INDICATOR_DATETIME_LOCATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocationClass)) +#define INDICATOR_DATETIME_LOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocationClass)) +#define INDICATOR_IS_DATETIME_LOCATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION)) + +typedef struct _IndicatorDatetimeLocation IndicatorDatetimeLocation; +typedef struct _IndicatorDatetimeLocationClass IndicatorDatetimeLocationClass; + +GType indicator_datetime_location_get_type (void); + +/** + * Abstract Base Class for the mechanisms that determine timezone by location + */ +struct _IndicatorDatetimeLocation +{ + /*< private >*/ + GObject parent; +}; + +struct _IndicatorDatetimeLocationClass +{ + GObjectClass parent_class; + + /* virtual functions */ + const char * (*get_timezone) (IndicatorDatetimeLocation * self); +}; + +/*** +**** +***/ + +#define INDICATOR_DATETIME_LOCATION_PROPERTY_TIMEZONE "timezone" + +const char * indicator_datetime_location_get_timezone (IndicatorDatetimeLocation *); + +void indicator_datetime_location_notify_timezone (IndicatorDatetimeLocation *); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_LOCATION__H__ */ -- cgit v1.2.3 From 5e6762db2485ef88859bdb8fcca636c3dc8aa832 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 3 May 2013 09:54:53 -0700 Subject: fix a g_critical by calling gtk_init() before we try to get the icon size. --- src/datetime-service.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/datetime-service.c b/src/datetime-service.c index 3738084..e7fc631 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -1276,6 +1276,8 @@ on_use_geoclue_changed_cb (GSettings *settings, int main (int argc, char ** argv) { + gtk_init (&argc, &argv); + /* Acknowledging the service init and setting up the interface */ service = indicator_service_new_version(SERVICE_NAME, SERVICE_VERSION); g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); -- cgit v1.2.3 From ad4d799e778a7c31c4c10e77fac4053e71ac9a75 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 3 May 2013 14:29:45 -0700 Subject: avoid pointer comparisons in set_timezone() --- src/location-geoclue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/location-geoclue.c b/src/location-geoclue.c index 2e57f39..87358fb 100644 --- a/src/location-geoclue.c +++ b/src/location-geoclue.c @@ -52,7 +52,7 @@ set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) { priv_t * p = self->priv; - if (p->timezone != timezone) + if (g_strcmp0 (p->timezone, timezone)) { g_free (p->timezone); p->timezone = g_strdup (timezone); -- cgit v1.2.3 From ec7db5b53511247719434175951c2fb9daefd0f2 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 3 May 2013 14:31:45 -0700 Subject: make the flow in on_use_geoclue_changed_cb() a little easier to read --- src/datetime-service.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/datetime-service.c b/src/datetime-service.c index 52d9647..ff22db5 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -1255,16 +1255,15 @@ on_use_geoclue_changed_cb (GSettings *settings, gchar *key G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { - const gboolean using = geo_location != NULL; - const gboolean should_use = g_settings_get_boolean (conf, "show-auto-detected-location"); + const gboolean use_geoclue = g_settings_get_boolean (conf, "show-auto-detected-location"); - if (using && !should_use) + if (geo_location && !use_geoclue) { g_signal_handlers_disconnect_by_func (geo_location, update_location_menu_items, 0); g_clear_object (&geo_location); update_location_menu_items (); } - else if (should_use && !using) + else if (use_geoclue && !geo_location) { geo_location = indicator_datetime_location_geoclue_new (); g_signal_connect (geo_location, "notify::timezone", -- cgit v1.2.3 From cc92756aaa57060539f57ce92d41ded0966b92d1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 6 May 2013 13:51:27 -0700 Subject: readability changes suggested by lars --- src/datetime-service.c | 7 +++---- src/location-geoclue.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/datetime-service.c b/src/datetime-service.c index a8f941a..7b7c2db 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -1256,16 +1256,15 @@ on_use_geoclue_changed_cb (GSettings *settings, gchar *key G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { - const gboolean using = geo_location != NULL; - const gboolean should_use = g_settings_get_boolean (conf, "show-auto-detected-location"); + const gboolean use_geoclue = g_settings_get_boolean (conf, "show-auto-detected-location"); - if (using && !should_use) + if (geo_location && !use_geoclue) { g_signal_handlers_disconnect_by_func (geo_location, update_location_menu_items, 0); g_clear_object (&geo_location); update_location_menu_items (); } - else if (should_use && !using) + else if (use_geoclue && !geo_location) { geo_location = indicator_datetime_location_geoclue_new (); g_signal_connect (geo_location, "notify::timezone", diff --git a/src/location-geoclue.c b/src/location-geoclue.c index 2e57f39..87358fb 100644 --- a/src/location-geoclue.c +++ b/src/location-geoclue.c @@ -52,7 +52,7 @@ set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) { priv_t * p = self->priv; - if (p->timezone != timezone) + if (g_strcmp0 (p->timezone, timezone)) { g_free (p->timezone); p->timezone = g_strdup (timezone); -- cgit v1.2.3 From 5d8b176ab3c072faa259bfc3de2b8de103437463 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 6 May 2013 14:49:34 -0700 Subject: make location-file (the /etc/timezone monitor) a sibling of location-geoclue (the geoclue service monitor) --- src/Makefile.am | 2 + src/datetime-service.c | 75 +++++----------- src/location-file.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++++ src/location-file.h | 61 +++++++++++++ src/location-geoclue.h | 4 +- 5 files changed, 322 insertions(+), 55 deletions(-) create mode 100644 src/location-file.c create mode 100644 src/location-file.h diff --git a/src/Makefile.am b/src/Makefile.am index f9b8562..2efff8f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,6 +13,8 @@ indicator_datetime_service_SOURCES = \ datetime-service.c \ location.c \ location.h \ + location-file.c \ + location-file.h \ location-geoclue.c \ location-geoclue.h \ utils.c \ diff --git a/src/datetime-service.c b/src/datetime-service.c index 7b7c2db..35a5e53 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -45,6 +45,7 @@ with this program. If not, see . #include "datetime-interface.h" #include "dbus-shared.h" +#include "location-file.h" #include "location-geoclue.h" #include "settings-shared.h" #include "utils.h" @@ -91,10 +92,7 @@ static GSettings * conf = NULL; static ESourceRegistry * source_registry = NULL; static GList * appointment_sources = NULL; static IndicatorDatetimeLocation * geo_location = NULL; - - -/* Our 2 important timezones */ -static gchar * current_timezone = NULL; +static IndicatorDatetimeLocation * tz_file = NULL; struct comp_instance { ECalComponent *comp; @@ -196,11 +194,14 @@ update_location_menu_items (void) } /* maybe add current_timezone */ - if (current_timezone != NULL) { - const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); - gchar * name = get_current_zone_name (current_timezone); - locations = locations_add (locations, current_timezone, name, visible, now); - g_free (name); + if (tz_file != NULL) { + const char * tz = indicator_datetime_location_get_timezone (tz_file); + if (tz && *tz) { + const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); + gchar * name = get_current_zone_name (tz); + locations = locations_add (locations, tz, name, visible, now); + g_free (name); + } } /* maybe add the user-specified custom locations */ @@ -250,27 +251,6 @@ update_location_menu_items (void) dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, have_visible_location); } -/* Update the current timezone */ -static void -update_current_timezone (void) { - /* Clear old data */ - if (current_timezone != NULL) { - g_free(current_timezone); - current_timezone = NULL; - } - - current_timezone = read_timezone (); - if (current_timezone == NULL) { - return; - } - - g_debug("System timezone is: %s", current_timezone); - - update_location_menu_items(); - - return; -} - static void quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data) { @@ -687,6 +667,7 @@ update_appointment_menu_items (gpointer user_data __attribute__ ((unused))) const int mday = today->tm_mday; const int mon = today->tm_mon; const int year = today->tm_year; + const char * current_timezone; int start_month_saved = mon; @@ -723,6 +704,8 @@ update_appointment_menu_items (gpointer user_data __attribute__ ((unused))) g_list_free_full (comp_instances, (GDestroyNotify)comp_instance_free); comp_instances = NULL; + current_timezone = indicator_datetime_location_get_timezone (tz_file); + // Generate instances for all sources for (s=appointment_sources; s!=NULL; s=s->next) { @@ -1073,31 +1056,15 @@ on_clock_skew (void) return; } -/* Run when the timezone file changes */ static void -timezone_changed (GFileMonitor * monitor, GFile * file, GFile * otherfile, GFileMonitorEvent event, gpointer user_data) +on_timezone_changed (void) { - update_current_timezone(); - on_clock_skew(); - return; -} + update_location_menu_items (); -/* Set up monitoring the timezone file */ -static void -build_timezone (DatetimeInterface * dbus) -{ - GFile * timezonefile = g_file_new_for_path(TIMEZONE_FILE); - GFileMonitor * monitor = g_file_monitor_file(timezonefile, G_FILE_MONITOR_NONE, NULL, NULL); - if (monitor != NULL) { - g_signal_connect(G_OBJECT(monitor), "changed", G_CALLBACK(timezone_changed), dbus); - g_debug("Monitoring timezone file: '" TIMEZONE_FILE "'"); - } else { - g_warning("Unable to monitor timezone file: '" TIMEZONE_FILE "'"); - } - g_object_unref(timezonefile); - return; + on_clock_skew (); } + /* Source ID for the timer */ static guint day_timer = 0; @@ -1315,7 +1282,7 @@ main (int argc, char ** argv) build_menus(root); /* Cache the timezone */ - update_current_timezone(); + update_location_menu_items (); /* Setup geoclue */ on_use_geoclue_changed_cb (conf, NULL, NULL); @@ -1324,7 +1291,8 @@ main (int argc, char ** argv) dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL); /* Setup timezone watch */ - build_timezone(dbus); + tz_file = indicator_datetime_location_file_new (TIMEZONE_FILE); + g_signal_connect (tz_file, "notify::timezone", G_CALLBACK(on_timezone_changed), NULL); /* Set up the day timer */ day_timer_reset(); @@ -1357,7 +1325,8 @@ main (int argc, char ** argv) icaltimezone_free_builtin_timezones(); - g_clear_object (&geo_location); + g_object_unref (geo_location); + g_object_unref (tz_file); return 0; } diff --git a/src/location-file.c b/src/location-file.c new file mode 100644 index 0000000..ff6fd47 --- /dev/null +++ b/src/location-file.c @@ -0,0 +1,235 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "location-file.h" + +enum +{ + PROP_0, + PROP_FILENAME, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +struct _IndicatorDatetimeLocationFilePriv +{ + gchar * filename; + GFile * file; + GFileMonitor * monitor; + gchar * timezone; +}; + +typedef IndicatorDatetimeLocationFilePriv priv_t; + +G_DEFINE_TYPE (IndicatorDatetimeLocationFile, + indicator_datetime_location_file, + INDICATOR_TYPE_DATETIME_LOCATION) + +/*** +**** +***/ + +static void +reload (IndicatorDatetimeLocationFile * self) +{ + priv_t * p = self->priv; + + GError * err = NULL; + gchar * new_timezone = NULL; + + if (!g_file_get_contents (p->filename, &new_timezone, NULL, &err)) + { + g_warning ("Unable to read timezone file '%s': %s", p->filename, err->message); + g_error_free (err); + } + else + { + g_strstrip (new_timezone); + g_free (p->timezone); + p->timezone = new_timezone; + indicator_datetime_location_notify_timezone (INDICATOR_DATETIME_LOCATION(self)); + } +} + +static void +set_filename (IndicatorDatetimeLocationFile * self, const char * filename) +{ + GError * err; + priv_t * p = self->priv; + + g_clear_object (&p->monitor); + g_clear_object (&p->file); + g_free (p->filename); + + p->filename = g_strdup (filename); + p->file = g_file_new_for_path (p->filename); + + err = NULL; + p->monitor = g_file_monitor_file (p->file, G_FILE_MONITOR_NONE, NULL, &err); + if (err != NULL) + { + g_warning ("Unable to monitor timezone file '%s': %s", TIMEZONE_FILE, err->message); + g_error_free (err); + } + else + { + g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); + g_debug ("Monitoring timezone file '%s'", p->filename); + } + + reload (self); +} + +/*** +**** IndicatorDatetimeLocationClass funcs +***/ + +static const char * +my_get_timezone (IndicatorDatetimeLocation * self) +{ + return INDICATOR_DATETIME_LOCATION_FILE(self)->priv->timezone; +} + +/*** +**** GObjectClass funcs +***/ + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); + + switch (property_id) + { + case PROP_FILENAME: + g_value_set_string (value, self->priv->filename); + 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) +{ + IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); + + switch (property_id) + { + case PROP_FILENAME: + set_filename (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * o) +{ + IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); + priv_t * p = self->priv; + + g_clear_object (&p->monitor); + g_clear_object (&p->file); + + G_OBJECT_CLASS (indicator_datetime_location_file_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); + priv_t * p = self->priv; + + g_free (p->filename); + g_free (p->timezone); + + G_OBJECT_CLASS (indicator_datetime_location_file_parent_class)->finalize (o); +} + +/*** +**** +***/ + +static void +indicator_datetime_location_file_class_init (IndicatorDatetimeLocationFileClass * klass) +{ + GObjectClass * object_class; + IndicatorDatetimeLocationClass * location_class; + const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + object_class->set_property = my_set_property; + object_class->get_property = my_get_property; + + location_class = INDICATOR_DATETIME_LOCATION_CLASS (klass); + location_class->get_timezone = my_get_timezone; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimeLocationFilePriv)); + + /* install properties */ + + properties[PROP_0] = NULL; + + properties[PROP_FILENAME] = g_param_spec_string ("filename", + "Filename", + "Filename to monitor for TZ changes", + "", + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +indicator_datetime_location_file_init (IndicatorDatetimeLocationFile * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_LOCATION_FILE, + IndicatorDatetimeLocationFilePriv); +} + +/*** +**** Public +***/ + +IndicatorDatetimeLocation * +indicator_datetime_location_file_new (const char * filename) +{ + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_LOCATION_FILE, "filename", filename, NULL); + + return INDICATOR_DATETIME_LOCATION (o); +} diff --git a/src/location-file.h b/src/location-file.h new file mode 100644 index 0000000..8ed87ad --- /dev/null +++ b/src/location-file.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_LOCATION_FILE__H__ +#define __INDICATOR_DATETIME_LOCATION_FILE__H__ + +#include +#include + +#include "location.h" /* parent class */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_LOCATION_FILE (indicator_datetime_location_file_get_type()) +#define INDICATOR_DATETIME_LOCATION_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE, IndicatorDatetimeLocationFile)) +#define INDICATOR_DATETIME_LOCATION_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE, IndicatorDatetimeLocationFileClass)) +#define INDICATOR_IS_DATETIME_LOCATION_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE)) + +typedef struct _IndicatorDatetimeLocationFile IndicatorDatetimeLocationFile; +typedef struct _IndicatorDatetimeLocationFilePriv IndicatorDatetimeLocationFilePriv; +typedef struct _IndicatorDatetimeLocationFileClass IndicatorDatetimeLocationFileClass; + +GType indicator_datetime_location_file_get_type (void); + +/** + * An implementation of IndicatorDatetimeLocation which determines the timezone + * from monitoring a local file, such as /etc/timezone + */ +struct _IndicatorDatetimeLocationFile +{ + /*< private >*/ + IndicatorDatetimeLocation parent; + IndicatorDatetimeLocationFilePriv * priv; +}; + +struct _IndicatorDatetimeLocationFileClass +{ + IndicatorDatetimeLocationClass parent_class; +}; + +IndicatorDatetimeLocation * indicator_datetime_location_file_new (const char * filename); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_LOCATION_FILE__H__ */ diff --git a/src/location-geoclue.h b/src/location-geoclue.h index 7b65917..5b6d232 100644 --- a/src/location-geoclue.h +++ b/src/location-geoclue.h @@ -39,8 +39,8 @@ typedef struct _IndicatorDatetimeLocationGeoclueClass IndicatorDatetimeLocatio GType indicator_datetime_location_geoclue_get_type (void); /** - * An implementation of IndicatorDatetimeLocation that gets its user information - * from org.freedesktop.ConsoleKit and org.freedesktop.Accounts over DBus. + * An implementation of IndicatorDatetimeLocation which determines the timezone + * from calling a GeoClue service over DBus. */ struct _IndicatorDatetimeLocationGeoclue { -- cgit v1.2.3 From 76422d4997401b8f278b7316a23a324655c12c92 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 6 May 2013 14:57:00 -0700 Subject: rename the 'DatetimeLocation' classes to the more accurate name 'DatetimeTimezone' --- src/Makefile.am | 12 +-- src/datetime-service.c | 18 ++-- src/location-file.c | 235 -------------------------------------------- src/location-file.h | 61 ------------ src/location-geoclue.c | 261 ------------------------------------------------- src/location-geoclue.h | 61 ------------ src/location.c | 108 -------------------- src/location.h | 68 ------------- src/timezone-file.c | 235 ++++++++++++++++++++++++++++++++++++++++++++ src/timezone-file.h | 61 ++++++++++++ src/timezone-geoclue.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++ src/timezone-geoclue.h | 61 ++++++++++++ src/timezone.c | 108 ++++++++++++++++++++ src/timezone.h | 68 +++++++++++++ 14 files changed, 809 insertions(+), 809 deletions(-) delete mode 100644 src/location-file.c delete mode 100644 src/location-file.h delete mode 100644 src/location-geoclue.c delete mode 100644 src/location-geoclue.h delete mode 100644 src/location.c delete mode 100644 src/location.h create mode 100644 src/timezone-file.c create mode 100644 src/timezone-file.h create mode 100644 src/timezone-geoclue.c create mode 100644 src/timezone-geoclue.h create mode 100644 src/timezone.c create mode 100644 src/timezone.h diff --git a/src/Makefile.am b/src/Makefile.am index 2efff8f..67b085a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,12 +11,12 @@ indicator_datetime_service_SOURCES = \ datetime-interface.h \ gen-datetime-service.xml.c \ datetime-service.c \ - location.c \ - location.h \ - location-file.c \ - location-file.h \ - location-geoclue.c \ - location-geoclue.h \ + timezone.c \ + timezone.h \ + timezone-file.c \ + timezone-file.h \ + timezone-geoclue.c \ + timezone-geoclue.h \ utils.c \ utils.h \ dbus-shared.h \ diff --git a/src/datetime-service.c b/src/datetime-service.c index 35a5e53..0551068 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -45,9 +45,9 @@ with this program. If not, see . #include "datetime-interface.h" #include "dbus-shared.h" -#include "location-file.h" -#include "location-geoclue.h" #include "settings-shared.h" +#include "timezone-file.h" +#include "timezone-geoclue.h" #include "utils.h" /* how often to check for clock skew */ @@ -91,8 +91,8 @@ static time_t start_time_appointments = (time_t) 0; static GSettings * conf = NULL; static ESourceRegistry * source_registry = NULL; static GList * appointment_sources = NULL; -static IndicatorDatetimeLocation * geo_location = NULL; -static IndicatorDatetimeLocation * tz_file = NULL; +static IndicatorDatetimeTimezone * geo_location = NULL; +static IndicatorDatetimeTimezone * tz_file = NULL; struct comp_instance { ECalComponent *comp; @@ -184,7 +184,7 @@ update_location_menu_items (void) /* maybe add geo_timezone */ if (geo_location != NULL) { - const char * geo_timezone = indicator_datetime_location_get_timezone (geo_location); + const char * geo_timezone = indicator_datetime_timezone_get_timezone (geo_location); if (geo_timezone && *geo_timezone) { const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); gchar * name = get_current_zone_name (geo_timezone); @@ -195,7 +195,7 @@ update_location_menu_items (void) /* maybe add current_timezone */ if (tz_file != NULL) { - const char * tz = indicator_datetime_location_get_timezone (tz_file); + const char * tz = indicator_datetime_timezone_get_timezone (tz_file); if (tz && *tz) { const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); gchar * name = get_current_zone_name (tz); @@ -704,7 +704,7 @@ update_appointment_menu_items (gpointer user_data __attribute__ ((unused))) g_list_free_full (comp_instances, (GDestroyNotify)comp_instance_free); comp_instances = NULL; - current_timezone = indicator_datetime_location_get_timezone (tz_file); + current_timezone = indicator_datetime_timezone_get_timezone (tz_file); // Generate instances for all sources for (s=appointment_sources; s!=NULL; s=s->next) { @@ -1233,7 +1233,7 @@ on_use_geoclue_changed_cb (GSettings *settings, } else if (use_geoclue && !geo_location) { - geo_location = indicator_datetime_location_geoclue_new (); + geo_location = indicator_datetime_timezone_geoclue_new (); g_signal_connect (geo_location, "notify::timezone", G_CALLBACK(update_location_menu_items), NULL); } @@ -1291,7 +1291,7 @@ main (int argc, char ** argv) dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL); /* Setup timezone watch */ - tz_file = indicator_datetime_location_file_new (TIMEZONE_FILE); + tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE); g_signal_connect (tz_file, "notify::timezone", G_CALLBACK(on_timezone_changed), NULL); /* Set up the day timer */ diff --git a/src/location-file.c b/src/location-file.c deleted file mode 100644 index ff6fd47..0000000 --- a/src/location-file.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "config.h" - -#include -#include -#include - -#include "location-file.h" - -enum -{ - PROP_0, - PROP_FILENAME, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -struct _IndicatorDatetimeLocationFilePriv -{ - gchar * filename; - GFile * file; - GFileMonitor * monitor; - gchar * timezone; -}; - -typedef IndicatorDatetimeLocationFilePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeLocationFile, - indicator_datetime_location_file, - INDICATOR_TYPE_DATETIME_LOCATION) - -/*** -**** -***/ - -static void -reload (IndicatorDatetimeLocationFile * self) -{ - priv_t * p = self->priv; - - GError * err = NULL; - gchar * new_timezone = NULL; - - if (!g_file_get_contents (p->filename, &new_timezone, NULL, &err)) - { - g_warning ("Unable to read timezone file '%s': %s", p->filename, err->message); - g_error_free (err); - } - else - { - g_strstrip (new_timezone); - g_free (p->timezone); - p->timezone = new_timezone; - indicator_datetime_location_notify_timezone (INDICATOR_DATETIME_LOCATION(self)); - } -} - -static void -set_filename (IndicatorDatetimeLocationFile * self, const char * filename) -{ - GError * err; - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - g_clear_object (&p->file); - g_free (p->filename); - - p->filename = g_strdup (filename); - p->file = g_file_new_for_path (p->filename); - - err = NULL; - p->monitor = g_file_monitor_file (p->file, G_FILE_MONITOR_NONE, NULL, &err); - if (err != NULL) - { - g_warning ("Unable to monitor timezone file '%s': %s", TIMEZONE_FILE, err->message); - g_error_free (err); - } - else - { - g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); - g_debug ("Monitoring timezone file '%s'", p->filename); - } - - reload (self); -} - -/*** -**** IndicatorDatetimeLocationClass funcs -***/ - -static const char * -my_get_timezone (IndicatorDatetimeLocation * self) -{ - return INDICATOR_DATETIME_LOCATION_FILE(self)->priv->timezone; -} - -/*** -**** GObjectClass funcs -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - g_value_set_string (value, self->priv->filename); - 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) -{ - IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - set_filename (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - g_clear_object (&p->file); - - G_OBJECT_CLASS (indicator_datetime_location_file_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeLocationFile * self = INDICATOR_DATETIME_LOCATION_FILE (o); - priv_t * p = self->priv; - - g_free (p->filename); - g_free (p->timezone); - - G_OBJECT_CLASS (indicator_datetime_location_file_parent_class)->finalize (o); -} - -/*** -**** -***/ - -static void -indicator_datetime_location_file_class_init (IndicatorDatetimeLocationFileClass * klass) -{ - GObjectClass * object_class; - IndicatorDatetimeLocationClass * location_class; - const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - object_class->set_property = my_set_property; - object_class->get_property = my_get_property; - - location_class = INDICATOR_DATETIME_LOCATION_CLASS (klass); - location_class->get_timezone = my_get_timezone; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeLocationFilePriv)); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_FILENAME] = g_param_spec_string ("filename", - "Filename", - "Filename to monitor for TZ changes", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_location_file_init (IndicatorDatetimeLocationFile * self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_LOCATION_FILE, - IndicatorDatetimeLocationFilePriv); -} - -/*** -**** Public -***/ - -IndicatorDatetimeLocation * -indicator_datetime_location_file_new (const char * filename) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_LOCATION_FILE, "filename", filename, NULL); - - return INDICATOR_DATETIME_LOCATION (o); -} diff --git a/src/location-file.h b/src/location-file.h deleted file mode 100644 index 8ed87ad..0000000 --- a/src/location-file.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_LOCATION_FILE__H__ -#define __INDICATOR_DATETIME_LOCATION_FILE__H__ - -#include -#include - -#include "location.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_LOCATION_FILE (indicator_datetime_location_file_get_type()) -#define INDICATOR_DATETIME_LOCATION_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE, IndicatorDatetimeLocationFile)) -#define INDICATOR_DATETIME_LOCATION_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE, IndicatorDatetimeLocationFileClass)) -#define INDICATOR_IS_DATETIME_LOCATION_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION_FILE)) - -typedef struct _IndicatorDatetimeLocationFile IndicatorDatetimeLocationFile; -typedef struct _IndicatorDatetimeLocationFilePriv IndicatorDatetimeLocationFilePriv; -typedef struct _IndicatorDatetimeLocationFileClass IndicatorDatetimeLocationFileClass; - -GType indicator_datetime_location_file_get_type (void); - -/** - * An implementation of IndicatorDatetimeLocation which determines the timezone - * from monitoring a local file, such as /etc/timezone - */ -struct _IndicatorDatetimeLocationFile -{ - /*< private >*/ - IndicatorDatetimeLocation parent; - IndicatorDatetimeLocationFilePriv * priv; -}; - -struct _IndicatorDatetimeLocationFileClass -{ - IndicatorDatetimeLocationClass parent_class; -}; - -IndicatorDatetimeLocation * indicator_datetime_location_file_new (const char * filename); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_LOCATION_FILE__H__ */ diff --git a/src/location-geoclue.c b/src/location-geoclue.c deleted file mode 100644 index 87358fb..0000000 --- a/src/location-geoclue.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "config.h" - -#include -#include - -#include -#include - -#include "location-geoclue.h" - -struct _IndicatorDatetimeLocationGeocluePriv -{ - GeoclueMaster * master; - GeoclueMasterClient * client; - GeoclueAddress * address; - gchar * timezone; -}; - -typedef IndicatorDatetimeLocationGeocluePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeLocationGeoclue, - indicator_datetime_location_geoclue, - INDICATOR_TYPE_DATETIME_LOCATION) - -static void geo_restart (IndicatorDatetimeLocationGeoclue * self); - -/*** -**** -***/ - -static void -set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) -{ - priv_t * p = self->priv; - - if (g_strcmp0 (p->timezone, timezone)) - { - g_free (p->timezone); - p->timezone = g_strdup (timezone); - indicator_datetime_location_notify_timezone (INDICATOR_DATETIME_LOCATION(self)); - } -} - -static void -on_address_changed (GeoclueAddress * address, - int timestamp, - GHashTable * addy_data, - GeoclueAccuracy * accuracy, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); - } - else - { - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); - const char * timezone = g_hash_table_lookup (addy_data, "timezone"); - set_timezone (self, timezone); - } -} - -static void -on_address_created (GeoclueMasterClient * master, - GeoclueAddress * address, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); - } - else - { - priv_t * p = INDICATOR_DATETIME_LOCATION_GEOCLUE(gself)->priv; - - g_assert (p->address == NULL); - p->address = g_object_ref (address); - - geoclue_address_get_address_async (address, on_address_changed, gself); - g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed), gself); - } -} - -static void -on_requirements_set (GeoclueMasterClient * master, GError * error, gpointer user_data) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); - } -} - -static void -on_client_created (GeoclueMaster * master, - GeoclueMasterClient * client, - gchar * path, - GError * error, - gpointer gself) -{ - g_debug ("Created Geoclue client at: %s", path); - - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); - } - else if (client == NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); - priv_t * p = self->priv; - - g_clear_object (&p->client); - p->client = g_object_ref (client); - g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself); - - geoclue_master_client_set_requirements_async (p->client, - GEOCLUE_ACCURACY_LEVEL_REGION, - 0, - FALSE, - GEOCLUE_RESOURCE_ALL, - on_requirements_set, - NULL); - - geoclue_master_client_create_address_async (p->client, on_address_created, gself); - } -} - -static void -geo_start (IndicatorDatetimeLocationGeoclue * self) -{ - priv_t * p = self->priv; - - g_assert (p->master == NULL); - p->master = geoclue_master_get_default (); - geoclue_master_create_client_async (p->master, on_client_created, self); -} - -static void -geo_stop (IndicatorDatetimeLocationGeoclue * self) -{ - priv_t * p = self->priv; - - if (p->address != NULL) - { - g_signal_handlers_disconnect_by_func (p->address, on_address_changed, self); - g_clear_object (&p->address); - } - - if (p->client != NULL) - { - g_signal_handlers_disconnect_by_func (p->client, geo_restart, self); - g_clear_object (&p->client); - } - - g_clear_object (&p->master); -} - -static void -geo_restart (IndicatorDatetimeLocationGeoclue * self) -{ - geo_stop (self); - geo_start (self); -} - -/*** -**** -***/ - -static const char * -my_get_timezone (IndicatorDatetimeLocation * self) -{ - return INDICATOR_DATETIME_LOCATION_GEOCLUE(self)->priv->timezone; -} - -static void -my_dispose (GObject * o) -{ - geo_stop (INDICATOR_DATETIME_LOCATION_GEOCLUE (o)); - - G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (o); - priv_t * p = self->priv; - - g_free (p->timezone); - - G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->finalize (o); -} - -static void -indicator_datetime_location_geoclue_class_init (IndicatorDatetimeLocationGeoclueClass * klass) -{ - GObjectClass * object_class; - IndicatorDatetimeLocationClass * location_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - - location_class = INDICATOR_DATETIME_LOCATION_CLASS (klass); - location_class->get_timezone = my_get_timezone; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeLocationGeocluePriv)); -} - -static void -indicator_datetime_location_geoclue_init (IndicatorDatetimeLocationGeoclue * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, - IndicatorDatetimeLocationGeocluePriv); - self->priv = p; - - geo_start (self); -} - -/*** -**** Public -***/ - -IndicatorDatetimeLocation * -indicator_datetime_location_geoclue_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, NULL); - - return INDICATOR_DATETIME_LOCATION (o); -} diff --git a/src/location-geoclue.h b/src/location-geoclue.h deleted file mode 100644 index 5b6d232..0000000 --- a/src/location-geoclue.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ -#define __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ - -#include -#include - -#include "location.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE (indicator_datetime_location_geoclue_get_type()) -#define INDICATOR_DATETIME_LOCATION_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, IndicatorDatetimeLocationGeoclue)) -#define INDICATOR_DATETIME_LOCATION_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, IndicatorDatetimeLocationGeoclueClass)) -#define INDICATOR_IS_DATETIME_LOCATION_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE)) - -typedef struct _IndicatorDatetimeLocationGeoclue IndicatorDatetimeLocationGeoclue; -typedef struct _IndicatorDatetimeLocationGeocluePriv IndicatorDatetimeLocationGeocluePriv; -typedef struct _IndicatorDatetimeLocationGeoclueClass IndicatorDatetimeLocationGeoclueClass; - -GType indicator_datetime_location_geoclue_get_type (void); - -/** - * An implementation of IndicatorDatetimeLocation which determines the timezone - * from calling a GeoClue service over DBus. - */ -struct _IndicatorDatetimeLocationGeoclue -{ - /*< private >*/ - IndicatorDatetimeLocation parent; - IndicatorDatetimeLocationGeocluePriv * priv; -}; - -struct _IndicatorDatetimeLocationGeoclueClass -{ - IndicatorDatetimeLocationClass parent_class; -}; - -IndicatorDatetimeLocation * indicator_datetime_location_geoclue_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ */ diff --git a/src/location.c b/src/location.c deleted file mode 100644 index 12e25c3..0000000 --- a/src/location.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "location.h" - -G_DEFINE_TYPE (IndicatorDatetimeLocation, - indicator_datetime_location, - G_TYPE_OBJECT) - -enum -{ - PROP_0, - PROP_TIMEZONE, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeLocation * self = INDICATOR_DATETIME_LOCATION (o); - - switch (property_id) - { - case PROP_TIMEZONE: - g_value_set_string (value, indicator_datetime_location_get_timezone (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_dispose (GObject * object) -{ - G_OBJECT_CLASS (indicator_datetime_location_parent_class)->dispose (object); -} - -static void -/* cppcheck-suppress unusedFunction */ -indicator_datetime_location_class_init (IndicatorDatetimeLocationClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; - - object_class = G_OBJECT_CLASS (klass); - object_class->get_property = my_get_property; - object_class->dispose = my_dispose; - - klass->get_timezone = NULL; - - properties[PROP_0] = NULL; - - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", - "Timezone", - "Timezone", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_location_init (IndicatorDatetimeLocation * self G_GNUC_UNUSED) -{ -} - -/*** -**** -***/ - -const char * -indicator_datetime_location_get_timezone (IndicatorDatetimeLocation * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_LOCATION (self), NULL); - - return INDICATOR_DATETIME_LOCATION_GET_CLASS (self)->get_timezone (self); -} - -void -indicator_datetime_location_notify_timezone (IndicatorDatetimeLocation * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_LOCATION (self)); - - g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); -} - diff --git a/src/location.h b/src/location.h deleted file mode 100644 index f9fd2ce..0000000 --- a/src/location.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_LOCATION__H__ -#define __INDICATOR_DATETIME_LOCATION__H__ - -#include -#include - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_LOCATION (indicator_datetime_location_get_type()) -#define INDICATOR_DATETIME_LOCATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocation)) -#define INDICATOR_DATETIME_LOCATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocationClass)) -#define INDICATOR_DATETIME_LOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_LOCATION, IndicatorDatetimeLocationClass)) -#define INDICATOR_IS_DATETIME_LOCATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_LOCATION)) - -typedef struct _IndicatorDatetimeLocation IndicatorDatetimeLocation; -typedef struct _IndicatorDatetimeLocationClass IndicatorDatetimeLocationClass; - -GType indicator_datetime_location_get_type (void); - -/** - * Abstract Base Class for the mechanisms that determine timezone by location - */ -struct _IndicatorDatetimeLocation -{ - /*< private >*/ - GObject parent; -}; - -struct _IndicatorDatetimeLocationClass -{ - GObjectClass parent_class; - - /* virtual functions */ - const char * (*get_timezone) (IndicatorDatetimeLocation * self); -}; - -/*** -**** -***/ - -#define INDICATOR_DATETIME_LOCATION_PROPERTY_TIMEZONE "timezone" - -const char * indicator_datetime_location_get_timezone (IndicatorDatetimeLocation *); - -void indicator_datetime_location_notify_timezone (IndicatorDatetimeLocation *); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_LOCATION__H__ */ diff --git a/src/timezone-file.c b/src/timezone-file.c new file mode 100644 index 0000000..c7c2cf6 --- /dev/null +++ b/src/timezone-file.c @@ -0,0 +1,235 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "timezone-file.h" + +enum +{ + PROP_0, + PROP_FILENAME, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +struct _IndicatorDatetimeTimezoneFilePriv +{ + gchar * filename; + GFile * file; + GFileMonitor * monitor; + gchar * timezone; +}; + +typedef IndicatorDatetimeTimezoneFilePriv priv_t; + +G_DEFINE_TYPE (IndicatorDatetimeTimezoneFile, + indicator_datetime_timezone_file, + INDICATOR_TYPE_DATETIME_TIMEZONE) + +/*** +**** +***/ + +static void +reload (IndicatorDatetimeTimezoneFile * self) +{ + priv_t * p = self->priv; + + GError * err = NULL; + gchar * new_timezone = NULL; + + if (!g_file_get_contents (p->filename, &new_timezone, NULL, &err)) + { + g_warning ("Unable to read timezone file '%s': %s", p->filename, err->message); + g_error_free (err); + } + else + { + g_strstrip (new_timezone); + g_free (p->timezone); + p->timezone = new_timezone; + indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self)); + } +} + +static void +set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename) +{ + GError * err; + priv_t * p = self->priv; + + g_clear_object (&p->monitor); + g_clear_object (&p->file); + g_free (p->filename); + + p->filename = g_strdup (filename); + p->file = g_file_new_for_path (p->filename); + + err = NULL; + p->monitor = g_file_monitor_file (p->file, G_FILE_MONITOR_NONE, NULL, &err); + if (err != NULL) + { + g_warning ("Unable to monitor timezone file '%s': %s", TIMEZONE_FILE, err->message); + g_error_free (err); + } + else + { + g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); + g_debug ("Monitoring timezone file '%s'", p->filename); + } + + reload (self); +} + +/*** +**** IndicatorDatetimeTimezoneClass funcs +***/ + +static const char * +my_get_timezone (IndicatorDatetimeTimezone * self) +{ + return INDICATOR_DATETIME_TIMEZONE_FILE(self)->priv->timezone; +} + +/*** +**** GObjectClass funcs +***/ + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); + + switch (property_id) + { + case PROP_FILENAME: + g_value_set_string (value, self->priv->filename); + 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) +{ + IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); + + switch (property_id) + { + case PROP_FILENAME: + set_filename (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * o) +{ + IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); + priv_t * p = self->priv; + + g_clear_object (&p->monitor); + g_clear_object (&p->file); + + G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); + priv_t * p = self->priv; + + g_free (p->filename); + g_free (p->timezone); + + G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->finalize (o); +} + +/*** +**** +***/ + +static void +indicator_datetime_timezone_file_class_init (IndicatorDatetimeTimezoneFileClass * klass) +{ + GObjectClass * object_class; + IndicatorDatetimeTimezoneClass * location_class; + const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + object_class->set_property = my_set_property; + object_class->get_property = my_get_property; + + location_class = INDICATOR_DATETIME_TIMEZONE_CLASS (klass); + location_class->get_timezone = my_get_timezone; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneFilePriv)); + + /* install properties */ + + properties[PROP_0] = NULL; + + properties[PROP_FILENAME] = g_param_spec_string ("filename", + "Filename", + "Filename to monitor for TZ changes", + "", + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +indicator_datetime_timezone_file_init (IndicatorDatetimeTimezoneFile * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, + IndicatorDatetimeTimezoneFilePriv); +} + +/*** +**** Public +***/ + +IndicatorDatetimeTimezone * +indicator_datetime_timezone_file_new (const char * filename) +{ + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, "filename", filename, NULL); + + return INDICATOR_DATETIME_TIMEZONE (o); +} diff --git a/src/timezone-file.h b/src/timezone-file.h new file mode 100644 index 0000000..7186beb --- /dev/null +++ b/src/timezone-file.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE_FILE__H__ +#define __INDICATOR_DATETIME_TIMEZONE_FILE__H__ + +#include +#include + +#include "timezone.h" /* parent class */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_TIMEZONE_FILE (indicator_datetime_timezone_file_get_type()) +#define INDICATOR_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFile)) +#define INDICATOR_DATETIME_TIMEZONE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFileClass)) +#define INDICATOR_IS_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE)) + +typedef struct _IndicatorDatetimeTimezoneFile IndicatorDatetimeTimezoneFile; +typedef struct _IndicatorDatetimeTimezoneFilePriv IndicatorDatetimeTimezoneFilePriv; +typedef struct _IndicatorDatetimeTimezoneFileClass IndicatorDatetimeTimezoneFileClass; + +GType indicator_datetime_timezone_file_get_type (void); + +/** + * An implementation of IndicatorDatetimeTimezone which determines the timezone + * from monitoring a local file, such as /etc/timezone + */ +struct _IndicatorDatetimeTimezoneFile +{ + /*< private >*/ + IndicatorDatetimeTimezone parent; + IndicatorDatetimeTimezoneFilePriv * priv; +}; + +struct _IndicatorDatetimeTimezoneFileClass +{ + IndicatorDatetimeTimezoneClass parent_class; +}; + +IndicatorDatetimeTimezone * indicator_datetime_timezone_file_new (const char * filename); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_TIMEZONE_FILE__H__ */ diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c new file mode 100644 index 0000000..5271945 --- /dev/null +++ b/src/timezone-geoclue.c @@ -0,0 +1,261 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "timezone-geoclue.h" + +struct _IndicatorDatetimeTimezoneGeocluePriv +{ + GeoclueMaster * master; + GeoclueMasterClient * client; + GeoclueAddress * address; + gchar * timezone; +}; + +typedef IndicatorDatetimeTimezoneGeocluePriv priv_t; + +G_DEFINE_TYPE (IndicatorDatetimeTimezoneGeoclue, + indicator_datetime_timezone_geoclue, + INDICATOR_TYPE_DATETIME_TIMEZONE) + +static void geo_restart (IndicatorDatetimeTimezoneGeoclue * self); + +/*** +**** +***/ + +static void +set_timezone (IndicatorDatetimeTimezoneGeoclue * self, const gchar * timezone) +{ + priv_t * p = self->priv; + + if (g_strcmp0 (p->timezone, timezone)) + { + g_free (p->timezone); + p->timezone = g_strdup (timezone); + indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self)); + } +} + +static void +on_address_changed (GeoclueAddress * address, + int timestamp, + GHashTable * addy_data, + GeoclueAccuracy * accuracy, + GError * error, + gpointer gself) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else + { + IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); + const char * timezone = g_hash_table_lookup (addy_data, "timezone"); + set_timezone (self, timezone); + } +} + +static void +on_address_created (GeoclueMasterClient * master, + GeoclueAddress * address, + GError * error, + gpointer gself) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else + { + priv_t * p = INDICATOR_DATETIME_TIMEZONE_GEOCLUE(gself)->priv; + + g_assert (p->address == NULL); + p->address = g_object_ref (address); + + geoclue_address_get_address_async (address, on_address_changed, gself); + g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed), gself); + } +} + +static void +on_requirements_set (GeoclueMasterClient * master, GError * error, gpointer user_data) +{ + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +static void +on_client_created (GeoclueMaster * master, + GeoclueMasterClient * client, + gchar * path, + GError * error, + gpointer gself) +{ + g_debug ("Created Geoclue client at: %s", path); + + if (error != NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + g_error_free (error); + } + else if (client == NULL) + { + g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); + } + else + { + IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); + priv_t * p = self->priv; + + g_clear_object (&p->client); + p->client = g_object_ref (client); + g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself); + + geoclue_master_client_set_requirements_async (p->client, + GEOCLUE_ACCURACY_LEVEL_REGION, + 0, + FALSE, + GEOCLUE_RESOURCE_ALL, + on_requirements_set, + NULL); + + geoclue_master_client_create_address_async (p->client, on_address_created, gself); + } +} + +static void +geo_start (IndicatorDatetimeTimezoneGeoclue * self) +{ + priv_t * p = self->priv; + + g_assert (p->master == NULL); + p->master = geoclue_master_get_default (); + geoclue_master_create_client_async (p->master, on_client_created, self); +} + +static void +geo_stop (IndicatorDatetimeTimezoneGeoclue * self) +{ + priv_t * p = self->priv; + + if (p->address != NULL) + { + g_signal_handlers_disconnect_by_func (p->address, on_address_changed, self); + g_clear_object (&p->address); + } + + if (p->client != NULL) + { + g_signal_handlers_disconnect_by_func (p->client, geo_restart, self); + g_clear_object (&p->client); + } + + g_clear_object (&p->master); +} + +static void +geo_restart (IndicatorDatetimeTimezoneGeoclue * self) +{ + geo_stop (self); + geo_start (self); +} + +/*** +**** +***/ + +static const char * +my_get_timezone (IndicatorDatetimeTimezone * self) +{ + return INDICATOR_DATETIME_TIMEZONE_GEOCLUE(self)->priv->timezone; +} + +static void +my_dispose (GObject * o) +{ + geo_stop (INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o)); + + G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o); + priv_t * p = self->priv; + + g_free (p->timezone); + + G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->finalize (o); +} + +static void +indicator_datetime_timezone_geoclue_class_init (IndicatorDatetimeTimezoneGeoclueClass * klass) +{ + GObjectClass * object_class; + IndicatorDatetimeTimezoneClass * location_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + location_class = INDICATOR_DATETIME_TIMEZONE_CLASS (klass); + location_class->get_timezone = my_get_timezone; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneGeocluePriv)); +} + +static void +indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, + IndicatorDatetimeTimezoneGeocluePriv); + self->priv = p; + + geo_start (self); +} + +/*** +**** Public +***/ + +IndicatorDatetimeTimezone * +indicator_datetime_timezone_geoclue_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, NULL); + + return INDICATOR_DATETIME_TIMEZONE (o); +} diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h new file mode 100644 index 0000000..fcb3ab7 --- /dev/null +++ b/src/timezone-geoclue.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ +#define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ + +#include +#include + +#include "timezone.h" /* parent class */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE (indicator_datetime_timezone_geoclue_get_type()) +#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclue)) +#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclueClass)) +#define INDICATOR_IS_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE)) + +typedef struct _IndicatorDatetimeTimezoneGeoclue IndicatorDatetimeTimezoneGeoclue; +typedef struct _IndicatorDatetimeTimezoneGeocluePriv IndicatorDatetimeTimezoneGeocluePriv; +typedef struct _IndicatorDatetimeTimezoneGeoclueClass IndicatorDatetimeTimezoneGeoclueClass; + +GType indicator_datetime_timezone_geoclue_get_type (void); + +/** + * An implementation of IndicatorDatetimeTimezone which determines the timezone + * from calling a GeoClue service over DBus. + */ +struct _IndicatorDatetimeTimezoneGeoclue +{ + /*< private >*/ + IndicatorDatetimeTimezone parent; + IndicatorDatetimeTimezoneGeocluePriv * priv; +}; + +struct _IndicatorDatetimeTimezoneGeoclueClass +{ + IndicatorDatetimeTimezoneClass parent_class; +}; + +IndicatorDatetimeTimezone * indicator_datetime_timezone_geoclue_new (void); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ */ diff --git a/src/timezone.c b/src/timezone.c new file mode 100644 index 0000000..9e671ed --- /dev/null +++ b/src/timezone.c @@ -0,0 +1,108 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "timezone.h" + +G_DEFINE_TYPE (IndicatorDatetimeTimezone, + indicator_datetime_timezone, + G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_TIMEZONE, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimeTimezone * self = INDICATOR_DATETIME_TIMEZONE (o); + + switch (property_id) + { + case PROP_TIMEZONE: + g_value_set_string (value, indicator_datetime_timezone_get_timezone (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * object) +{ + G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->dispose (object); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = my_get_property; + object_class->dispose = my_dispose; + + klass->get_timezone = NULL; + + properties[PROP_0] = NULL; + + properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", + "Timezone", + "Timezone", + "", + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self G_GNUC_UNUSED) +{ +} + +/*** +**** +***/ + +const char * +indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone * self) +{ + g_return_val_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self), NULL); + + return INDICATOR_DATETIME_TIMEZONE_GET_CLASS (self)->get_timezone (self); +} + +void +indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone * self) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self)); + + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); +} + diff --git a/src/timezone.h b/src/timezone.h new file mode 100644 index 0000000..ac48e6e --- /dev/null +++ b/src/timezone.h @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE__H__ +#define __INDICATOR_DATETIME_TIMEZONE__H__ + +#include +#include + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_TIMEZONE (indicator_datetime_timezone_get_type()) +#define INDICATOR_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezone)) +#define INDICATOR_DATETIME_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) +#define INDICATOR_DATETIME_TIMEZONE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) +#define INDICATOR_IS_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE)) + +typedef struct _IndicatorDatetimeTimezone IndicatorDatetimeTimezone; +typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass; + +GType indicator_datetime_timezone_get_type (void); + +/** + * Abstract Base Class for the mechanisms that determine timezone by location + */ +struct _IndicatorDatetimeTimezone +{ + /*< private >*/ + GObject parent; +}; + +struct _IndicatorDatetimeTimezoneClass +{ + GObjectClass parent_class; + + /* virtual functions */ + const char * (*get_timezone) (IndicatorDatetimeTimezone * self); +}; + +/*** +**** +***/ + +#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone" + +const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *); + +void indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone *); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_TIMEZONE__H__ */ -- cgit v1.2.3 From a2c380c8f271486eb6dfeb82a46ca7a95aca85c5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 6 May 2013 17:51:48 -0500 Subject: in timezone-file, check to see if the new timezone differs from the old one before emitting a property-change notification --- src/timezone-file.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/timezone-file.c b/src/timezone-file.c index c7c2cf6..3a83c5a 100644 --- a/src/timezone-file.c +++ b/src/timezone-file.c @@ -62,15 +62,22 @@ reload (IndicatorDatetimeTimezoneFile * self) if (!g_file_get_contents (p->filename, &new_timezone, NULL, &err)) { - g_warning ("Unable to read timezone file '%s': %s", p->filename, err->message); + g_warning ("%s Unable to read timezone file '%s': %s", G_STRLOC, p->filename, err->message); g_error_free (err); } else { g_strstrip (new_timezone); - g_free (p->timezone); - p->timezone = new_timezone; - indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self)); + + if (g_strcmp0 (p->timezone, new_timezone)) + { + g_free (p->timezone); + p->timezone = g_strdup (new_timezone); + g_debug ("%s new timezone set: '%s'", G_STRLOC, p->timezone); + indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self)); + } + + g_free (new_timezone); } } @@ -91,13 +98,13 @@ set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename) p->monitor = g_file_monitor_file (p->file, G_FILE_MONITOR_NONE, NULL, &err); if (err != NULL) { - g_warning ("Unable to monitor timezone file '%s': %s", TIMEZONE_FILE, err->message); + g_warning ("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message); g_error_free (err); } else { g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); - g_debug ("Monitoring timezone file '%s'", p->filename); + g_debug ("%s Monitoring timezone file '%s'", G_STRLOC, p->filename); } reload (self); -- cgit v1.2.3 From e897e7aa9fb18fc83d177acab1dd18e0284429a6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 12 May 2013 20:45:21 -0500 Subject: minor documentation & #include cleanups to the timezone classes --- src/timezone-file.c | 4 +--- src/timezone-file.h | 7 ++----- src/timezone-geoclue.c | 7 ------- src/timezone-geoclue.h | 6 +----- src/timezone.h | 15 +++++++++++---- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/timezone-file.c b/src/timezone-file.c index 3a83c5a..2adf2ca 100644 --- a/src/timezone-file.c +++ b/src/timezone-file.c @@ -19,9 +19,7 @@ #include "config.h" -#include -#include -#include +#include /* GFile, GFileMonitor */ #include "timezone-file.h" diff --git a/src/timezone-file.h b/src/timezone-file.h index 7186beb..b02abe1 100644 --- a/src/timezone-file.h +++ b/src/timezone-file.h @@ -20,9 +20,6 @@ #ifndef __INDICATOR_DATETIME_TIMEZONE_FILE__H__ #define __INDICATOR_DATETIME_TIMEZONE_FILE__H__ -#include -#include - #include "timezone.h" /* parent class */ G_BEGIN_DECLS @@ -39,8 +36,8 @@ typedef struct _IndicatorDatetimeTimezoneFileClass IndicatorDatetimeTimezoneFi GType indicator_datetime_timezone_file_get_type (void); /** - * An implementation of IndicatorDatetimeTimezone which determines the timezone - * from monitoring a local file, such as /etc/timezone + * An IndicatorDatetimeTimezone which uses a local file, + * such as /etc/timezone, to determine the timezone. */ struct _IndicatorDatetimeTimezoneFile { diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c index 5271945..4c3c7d8 100644 --- a/src/timezone-geoclue.c +++ b/src/timezone-geoclue.c @@ -19,9 +19,6 @@ #include "config.h" -#include -#include - #include #include @@ -128,10 +125,6 @@ on_client_created (GeoclueMaster * master, g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); g_error_free (error); } - else if (client == NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } else { IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h index fcb3ab7..059bd81 100644 --- a/src/timezone-geoclue.h +++ b/src/timezone-geoclue.h @@ -20,9 +20,6 @@ #ifndef __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ #define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ -#include -#include - #include "timezone.h" /* parent class */ G_BEGIN_DECLS @@ -39,8 +36,7 @@ typedef struct _IndicatorDatetimeTimezoneGeoclueClass IndicatorDatetimeTimezon GType indicator_datetime_timezone_geoclue_get_type (void); /** - * An implementation of IndicatorDatetimeTimezone which determines the timezone - * from calling a GeoClue service over DBus. + * An IndicatorDatetimeTimezone which uses GeoClue to determine the timezone. */ struct _IndicatorDatetimeTimezoneGeoclue { diff --git a/src/timezone.h b/src/timezone.h index ac48e6e..cadeb6f 100644 --- a/src/timezone.h +++ b/src/timezone.h @@ -21,7 +21,7 @@ #define __INDICATOR_DATETIME_TIMEZONE__H__ #include -#include +#include /* parent class */ G_BEGIN_DECLS @@ -36,8 +36,17 @@ typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass; GType indicator_datetime_timezone_get_type (void); +#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone" + /** - * Abstract Base Class for the mechanisms that determine timezone by location + * Abstract Base Class for objects that provide a timezone. + * + * This is used in datetime to determine the user's current timezone + * so that it can be displayed more prominently in the locations + * section of the indicator's menu. + * + * This class has a 'timezone' property that clients can watch + * for change notifications. */ struct _IndicatorDatetimeTimezone { @@ -57,8 +66,6 @@ struct _IndicatorDatetimeTimezoneClass **** ***/ -#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone" - const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *); void indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone *); -- cgit v1.2.3 From b08ab642714d991b70f9bd907a709edb24c8f653 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 12 May 2013 21:08:37 -0500 Subject: extract EDS code into its own mockable class --- src/Makefile.am | 4 + src/datetime-service.c | 814 +++++++++++++++++-------------------------------- src/planner-eds.c | 330 ++++++++++++++++++++ src/planner-eds.h | 60 ++++ src/planner.c | 232 ++++++++++++++ src/planner.h | 138 +++++++++ 6 files changed, 1046 insertions(+), 532 deletions(-) create mode 100644 src/planner-eds.c create mode 100644 src/planner-eds.h create mode 100644 src/planner.c create mode 100644 src/planner.h diff --git a/src/Makefile.am b/src/Makefile.am index 67b085a..4a34c33 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,10 @@ indicator_datetime_service_SOURCES = \ datetime-interface.c \ datetime-interface.h \ gen-datetime-service.xml.c \ + planner.c \ + planner.h \ + planner-eds.c \ + planner-eds.h \ datetime-service.c \ timezone.c \ timezone.h \ diff --git a/src/datetime-service.c b/src/datetime-service.c index 0551068..4d2da12 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -28,24 +28,19 @@ with this program. If not, see . #include #include #include -#include +#include /* fabs() */ #include #include #include #include -#include -#include -#include -#include -// Other users of ecal seem to also include these, not sure why they should be included by the above -#include #include #include "datetime-interface.h" #include "dbus-shared.h" #include "settings-shared.h" +#include "planner-eds.h" #include "timezone-file.h" #include "timezone-geoclue.h" #include "utils.h" @@ -63,7 +58,7 @@ with this program. If not, see . #define SETTINGS_APP_INVOCATION "gnome-control-center datetime" #endif -static gboolean update_appointment_menu_items (gpointer user_data); +static void update_appointment_menu_items (void); static void update_location_menu_items (void); static void day_timer_reset (void); static gboolean get_greeter_mode (void); @@ -83,79 +78,78 @@ static DbusmenuMenuitem * settings = NULL; static DbusmenuMenuitem * events_separator = NULL; static DbusmenuMenuitem * locations_separator = NULL; static DbusmenuMenuitem * add_appointment = NULL; -static DbusmenuMenuitem * appointments[MAX_APPOINTMENT_MENUITEMS]; +static DbusmenuMenuitem * appointment_menuitems[MAX_APPOINTMENT_MENUITEMS]; static GSList * location_menu_items = NULL; -static GList * comp_instances = NULL; static gboolean updating_appointments = FALSE; static time_t start_time_appointments = (time_t) 0; static GSettings * conf = NULL; -static ESourceRegistry * source_registry = NULL; -static GList * appointment_sources = NULL; static IndicatorDatetimeTimezone * geo_location = NULL; static IndicatorDatetimeTimezone * tz_file = NULL; - -struct comp_instance { - ECalComponent *comp; - time_t start; - time_t end; - ESource *source; -}; +static IndicatorDatetimePlanner * planner = NULL; /** * A temp struct used by update_location_menu_items() for pruning duplicates and sorting. */ struct TimeLocation { - gint32 offset; - gchar * zone; - gchar * name; - gboolean visible; + gint32 offset; + gchar * zone; + gchar * name; + gboolean visible; }; + static void time_location_free (struct TimeLocation * loc) { - g_free (loc->name); - g_free (loc->zone); - g_free (loc); + 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; - g_time_zone_unref (tz); - g_debug ("%s zone '%s' name '%s' offset is %d", G_STRLOC, zone, name, (int)loc->offset); - return loc; + 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; + g_time_zone_unref (tz); + g_debug ("%s zone '%s' name '%s' offset is %d", G_STRLOC, zone, name, (int)loc->offset); + 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 */ - g_debug ("%s comparing '%s' (%d) to '%s' (%d), returning %d", G_STRLOC, a->name, (int)a->offset, b->name, (int)b->offset, ret); - return ret; + 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 */ + g_debug ("%s comparing '%s' (%d) to '%s' (%d), returning %d", G_STRLOC, a->name, (int)a->offset, b->name, (int)b->offset, ret); + 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); + struct TimeLocation * loc = time_location_new (zone, name, visible, now); - if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare) == NULL) { - g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name); - locations = g_slist_append (locations, loc); - } else { - g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name); - time_location_free (loc); - } - return locations; + if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare) == NULL) + { + g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name); + locations = g_slist_append (locations, loc); + } + else + { + g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name); + time_location_free (loc); + } + + return locations; } /* Update the timezone entries */ @@ -365,8 +359,14 @@ activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, static gboolean update_appointment_menu_items_idle (gpointer user_data) { - update_appointment_menu_items(user_data); - return FALSE; + update_appointment_menu_items(); + return G_SOURCE_REMOVE; +} + +static void +update_appointment_menu_items_soon (void) +{ + g_idle_add (update_appointment_menu_items_idle, NULL); } static void @@ -375,9 +375,9 @@ hide_all_appointments (void) int i; for (i=0; i %s",(int)start_time_appointments, ctime(&start_time_appointments)); - g_idle_add(update_appointment_menu_items_idle, NULL); + update_appointment_menu_items_soon (); return TRUE; } @@ -437,78 +437,65 @@ day_selected_double_click_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, GVariant * variant, guint timestamp G_GNUC_UNUSED) { - const time_t evotime = (time_t)g_variant_get_uint32(variant); - - g_debug("Received day-selected-double-click with timestamp: %d -> %s",(int)evotime, ctime(&evotime)); - - gchar *ad = isodate_from_time_t(evotime); - gchar *cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL); - - execute_command (cmd); - - g_free (cmd); - g_free (ad); - - return TRUE; + const time_t evotime = (time_t) g_variant_get_uint32 (variant); + GDateTime * dt = g_date_time_new_from_unix_utc (evotime); + indicator_datetime_planner_activate_time (planner, dt); + g_date_time_unref (dt); + return TRUE; } static guint ecaltimer = 0; +static gboolean +update_appointment_menu_items_timerfunc (gpointer user_data G_GNUC_UNUSED) +{ + update_appointment_menu_items (); + return G_SOURCE_CONTINUE; +} + static void start_ecal_timer(void) { - if (ecaltimer != 0) { - g_source_remove(ecaltimer); - ecaltimer = 0; - } - if (update_appointment_menu_items(NULL)) - ecaltimer = g_timeout_add_seconds(60*5, update_appointment_menu_items, NULL); + if (ecaltimer != 0) + ecaltimer = g_timeout_add_seconds (60*5, update_appointment_menu_items_timerfunc, NULL); } static void stop_ecal_timer(void) { - if (ecaltimer != 0) { - g_source_remove(ecaltimer); - ecaltimer = 0; - } + if (ecaltimer != 0) + { + g_source_remove (ecaltimer); + ecaltimer = 0; + } } static gboolean idle_start_ecal_timer (gpointer data) { - start_ecal_timer(); - return FALSE; + start_ecal_timer(); + return FALSE; } static void show_events_changed (void) { - if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) { - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - start_ecal_timer(); - } else { - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - hide_all_appointments (); - stop_ecal_timer(); - } -} + const gboolean b = g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S); -static gboolean -calendar_app_is_usable (void) -{ - /* confirm that it's installed... */ - gchar *evo = g_find_program_in_path("evolution"); - if (evo == NULL) - return FALSE; - g_debug ("found calendar app: '%s'", evo); - g_free (evo); + dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, b); + dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - /* see if there are any calendar sources */ - return appointment_sources > 0; + if (b) + { + start_ecal_timer(); + } + else + { + hide_all_appointments (); + stop_ecal_timer(); + } } + /* Looks for the calendar application and enables the item if we have one, starts ecal timer if events are turned on */ static gboolean @@ -518,7 +505,7 @@ check_for_calendar (gpointer user_data) dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - if (!get_greeter_mode () && calendar_app_is_usable()) { + if (!get_greeter_mode () && indicator_datetime_planner_is_configured(planner)) { int i; int pos = 2; @@ -536,7 +523,7 @@ check_for_calendar (gpointer user_data) dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, APPOINTMENT_MENUITEM_TYPE); dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - appointments[i] = item; + appointment_menuitems[i] = item; dbusmenu_menuitem_child_add_position(root, item, pos++); } @@ -579,376 +566,206 @@ check_for_calendar (gpointer user_data) return FALSE; } -static gint -compare_comp_instances (gconstpointer ga, gconstpointer gb) +static GdkPixbuf * +create_color_icon_pixbuf (const char * color_spec) { - const struct comp_instance * a = ga; - const struct comp_instance * b = gb; + static int width = -1; + static int height = -1; + GdkPixbuf * pixbuf = NULL; - /* sort by start time */ - if (a->start < b->start) return -1; - if (a->start > b->start) return 1; - return 0; -} + if (width == -1) + { + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height); + width = CLAMP (width, 10, 30); + height = CLAMP (height, 10, 30); + } -static struct comp_instance* -comp_instance_new (ECalComponent * comp, time_t start, time_t end, ESource * source) -{ - g_debug("Using times start %s, end %s", ctime(&start), ctime(&end)); - - struct comp_instance *ci = g_new (struct comp_instance, 1); - ci->comp = g_object_ref (comp); - ci->source = source; - ci->start = start; - ci->end = end; - return ci; -} -static void -comp_instance_free (struct comp_instance* ci) -{ - if (ci != NULL) { - g_clear_object (&ci->comp); - g_free (ci); - } -} + if (color_spec && *color_spec) + { + cairo_surface_t * surface; + cairo_t * cr; + GdkRGBA rgba; -static gboolean -populate_appointment_instances (ECalComponent * comp, - time_t start, - time_t end, - gpointer data) -{ - const ECalComponentVType vtype = e_cal_component_get_vtype (comp); + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create (surface); - if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) - { - icalproperty_status status; - e_cal_component_get_status (comp, &status); + if (gdk_rgba_parse (&rgba, color_spec)) + gdk_cairo_set_source_rgba (cr, &rgba); - if ((status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) - { - gchar * str = e_cal_component_get_as_string (comp); - g_debug("Appending item %s", str); - struct comp_instance *ci = comp_instance_new (comp, start, end, E_SOURCE(data)); - comp_instances = g_list_append (comp_instances, ci); - g_free (str); - } - } + cairo_paint (cr); + cairo_set_source_rgba (cr, 0, 0, 0, 0.5); + cairo_set_line_width (cr, 1); + cairo_rectangle (cr, 0.5, 0.5, width-1, height-1); + cairo_stroke (cr); - return TRUE; /* tell eds to keep iterating */ + pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + } + + return pixbuf; } -/* Populate the menu with todays, next 5 appointments. + +/** + * Populate the menu with todays, next MAX_APPOINTMENT_MENUITEMS appointments. * we should hook into the ABOUT TO SHOW signal and use that to update the appointments. * Experience has shown that caldav's and webcals can be slow to load from eds * this is a problem mainly on the EDS side of things, not ours. */ -static gboolean -update_appointment_menu_items (gpointer user_data __attribute__ ((unused))) +static void +update_appointment_menu_items (void) { - // FFR: we should take into account short term timers, for instance - // tea timers, pomodoro timers etc... that people may add, this is hinted to in the spec. - g_debug("Update appointments called"); - if (calendar == NULL) return FALSE; - if (!g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) return FALSE; - if (updating_appointments) return TRUE; - updating_appointments = TRUE; - - time_t curtime = 0, t1 = 0, t2 = 0; - GList *l, *s; - GError *gerror = NULL; - gint i; - gint width = 0, height = 0; - GList * sources = NULL; - - // Get today & work out query times - time(&curtime); - struct tm *today = localtime(&curtime); - const int mday = today->tm_mday; - const int mon = today->tm_mon; - const int year = today->tm_year; - const char * current_timezone; - - int start_month_saved = mon; - - struct tm *start_tm = NULL; - int this_year = today->tm_year + 1900; - int days[12]={31,28,31,30,31,30,31,31,30,31,30,31}; - if ((this_year % 400 == 0) || (this_year % 100 > 0 && this_year % 4 == 0)) days[1] = 29; - - int highlightdays = days[mon] - mday + 1; - t1 = curtime; // By default the current time is the appointment start time. - - if (start_time_appointments > 0) { - start_tm = localtime(&start_time_appointments); - int start_month = start_tm->tm_mon; - start_month_saved = start_month; - int start_year = start_tm->tm_year + 1900; - if ((start_month != mon) || (start_year != this_year)) { - // Set t1 to the start of that month. - struct tm month_start = {0}; - month_start.tm_year = start_tm->tm_year; - month_start.tm_mon = start_tm->tm_mon; - month_start.tm_mday = 1; - t1 = mktime(&month_start); - highlightdays = days[start_month]; - } - } - - g_debug("Will highlight %d days from %s", highlightdays, ctime(&t1)); + char * str; + int64_t t; + GSList * l; + GSList * appointments; + gint i; + GDateTime * begin; + GDateTime * end; + GdkPixbuf * pixbuf; + gint apt_output; + GVariantBuilder markeddays; + GVariant * marks; + + // FFR: we should take into account short term timers, for instance + // tea timers, pomodoro timers etc... that people may add, this is hinted to in the spec. + + g_debug ("Update appointments called"); + + if (calendar == NULL) + return; + if (!g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) + return; + if (updating_appointments) + return; - highlightdays = highlightdays + 7; // Minimum of 7 days ahead - t2 = t1 + (time_t) (highlightdays * 24 * 60 * 60); - - // clear any previous comp_instances - g_list_free_full (comp_instances, (GDestroyNotify)comp_instance_free); - comp_instances = NULL; - - current_timezone = indicator_datetime_timezone_get_timezone (tz_file); - - // Generate instances for all sources - for (s=appointment_sources; s!=NULL; s=s->next) { - - ESource *source = E_SOURCE (s->data); - ECalClient *ecal = e_cal_client_new(source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, &gerror); - - if (!ecal) { - g_debug ("Cannot create ecal client: %s", gerror->message); - g_clear_error (&gerror); - continue; - } - - icaltimezone* current_zone = icaltimezone_get_builtin_timezone(current_timezone); - if (!current_zone) { - // current_timezone may be a TZID? - current_zone = icaltimezone_get_builtin_timezone_from_tzid(current_timezone); - } - - e_cal_client_set_default_timezone (ecal, current_zone); - - g_debug("Checking if source %s is enabled", e_source_get_uid(source)); - if (e_source_get_enabled (source)) { - g_debug("source is enabled, generating instances"); - - if (!e_client_open_sync (E_CLIENT (ecal), TRUE, NULL, &gerror)) { - g_debug("Failed to open source: %s", gerror->message); - g_clear_error (&gerror); - g_object_unref(ecal); - continue; - } - - e_cal_client_generate_instances_sync (ecal, - t1, - t2, - populate_appointment_instances, - source); - } - g_object_unref(ecal); - } - - g_debug("Number of ECalComponents returned: %d", g_list_length(comp_instances)); - GList *sorted_comp_instances = g_list_sort(comp_instances, compare_comp_instances); - comp_instances = NULL; - g_debug("Components sorted"); - - hide_all_appointments (); - - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); - if (width <= 0) width = 12; - if (height <= 0) height = 12; - if (width > 30) width = 12; - if (height > 30) height = 12; + updating_appointments = TRUE; - gchar *time_format_str = g_settings_get_string(conf, SETTINGS_TIME_FORMAT_S); - gint apt_output; - if (g_strcmp0(time_format_str, "12-hour") == 0) { - apt_output = SETTINGS_TIME_12_HOUR; - } else if (g_strcmp0(time_format_str, "24-hour") == 0) { - apt_output = SETTINGS_TIME_24_HOUR; - } else if (is_locale_12h()) { - apt_output = SETTINGS_TIME_12_HOUR; - } else { - apt_output = SETTINGS_TIME_24_HOUR; - } - g_free (time_format_str); - - GVariantBuilder markeddays; - g_variant_builder_init (&markeddays, G_VARIANT_TYPE ("ai")); + g_variant_builder_init (&markeddays, G_VARIANT_TYPE ("ai")); + + t = start_time_appointments; + if (!t) + t = time (NULL); /* FIXME: not mockable */ + begin = g_date_time_new_from_unix_local (t); + end = g_date_time_add_months (begin, 1); + indicator_datetime_planner_set_timezone (planner, indicator_datetime_timezone_get_timezone (tz_file)); + appointments = indicator_datetime_planner_get_appointments (planner, begin, end); + + hide_all_appointments (); + + /* decide whether to use 12hr or 24hr format */ + str = g_settings_get_string (conf, SETTINGS_TIME_FORMAT_S); + if (g_strcmp0 (str, "12-hour") == 0) + apt_output = SETTINGS_TIME_12_HOUR; + else if (g_strcmp0 (str, "24-hour") == 0) + apt_output = SETTINGS_TIME_24_HOUR; + else if (is_locale_12h()) + apt_output = SETTINGS_TIME_12_HOUR; + else + apt_output = SETTINGS_TIME_24_HOUR; + g_free (str); - i = 0; - for (l = sorted_comp_instances; l; l = l->next) { - struct comp_instance *ci = l->data; - ECalComponent *ecalcomp = ci->comp; - char right[20]; - //const gchar *uri; - DbusmenuMenuitem * item; - - ECalComponentVType vtype = e_cal_component_get_vtype (ecalcomp); - struct tm due_data = {0}; - struct tm *due = NULL; - if (vtype == E_CAL_COMPONENT_EVENT) due = localtime_r(&ci->start, &due_data); - else if (vtype == E_CAL_COMPONENT_TODO) due = localtime_r(&ci->end, &due_data); - else continue; - - const int dmday = due->tm_mday; - const int dmon = due->tm_mon; - const int dyear = due->tm_year; - - if (start_month_saved == dmon) { - // Mark day if our query hasn't hit the next month. - g_debug("Adding marked date %s, %d", ctime(&ci->start), dmday); - g_variant_builder_add (&markeddays, "i", dmday); - } + i = 0; + for (l=appointments; l!=NULL; l=l->next) + { + GDateTime * due; + DbusmenuMenuitem * item; + const struct IndicatorDatetimeAppt * appt = l->data; + char * right = NULL; + + due = appt->is_event ? appt->begin : appt->end; + + /* mark day if our query hasn't hit the next month. */ + if (g_date_time_get_month (begin) == g_date_time_get_month (due)) + g_variant_builder_add (&markeddays, "i", g_date_time_get_day_of_month (due)); + + if (i >= MAX_APPOINTMENT_MENUITEMS) + continue; + + item = appointment_menuitems[i]; + i++; + + /* remove the icon as we might not replace it on error */ + dbusmenu_menuitem_property_remove(item, APPOINTMENT_MENUITEM_PROP_ICON); + + /* remove the activate handler */ + g_signal_handlers_disconnect_matched(G_OBJECT(item), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(activate_cb), NULL); + + dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); + dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); + dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_LABEL, appt->summary); + + gboolean full_day = FALSE; + if (appt->is_event) + 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 languages with very long day names. */ + right = g_date_time_format (due, _("%A")); + } + else + { + int ay, am, ad; + int by, bm, bd; + gboolean same_day; + gboolean hr12; + const char * fmt; + + g_date_time_get_ymd (due, &ay, &am, &ad); + g_date_time_get_ymd (begin, &by, &bm, &bd); + same_day = (ay==by) && (am==bm) && (ad==bd); + hr12 = apt_output == SETTINGS_TIME_12_HOUR; + + if (same_day && hr12) + fmt = _(DEFAULT_TIME_12_FORMAT); + else if (same_day) + fmt = _(DEFAULT_TIME_24_FORMAT); + else if (hr12) + fmt = _(DEFAULT_TIME_12_FORMAT_WITH_DAY); + else + fmt = _(DEFAULT_TIME_24_FORMAT_WITH_DAY); + + right = g_date_time_format (due, fmt); + } + + dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right); + g_free (right); - // If the appointment time is less than the selected date, - // don't create an appointment item for it. - if (vtype == E_CAL_COMPONENT_EVENT) { - if (ci->start < start_time_appointments) continue; - } else if (vtype == E_CAL_COMPONENT_TODO) { - if (ci->end < start_time_appointments) continue; - } - - if (i >= MAX_APPOINTMENT_MENUITEMS) - continue; - - item = appointments[i]; - i++; - - /* Remove the icon as we might not replace it on error */ - dbusmenu_menuitem_property_remove(item, APPOINTMENT_MENUITEM_PROP_ICON); - - /* Remove the activate handler */ - g_signal_handlers_disconnect_matched(G_OBJECT(item), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(activate_cb), NULL); - - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - - - // Label text - ECalComponentText valuetext; - e_cal_component_get_summary (ecalcomp, &valuetext); - const gchar * summary = valuetext.value; - g_debug("Summary: %s", summary); - dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_LABEL, summary); - - gboolean full_day = FALSE; - if (vtype == E_CAL_COMPONENT_EVENT) { - time_t start = ci->start; - if (time_add_day(start, 1) == ci->end) { - full_day = TRUE; - } - } - - // Due text - if (full_day) { - struct tm fulldaytime = {0}; - localtime_r(&ci->start, &fulldaytime); + // Now we pull out the URI for the calendar event and try to create a URI that'll work when we execute evolution + // FIXME Because the URI stuff is really broken, we're going to open the calendar at todays date instead + //e_cal_component_get_uid(ecalcomp, &uri); +/* FIXME: appointment menuitems aren't clickable */ +#if 0 + gchar * ad = isodate_from_time_t(mktime(due)); + gchar * cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL); + g_debug("Command to Execute: %s", cmd); + g_signal_connect_data (G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK(activate_cb), cmd, (GClosureNotify)g_free, 0); + g_free (ad); +#endif - /* 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. */ - strftime(right, 20, _("%A"), &fulldaytime); - } else { - if (apt_output == SETTINGS_TIME_12_HOUR) { - if ((mday == dmday) && (mon == dmon) && (year == dyear)) - strftime(right, 20, _(DEFAULT_TIME_12_FORMAT), due); - else - strftime(right, 20, _(DEFAULT_TIME_12_FORMAT_WITH_DAY), due); - } else if (apt_output == SETTINGS_TIME_24_HOUR) { - if ((mday == dmday) && (mon == dmon) && (year == dyear)) - strftime(right, 20, _(DEFAULT_TIME_24_FORMAT), due); - else - strftime(right, 20, _(DEFAULT_TIME_24_FORMAT_WITH_DAY), due); - } - } - g_debug("Appointment time: %s, for date %s", right, asctime(due)); - dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right); - - // Now we pull out the URI for the calendar event and try to create a URI that'll work when we execute evolution - // FIXME Because the URI stuff is really broken, we're going to open the calendar at todays date instead - //e_cal_component_get_uid(ecalcomp, &uri); - gchar * ad = isodate_from_time_t(mktime(due)); - gchar * cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL); - g_debug("Command to Execute: %s", cmd); - g_signal_connect_data (G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(activate_cb), cmd, (GClosureNotify)g_free, 0); - g_free (ad); - - const gchar *color_spec = e_source_selectable_get_color (e_source_get_extension (ci->source, E_SOURCE_EXTENSION_CALENDAR)); - g_debug("Colour to use: %s", color_spec); - - // Draw the correct icon for the appointment type and then tint it using mask fill. - // For now we'll create a circle - if (color_spec != NULL) { - g_debug("Creating a cairo surface: size, %d by %d", width, height); - cairo_surface_t *surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height ); - cairo_t *cr = cairo_create(surface); - GdkRGBA rgba; - if (gdk_rgba_parse (&rgba, color_spec)) - gdk_cairo_set_source_rgba (cr, &rgba); - cairo_paint(cr); - cairo_set_source_rgba(cr, 0,0,0,0.5); - cairo_set_line_width(cr, 1); - cairo_rectangle (cr, 0.5, 0.5, width-1, height-1); - cairo_stroke(cr); - // Convert to pixbuf, in gtk3 this is done with gdk_pixbuf_get_from_surface - cairo_content_t content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR; - GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - !!(content & CAIRO_CONTENT_ALPHA), - 8, width, height); - if (pixbuf != NULL) { - gint sstride = cairo_image_surface_get_stride( surface ); - gint dstride = gdk_pixbuf_get_rowstride (pixbuf); - guchar *spixels = cairo_image_surface_get_data( surface ); - guchar *dpixels = gdk_pixbuf_get_pixels (pixbuf); - - int x, y; - for (y = 0; y < height; y++) { - guint32 *src = (guint32 *) spixels; - - for (x = 0; x < width; x++) { - guint alpha = src[x] >> 24; - - if (alpha == 0) { - dpixels[x * 4 + 0] = 0; - dpixels[x * 4 + 1] = 0; - dpixels[x * 4 + 2] = 0; - } else { - dpixels[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; - dpixels[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; - dpixels[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; - } - dpixels[x * 4 + 3] = alpha; - } - spixels += sstride; - dpixels += dstride; - } - - dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf); - g_clear_object (&pixbuf); - } else { - g_debug("Creating pixbuf from surface failed"); - } - cairo_surface_destroy (surface); - cairo_destroy(cr); - } - g_debug("Adding appointment: %p", item); - } - - g_clear_error (&gerror); + if ((pixbuf = create_color_icon_pixbuf (appt->color))) + { + dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf); + g_clear_object (&pixbuf); + } + } - g_list_free_full (sorted_comp_instances, (GDestroyNotify)comp_instance_free); - sorted_comp_instances = NULL; - GVariant * marks = g_variant_builder_end (&markeddays); - dbusmenu_menuitem_property_set_variant (calendar, CALENDAR_MENUITEM_PROP_MARKS, marks); + marks = g_variant_builder_end (&markeddays); + dbusmenu_menuitem_property_set_variant (calendar, CALENDAR_MENUITEM_PROP_MARKS, marks); - g_clear_object (&sources); + g_slist_free_full (appointments, (GDestroyNotify)indicator_datetime_appt_free); - updating_appointments = FALSE; - g_debug("End of objects"); - return TRUE; + updating_appointments = FALSE; + g_date_time_unref (end); + g_date_time_unref (begin); } /* Looks for the time and date admin application and enables the @@ -971,19 +788,6 @@ check_for_timeadmin (gpointer user_data) return FALSE; } -static void -show_locations_changed (void) -{ - /* Re-calculate */ - update_location_menu_items(); -} - -static void -time_format_changed (void) -{ - update_appointment_menu_items(NULL); -} - /* Does the work to build the default menu, really calls out to other functions but this is the core to clean up the main function. */ @@ -1020,11 +824,11 @@ build_menus (DbusmenuMenuitem * root) update_location_menu_items(); - g_signal_connect (conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL); - g_signal_connect (conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (show_locations_changed), NULL); - g_signal_connect (conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL); g_signal_connect (conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), NULL); - g_signal_connect (conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (time_format_changed), NULL); + g_signal_connect_swapped (conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (update_location_menu_items), NULL); + g_signal_connect_swapped (conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (update_location_menu_items), NULL); + g_signal_connect_swapped (conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (update_location_menu_items), NULL); + g_signal_connect_swapped (conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (update_appointment_menu_items), NULL); DbusmenuMenuitem * separator = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); @@ -1037,6 +841,7 @@ build_menus (DbusmenuMenuitem * root) g_signal_connect(G_OBJECT(settings), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), SETTINGS_APP_INVOCATION); dbusmenu_menuitem_child_append(root, settings); g_idle_add(check_for_timeadmin, NULL); + update_appointment_menu_items_soon (); } return; @@ -1106,7 +911,7 @@ static gboolean skew_check_timer_func (gpointer unused G_GNUC_UNUSED) { static time_t prev_time = 0; - const time_t cur_time = time (NULL); + const time_t cur_time = time (NULL); /* FIXME: not mockable */ const double diff_sec = fabs (difftime (cur_time, prev_time)); if (prev_time && (diff_sec > SKEW_DIFF_THRESHOLD_SEC)) { @@ -1173,51 +978,6 @@ service_shutdown (IndicatorService * service, gpointer user_data) return; } -static void -free_appointment_sources (void) -{ - g_list_free_full (appointment_sources, g_object_unref); - appointment_sources = NULL; -} - -static void -source_changed_cb (ESource *source __attribute__ ((unused)), - gpointer user_data) -{ - update_appointment_menu_items (user_data); -} - -static void -init_appointment_sources (ESourceRegistry *registry) -{ - GList * l; - - appointment_sources = e_source_registry_list_sources (registry, E_SOURCE_EXTENSION_CALENDAR); - - for (l=appointment_sources; l!=NULL; l=l->next) - g_signal_connect (G_OBJECT(l->data), "changed", G_CALLBACK (source_changed_cb), NULL); -} - -/* rebuilds both the appointment sources and menu */ -static void -update_appointments (ESourceRegistry *registry, - ESource *source __attribute__ ((unused)), - gpointer user_data __attribute__ ((unused))) -{ - free_appointment_sources (); - init_appointment_sources (registry); - - update_appointment_menu_items (NULL); -} - -static void -source_registry_changed_cb (ESourceRegistry *registry __attribute__ ((unused)), - ESource *source __attribute__ ((unused)), - gpointer user_data) -{ - update_appointment_menu_items (user_data); -} - static void on_use_geoclue_changed_cb (GSettings *settings, gchar *key G_GNUC_UNUSED, @@ -1264,15 +1024,9 @@ main (int argc, char ** argv) /* Build our list of appointment calendar sources. When a source changes, update our menu items. When sources are added or removed, update our list and menu items. */ - source_registry = e_source_registry_new_sync (NULL, NULL); - g_object_connect (source_registry, - "signal::source-added", G_CALLBACK (update_appointments), NULL, - "signal::source-removed", G_CALLBACK (update_appointments), NULL, - "signal::source-changed", G_CALLBACK (source_registry_changed_cb), NULL, - "signal::source-disabled", G_CALLBACK (source_registry_changed_cb), NULL, - "signal::source-enabled", G_CALLBACK (source_registry_changed_cb), NULL, - NULL); - init_appointment_sources (source_registry); + planner = indicator_datetime_planner_eds_new (); + g_signal_connect (planner, "appointments-changed", + G_CALLBACK(update_appointment_menu_items_soon), NULL); /* Building the base menu */ server = dbusmenu_server_new(MENU_OBJ); @@ -1314,16 +1068,12 @@ main (int argc, char ** argv) mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); - free_appointment_sources(); - g_object_unref(G_OBJECT(conf)); g_object_unref(G_OBJECT(dbus)); g_object_unref(G_OBJECT(service)); g_object_unref(G_OBJECT(server)); g_object_unref(G_OBJECT(root)); - g_object_unref(G_OBJECT(source_registry)); - - icaltimezone_free_builtin_timezones(); + g_object_unref(G_OBJECT(planner)); g_object_unref (geo_location); g_object_unref (tz_file); diff --git a/src/planner-eds.c b/src/planner-eds.c new file mode 100644 index 0000000..6677b32 --- /dev/null +++ b/src/planner-eds.c @@ -0,0 +1,330 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include /* GFile, GFileMonitor */ + +#include +#include +#include +#include + +#include "planner-eds.h" + +struct _IndicatorDatetimePlannerEdsPriv +{ + ESourceRegistry * source_registry; +}; + +typedef IndicatorDatetimePlannerEdsPriv priv_t; + +G_DEFINE_TYPE (IndicatorDatetimePlannerEds, + indicator_datetime_planner_eds, + INDICATOR_TYPE_DATETIME_PLANNER) + +/*** +**** +***/ + +void +indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt) +{ + if (appt != NULL) + { + g_date_time_unref (appt->end); + g_date_time_unref (appt->begin); + g_free (appt->color); + g_free (appt->summary); + g_free (appt); + } +} + +/*** +**** my_get_appointments() helpers +***/ + +struct my_get_appointments_data +{ + ESource * source; + GSList * appointments; +}; + +static gboolean +my_get_appointments_foreach (ECalComponent * component, + time_t begin, + time_t end, + gpointer gdata) +{ + const ECalComponentVType vtype = e_cal_component_get_vtype (component); + struct my_get_appointments_data * data = gdata; + + if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) + { + icalproperty_status status; + e_cal_component_get_status (component, &status); + if ((status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) + { + ECalComponentText text; + struct IndicatorDatetimeAppt * appt = g_new0 (struct IndicatorDatetimeAppt, 1); + + text.value = ""; + e_cal_component_get_summary (component, &text); + + appt->begin = g_date_time_new_from_unix_local (begin); + appt->end = g_date_time_new_from_unix_local (end); + appt->color = e_source_selectable_dup_color (e_source_get_extension (data->source, E_SOURCE_EXTENSION_CALENDAR)); + appt->is_event = vtype == E_CAL_COMPONENT_EVENT; + appt->summary = g_strdup (text.value); + + data->appointments = g_slist_prepend (data->appointments, appt); + } + } + + return G_SOURCE_CONTINUE; +} + + +/*** +**** IndicatorDatetimePlanner virtual funcs +***/ + +static GSList * +my_get_appointments (IndicatorDatetimePlanner * planner, + GDateTime * begin_datetime, + GDateTime * end_datetime) +{ + GList * l; + GList * sources; + priv_t * p; + const char * str; + icaltimezone * default_timezone; + struct my_get_appointments_data data; + const int64_t begin = g_date_time_to_unix (begin_datetime); + const int64_t end = g_date_time_to_unix (end_datetime); + + p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv; + + /** + *** init the default timezone + **/ + + default_timezone = NULL; + + if ((str = indicator_datetime_planner_get_timezone (planner))) + { + default_timezone = icaltimezone_get_builtin_timezone (str); + + if (default_timezone == NULL) /* maybe str is a tzid? */ + default_timezone = icaltimezone_get_builtin_timezone_from_tzid (str); + } + + /** + *** walk through the sources to build the appointment list + **/ + + data.source = NULL; + data.appointments = NULL; + + sources = e_source_registry_list_sources (p->source_registry, E_SOURCE_EXTENSION_CALENDAR); + for (l=sources; l!=NULL; l=l->next) + { + GError * err; + ESource * source; + ECalClient * ecc; + + source = E_SOURCE (l->data); + if (e_source_get_enabled (source)) + { + err = NULL; + ecc = e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, &err); + if (err != NULL) + { + g_warning ("Can't create ecal client: %s", err->message); + g_error_free (err); + } + else + { + if (!e_client_open_sync (E_CLIENT (ecc), TRUE, NULL, &err)) + { + g_debug ("Failed to open ecal client: %s", err->message); + g_error_free (err); + } + else + { + if (default_timezone != NULL) + e_cal_client_set_default_timezone (ecc, default_timezone); + + data.source = source; + e_cal_client_generate_instances_sync (ecc, begin, end, my_get_appointments_foreach, &data); + } + + g_object_unref (ecc); + } + } + } + + g_list_free_full (sources, g_object_unref); + + g_debug ("%s EDS get_appointments returning %d appointments", G_STRLOC, g_slist_length (data.appointments)); + return data.appointments; +} + +gboolean +my_is_configured (IndicatorDatetimePlanner * planner) +{ + GList * sources; + gboolean have_sources; + IndicatorDatetimePlannerEds * self; + + /* confirm that it's installed... */ + gchar *evo = g_find_program_in_path ("evolution"); + if (evo == NULL) + return FALSE; + + g_debug ("found calendar app: '%s'", evo); + g_free (evo); + + /* see if there are any calendar sources */ + self = INDICATOR_DATETIME_PLANNER_EDS (planner); + sources = e_source_registry_list_sources (self->priv->source_registry, E_SOURCE_EXTENSION_CALENDAR); + have_sources = sources != NULL; + g_list_free_full (sources, g_object_unref); + return have_sources; +} + +static void +my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED) +{ + GError * error = NULL; + const char * const command = "evolution -c calendar"; + + if (!g_spawn_command_line_async (command, &error)) + { + g_warning ("Unable to start %s: %s", command, error->message); + g_error_free (error); + } +} + +static void +my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, + GDateTime * activate_time) +{ + gchar * isodate; + gchar * command; + GError * error; + + isodate = g_date_time_format (activate_time, "%F"); + command = g_strconcat ("evolution calendar:///?startdate=", isodate, NULL); + error = 0; + if (!g_spawn_command_line_async (command, &error)) + { + g_warning ("Unable to start %s: %s", command, error->message); + g_error_free (error); + } + + g_free (command); + g_free (isodate); +} + +/*** +**** GObject virtual funcs +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o); + priv_t * p = self->priv; + + if (p->source_registry != NULL) + { + g_signal_handlers_disconnect_by_func (p->source_registry, + indicator_datetime_planner_emit_appointments_changed, + self); + + g_clear_object (&self->priv->source_registry); + } + + G_OBJECT_CLASS (indicator_datetime_planner_eds_parent_class)->dispose (o); +} + +/*** +**** Insantiation +***/ + +static void +indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * klass) +{ + GObjectClass * object_class; + IndicatorDatetimePlannerClass * planner_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + + planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass); + planner_class->is_configured = my_is_configured; + planner_class->activate = my_activate; + planner_class->activate_time = my_activate_time; + planner_class->get_appointments = my_get_appointments; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv)); +} + +static void +indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self) +{ + priv_t * p; + GError * err; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_PLANNER_EDS, + IndicatorDatetimePlannerEdsPriv); + + self->priv = p; + + err = 0; + p->source_registry = e_source_registry_new_sync (NULL, &err); + if (err != NULL) + { + g_warning ("indicator-datetime cannot show EDS appointments: %s", err->message); + g_error_free (err); + } + else + { + gpointer o = p->source_registry; + g_signal_connect_swapped (o, "source-added", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self); + g_signal_connect_swapped (o, "source-removed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self); + g_signal_connect_swapped (o, "source-changed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self); + g_signal_connect_swapped (o, "source-disabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self); + g_signal_connect_swapped (o, "source-enabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self); + } +} + +/*** +**** Public +***/ + +IndicatorDatetimePlanner * +indicator_datetime_planner_eds_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_EDS, NULL); + + return INDICATOR_DATETIME_PLANNER (o); +} diff --git a/src/planner-eds.h b/src/planner-eds.h new file mode 100644 index 0000000..a2c803a --- /dev/null +++ b/src/planner-eds.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_PLANNER_EDS__H__ +#define __INDICATOR_DATETIME_PLANNER_EDS__H__ + +#include "planner.h" /* parent class */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_PLANNER_EDS (indicator_datetime_planner_eds_get_type()) +#define INDICATOR_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEds)) +#define INDICATOR_DATETIME_PLANNER_EDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEdsClass)) +#define INDICATOR_IS_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS)) + +typedef struct _IndicatorDatetimePlannerEds IndicatorDatetimePlannerEds; +typedef struct _IndicatorDatetimePlannerEdsPriv IndicatorDatetimePlannerEdsPriv; +typedef struct _IndicatorDatetimePlannerEdsClass IndicatorDatetimePlannerEdsClass; + +GType indicator_datetime_planner_eds_get_type (void); + +/** + * An IndicatorDatetimePlanner which uses Evolution Data Server + * to get its list of appointments. + */ +struct _IndicatorDatetimePlannerEds +{ + /*< private >*/ + IndicatorDatetimePlanner parent; + IndicatorDatetimePlannerEdsPriv * priv; +}; + +struct _IndicatorDatetimePlannerEdsClass +{ + IndicatorDatetimePlannerClass parent_class; +}; + +gboolean indicator_datetime_planner_eds_is_usable (void); + +IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_PLANNER_EDS__H__ */ diff --git a/src/planner.c b/src/planner.c new file mode 100644 index 0000000..f16a05a --- /dev/null +++ b/src/planner.c @@ -0,0 +1,232 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "planner.h" + +/** +*** Signals Boilerplate +**/ + +enum +{ + SIGNAL_APPTS_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + +/** +*** Properties Boilerplate +**/ + +enum +{ + PROP_0, + PROP_TIMEZONE, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST] = { 0 }; + +/** +*** GObject Boilerplate +**/ + +G_DEFINE_TYPE (IndicatorDatetimePlanner, + indicator_datetime_planner, + G_TYPE_OBJECT) + +struct _IndicatorDatetimePlannerPriv +{ + char * timezone; +}; + +/*** +**** GObjectClass virtual funcs +***/ + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); + + switch (property_id) + { + case PROP_TIMEZONE: + g_value_set_string (value, self->priv->timezone); + 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) +{ + IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); + + switch (property_id) + { + case PROP_TIMEZONE: + indicator_datetime_planner_set_timezone (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_finalize (GObject * o) +{ + IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER(o); + + g_free (self->priv->timezone); + + G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_datetime_planner_class_init (IndicatorDatetimePlannerClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; + + g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerPriv)); + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = my_finalize; + object_class->get_property = my_get_property; + object_class->set_property = my_set_property; + + klass->get_appointments = NULL; + + signals[SIGNAL_APPTS_CHANGED] = g_signal_new ("appointments-changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorDatetimePlannerClass, appointments_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* install properties */ + + properties[PROP_0] = NULL; + + properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", + "Timezone", + "Default timezone for the EDS appointments", + "", + flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +indicator_datetime_planner_init (IndicatorDatetimePlanner * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_DATETIME_PLANNER, + IndicatorDatetimePlannerPriv); +} + +/*** +**** Public API +***/ + +void +indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); + + g_signal_emit (self, signals[SIGNAL_APPTS_CHANGED], 0, NULL); +} + +static gint +compare_appointments_by_start_time (gconstpointer ga, gconstpointer gb) +{ + const struct IndicatorDatetimeAppt * a = ga; + const struct IndicatorDatetimeAppt * b = gb; + + return g_date_time_compare (a->begin, b->begin); +} + +GSList * +indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end) +{ + GSList * appointments; + + g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); + + appointments = INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->get_appointments (self, begin, end); + return g_slist_sort (appointments, compare_appointments_by_start_time); +} + +gboolean +indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self) +{ + g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), FALSE); + + return INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->is_configured (self); +} + +void +indicator_datetime_planner_activate (IndicatorDatetimePlanner * self) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); + + INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate (self); +} + +void +indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); + + INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate_time (self, time); +} + +void +indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone) +{ + g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); + + g_free (self->priv->timezone); + self->priv->timezone = g_strdup (timezone); + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); +} + +const char * +indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self) +{ + g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); + + return self->priv->timezone; +} diff --git a/src/planner.h b/src/planner.h new file mode 100644 index 0000000..45a6d3c --- /dev/null +++ b/src/planner.h @@ -0,0 +1,138 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_PLANNER__H__ +#define __INDICATOR_DATETIME_PLANNER__H__ + +#include +#include /* parent class */ +#include /* GdkRGBA */ + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_DATETIME_PLANNER (indicator_datetime_planner_get_type()) +#define INDICATOR_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlanner)) +#define INDICATOR_DATETIME_PLANNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) +#define INDICATOR_DATETIME_PLANNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) +#define INDICATOR_IS_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER)) + +typedef struct _IndicatorDatetimePlanner IndicatorDatetimePlanner; +typedef struct _IndicatorDatetimePlannerPriv IndicatorDatetimePlannerPriv; +typedef struct _IndicatorDatetimePlannerClass IndicatorDatetimePlannerClass; + +GType indicator_datetime_planner_get_type (void); + +struct IndicatorDatetimeAppt +{ + char * color; + char * summary; + GDateTime * begin; + GDateTime * end; + gboolean is_event; +}; + +/** + * Abstract Base Class for objects that provides appointments and events. + * + * These will be listed in the appointments section of indicator-datetime's menu. + */ +struct _IndicatorDatetimePlanner +{ + /*< private >*/ + GObject parent; + IndicatorDatetimePlannerPriv * priv; +}; + +struct _IndicatorDatetimePlannerClass +{ + GObjectClass parent_class; + + /* signals */ + + void (*appointments_changed) (IndicatorDatetimePlanner * self); + + /* virtual functions */ + + GSList* (*get_appointments) (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end); + + gboolean (*is_configured) (IndicatorDatetimePlanner * self); + void (*activate) (IndicatorDatetimePlanner * self); + void (*activate_time) (IndicatorDatetimePlanner * self, GDateTime *); +}; + +/*** +**** +***/ + +void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt); + +/** + * Get a list of appointments, sorted by start time. + * + * An easy way to free the list properly in one step is as follows: + * + * g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free); + * + * + * Return value: (element-type IndicatorDatetimeAppt) + * (transfer full): + * list of appointments + */ +GSList * indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end); + +/** + * Returns false if the planner's backend is not configured. + * + * This can be used on startup to determine whether or not to use this planner. + */ +gboolean indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self); + +/** + * Activate this planner. + * + * This is used to activate the planner's backend's event editor. + */ +void indicator_datetime_planner_activate (IndicatorDatetimePlanner * self); + +/** + * Activate this planner. + * + * This is used to activate the planner's backend's event editor, + * with an added hint of the specific time that the user would like to edit. + */ +void indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time); + +/** + * Set the timezone. + * + * This is used as a default timezone if the backend's events don't provide their own. + */ +void indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone); + +const char * indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self); + + +/** + * Emits the "appointments-changed" signal. This should only be called by subclasses. + */ +void indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_PLANNER__H__ */ -- cgit v1.2.3 From ca06cb7631136e4ce1d376895b23053791e4f7c3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 12 May 2013 23:01:29 -0500 Subject: compile cleanly with -Wextra --- src/Makefile.am | 2 +- src/datetime-interface.c | 2 +- src/datetime-service.c | 53 +++++++++++++++++++++++++++++------------------- src/timezone-geoclue.c | 14 +++++++------ 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 4a34c33..9350f86 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ indicator_datetime_service_SOURCES = \ dbus-shared.h \ settings-shared.h indicator_datetime_service_CFLAGS = \ - -Wall \ + -Wall -Wextra \ -Werror \ $(SERVICE_CFLAGS) \ $(COVERAGE_CFLAGS) \ diff --git a/src/datetime-interface.c b/src/datetime-interface.c index e67be85..72c7437 100644 --- a/src/datetime-interface.c +++ b/src/datetime-interface.c @@ -108,7 +108,7 @@ datetime_interface_init (DatetimeInterface *self) } static void -bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data) +bus_get_cb (GObject * object G_GNUC_UNUSED, GAsyncResult * res, gpointer user_data) { GError * error = NULL; GDBusConnection * connection = g_bus_get_finish(res, &error); diff --git a/src/datetime-service.c b/src/datetime-service.c index 4d2da12..cc263f6 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -201,7 +201,7 @@ update_location_menu_items (void) /* maybe add the user-specified custom locations */ gchar ** user_locations = g_settings_get_strv (conf, SETTINGS_LOCATIONS_S); if (user_locations != NULL) { - gint i; + guint i; const guint location_count = g_strv_length (user_locations); const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_LOCATIONS_S); g_debug ("%s Found %u user-specified locations", G_STRLOC, location_count); @@ -246,7 +246,7 @@ update_location_menu_items (void) } static void -quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data) +quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data G_GNUC_UNUSED) { GError * error = NULL; GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error); @@ -261,7 +261,7 @@ quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data) } static void -quick_set_tz_proxy_cb (GObject *object, GAsyncResult *res, gpointer zone) +quick_set_tz_proxy_cb (GObject *object G_GNUC_UNUSED, GAsyncResult *res, gpointer zone) { GError * error = NULL; @@ -281,7 +281,7 @@ quick_set_tz_proxy_cb (GObject *object, GAsyncResult *res, gpointer zone) } static void -quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data) +quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { const gchar * tz = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_ZONE); g_debug("Quick setting timezone to: %s", tz); @@ -308,7 +308,7 @@ quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data) /* Updates the label in the date menuitem */ static gboolean -update_datetime (gpointer user_data) +update_datetime (gpointer uused G_GNUC_UNUSED) { GDateTime *datetime; gchar *utf8; @@ -357,9 +357,9 @@ activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, } static gboolean -update_appointment_menu_items_idle (gpointer user_data) +update_appointment_menu_items_idle (gpointer unused G_GNUC_UNUSED) { - update_appointment_menu_items(); + update_appointment_menu_items (); return G_SOURCE_REMOVE; } @@ -383,7 +383,10 @@ hide_all_appointments (void) } static gboolean -month_changed_cb (DbusmenuMenuitem * menuitem, gchar *name, GVariant *variant, guint timestamp) +month_changed_cb (DbusmenuMenuitem * menuitem, + gchar * name G_GNUC_UNUSED, + GVariant * variant, + guint timestamp G_GNUC_UNUSED) { start_time_appointments = (time_t)g_variant_get_uint32(variant); @@ -399,7 +402,10 @@ month_changed_cb (DbusmenuMenuitem * menuitem, gchar *name, GVariant *variant, g } static gboolean -day_selected_cb (DbusmenuMenuitem * menuitem, gchar *name, GVariant *variant, guint timestamp) +day_selected_cb (DbusmenuMenuitem * menuitem, + gchar * name G_GNUC_UNUSED, + GVariant * variant, + guint timestamp G_GNUC_UNUSED) { time_t new_time = (time_t)g_variant_get_uint32(variant); g_warn_if_fail(new_time != 0); @@ -470,10 +476,10 @@ stop_ecal_timer(void) } } static gboolean -idle_start_ecal_timer (gpointer data) +idle_start_ecal_timer (gpointer unused G_GNUC_UNUSED) { start_ecal_timer(); - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -499,7 +505,7 @@ show_events_changed (void) /* Looks for the calendar application and enables the item if we have one, starts ecal timer if events are turned on */ static gboolean -check_for_calendar (gpointer user_data) +check_for_calendar (gpointer unused G_GNUC_UNUSED) { g_return_val_if_fail (calendar != NULL, FALSE); @@ -771,7 +777,7 @@ update_appointment_menu_items (void) /* Looks for the time and date admin application and enables the item we have one */ static gboolean -check_for_timeadmin (gpointer user_data) +check_for_timeadmin (gpointer unused G_GNUC_UNUSED) { g_return_val_if_fail (settings != NULL, FALSE); @@ -785,7 +791,7 @@ check_for_timeadmin (gpointer user_data) dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); } - return FALSE; + return G_SOURCE_REMOVE; } /* Does the work to build the default menu, really calls out @@ -876,7 +882,7 @@ static guint day_timer = 0; /* Execute at a given time, update and setup a new timer to go again. */ static gboolean -day_timer_func (gpointer user_data) +day_timer_func (gpointer unused G_GNUC_UNUSED) { day_timer = 0; /* Reset up each time to reduce error */ @@ -924,8 +930,11 @@ skew_check_timer_func (gpointer unused G_GNUC_UNUSED) } static void -session_active_change_cb (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, - GVariant * parameters, gpointer user_data) +session_active_change_cb (GDBusProxy * proxy G_GNUC_UNUSED, + gchar * sender_name G_GNUC_UNUSED, + gchar * signal_name, + GVariant * parameters, + gpointer user_data G_GNUC_UNUSED) { // Suspending / returning from suspend (true / false) if (g_strcmp0(signal_name, "PrepareForSleep") == 0) { @@ -936,12 +945,13 @@ session_active_change_cb (GDBusProxy * proxy, gchar * sender_name, gchar * signa on_clock_skew (); } } - return; } /* for hooking into console kit signal on wake from suspend */ static void -system_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data) +system_proxy_cb (GObject * object G_GNUC_UNUSED, + GAsyncResult * res, + gpointer user_data) { GError * error = NULL; @@ -971,7 +981,8 @@ get_greeter_mode (void) /* Repsonds to the service object saying it's time to shutdown. It stops the mainloop. */ static void -service_shutdown (IndicatorService * service, gpointer user_data) +service_shutdown (IndicatorService * service G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) { g_warning("Shutting down service!"); g_main_loop_quit(mainloop); @@ -983,7 +994,7 @@ on_use_geoclue_changed_cb (GSettings *settings, gchar *key G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { - const gboolean use_geoclue = g_settings_get_boolean (conf, "show-auto-detected-location"); + const gboolean use_geoclue = g_settings_get_boolean (settings, "show-auto-detected-location"); if (geo_location && !use_geoclue) { diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c index 4c3c7d8..08f272d 100644 --- a/src/timezone-geoclue.c +++ b/src/timezone-geoclue.c @@ -58,10 +58,10 @@ set_timezone (IndicatorDatetimeTimezoneGeoclue * self, const gchar * timezone) } static void -on_address_changed (GeoclueAddress * address, - int timestamp, +on_address_changed (GeoclueAddress * address G_GNUC_UNUSED, + int timestamp G_GNUC_UNUSED, GHashTable * addy_data, - GeoclueAccuracy * accuracy, + GeoclueAccuracy * accuracy G_GNUC_UNUSED, GError * error, gpointer gself) { @@ -79,7 +79,7 @@ on_address_changed (GeoclueAddress * address, } static void -on_address_created (GeoclueMasterClient * master, +on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED, GeoclueAddress * address, GError * error, gpointer gself) @@ -102,7 +102,9 @@ on_address_created (GeoclueMasterClient * master, } static void -on_requirements_set (GeoclueMasterClient * master, GError * error, gpointer user_data) +on_requirements_set (GeoclueMasterClient * master G_GNUC_UNUSED, + GError * error, + gpointer user_data G_GNUC_UNUSED) { if (error != NULL) { @@ -112,7 +114,7 @@ on_requirements_set (GeoclueMasterClient * master, GError * error, gpointer user } static void -on_client_created (GeoclueMaster * master, +on_client_created (GeoclueMaster * master G_GNUC_UNUSED, GeoclueMasterClient * client, gchar * path, GError * error, -- cgit v1.2.3 From 36c6842f3eb704a72b5931152eb71d399d5f4cb1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 12 May 2013 23:03:12 -0500 Subject: g_object_unref doesn't require G_OBJECT() --- src/datetime-service.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/datetime-service.c b/src/datetime-service.c index cc263f6..a9e61d8 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -165,7 +165,7 @@ update_location_menu_items (void) DbusmenuMenuitem * item = DBUSMENU_MENUITEM(location_menu_items->data); location_menu_items = g_slist_remove(location_menu_items, item); dbusmenu_menuitem_child_delete(root, DBUSMENU_MENUITEM(item)); - g_object_unref(G_OBJECT(item)); + g_object_unref(item); } /*** @@ -1079,13 +1079,12 @@ main (int argc, char ** argv) mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); - g_object_unref(G_OBJECT(conf)); - g_object_unref(G_OBJECT(dbus)); - g_object_unref(G_OBJECT(service)); - g_object_unref(G_OBJECT(server)); - g_object_unref(G_OBJECT(root)); - g_object_unref(G_OBJECT(planner)); - + g_object_unref (conf); + g_object_unref (dbus); + g_object_unref (service); + g_object_unref (server); + g_object_unref (root); + g_object_unref (planner); g_object_unref (geo_location); g_object_unref (tz_file); -- cgit v1.2.3 From 7779a357b79937d6ed06eae2f7bbdce1162bcff5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 13 May 2013 00:57:11 -0500 Subject: minor tweak to properties array initialization --- src/timezone.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/timezone.c b/src/timezone.c index 9e671ed..546a3e3 100644 --- a/src/timezone.c +++ b/src/timezone.c @@ -30,7 +30,7 @@ enum PROP_LAST }; -static GParamSpec * properties[PROP_LAST] = { 0 }; +static GParamSpec * properties[PROP_LAST] = { 0, }; static void my_get_property (GObject * o, @@ -70,8 +70,6 @@ indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass) klass->get_timezone = NULL; - properties[PROP_0] = NULL; - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", "Timezone", "Timezone", -- cgit v1.2.3 From 6bfac9d944fc7b29471039fef16fe1f1aefdfb7b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 13 May 2013 00:57:42 -0500 Subject: wrap globals into a struct as a precursor to turning the service code into a GObject --- src/datetime-service.c | 788 ++++++++++++++++++++++++++----------------------- 1 file changed, 417 insertions(+), 371 deletions(-) diff --git a/src/datetime-service.c b/src/datetime-service.c index a9e61d8..1a29949 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -7,16 +7,16 @@ Copyright 2010 Canonical Ltd. Authors: Ted Gould -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 +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 +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 +You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -27,7 +27,6 @@ with this program. If not, see . #include #include #include -#include #include /* fabs() */ #include @@ -58,34 +57,40 @@ with this program. If not, see . #define SETTINGS_APP_INVOCATION "gnome-control-center datetime" #endif -static void update_appointment_menu_items (void); -static void update_location_menu_items (void); -static void day_timer_reset (void); static gboolean get_greeter_mode (void); static void quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data); -static IndicatorService * service = NULL; -static GMainLoop * mainloop = NULL; -static DbusmenuServer * server = NULL; static DbusmenuMenuitem * root = NULL; static DatetimeInterface * dbus = NULL; -/* Global Items */ -static DbusmenuMenuitem * date = NULL; -static DbusmenuMenuitem * calendar = NULL; -static DbusmenuMenuitem * settings = NULL; -static DbusmenuMenuitem * events_separator = NULL; -static DbusmenuMenuitem * locations_separator = NULL; -static DbusmenuMenuitem * add_appointment = NULL; -static DbusmenuMenuitem * appointment_menuitems[MAX_APPOINTMENT_MENUITEMS]; -static GSList * location_menu_items = NULL; -static gboolean updating_appointments = FALSE; -static time_t start_time_appointments = (time_t) 0; -static GSettings * conf = NULL; -static IndicatorDatetimeTimezone * geo_location = NULL; -static IndicatorDatetimeTimezone * tz_file = NULL; -static IndicatorDatetimePlanner * planner = NULL; +typedef struct IndicatorDatetimeService +{ + DbusmenuMenuitem * date; + DbusmenuMenuitem * calendar; + DbusmenuMenuitem * settings; + DbusmenuMenuitem * events_separator; + DbusmenuMenuitem * locations_separator; + DbusmenuMenuitem * add_appointment; + DbusmenuMenuitem * appointment_menuitems[MAX_APPOINTMENT_MENUITEMS]; + + GSList * location_menu_items; + gboolean updating_appointments; + time_t start_time_appointments; + GSettings * conf; + + IndicatorDatetimeTimezone * geo_location; + IndicatorDatetimeTimezone * tz_file; + IndicatorDatetimePlanner * planner; + + guint ecaltimer; + guint day_timer; +} +IndicatorDatetimeService; + +static void update_location_menu_items (IndicatorDatetimeService * self); +static void update_appointment_menu_items (IndicatorDatetimeService * self); +static void day_timer_reset (IndicatorDatetimeService * self); /** * A temp struct used by update_location_menu_items() for pruning duplicates and sorting. @@ -154,18 +159,18 @@ locations_add (GSList * locations, const char * zone, const char * name, gboolea /* Update the timezone entries */ static void -update_location_menu_items (void) +update_location_menu_items (IndicatorDatetimeService * self) { /* if we're in greeter mode, don't bother */ - if (locations_separator == NULL) + if (self->locations_separator == NULL) return; /* remove the previous locations */ - while (location_menu_items != NULL) { - DbusmenuMenuitem * item = DBUSMENU_MENUITEM(location_menu_items->data); - location_menu_items = g_slist_remove(location_menu_items, item); - dbusmenu_menuitem_child_delete(root, DBUSMENU_MENUITEM(item)); - g_object_unref(item); + while (self->location_menu_items != NULL) { + DbusmenuMenuitem * item = DBUSMENU_MENUITEM(self->location_menu_items->data); + self->location_menu_items = g_slist_remove (self->location_menu_items, item); + dbusmenu_menuitem_child_delete (root, DBUSMENU_MENUITEM(item)); + g_object_unref (item); } /*** @@ -174,13 +179,13 @@ update_location_menu_items (void) ***/ GSList * locations = NULL; - const time_t now = time(NULL); + const time_t now = time(NULL); /* FIXME: unmockable */ /* maybe add geo_timezone */ - if (geo_location != NULL) { - const char * geo_timezone = indicator_datetime_timezone_get_timezone (geo_location); + if (self->geo_location != NULL) { + const char * geo_timezone = indicator_datetime_timezone_get_timezone (self->geo_location); if (geo_timezone && *geo_timezone) { - const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); + const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_DETECTED_S); gchar * name = get_current_zone_name (geo_timezone); locations = locations_add (locations, geo_timezone, name, visible, now); g_free (name); @@ -188,10 +193,10 @@ update_location_menu_items (void) } /* maybe add current_timezone */ - if (tz_file != NULL) { - const char * tz = indicator_datetime_timezone_get_timezone (tz_file); + if (self->tz_file != NULL) { + const char * tz = indicator_datetime_timezone_get_timezone (self->tz_file); if (tz && *tz) { - const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S); + const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_DETECTED_S); gchar * name = get_current_zone_name (tz); locations = locations_add (locations, tz, name, visible, now); g_free (name); @@ -199,11 +204,11 @@ update_location_menu_items (void) } /* maybe add the user-specified custom locations */ - gchar ** user_locations = g_settings_get_strv (conf, SETTINGS_LOCATIONS_S); - if (user_locations != NULL) { + gchar ** user_locations = g_settings_get_strv (self->conf, SETTINGS_LOCATIONS_S); + if (user_locations != NULL) { guint i; const guint location_count = g_strv_length (user_locations); - const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_LOCATIONS_S); + const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_LOCATIONS_S); g_debug ("%s Found %u user-specified locations", G_STRLOC, location_count); for (i=0; ilocations_separator, root)+1; GSList * l; gboolean have_visible_location = FALSE; for (l=locations; l!=NULL; l=l->next) { @@ -233,7 +238,7 @@ update_location_menu_items (void) dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, loc->visible); dbusmenu_menuitem_child_add_position (root, item, offset++); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(quick_set_tz), NULL); - location_menu_items = g_slist_append (location_menu_items, item); + self->location_menu_items = g_slist_append (self->location_menu_items, item); if (loc->visible) have_visible_location = TRUE; time_location_free (loc); @@ -242,7 +247,7 @@ update_location_menu_items (void) locations = NULL; /* if there's at least one item being shown, show the separator too */ - dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, have_visible_location); + dbusmenu_menuitem_property_set_bool (self->locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, have_visible_location); } static void @@ -308,29 +313,27 @@ quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp G_GNUC_UNUSED, gpoint /* Updates the label in the date menuitem */ static gboolean -update_datetime (gpointer uused G_GNUC_UNUSED) +update_datetime (gpointer gself) { GDateTime *datetime; - gchar *utf8; + gchar * utf8; + IndicatorDatetimeService * self = gself; g_debug("Updating Date/Time"); - datetime = g_date_time_new_now_local (); + datetime = g_date_time_new_now_local (); /* FIXME: unmockable */ if (datetime == NULL) { g_warning("Error getting local time"); - dbusmenu_menuitem_property_set(date, DBUSMENU_MENUITEM_PROP_LABEL, _("Error getting time")); - g_date_time_unref (datetime); + dbusmenu_menuitem_property_set(self->date, DBUSMENU_MENUITEM_PROP_LABEL, _("Error getting time")); return FALSE; } /* eranslators: strftime(3) style date format on top of the menu when you click on the clock */ utf8 = g_date_time_format (datetime, _("%A, %e %B %Y")); - - dbusmenu_menuitem_property_set(date, DBUSMENU_MENUITEM_PROP_LABEL, utf8); - - g_date_time_unref (datetime); + dbusmenu_menuitem_property_set (self->date, DBUSMENU_MENUITEM_PROP_LABEL, utf8); g_free(utf8); + g_date_time_unref (datetime); return G_SOURCE_REMOVE; } @@ -357,147 +360,166 @@ activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, } static gboolean -update_appointment_menu_items_idle (gpointer unused G_GNUC_UNUSED) +update_appointment_menu_items_idle (gpointer gself) { - update_appointment_menu_items (); + update_appointment_menu_items (gself); + return G_SOURCE_REMOVE; } static void -update_appointment_menu_items_soon (void) +update_appointment_menu_items_soon (IndicatorDatetimeService * self) { - g_idle_add (update_appointment_menu_items_idle, NULL); + g_idle_add (update_appointment_menu_items_idle, self); } static void -hide_all_appointments (void) +hide_all_appointments (IndicatorDatetimeService * self) { - int i; + int i; - for (i=0; iappointment_menuitems[i]) + { + dbusmenu_menuitem_property_set_bool (self->appointment_menuitems[i], DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + dbusmenu_menuitem_property_set_bool (self->appointment_menuitems[i], DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + } + } } static gboolean month_changed_cb (DbusmenuMenuitem * menuitem, gchar * name G_GNUC_UNUSED, GVariant * variant, - guint timestamp G_GNUC_UNUSED) + guint timestamp G_GNUC_UNUSED, + gpointer gself) { - start_time_appointments = (time_t)g_variant_get_uint32(variant); + IndicatorDatetimeService * self = gself; + + self->start_time_appointments = (time_t)g_variant_get_uint32(variant); - g_debug("Received month changed with timestamp: %d -> %s",(int)start_time_appointments, ctime(&start_time_appointments)); - /* By default one of the first things we do is - clear the marks as we don't know the correct - ones yet and we don't want to confuse the - user. */ - dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); - - update_appointment_menu_items_soon (); - return TRUE; + g_debug("Received month changed with timestamp: %d -> %s",(int)self->start_time_appointments, ctime(&self->start_time_appointments)); + + /* By default, one of the first things we do is + clear the marks as we don't know the correct + ones yet and we don't want to confuse the user. */ + + dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); + + update_appointment_menu_items_soon (self); + return TRUE; } static gboolean day_selected_cb (DbusmenuMenuitem * menuitem, gchar * name G_GNUC_UNUSED, GVariant * variant, - guint timestamp G_GNUC_UNUSED) + guint timestamp G_GNUC_UNUSED, + gpointer gself) { - time_t new_time = (time_t)g_variant_get_uint32(variant); - g_warn_if_fail(new_time != 0); + time_t new_time; + IndicatorDatetimeService * self = gself; - if (start_time_appointments == 0 || new_time == 0) { - /* If we've got nothing, assume everyhting is going to - get repopulated, let's start with a clean slate */ - dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); - } else { - /* No check to see if we changed months. If we did we'll - want to clear the marks. Otherwise we're cool keeping - them around. */ - struct tm start_tm; - struct tm new_tm; + new_time = (time_t)g_variant_get_uint32(variant); - localtime_r(&start_time_appointments, &start_tm); - localtime_r(&new_time, &new_tm); + if (self->start_time_appointments == 0 || new_time == 0) + { + /* If we've got nothing, assume everyhting is going to + get repopulated, let's start with a clean slate */ + dbusmenu_menuitem_property_remove (menuitem, CALENDAR_MENUITEM_PROP_MARKS); + } + else + { + /* No check to see if we changed months. If we did we'll + want to clear the marks. Otherwise we're cool keeping + them around. */ + struct tm start_tm; + struct tm new_tm; - if (start_tm.tm_mon != new_tm.tm_mon) { - dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); - } - } + localtime_r (&self->start_time_appointments, &start_tm); + localtime_r (&new_time, &new_tm); - start_time_appointments = new_time; + if (start_tm.tm_mon != new_tm.tm_mon) + dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); + } + + self->start_time_appointments = new_time; - g_debug("Received day-selected with timestamp: %d -> %s",(int)start_time_appointments, ctime(&start_time_appointments)); - update_appointment_menu_items_soon (); + g_debug ("Received day-selected with timestamp: %d -> %s",(int)self->start_time_appointments, ctime(&self->start_time_appointments)); + update_appointment_menu_items_soon (self); - return TRUE; + return TRUE; } static gboolean day_selected_double_click_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, gchar * name G_GNUC_UNUSED, GVariant * variant, - guint timestamp G_GNUC_UNUSED) + guint timestamp G_GNUC_UNUSED, + gpointer gself) { - const time_t evotime = (time_t) g_variant_get_uint32 (variant); - GDateTime * dt = g_date_time_new_from_unix_utc (evotime); - indicator_datetime_planner_activate_time (planner, dt); + time_t evotime; + GDateTime * dt; + IndicatorDatetimeService * self = gself; + + evotime = (time_t) g_variant_get_uint32 (variant); + dt = g_date_time_new_from_unix_utc (evotime); + indicator_datetime_planner_activate_time (self->planner, dt); g_date_time_unref (dt); + return TRUE; } -static guint ecaltimer = 0; static gboolean -update_appointment_menu_items_timerfunc (gpointer user_data G_GNUC_UNUSED) +update_appointment_menu_items_timerfunc (gpointer self) { - update_appointment_menu_items (); + update_appointment_menu_items (self); return G_SOURCE_CONTINUE; } static void -start_ecal_timer(void) +start_ecal_timer (IndicatorDatetimeService * self) { - if (ecaltimer != 0) - ecaltimer = g_timeout_add_seconds (60*5, update_appointment_menu_items_timerfunc, NULL); + if (self->ecaltimer != 0) + self->ecaltimer = g_timeout_add_seconds (60*5, update_appointment_menu_items_timerfunc, self); } static void -stop_ecal_timer(void) +stop_ecal_timer (IndicatorDatetimeService * self) { - if (ecaltimer != 0) + if (self->ecaltimer != 0) { - g_source_remove (ecaltimer); - ecaltimer = 0; + g_source_remove (self->ecaltimer); + self->ecaltimer = 0; } } + static gboolean -idle_start_ecal_timer (gpointer unused G_GNUC_UNUSED) +idle_start_ecal_timer (gpointer gself) { - start_ecal_timer(); + start_ecal_timer (gself); + return G_SOURCE_REMOVE; } static void -show_events_changed (void) +show_events_changed (IndicatorDatetimeService * self) { - const gboolean b = g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S); + const gboolean b = g_settings_get_boolean (self->conf, SETTINGS_SHOW_EVENTS_S); - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, b); + dbusmenu_menuitem_property_set_bool (self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, b); + dbusmenu_menuitem_property_set_bool (self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, b); if (b) { - start_ecal_timer(); + start_ecal_timer (self); } else { - hide_all_appointments (); - stop_ecal_timer(); + hide_all_appointments (self); + stop_ecal_timer (self); } } @@ -505,23 +527,26 @@ show_events_changed (void) /* Looks for the calendar application and enables the item if we have one, starts ecal timer if events are turned on */ static gboolean -check_for_calendar (gpointer unused G_GNUC_UNUSED) +check_for_calendar (gpointer gself) { - g_return_val_if_fail (calendar != NULL, FALSE); + gboolean b; + IndicatorDatetimeService * self = gself; + + g_return_val_if_fail (self->calendar != NULL, FALSE); - dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); + dbusmenu_menuitem_property_set_bool(self->date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - if (!get_greeter_mode () && indicator_datetime_planner_is_configured(planner)) { + if (!get_greeter_mode () && indicator_datetime_planner_is_configured(self->planner)) { int i; int pos = 2; - g_signal_connect (G_OBJECT(date), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + g_signal_connect (G_OBJECT(self->date), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_cb), "evolution -c calendar"); - events_separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(events_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_add_position(root, events_separator, pos++); + self->events_separator = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(self->events_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); + dbusmenu_menuitem_child_add_position(root, self->events_separator, pos++); for (i=0; iappointment_menuitems[i] = item; dbusmenu_menuitem_child_add_position(root, item, pos++); } - add_appointment = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Event…")); - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - g_signal_connect(G_OBJECT(add_appointment), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), "evolution -c calendar"); - dbusmenu_menuitem_child_add_position (root, add_appointment, pos++); + self->add_appointment = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set (self->add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Event…")); + dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); + g_signal_connect(G_OBJECT(self->add_appointment), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), "evolution -c calendar"); + dbusmenu_menuitem_child_add_position (root, self->add_appointment, pos++); - if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) { - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - g_idle_add((GSourceFunc)idle_start_ecal_timer, NULL); + if (g_settings_get_boolean(self->conf, SETTINGS_SHOW_EVENTS_S)) { + dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); + dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); + g_idle_add((GSourceFunc)idle_start_ecal_timer, self); } else { - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - stop_ecal_timer(); + dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + stop_ecal_timer (self); } // Connect to calendar events - g_signal_connect(calendar, "event::month-changed", G_CALLBACK(month_changed_cb), NULL); - g_signal_connect(calendar, "event::day-selected", G_CALLBACK(day_selected_cb), NULL); - g_signal_connect(calendar, "event::day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), NULL); + g_signal_connect(self->calendar, "event::month-changed", G_CALLBACK(month_changed_cb), self); + g_signal_connect(self->calendar, "event::day-selected", G_CALLBACK(day_selected_cb), self); + g_signal_connect(self->calendar, "event::day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), self); } else { g_debug("Unable to find calendar app."); - if (add_appointment != NULL) - dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - if (events_separator != NULL) - dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + if (self->add_appointment != NULL) + dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + if (self->events_separator != NULL) + dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); } - if (g_settings_get_boolean(conf, SETTINGS_SHOW_CALENDAR_S)) { - dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } else { - dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } + b = g_settings_get_boolean (self->conf, SETTINGS_SHOW_CALENDAR_S); + dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_ENABLED, b); + dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, b); return FALSE; } @@ -592,7 +613,7 @@ create_color_icon_pixbuf (const char * color_spec) cairo_t * cr; GdkRGBA rgba; - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create (surface); if (gdk_rgba_parse (&rgba, color_spec)) @@ -615,16 +636,15 @@ create_color_icon_pixbuf (const char * color_spec) /** - * Populate the menu with todays, next MAX_APPOINTMENT_MENUITEMS appointments. + * Populate the menu with todays, next MAX_APPOINTMENT_MENUITEMS appointments. * we should hook into the ABOUT TO SHOW signal and use that to update the appointments. * Experience has shown that caldav's and webcals can be slow to load from eds - * this is a problem mainly on the EDS side of things, not ours. + * this is a problem mainly on the EDS side of things, not ours. */ static void -update_appointment_menu_items (void) +update_appointment_menu_items (IndicatorDatetimeService * self) { char * str; - int64_t t; GSList * l; GSList * appointments; gint i; @@ -640,29 +660,29 @@ update_appointment_menu_items (void) g_debug ("Update appointments called"); - if (calendar == NULL) + if (self->calendar == NULL) return; - if (!g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) + if (!g_settings_get_boolean (self->conf, SETTINGS_SHOW_EVENTS_S)) return; - if (updating_appointments) + if (self->updating_appointments) return; - updating_appointments = TRUE; + self->updating_appointments = TRUE; g_variant_builder_init (&markeddays, G_VARIANT_TYPE ("ai")); - t = start_time_appointments; - if (!t) - t = time (NULL); /* FIXME: not mockable */ - begin = g_date_time_new_from_unix_local (t); + if (self->start_time_appointments != 0) + begin = g_date_time_new_from_unix_local (self->start_time_appointments); + else + begin = g_date_time_new_now_local (); /* FIXME: unmockable */ end = g_date_time_add_months (begin, 1); - indicator_datetime_planner_set_timezone (planner, indicator_datetime_timezone_get_timezone (tz_file)); - appointments = indicator_datetime_planner_get_appointments (planner, begin, end); + indicator_datetime_planner_set_timezone (self->planner, indicator_datetime_timezone_get_timezone (self->tz_file)); + appointments = indicator_datetime_planner_get_appointments (self->planner, begin, end); - hide_all_appointments (); + hide_all_appointments (self); /* decide whether to use 12hr or 24hr format */ - str = g_settings_get_string (conf, SETTINGS_TIME_FORMAT_S); + str = g_settings_get_string (self->conf, SETTINGS_TIME_FORMAT_S); if (g_strcmp0 (str, "12-hour") == 0) apt_output = SETTINGS_TIME_12_HOUR; else if (g_strcmp0 (str, "24-hour") == 0) @@ -690,7 +710,7 @@ update_appointment_menu_items (void) if (i >= MAX_APPOINTMENT_MENUITEMS) continue; - item = appointment_menuitems[i]; + item = self->appointment_menuitems[i]; i++; /* remove the icon as we might not replace it on error */ @@ -765,11 +785,11 @@ update_appointment_menu_items (void) marks = g_variant_builder_end (&markeddays); - dbusmenu_menuitem_property_set_variant (calendar, CALENDAR_MENUITEM_PROP_MARKS, marks); + dbusmenu_menuitem_property_set_variant (self->calendar, CALENDAR_MENUITEM_PROP_MARKS, marks); g_slist_free_full (appointments, (GDestroyNotify)indicator_datetime_appt_free); - updating_appointments = FALSE; + self->updating_appointments = FALSE; g_date_time_unref (end); g_date_time_unref (begin); } @@ -777,156 +797,168 @@ update_appointment_menu_items (void) /* Looks for the time and date admin application and enables the item we have one */ static gboolean -check_for_timeadmin (gpointer unused G_GNUC_UNUSED) +check_for_timeadmin (gpointer gself) { - g_return_val_if_fail (settings != NULL, FALSE); + gchar * timeadmin; + IndicatorDatetimeService * self = gself; - gchar * timeadmin = g_find_program_in_path("gnome-control-center"); - if (timeadmin != NULL) { - g_debug("Found the gnome-control-center application: %s", timeadmin); - dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - g_free(timeadmin); - } else { - g_debug("Unable to find gnome-control-center app."); - dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - } + g_return_val_if_fail (self->settings != NULL, FALSE); - return G_SOURCE_REMOVE; + timeadmin = g_find_program_in_path ("gnome-control-center"); + if (timeadmin != NULL) + { + g_debug ("Found the gnome-control-center application: %s", timeadmin); + dbusmenu_menuitem_property_set_bool (self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); + g_free(timeadmin); + } + else + { + g_debug ("Unable to find gnome-control-center app."); + dbusmenu_menuitem_property_set_bool (self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + } + + return G_SOURCE_REMOVE; } /* Does the work to build the default menu, really calls out to other functions but this is the core to clean up the main function. */ static void -build_menus (DbusmenuMenuitem * root) +build_menus (IndicatorDatetimeService * self, DbusmenuMenuitem * root) { g_debug("Building Menus."); - if (date == NULL) { - date = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (date, DBUSMENU_MENUITEM_PROP_LABEL, _("No date yet…")); - dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - dbusmenu_menuitem_child_append(root, date); + if (self->date == NULL) { + self->date = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set (self->date, DBUSMENU_MENUITEM_PROP_LABEL, _("No date yet…")); + dbusmenu_menuitem_property_set_bool(self->date, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + dbusmenu_menuitem_child_append(root, self->date); - g_idle_add(update_datetime, NULL); + g_idle_add(update_datetime, self); } - if (calendar == NULL) { - calendar = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (calendar, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CALENDAR_MENUITEM_TYPE); + if (self->calendar == NULL) { + self->calendar = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set (self->calendar, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CALENDAR_MENUITEM_TYPE); /* insensitive until we check for available apps */ - dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - g_signal_connect (G_OBJECT(calendar), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + g_signal_connect (G_OBJECT(self->calendar), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_cb), "evolution -c calendar"); - dbusmenu_menuitem_child_append(root, calendar); + dbusmenu_menuitem_child_append(root, self->calendar); - g_idle_add(check_for_calendar, NULL); + g_idle_add(check_for_calendar, self); } if (!get_greeter_mode ()) { - locations_separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(locations_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_append(root, locations_separator); + self->locations_separator = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(self->locations_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); + dbusmenu_menuitem_property_set_bool (self->locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + dbusmenu_menuitem_child_append(root, self->locations_separator); - update_location_menu_items(); + update_location_menu_items (self); - g_signal_connect (conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), NULL); - g_signal_connect_swapped (conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (update_location_menu_items), NULL); - g_signal_connect_swapped (conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (update_location_menu_items), NULL); - g_signal_connect_swapped (conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (update_location_menu_items), NULL); - g_signal_connect_swapped (conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (update_appointment_menu_items), NULL); + g_signal_connect (self->conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), self); + g_signal_connect_swapped (self->conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (update_location_menu_items), self); + g_signal_connect_swapped (self->conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (update_location_menu_items), self); + g_signal_connect_swapped (self->conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (update_location_menu_items), self); + g_signal_connect_swapped (self->conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (update_appointment_menu_items), self); DbusmenuMenuitem * separator = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); dbusmenu_menuitem_child_append(root, separator); - settings = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (settings, DBUSMENU_MENUITEM_PROP_LABEL, _("Time & Date Settings…")); + self->settings = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set (self->settings, DBUSMENU_MENUITEM_PROP_LABEL, _("Time & Date Settings…")); /* insensitive until we check for available apps */ - dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - g_signal_connect(G_OBJECT(settings), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), SETTINGS_APP_INVOCATION); - dbusmenu_menuitem_child_append(root, settings); - g_idle_add(check_for_timeadmin, NULL); - update_appointment_menu_items_soon (); + dbusmenu_menuitem_property_set_bool(self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + g_signal_connect(G_OBJECT(self->settings), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), SETTINGS_APP_INVOCATION); + dbusmenu_menuitem_child_append(root, self->settings); + g_idle_add(check_for_timeadmin, self); + update_appointment_menu_items_soon (self); } return; } static void -on_clock_skew (void) +on_clock_skew (gpointer self) { - /* tell the indicators to refresh */ - if (IS_DATETIME_INTERFACE (dbus)) - datetime_interface_update (DATETIME_INTERFACE(dbus)); - - /* update our day label */ - update_datetime (NULL); - day_timer_reset(); + /* tell the indicators to refresh */ + if (IS_DATETIME_INTERFACE (dbus)) + datetime_interface_update (DATETIME_INTERFACE(dbus)); - return; + /* update our day label */ + update_datetime (self); + day_timer_reset (self); } static void -on_timezone_changed (void) +on_timezone_changed (gpointer self) { - update_location_menu_items (); + update_location_menu_items (self); - on_clock_skew (); + on_clock_skew (self); } - -/* Source ID for the timer */ -static guint day_timer = 0; - /* Execute at a given time, update and setup a new timer to go again. */ static gboolean -day_timer_func (gpointer unused G_GNUC_UNUSED) +day_timer_func (gpointer self) { - day_timer = 0; - /* Reset up each time to reduce error */ - day_timer_reset(); - update_datetime(NULL); - return G_SOURCE_REMOVE; + day_timer_reset (self); + update_datetime (self); + + return G_SOURCE_REMOVE; } /* Sets up the time to launch the timer to update the date in the datetime entry */ static void -day_timer_reset (void) +day_timer_reset (IndicatorDatetimeService * self) { - if (day_timer != 0) { - g_source_remove(day_timer); - day_timer = 0; - } - - time_t t; - t = time(NULL); - struct tm * ltime = localtime(&t); + GDateTime * now; + GDateTime * tomorrow; + GDateTime * new_day; + guint seconds_until_tomorrow; - day_timer = g_timeout_add_seconds(((23 - ltime->tm_hour) * 60 * 60) + - ((59 - ltime->tm_min) * 60) + - ((60 - ltime->tm_sec)) + 60 /* one minute past */, - day_timer_func, NULL); + if (self->day_timer != 0) + { + g_source_remove (self->day_timer); + self->day_timer = 0; + } - return; + now = g_date_time_new_now_local (); + tomorrow = g_date_time_add_days (now, 1); + new_day = g_date_time_new_local (g_date_time_get_year (tomorrow), + g_date_time_get_month (tomorrow), + g_date_time_get_day_of_month (tomorrow), + 0, 0, 0); + seconds_until_tomorrow = (guint)(g_date_time_difference (new_day, now) / G_TIME_SPAN_SECOND); +g_message ("seconds until tomorrow is %u", seconds_until_tomorrow); + + self->day_timer = g_timeout_add_seconds (seconds_until_tomorrow + 15, + day_timer_func, + self); + + g_date_time_unref (new_day); + g_date_time_unref (tomorrow); + g_date_time_unref (now); } static gboolean -skew_check_timer_func (gpointer unused G_GNUC_UNUSED) +skew_check_timer_func (gpointer self) { - static time_t prev_time = 0; - const time_t cur_time = time (NULL); /* FIXME: not mockable */ - const double diff_sec = fabs (difftime (cur_time, prev_time)); + static time_t prev_time = 0; + const time_t cur_time = time (NULL); /* FIXME: unmockable */ + const double diff_sec = fabs (difftime (cur_time, prev_time)); - if (prev_time && (diff_sec > SKEW_DIFF_THRESHOLD_SEC)) { - g_debug (G_STRLOC" clock skew detected (%.0f seconds)", diff_sec); - on_clock_skew (); - } + if (prev_time && (diff_sec > SKEW_DIFF_THRESHOLD_SEC)) + { + g_debug (G_STRLOC" clock skew detected (%.0f seconds)", diff_sec); + on_clock_skew (self); + } - prev_time = cur_time; - return G_SOURCE_CONTINUE; + prev_time = cur_time; + return G_SOURCE_CONTINUE; } static void @@ -934,24 +966,28 @@ session_active_change_cb (GDBusProxy * proxy G_GNUC_UNUSED, gchar * sender_name G_GNUC_UNUSED, gchar * signal_name, GVariant * parameters, - gpointer user_data G_GNUC_UNUSED) + gpointer gself) { - // Suspending / returning from suspend (true / false) - if (g_strcmp0(signal_name, "PrepareForSleep") == 0) { - gboolean sleeping = FALSE; - g_variant_get (parameters, "(b)", &sleeping); - if (!sleeping) { - g_debug ("System has been resumed; adjusting clock"); - on_clock_skew (); - } - } + IndicatorDatetimeService * self = gself; + + /* suspending / returning from suspend (true / false) */ + if (g_strcmp0(signal_name, "PrepareForSleep") == 0) + { + gboolean sleeping = FALSE; + g_variant_get (parameters, "(b)", &sleeping); + if (!sleeping) + { + g_debug ("System has been resumed; adjusting clock"); + on_clock_skew (self); + } + } } /* for hooking into console kit signal on wake from suspend */ static void system_proxy_cb (GObject * object G_GNUC_UNUSED, GAsyncResult * res, - gpointer user_data) + gpointer gself) { GError * error = NULL; @@ -963,7 +999,7 @@ system_proxy_cb (GObject * object G_GNUC_UNUSED, return; } - g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), user_data); + g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), gself); } /**** @@ -980,33 +1016,33 @@ get_greeter_mode (void) /* Repsonds to the service object saying it's time to shutdown. It stops the mainloop. */ -static void +static void service_shutdown (IndicatorService * service G_GNUC_UNUSED, - gpointer user_data G_GNUC_UNUSED) + gpointer gmainloop) { - g_warning("Shutting down service!"); - g_main_loop_quit(mainloop); - return; + g_warning ("Shutting down service!"); + g_main_loop_quit (gmainloop); } static void -on_use_geoclue_changed_cb (GSettings *settings, - gchar *key G_GNUC_UNUSED, - gpointer user_data G_GNUC_UNUSED) +on_use_geoclue_changed_cb (GSettings * settings, + gchar * key G_GNUC_UNUSED, + gpointer gself) { + IndicatorDatetimeService * self = gself; const gboolean use_geoclue = g_settings_get_boolean (settings, "show-auto-detected-location"); - if (geo_location && !use_geoclue) + if (self->geo_location && !use_geoclue) { - g_signal_handlers_disconnect_by_func (geo_location, update_location_menu_items, 0); - g_clear_object (&geo_location); - update_location_menu_items (); + g_signal_handlers_disconnect_by_func (self->geo_location, update_location_menu_items, self); + g_clear_object (&self->geo_location); + update_location_menu_items (self); } - else if (use_geoclue && !geo_location) + else if (use_geoclue && !self->geo_location) { - geo_location = indicator_datetime_timezone_geoclue_new (); - g_signal_connect (geo_location, "notify::timezone", - G_CALLBACK(update_location_menu_items), NULL); + self->geo_location = indicator_datetime_timezone_geoclue_new (); + g_signal_connect_swapped (self->geo_location, "notify::timezone", + G_CALLBACK(update_location_menu_items), self); } } @@ -1014,79 +1050,89 @@ on_use_geoclue_changed_cb (GSettings *settings, int main (int argc, char ** argv) { - gtk_init (&argc, &argv); - - /* Acknowledging the service init and setting up the interface */ - service = indicator_service_new_version(SERVICE_NAME, SERVICE_VERSION); - g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); - - /* Setting up i18n and gettext. Apparently, we need - all of these. */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - /* Set up GSettings */ - conf = g_settings_new(SETTINGS_INTERFACE); - g_signal_connect (conf, "changed::show-auto-detected-location", - G_CALLBACK(on_use_geoclue_changed_cb), NULL); - // TODO Add a signal handler to catch other gsettings changes and respond to them - - /* Build our list of appointment calendar sources. - When a source changes, update our menu items. - When sources are added or removed, update our list and menu items. */ - planner = indicator_datetime_planner_eds_new (); - g_signal_connect (planner, "appointments-changed", - G_CALLBACK(update_appointment_menu_items_soon), NULL); - - /* Building the base menu */ - server = dbusmenu_server_new(MENU_OBJ); - root = dbusmenu_menuitem_new(); - dbusmenu_server_set_root(server, root); + GMainLoop * mainloop; + IndicatorService * service; + DbusmenuServer * server; + struct IndicatorDatetimeService self; + + memset (&self, 0, sizeof(struct IndicatorDatetimeService)); + + gtk_init (&argc, &argv); + mainloop = g_main_loop_new (NULL, FALSE); + + /* acknowledging the service init and setting up the interface */ + service = indicator_service_new_version(SERVICE_NAME, SERVICE_VERSION); + g_signal_connect (service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, + G_CALLBACK(service_shutdown), mainloop); + + /* setting up i18n and gettext. apparently we need all of these. */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + + /* set up GSettings */ + self.conf = g_settings_new (SETTINGS_INTERFACE); + g_signal_connect (self.conf, "changed::show-auto-detected-location", + G_CALLBACK(on_use_geoclue_changed_cb), &self); + + /* setup geoclue */ + on_use_geoclue_changed_cb (self.conf, NULL, &self); + + /* setup timezone watch */ + self.tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE); + g_signal_connect_swapped (self.tz_file, "notify::timezone", + G_CALLBACK(on_timezone_changed), &self); + + /* build our list of appointment calendar sources. + When a source changes, update our menu items. + When sources are added or removed, update our list and menu items. */ + self.planner = indicator_datetime_planner_eds_new (); + g_signal_connect_swapped (self.planner, "appointments-changed", + G_CALLBACK(update_appointment_menu_items_soon), &self); + + /* building the base menu */ + server = dbusmenu_server_new (MENU_OBJ); + root = dbusmenu_menuitem_new (); + dbusmenu_server_set_root (server, root); - build_menus(root); + build_menus (&self, root); - /* Cache the timezone */ - update_location_menu_items (); - - /* Setup geoclue */ - on_use_geoclue_changed_cb (conf, NULL, NULL); - - /* Setup dbus interface */ - dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL); - - /* Setup timezone watch */ - tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE); - g_signal_connect (tz_file, "notify::timezone", G_CALLBACK(on_timezone_changed), NULL); - - /* Set up the day timer */ - day_timer_reset(); - - /* Set up the skew-check timer */ - g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC, - skew_check_timer_func, - NULL); - - /* And watch for system resumes */ - 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", - NULL, system_proxy_cb, dbus); - - mainloop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(mainloop); - - g_object_unref (conf); - g_object_unref (dbus); - g_object_unref (service); - g_object_unref (server); - g_object_unref (root); - g_object_unref (planner); - g_object_unref (geo_location); - g_object_unref (tz_file); - - return 0; + /* cache the timezone */ + update_location_menu_items (&self); + + /* setup dbus interface */ + dbus = g_object_new (DATETIME_INTERFACE_TYPE, NULL); + + /* set up the day timer */ + day_timer_reset (&self); + + /* set up the skew-check timer */ + g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC, + skew_check_timer_func, + &self); + + /* and watch for system resumes */ + 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", + NULL, /* FIXME: cancellable */ + system_proxy_cb, + &self); + + g_main_loop_run (mainloop); + g_main_loop_unref (mainloop); + + g_object_unref (self.conf); + g_object_unref (dbus); + g_object_unref (service); + g_object_unref (server); + g_object_unref (root); + g_object_unref (self.planner); + g_object_unref (self.geo_location); + g_object_unref (self.tz_file); + + return 0; } -- cgit v1.2.3 From 358db84bca4f08f3f6822e0e4a62a8382366ad71 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 14 May 2013 12:44:51 -0500 Subject: document the service's actions and custom menuitems. This will also act as a workitem list for the upcoming GMenuification --- README | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/README b/README index e69de29..ebeed8b 100644 --- a/README +++ b/README @@ -0,0 +1,61 @@ +ACTIONS +======= + + * "aboutToShow" + Description: notifies the indicator that its menu is about to be shown. + Workitems that are expensive on startup (such as loading EDS to query + it for appointments, or loading GeoClue to guess our current location) + may be deferred until this is called. + So, calendarDay's state may not be fully populated until this is called. + FIXME: aboutToShow should be spec'ed once for all indicators, not ad-hoc + + * "activateSettings" + Description: opens a page for changing indicator-datetime's settings + State: NULL + Parameter: NULL + + * "activatePlanner" + Description: opens up the appointment editor. + State: NULL + Parameter: uint64 (a time_t hinting which day/time to show in the planner, + or 0 for the current day) + + * "calendarDay" + Description: set which month/day should be given focus in the indicator's + calendar and to use as a starting point when listing upcoming + appointments in the indicator. + Client code implementing the calendar widget should call this + when the user clicks to a different day, month, or year. + State: a dictionary containing these key value pairs: + "calendarDay": string ("YYYY-MM-DD"). Used by the calendar menuitem + to know which year/month should be visible and which + day should have the cursor. + "daysWithAppointments": an array of day-of-month ints. Used by the + calendar menuitem to mark appointment days. + Parameter: uint64 (a time_t specifying which day to give focus to) + + * "location" + Description: Sets the user's current location. Called by location menuitems. + State: string containing a location and a timezone + Parameter: string containing a location and a timezone + + +CUSTOM MENUITEMS +================ + + * Calendar + - x-canonical-type s "com.canonical.indicator.calendar" + + * Appointment + - x-canonical-type s "com.canonical.indicator.appointment" + - x-canonical-color s color of the appt's type, to give a visual cue + - label s short summary of the appointment + - x-canonical-right-label s the date of the appointment + + * Location + - x-canonical-type s "com.canonical.indicator.location" + - label s the location's name, eg "Oklahoma City" + - x-canonical-timezone s timezone that the location is in + - x-canonical-time-format s strftime format string + + -- cgit v1.2.3 From 01bd6c3f35cdef3d9dae9241e7519a59c6d8139d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 14 May 2013 13:18:04 -0500 Subject: add a service boilerplate similar to the one in indicator-session. This doesn't do anything yet; the sections will be added in subsequent commits --- src/Makefile.am | 7 +- src/main.c | 96 +++++++++ src/service.c | 638 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/service.h | 71 +++++++ 4 files changed, 810 insertions(+), 2 deletions(-) create mode 100644 src/main.c create mode 100644 src/service.c create mode 100644 src/service.h diff --git a/src/Makefile.am b/src/Makefile.am index 9350f86..fee2245 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,9 @@ indicator_datetime_service_SOURCES = \ planner.h \ planner-eds.c \ planner-eds.h \ - datetime-service.c \ + service.c \ + service.h \ + main.c \ timezone.c \ timezone.h \ timezone-file.c \ @@ -26,7 +28,8 @@ indicator_datetime_service_SOURCES = \ dbus-shared.h \ settings-shared.h indicator_datetime_service_CFLAGS = \ - -Wall -Wextra \ + -Wall \ + -Wextra -Wno-missing-field-initializers \ -Werror \ $(SERVICE_CFLAGS) \ $(COVERAGE_CFLAGS) \ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..162bb16 --- /dev/null +++ b/src/main.c @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "config.h" + +#include +#include /* exit() */ + +#include +#include + +#include "service.h" + +/*** +**** +***/ + +static gboolean replace = FALSE; + +static void +parse_command_line (int * argc, char *** argv) +{ + GError * error; + GOptionContext * option_context; + + static GOptionEntry entries[] = + { + { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the currently-running service", NULL }, + { NULL } + }; + + error = NULL; + option_context = g_option_context_new ("- indicator-datetime service"); + g_option_context_add_main_entries (option_context, entries, GETTEXT_PACKAGE); + if (!g_option_context_parse (option_context, argc, argv, &error)) + { + g_print ("option parsing failed: %s\n", error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + g_option_context_free (option_context); +} + +/*** +**** +***/ + +static void +on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) +{ + g_debug ("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit ((GMainLoop*)loop); +} + +int +main (int argc, char ** argv) +{ + GMainLoop * loop; + IndicatorDatetimeService * service; + + /* boilerplate i18n */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + + parse_command_line (&argc, &argv); + + /* run */ + service = indicator_datetime_service_new (replace); + 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); + + /* cleanup */ + g_clear_object (&service); + g_main_loop_unref (loop); + return 0; +} diff --git a/src/service.c b/src/service.c new file mode 100644 index 0000000..f5e16a0 --- /dev/null +++ b/src/service.c @@ -0,0 +1,638 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include + +#include +#include + +#include "service.h" + +/* FIXME: remove -test */ +#define BUS_NAME "com.canonical.indicator.datetime-test" +#define BUS_PATH "/com/canonical/indicator/datetime" + +G_DEFINE_TYPE (IndicatorDatetimeService, + indicator_datetime_service, + G_TYPE_OBJECT) + +/* signals enum */ +enum +{ + NAME_LOST, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum +{ + PROP_0, + PROP_REPLACE, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST]; + +enum +{ + SECTION_HEADER = (1<<0), + SECTION_CALENDAR = (1<<1), + SECTION_APPOINTMENTS = (1<<2), + SECTION_LOCATIONS = (1<<3), + SECTION_SETTINGS = (1<<4), +}; + +enum +{ + PROFILE_DESKTOP, + PROFILE_GREETER, + N_PROFILES +}; + +static const char * const menu_names[N_PROFILES] = +{ + "desktop", + "desktop_greeter" +}; + +struct ProfileMenuInfo +{ + /* the root level -- the header is the only child of this */ + GMenu * menu; + + /* parent of the sections. This is the header's submenu */ + GMenu * submenu; + + guint export_id; +}; + +struct _IndicatorDatetimeServicePrivate +{ + guint own_id; + GSimpleActionGroup * actions; + guint actions_export_id; + struct ProfileMenuInfo menus[N_PROFILES]; + guint rebuild_id; + int rebuild_flags; + GDBusConnection * conn; + GCancellable * cancellable; + GSimpleAction * header_action; + + gboolean replace; +}; + +typedef IndicatorDatetimeServicePrivate priv_t; + +/*** +**** +***/ + +static void rebuild_now (IndicatorDatetimeService * self, int section); +static void rebuild_soon (IndicatorDatetimeService * self, int section); + +static inline void +rebuild_header_soon (IndicatorDatetimeService * self) +{ + rebuild_soon (self, SECTION_HEADER); +} +static inline void +rebuild_calendar_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) +{ + rebuild_soon (self, SECTION_SETTINGS); +} + +/*** +**** +***/ + +static void +update_header_action (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; + + g_return_if_fail (p->header_action != NULL); + + v = g_variant_new ("(sssb)", label, iconstr, a11y, TRUE); + g_simple_action_set_state (p->header_action, v); + g_free (a11y); +} + +/*** +**** +***/ + +static GMenuModel * +create_calendar_section (IndicatorDatetimeService * self G_GNUC_UNUSED) +{ + GMenu * menu; + + menu = g_menu_new (); + + return G_MENU_MODEL (menu); +} + +static GMenuModel * +create_appointments_section (IndicatorDatetimeService * self G_GNUC_UNUSED) +{ + GMenu * menu; + + menu = g_menu_new (); + + return G_MENU_MODEL (menu); +} + +static GMenuModel * +create_locations_section (IndicatorDatetimeService * self G_GNUC_UNUSED) +{ + GMenu * menu; + + menu = g_menu_new (); + + return G_MENU_MODEL (menu); +} + +static GMenuModel * +create_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED) +{ + GMenu * menu; + + menu = g_menu_new (); + + g_menu_append (menu, _("Date and Time Settings\342\200\246"), "indicator.activateSettings"); + + return G_MENU_MODEL (menu); +} + +static void +create_menu (IndicatorDatetimeService * self, int profile) +{ + GMenu * menu; + GMenu * submenu; + GMenuItem * header; + GMenuModel * sections[16]; + int i; + int n = 0; + + g_assert (0<=profile && profilepriv->menus[profile].menu == NULL); + + if (profile == PROFILE_DESKTOP) + { + sections[n++] = create_calendar_section (self); + sections[n++] = create_appointments_section (self); + sections[n++] = create_locations_section (self); + sections[n++] = create_settings_section (self); + } + else if (profile == PROFILE_GREETER) + { + /* FIXME: what goes here? */ + } + + /* add sections to the submenu */ + submenu = g_menu_new (); + for (i=0; ipriv->menus[profile].menu = menu; + self->priv->menus[profile].submenu = submenu; +} + +/*** +**** GActions +***/ + +static void +on_settings_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself G_GNUC_UNUSED) +{ + g_message ("settings activated"); +} + +static void +init_gactions (IndicatorDatetimeService * self) +{ + GVariant * v; + GSimpleAction * a; + priv_t * p = self->priv; + + GActionEntry entries[] = { + { "activateSettings", on_settings_activated, NULL, NULL, NULL }, + }; + + p->actions = g_simple_action_group_new (); + + g_action_map_add_action_entries (G_ACTION_MAP(p->actions), + entries, + G_N_ELEMENTS(entries), + self); + + /* add the header action */ + v = g_variant_new ("(sssb)", "Hello World", "icon", "a11y", TRUE); + a = g_simple_action_new_stateful ("_header", NULL, v); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->header_action = a; + + rebuild_now (self, SECTION_HEADER); +} + +/*** +**** +***/ + +/** + * A small helper function for rebuild_now(). + * - removes the previous section + * - adds and unrefs the new section + */ +static void +rebuild_section (GMenu * parent, int pos, GMenuModel * new_section) +{ + g_menu_remove (parent, pos); + g_menu_insert_section (parent, pos, NULL, new_section); + g_object_unref (new_section); +} + +static void +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]; + + if (sections & SECTION_HEADER) + { + update_header_action (self); + } + + if (sections & SECTION_CALENDAR) + { + rebuild_section (desktop->submenu, 0, create_calendar_section (self)); + } + + if (sections & SECTION_APPOINTMENTS) + { + rebuild_section (desktop->submenu, 1, create_appointments_section (self)); + } + + if (sections & SECTION_LOCATIONS) + { + rebuild_section (desktop->submenu, 2, create_locations_section (self)); + } + + if (sections & SECTION_SETTINGS) + { + rebuild_section (desktop->submenu, 3, create_settings_section (self)); + //rebuild_section (greeter->submenu, 0, create_datetime_section(self)); + } +} + +static int +rebuild_timeout_func (IndicatorDatetimeService * self) +{ + priv_t * p = self->priv; + rebuild_now (self, p->rebuild_flags); + p->rebuild_flags = 0; + p->rebuild_id = 0; + return G_SOURCE_REMOVE; +} + +static void +rebuild_soon (IndicatorDatetimeService * self, int section) +{ + priv_t * p = self->priv; + + p->rebuild_flags |= section; + + if (p->rebuild_id == 0) + { + /* Change events seem to come over the bus in small bursts. This msec + value is an arbitrary number that tries to be large enough to fold + multiple events into a single rebuild, but small enough that the + user won't notice any lag. */ + static const int REBUILD_INTERVAL_MSEC = 500; + + p->rebuild_id = g_timeout_add (REBUILD_INTERVAL_MSEC, + (GSourceFunc)rebuild_timeout_func, + self); + } +} + +/*** +**** GDBus +***/ + +static void +on_bus_acquired (GDBusConnection * connection, + const gchar * name, + gpointer gself) +{ + int i; + guint id; + GError * err = NULL; + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(gself); + priv_t * p = self->priv; + + g_debug ("bus acquired: %s", name); + + p->conn = g_object_ref (G_OBJECT (connection)); + + /* export the actions */ + if ((id = g_dbus_connection_export_action_group (connection, + BUS_PATH, + G_ACTION_GROUP (p->actions), + &err))) + { + p->actions_export_id = id; + } + else + { + g_warning ("cannot export action group: %s", err->message); + g_clear_error (&err); + } + + /* export the menus */ + for (i=0; imenus[i]; + + if (menu->menu == NULL) + create_menu (self, i); + + if ((id = g_dbus_connection_export_menu_model (connection, + path, + G_MENU_MODEL (menu->menu), + &err))) + { + menu->export_id = id; + } + else + { + g_warning ("cannot export %s menu: %s", menu_names[i], err->message); + g_clear_error (&err); + } + + g_free (path); + } +} + +static void +unexport (IndicatorDatetimeService * self) +{ + int i; + priv_t * p = self->priv; + + /* unexport the menus */ + for (i=0; ipriv->menus[i].export_id; + + if (*id) + { + g_dbus_connection_unexport_menu_model (p->conn, *id); + *id = 0; + } + } + + /* unexport the actions */ + if (p->actions_export_id) + { + g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id); + p->actions_export_id = 0; + } +} + +static void +on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, + const gchar * name, + gpointer gself) +{ + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); + + g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name); + + unexport (self); + + g_signal_emit (self, signals[NAME_LOST], 0, NULL); +} + +/*** +**** GObject virtual functions +***/ + +static void +my_constructed (GObject * o) +{ + GBusNameOwnerFlags owner_flags; + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o); + + /* 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) + 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); +} + +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_REPLACE: + g_value_set_boolean (value, self->priv->replace); + 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_REPLACE: + self->priv->replace = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * o) +{ + int i; + IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o); + priv_t * p = self->priv; + + if (p->own_id) + { + g_bus_unown_name (p->own_id); + p->own_id = 0; + } + + unexport (self); + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + if (p->rebuild_id) + { + g_source_remove (p->rebuild_id); + p->rebuild_id = 0; + } + + g_clear_object (&p->actions); + + for (i=0; imenus[i].menu); + + g_clear_object (&p->header_action); + g_clear_object (&p->conn); + + G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_datetime_service_init (IndicatorDatetimeService * self) +{ + priv_t * p; + + /* 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 (); + + init_gactions (self); +} + +static void +indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = my_dispose; + 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); + + properties[PROP_0] = NULL; + + properties[PROP_REPLACE] = g_param_spec_boolean ("replace", + "Replace Service", + "Replace existing service", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +/*** +**** Public API +***/ + +IndicatorDatetimeService * +indicator_datetime_service_new (gboolean replace) +{ + GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, + "replace", replace, + NULL); + + return INDICATOR_DATETIME_SERVICE (o); +} diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..594d7fe --- /dev/null +++ b/src/service.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifndef __INDICATOR_DATETIME_SERVICE_H__ +#define __INDICATOR_DATETIME_SERVICE_H__ + +#include +#include + +G_BEGIN_DECLS + +/* standard GObject macros */ +#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type()) +#define INDICATOR_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeService)) +#define INDICATOR_DATETIME_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeServiceClass)) +#define INDICATOR_DATETIME_SERVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeServiceClass)) +#define INDICATOR_IS_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_SERVICE)) + +typedef struct _IndicatorDatetimeService IndicatorDatetimeService; +typedef struct _IndicatorDatetimeServiceClass IndicatorDatetimeServiceClass; +typedef struct _IndicatorDatetimeServicePrivate IndicatorDatetimeServicePrivate; + +/* signal keys */ +#define INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST "name-lost" + +/** + * The Indicator Datetime Service. + */ +struct _IndicatorDatetimeService +{ + /*< private >*/ + GObject parent; + IndicatorDatetimeServicePrivate * priv; +}; + +struct _IndicatorDatetimeServiceClass +{ + GObjectClass parent_class; + + /* signals */ + + void (* name_lost)(IndicatorDatetimeService * self); +}; + +/*** +**** +***/ + +GType indicator_datetime_service_get_type (void); + +IndicatorDatetimeService * indicator_datetime_service_new (gboolean replace); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_SERVICE_H__ */ -- cgit v1.2.3 From eee67421e45b110332463f2e4193211f73930e32 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 13:46:05 -0500 Subject: in utils.c, remove unused function read_timezone() --- src/utils.c | 24 ------------------------ src/utils.h | 1 - 2 files changed, 25 deletions(-) diff --git a/src/utils.c b/src/utils.c index f5aecd6..07a2022 100644 --- a/src/utils.c +++ b/src/utils.c @@ -117,30 +117,6 @@ get_current_zone_name (const gchar * location) return rv; } -gchar * -read_timezone () -{ - GError * error = NULL; - gchar * tempzone = NULL; - if (!g_file_get_contents(TIMEZONE_FILE, &tempzone, NULL, &error)) { - g_warning("Unable to read timezone file '" TIMEZONE_FILE "': %s", error->message); - g_error_free(error); - return NULL; - } - - /* This shouldn't happen, so let's make it a big boom! */ - g_return_val_if_fail(tempzone != NULL, NULL); - - /* Note: this really makes sense as strstrip works in place - so we end up with something a little odd without the dup - so we have the dup to make sure everything is as expected - for everyone else. */ - gchar * rv = g_strdup(g_strstrip(tempzone)); - g_free(tempzone); - - return rv; -} - /* Translate msg according to the locale specified by LC_TIME */ static char * T_(const char *msg) diff --git a/src/utils.h b/src/utils.h index 788d516..c2bc0c5 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,7 +30,6 @@ G_BEGIN_DECLS gboolean is_locale_12h (void); void split_settings_location (const gchar * location, gchar ** zone, gchar ** name); gchar * get_current_zone_name (const gchar * location); -gchar * read_timezone (); gchar * generate_format_string_full (gboolean show_day, gboolean show_date); gchar * generate_format_string_at_time (GDateTime * time); -- cgit v1.2.3 From b7a6a66b79961e2b0103c34cf4f4b2e07843e648 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 15:57:34 -0500 Subject: remove gdk include; indicator-datetime-service no longer uses gdk --- src/planner.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/planner.h b/src/planner.h index 45a6d3c..7499b75 100644 --- a/src/planner.h +++ b/src/planner.h @@ -22,7 +22,6 @@ #include #include /* parent class */ -#include /* GdkRGBA */ G_BEGIN_DECLS -- cgit v1.2.3 From bc63a95ba6cef880f17a77011042a38a4ea22e76 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 15:58:11 -0500 Subject: when we can't own the name, print a g_message saying so before exiting --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 162bb16..aa29456 100644 --- a/src/main.c +++ b/src/main.c @@ -65,7 +65,7 @@ parse_command_line (int * argc, char *** argv) static void on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) { - g_debug ("exiting: service couldn't acquire or lost ownership of busname"); + g_message ("exiting: service couldn't acquire or lost ownership of busname"); g_main_loop_quit ((GMainLoop*)loop); } -- cgit v1.2.3 From c0a3ba1cb8c6ebf8b353a821e5fe728ac885e0a1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 15:58:36 -0500 Subject: fix invocation of evolution's appointment editor --- src/planner-eds.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/planner-eds.c b/src/planner-eds.c index 6677b32..8fdc50b 100644 --- a/src/planner-eds.c +++ b/src/planner-eds.c @@ -228,15 +228,15 @@ my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, { gchar * isodate; gchar * command; - GError * error; + GError * err; - isodate = g_date_time_format (activate_time, "%F"); - command = g_strconcat ("evolution calendar:///?startdate=", isodate, NULL); - error = 0; - if (!g_spawn_command_line_async (command, &error)) + isodate = g_date_time_format (activate_time, "%Y%m%d"); + command = g_strdup_printf ("evolution \"calendar:///?startdate=%s\"", isodate); + err = 0; + if (!g_spawn_command_line_async (command, &err)) { - g_warning ("Unable to start %s: %s", command, error->message); - g_error_free (error); + g_warning ("Unable to start %s: %s", command, err->message); + g_error_free (err); } g_free (command); -- cgit v1.2.3 From 02de1c63c25e9b978f32ee1a1f1969f48ed62c18 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 15:59:13 -0500 Subject: in geoclue's async callback functions, the caller owns the GErrors, we shouldn't free them. --- src/timezone-geoclue.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c index 08f272d..239ac50 100644 --- a/src/timezone-geoclue.c +++ b/src/timezone-geoclue.c @@ -68,7 +68,6 @@ on_address_changed (GeoclueAddress * address G_GNUC_UNUSED, if (error != NULL) { g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); } else { @@ -87,7 +86,6 @@ on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED, if (error != NULL) { g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); } else { @@ -109,7 +107,6 @@ on_requirements_set (GeoclueMasterClient * master G_GNUC_UNUSED, if (error != NULL) { g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); } } @@ -125,7 +122,6 @@ on_client_created (GeoclueMaster * master G_GNUC_UNUSED, if (error != NULL) { g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - g_error_free (error); } else { -- cgit v1.2.3 From 8002a463e5fcd9d0b65ca2a445addf37cb117e32 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 15:59:58 -0500 Subject: minor copyediting --- src/timezone.c | 1 - src/timezone.h | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/timezone.c b/src/timezone.c index 546a3e3..a543155 100644 --- a/src/timezone.c +++ b/src/timezone.c @@ -103,4 +103,3 @@ indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone * self) g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); } - diff --git a/src/timezone.h b/src/timezone.h index cadeb6f..076bedc 100644 --- a/src/timezone.h +++ b/src/timezone.h @@ -41,9 +41,8 @@ GType indicator_datetime_timezone_get_type (void); /** * Abstract Base Class for objects that provide a timezone. * - * This is used in datetime to determine the user's current timezone - * so that it can be displayed more prominently in the locations - * section of the indicator's menu. + * We use this in datetime to determine the user's current timezone + * for display in the 'locations' section of the datetime indicator. * * This class has a 'timezone' property that clients can watch * for change notifications. -- cgit v1.2.3 From e392dd10a5dde1a0333fdeb963ecb1efecc8b19a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:01:43 -0500 Subject: remove the old indicator & service code --- src/datetime-interface.c | 200 ------ src/datetime-interface.h | 57 -- src/datetime-service.c | 1138 ---------------------------------- src/datetime-service.xml | 11 - src/indicator-datetime.c | 1535 ---------------------------------------------- 5 files changed, 2941 deletions(-) delete mode 100644 src/datetime-interface.c delete mode 100644 src/datetime-interface.h delete mode 100644 src/datetime-service.c delete mode 100644 src/datetime-service.xml delete mode 100644 src/indicator-datetime.c diff --git a/src/datetime-interface.c b/src/datetime-interface.c deleted file mode 100644 index 72c7437..0000000 --- a/src/datetime-interface.c +++ /dev/null @@ -1,200 +0,0 @@ -/* -An indicator to time and date related information in the menubar. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "datetime-interface.h" -#include "gen-datetime-service.xml.h" -#include "dbus-shared.h" - -/** - DatetimeInterfacePrivate: - @dbus_registration: The handle for this object being registered - on dbus. - - Structure to define the memory for the private area - of the datetime interface instance. -*/ -struct _DatetimeInterfacePrivate { - GDBusConnection * bus; - GCancellable * bus_cancel; - guint dbus_registration; -}; - -#define DATETIME_INTERFACE_GET_PRIVATE(o) (DATETIME_INTERFACE(o)->priv) - -/* GDBus Stuff */ -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * interface_info = NULL; - -static void datetime_interface_class_init (DatetimeInterfaceClass *klass); -static void datetime_interface_init (DatetimeInterface *self); -static void datetime_interface_dispose (GObject *object); -static void datetime_interface_finalize (GObject *object); -static void bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data); - -G_DEFINE_TYPE (DatetimeInterface, datetime_interface, G_TYPE_OBJECT); - -static void -datetime_interface_class_init (DatetimeInterfaceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (DatetimeInterfacePrivate)); - - object_class->dispose = datetime_interface_dispose; - object_class->finalize = datetime_interface_finalize; - - /* Setting up the DBus interfaces */ - if (node_info == NULL) { - GError * error = NULL; - - node_info = g_dbus_node_info_new_for_xml(_datetime_service, &error); - if (error != NULL) { - g_error("Unable to parse Datetime Service Interface description: %s", error->message); - g_error_free(error); - } - } - - if (interface_info == NULL) { - interface_info = g_dbus_node_info_lookup_interface(node_info, SERVICE_IFACE); - - if (interface_info == NULL) { - g_error("Unable to find interface '" SERVICE_IFACE "'"); - } - } - - return; -} - -static void -datetime_interface_init (DatetimeInterface *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, DATETIME_INTERFACE_TYPE, DatetimeInterfacePrivate); - - self->priv->bus = NULL; - self->priv->bus_cancel = NULL; - self->priv->dbus_registration = 0; - - self->priv->bus_cancel = g_cancellable_new(); - g_bus_get(G_BUS_TYPE_SESSION, - self->priv->bus_cancel, - bus_get_cb, - self); - - return; -} - -static void -bus_get_cb (GObject * object G_GNUC_UNUSED, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - GDBusConnection * connection = g_bus_get_finish(res, &error); - - if (error != NULL) { - g_error("OMG! Unable to get a connection to DBus: %s", error->message); - g_error_free(error); - return; - } - - DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(user_data); - - g_warn_if_fail(priv->bus == NULL); - priv->bus = connection; - - g_clear_object (&priv->bus_cancel); - - /* Now register our object on our new connection */ - priv->dbus_registration = g_dbus_connection_register_object(priv->bus, - SERVICE_OBJ, - interface_info, - NULL, - user_data, - NULL, - &error); - - if (error != NULL) { - g_error("Unable to register the object to DBus: %s", error->message); - g_error_free(error); - return; - } - - return; -} - -static void -datetime_interface_dispose (GObject *object) -{ - DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(object); - - if (priv->dbus_registration != 0) { - g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration); - /* Don't care if it fails, there's nothing we can do */ - priv->dbus_registration = 0; - } - - g_clear_object (&priv->bus); - - if (priv->bus_cancel != NULL) { - g_cancellable_cancel(priv->bus_cancel); - g_clear_object (&priv->bus_cancel); - } - - G_OBJECT_CLASS (datetime_interface_parent_class)->dispose (object); - return; -} - -static void -datetime_interface_finalize (GObject *object) -{ - - G_OBJECT_CLASS (datetime_interface_parent_class)->finalize (object); - return; -} - -void -datetime_interface_update (DatetimeInterface *self) -{ - g_return_if_fail(IS_DATETIME_INTERFACE(self)); - - DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(self); - GError * error = NULL; - - g_dbus_connection_emit_signal (priv->bus, - NULL, - SERVICE_OBJ, - SERVICE_IFACE, - "UpdateTime", - NULL, - &error); - - if (error != NULL) { - g_error("Unable to send UpdateTime signal: %s", error->message); - g_error_free(error); - return; - } - - return; -} diff --git a/src/datetime-interface.h b/src/datetime-interface.h deleted file mode 100644 index ae85605..0000000 --- a/src/datetime-interface.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -An indicator to time and date related information in the menubar. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#ifndef __DATETIME_INTERFACE_H__ -#define __DATETIME_INTERFACE_H__ - -#include -#include - -G_BEGIN_DECLS - -#define DATETIME_INTERFACE_TYPE (datetime_interface_get_type ()) -#define DATETIME_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DATETIME_INTERFACE_TYPE, DatetimeInterface)) -#define DATETIME_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DATETIME_INTERFACE_TYPE, DatetimeInterfaceClass)) -#define IS_DATETIME_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DATETIME_INTERFACE_TYPE)) -#define IS_DATETIME_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DATETIME_INTERFACE_TYPE)) -#define DATETIME_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DATETIME_INTERFACE_TYPE, DatetimeInterfaceClass)) - -typedef struct _DatetimeInterface DatetimeInterface; -typedef struct _DatetimeInterfacePrivate DatetimeInterfacePrivate; -typedef struct _DatetimeInterfaceClass DatetimeInterfaceClass; - -struct _DatetimeInterfaceClass { - GObjectClass parent_class; - - void (*update_time) (void); -}; - -struct _DatetimeInterface { - GObject parent; - DatetimeInterfacePrivate * priv; -}; - -GType datetime_interface_get_type (void); -void datetime_interface_update (DatetimeInterface *self); - -G_END_DECLS - -#endif diff --git a/src/datetime-service.c b/src/datetime-service.c deleted file mode 100644 index 1a29949..0000000 --- a/src/datetime-service.c +++ /dev/null @@ -1,1138 +0,0 @@ -/*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* -An indicator to time and date related information in the menubar. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#include -#include -#include - -#include -#include -#include -#include /* fabs() */ - -#include -#include -#include -#include - -#include - -#include "datetime-interface.h" -#include "dbus-shared.h" -#include "settings-shared.h" -#include "planner-eds.h" -#include "timezone-file.h" -#include "timezone-geoclue.h" -#include "utils.h" - -/* how often to check for clock skew */ -#define SKEW_CHECK_INTERVAL_SEC 10 - -#define MAX_APPOINTMENT_MENUITEMS 5 - -#define SKEW_DIFF_THRESHOLD_SEC (SKEW_CHECK_INTERVAL_SEC + 5) - -#ifdef HAVE_CCPANEL - #define SETTINGS_APP_INVOCATION "gnome-control-center indicator-datetime" -#else - #define SETTINGS_APP_INVOCATION "gnome-control-center datetime" -#endif - -static gboolean get_greeter_mode (void); - -static void quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data); - -static DbusmenuMenuitem * root = NULL; -static DatetimeInterface * dbus = NULL; - -typedef struct IndicatorDatetimeService -{ - DbusmenuMenuitem * date; - DbusmenuMenuitem * calendar; - DbusmenuMenuitem * settings; - DbusmenuMenuitem * events_separator; - DbusmenuMenuitem * locations_separator; - DbusmenuMenuitem * add_appointment; - DbusmenuMenuitem * appointment_menuitems[MAX_APPOINTMENT_MENUITEMS]; - - GSList * location_menu_items; - gboolean updating_appointments; - time_t start_time_appointments; - GSettings * conf; - - IndicatorDatetimeTimezone * geo_location; - IndicatorDatetimeTimezone * tz_file; - IndicatorDatetimePlanner * planner; - - guint ecaltimer; - guint day_timer; -} -IndicatorDatetimeService; - -static void update_location_menu_items (IndicatorDatetimeService * self); -static void update_appointment_menu_items (IndicatorDatetimeService * self); -static void day_timer_reset (IndicatorDatetimeService * self); - -/** - * A temp struct used by update_location_menu_items() for pruning duplicates and sorting. - */ -struct TimeLocation -{ - gint32 offset; - gchar * zone; - gchar * name; - gboolean visible; -}; - -static void -time_location_free (struct TimeLocation * loc) -{ - 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; - g_time_zone_unref (tz); - g_debug ("%s zone '%s' name '%s' offset is %d", G_STRLOC, zone, name, (int)loc->offset); - 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 */ - g_debug ("%s comparing '%s' (%d) to '%s' (%d), returning %d", G_STRLOC, a->name, (int)a->offset, b->name, (int)b->offset, ret); - 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) == NULL) - { - g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name); - locations = g_slist_append (locations, loc); - } - else - { - g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name); - time_location_free (loc); - } - - return locations; -} - -/* Update the timezone entries */ -static void -update_location_menu_items (IndicatorDatetimeService * self) -{ - /* if we're in greeter mode, don't bother */ - if (self->locations_separator == NULL) - return; - - /* remove the previous locations */ - while (self->location_menu_items != NULL) { - DbusmenuMenuitem * item = DBUSMENU_MENUITEM(self->location_menu_items->data); - self->location_menu_items = g_slist_remove (self->location_menu_items, item); - dbusmenu_menuitem_child_delete (root, DBUSMENU_MENUITEM(item)); - g_object_unref (item); - } - - /*** - **** Build a list of locations to add: use geo_timezone, - **** current_timezone, and SETTINGS_LOCATIONS_S, but omit duplicates. - ***/ - - GSList * locations = NULL; - const time_t now = time(NULL); /* FIXME: unmockable */ - - /* maybe add geo_timezone */ - if (self->geo_location != NULL) { - const char * geo_timezone = indicator_datetime_timezone_get_timezone (self->geo_location); - if (geo_timezone && *geo_timezone) { - const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_DETECTED_S); - gchar * name = get_current_zone_name (geo_timezone); - locations = locations_add (locations, geo_timezone, name, visible, now); - g_free (name); - } - } - - /* maybe add current_timezone */ - if (self->tz_file != NULL) { - const char * tz = indicator_datetime_timezone_get_timezone (self->tz_file); - if (tz && *tz) { - const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_DETECTED_S); - gchar * name = get_current_zone_name (tz); - locations = locations_add (locations, tz, name, visible, now); - g_free (name); - } - } - - /* maybe add the user-specified custom locations */ - gchar ** user_locations = g_settings_get_strv (self->conf, SETTINGS_LOCATIONS_S); - if (user_locations != NULL) { - guint i; - const guint location_count = g_strv_length (user_locations); - const gboolean visible = g_settings_get_boolean (self->conf, SETTINGS_SHOW_LOCATIONS_S); - g_debug ("%s Found %u user-specified locations", G_STRLOC, location_count); - for (i=0; ilocations_separator, root)+1; - GSList * l; - gboolean have_visible_location = FALSE; - for (l=locations; l!=NULL; l=l->next) { - struct TimeLocation * loc = l->data; - g_debug("%s Adding location: zone '%s', name '%s'", G_STRLOC, loc->zone, loc->name); - DbusmenuMenuitem * item = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, TIMEZONE_MENUITEM_TYPE); - dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_NAME, loc->name); - dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_ZONE, loc->zone); - dbusmenu_menuitem_property_set_bool (item, TIMEZONE_MENUITEM_PROP_RADIO, FALSE); - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, loc->visible); - dbusmenu_menuitem_child_add_position (root, item, offset++); - g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(quick_set_tz), NULL); - self->location_menu_items = g_slist_append (self->location_menu_items, item); - if (loc->visible) - have_visible_location = TRUE; - time_location_free (loc); - } - g_slist_free (locations); - locations = NULL; - - /* if there's at least one item being shown, show the separator too */ - dbusmenu_menuitem_property_set_bool (self->locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, have_visible_location); -} - -static void -quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data G_GNUC_UNUSED) -{ - GError * error = NULL; - GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error); - - if (error != NULL) { - g_warning("Could not set timezone using timedated: %s", error->message); - g_clear_error (&error); - return; - } - - g_variant_unref (answers); -} - -static void -quick_set_tz_proxy_cb (GObject *object G_GNUC_UNUSED, GAsyncResult *res, gpointer zone) -{ - GError * error = NULL; - - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - - if (error != NULL) { - g_warning("Could not grab DBus proxy for timedated: %s", error->message); - g_clear_error (&error); - g_free (zone); - return; - } - - g_dbus_proxy_call (proxy, "SetTimezone", g_variant_new ("(sb)", zone, TRUE), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, quick_set_tz_cb, NULL); - g_free (zone); - g_object_unref (proxy); -} - -static void -quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) -{ - const gchar * tz = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_ZONE); - g_debug("Quick setting timezone to: %s", tz); - - g_return_if_fail(tz != NULL); - - const gchar * name = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_NAME); - - /* Set it in gsettings so we don't lose user's preferred name */ - GSettings * conf = g_settings_new (SETTINGS_INTERFACE); - gchar * tz_full = g_strdup_printf ("%s %s", tz, name); - g_settings_set_string (conf, SETTINGS_TIMEZONE_NAME_S, tz_full); - g_free (tz_full); - g_object_unref (conf); - - 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", - NULL, quick_set_tz_proxy_cb, g_strdup (tz)); - - return; -} - -/* Updates the label in the date menuitem */ -static gboolean -update_datetime (gpointer gself) -{ - GDateTime *datetime; - gchar * utf8; - IndicatorDatetimeService * self = gself; - - g_debug("Updating Date/Time"); - - datetime = g_date_time_new_now_local (); /* FIXME: unmockable */ - if (datetime == NULL) { - g_warning("Error getting local time"); - dbusmenu_menuitem_property_set(self->date, DBUSMENU_MENUITEM_PROP_LABEL, _("Error getting time")); - return FALSE; - } - - /* eranslators: strftime(3) style date format on top of the menu when you click on the clock */ - utf8 = g_date_time_format (datetime, _("%A, %e %B %Y")); - dbusmenu_menuitem_property_set (self->date, DBUSMENU_MENUITEM_PROP_LABEL, utf8); - g_free(utf8); - - g_date_time_unref (datetime); - return G_SOURCE_REMOVE; -} - -/* Run a particular program based on an activation */ -static void -execute_command (const gchar * command) -{ - GError * error = NULL; - - g_debug("Issuing command '%s'", command); - if (!g_spawn_command_line_async(command, &error)) { - g_warning("Unable to start %s: %s", (char *)command, error->message); - g_clear_error (&error); - } -} - -/* Run a particular program based on an activation */ -static void -activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, - guint timestamp G_GNUC_UNUSED, - const gchar * command) -{ - execute_command (command); -} - -static gboolean -update_appointment_menu_items_idle (gpointer gself) -{ - update_appointment_menu_items (gself); - - return G_SOURCE_REMOVE; -} - -static void -update_appointment_menu_items_soon (IndicatorDatetimeService * self) -{ - g_idle_add (update_appointment_menu_items_idle, self); -} - -static void -hide_all_appointments (IndicatorDatetimeService * self) -{ - int i; - - for (i=0; iappointment_menuitems[i]) - { - dbusmenu_menuitem_property_set_bool (self->appointment_menuitems[i], DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - dbusmenu_menuitem_property_set_bool (self->appointment_menuitems[i], DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } - } -} - -static gboolean -month_changed_cb (DbusmenuMenuitem * menuitem, - gchar * name G_GNUC_UNUSED, - GVariant * variant, - guint timestamp G_GNUC_UNUSED, - gpointer gself) -{ - IndicatorDatetimeService * self = gself; - - self->start_time_appointments = (time_t)g_variant_get_uint32(variant); - - g_debug("Received month changed with timestamp: %d -> %s",(int)self->start_time_appointments, ctime(&self->start_time_appointments)); - - /* By default, one of the first things we do is - clear the marks as we don't know the correct - ones yet and we don't want to confuse the user. */ - - dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); - - update_appointment_menu_items_soon (self); - return TRUE; -} - -static gboolean -day_selected_cb (DbusmenuMenuitem * menuitem, - gchar * name G_GNUC_UNUSED, - GVariant * variant, - guint timestamp G_GNUC_UNUSED, - gpointer gself) -{ - time_t new_time; - IndicatorDatetimeService * self = gself; - - new_time = (time_t)g_variant_get_uint32(variant); - - if (self->start_time_appointments == 0 || new_time == 0) - { - /* If we've got nothing, assume everyhting is going to - get repopulated, let's start with a clean slate */ - dbusmenu_menuitem_property_remove (menuitem, CALENDAR_MENUITEM_PROP_MARKS); - } - else - { - /* No check to see if we changed months. If we did we'll - want to clear the marks. Otherwise we're cool keeping - them around. */ - struct tm start_tm; - struct tm new_tm; - - localtime_r (&self->start_time_appointments, &start_tm); - localtime_r (&new_time, &new_tm); - - if (start_tm.tm_mon != new_tm.tm_mon) - dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); - } - - self->start_time_appointments = new_time; - - g_debug ("Received day-selected with timestamp: %d -> %s",(int)self->start_time_appointments, ctime(&self->start_time_appointments)); - update_appointment_menu_items_soon (self); - - return TRUE; -} - -static gboolean -day_selected_double_click_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, - gchar * name G_GNUC_UNUSED, - GVariant * variant, - guint timestamp G_GNUC_UNUSED, - gpointer gself) -{ - time_t evotime; - GDateTime * dt; - IndicatorDatetimeService * self = gself; - - evotime = (time_t) g_variant_get_uint32 (variant); - dt = g_date_time_new_from_unix_utc (evotime); - indicator_datetime_planner_activate_time (self->planner, dt); - g_date_time_unref (dt); - - return TRUE; -} - - -static gboolean -update_appointment_menu_items_timerfunc (gpointer self) -{ - update_appointment_menu_items (self); - return G_SOURCE_CONTINUE; -} - -static void -start_ecal_timer (IndicatorDatetimeService * self) -{ - if (self->ecaltimer != 0) - self->ecaltimer = g_timeout_add_seconds (60*5, update_appointment_menu_items_timerfunc, self); -} - -static void -stop_ecal_timer (IndicatorDatetimeService * self) -{ - if (self->ecaltimer != 0) - { - g_source_remove (self->ecaltimer); - self->ecaltimer = 0; - } -} - -static gboolean -idle_start_ecal_timer (gpointer gself) -{ - start_ecal_timer (gself); - - return G_SOURCE_REMOVE; -} - -static void -show_events_changed (IndicatorDatetimeService * self) -{ - const gboolean b = g_settings_get_boolean (self->conf, SETTINGS_SHOW_EVENTS_S); - - dbusmenu_menuitem_property_set_bool (self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - dbusmenu_menuitem_property_set_bool (self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - - if (b) - { - start_ecal_timer (self); - } - else - { - hide_all_appointments (self); - stop_ecal_timer (self); - } -} - - -/* Looks for the calendar application and enables the item if - we have one, starts ecal timer if events are turned on */ -static gboolean -check_for_calendar (gpointer gself) -{ - gboolean b; - IndicatorDatetimeService * self = gself; - - g_return_val_if_fail (self->calendar != NULL, FALSE); - - dbusmenu_menuitem_property_set_bool(self->date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - - if (!get_greeter_mode () && indicator_datetime_planner_is_configured(self->planner)) { - - int i; - int pos = 2; - - g_signal_connect (G_OBJECT(self->date), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (activate_cb), "evolution -c calendar"); - - self->events_separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(self->events_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_add_position(root, self->events_separator, pos++); - - for (i=0; iappointment_menuitems[i] = item; - dbusmenu_menuitem_child_add_position(root, item, pos++); - } - - self->add_appointment = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (self->add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Event…")); - dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - g_signal_connect(G_OBJECT(self->add_appointment), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), "evolution -c calendar"); - dbusmenu_menuitem_child_add_position (root, self->add_appointment, pos++); - - if (g_settings_get_boolean(self->conf, SETTINGS_SHOW_EVENTS_S)) { - dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - g_idle_add((GSourceFunc)idle_start_ecal_timer, self); - } else { - dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - stop_ecal_timer (self); - } - - // Connect to calendar events - g_signal_connect(self->calendar, "event::month-changed", G_CALLBACK(month_changed_cb), self); - g_signal_connect(self->calendar, "event::day-selected", G_CALLBACK(day_selected_cb), self); - g_signal_connect(self->calendar, "event::day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), self); - } else { - g_debug("Unable to find calendar app."); - if (self->add_appointment != NULL) - dbusmenu_menuitem_property_set_bool(self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - if (self->events_separator != NULL) - dbusmenu_menuitem_property_set_bool(self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } - - b = g_settings_get_boolean (self->conf, SETTINGS_SHOW_CALENDAR_S); - dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_ENABLED, b); - dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - - return FALSE; -} - -static GdkPixbuf * -create_color_icon_pixbuf (const char * color_spec) -{ - static int width = -1; - static int height = -1; - GdkPixbuf * pixbuf = NULL; - - if (width == -1) - { - gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height); - width = CLAMP (width, 10, 30); - height = CLAMP (height, 10, 30); - } - - if (color_spec && *color_spec) - { - cairo_surface_t * surface; - cairo_t * cr; - GdkRGBA rgba; - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - - if (gdk_rgba_parse (&rgba, color_spec)) - gdk_cairo_set_source_rgba (cr, &rgba); - - cairo_paint (cr); - cairo_set_source_rgba (cr, 0, 0, 0, 0.5); - cairo_set_line_width (cr, 1); - cairo_rectangle (cr, 0.5, 0.5, width-1, height-1); - cairo_stroke (cr); - - pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height); - - cairo_destroy (cr); - cairo_surface_destroy (surface); - } - - return pixbuf; -} - - -/** - * Populate the menu with todays, next MAX_APPOINTMENT_MENUITEMS appointments. - * we should hook into the ABOUT TO SHOW signal and use that to update the appointments. - * Experience has shown that caldav's and webcals can be slow to load from eds - * this is a problem mainly on the EDS side of things, not ours. - */ -static void -update_appointment_menu_items (IndicatorDatetimeService * self) -{ - char * str; - GSList * l; - GSList * appointments; - gint i; - GDateTime * begin; - GDateTime * end; - GdkPixbuf * pixbuf; - gint apt_output; - GVariantBuilder markeddays; - GVariant * marks; - - // FFR: we should take into account short term timers, for instance - // tea timers, pomodoro timers etc... that people may add, this is hinted to in the spec. - - g_debug ("Update appointments called"); - - if (self->calendar == NULL) - return; - if (!g_settings_get_boolean (self->conf, SETTINGS_SHOW_EVENTS_S)) - return; - if (self->updating_appointments) - return; - - self->updating_appointments = TRUE; - - g_variant_builder_init (&markeddays, G_VARIANT_TYPE ("ai")); - - if (self->start_time_appointments != 0) - begin = g_date_time_new_from_unix_local (self->start_time_appointments); - else - begin = g_date_time_new_now_local (); /* FIXME: unmockable */ - end = g_date_time_add_months (begin, 1); - indicator_datetime_planner_set_timezone (self->planner, indicator_datetime_timezone_get_timezone (self->tz_file)); - appointments = indicator_datetime_planner_get_appointments (self->planner, begin, end); - - hide_all_appointments (self); - - /* decide whether to use 12hr or 24hr format */ - str = g_settings_get_string (self->conf, SETTINGS_TIME_FORMAT_S); - if (g_strcmp0 (str, "12-hour") == 0) - apt_output = SETTINGS_TIME_12_HOUR; - else if (g_strcmp0 (str, "24-hour") == 0) - apt_output = SETTINGS_TIME_24_HOUR; - else if (is_locale_12h()) - apt_output = SETTINGS_TIME_12_HOUR; - else - apt_output = SETTINGS_TIME_24_HOUR; - g_free (str); - - i = 0; - for (l=appointments; l!=NULL; l=l->next) - { - GDateTime * due; - DbusmenuMenuitem * item; - const struct IndicatorDatetimeAppt * appt = l->data; - char * right = NULL; - - due = appt->is_event ? appt->begin : appt->end; - - /* mark day if our query hasn't hit the next month. */ - if (g_date_time_get_month (begin) == g_date_time_get_month (due)) - g_variant_builder_add (&markeddays, "i", g_date_time_get_day_of_month (due)); - - if (i >= MAX_APPOINTMENT_MENUITEMS) - continue; - - item = self->appointment_menuitems[i]; - i++; - - /* remove the icon as we might not replace it on error */ - dbusmenu_menuitem_property_remove(item, APPOINTMENT_MENUITEM_PROP_ICON); - - /* remove the activate handler */ - g_signal_handlers_disconnect_matched(G_OBJECT(item), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(activate_cb), NULL); - - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_LABEL, appt->summary); - - gboolean full_day = FALSE; - if (appt->is_event) - 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 languages with very long day names. */ - right = g_date_time_format (due, _("%A")); - } - else - { - int ay, am, ad; - int by, bm, bd; - gboolean same_day; - gboolean hr12; - const char * fmt; - - g_date_time_get_ymd (due, &ay, &am, &ad); - g_date_time_get_ymd (begin, &by, &bm, &bd); - same_day = (ay==by) && (am==bm) && (ad==bd); - hr12 = apt_output == SETTINGS_TIME_12_HOUR; - - if (same_day && hr12) - fmt = _(DEFAULT_TIME_12_FORMAT); - else if (same_day) - fmt = _(DEFAULT_TIME_24_FORMAT); - else if (hr12) - fmt = _(DEFAULT_TIME_12_FORMAT_WITH_DAY); - else - fmt = _(DEFAULT_TIME_24_FORMAT_WITH_DAY); - - right = g_date_time_format (due, fmt); - } - - dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right); - g_free (right); - - // Now we pull out the URI for the calendar event and try to create a URI that'll work when we execute evolution - // FIXME Because the URI stuff is really broken, we're going to open the calendar at todays date instead - //e_cal_component_get_uid(ecalcomp, &uri); -/* FIXME: appointment menuitems aren't clickable */ -#if 0 - gchar * ad = isodate_from_time_t(mktime(due)); - gchar * cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL); - g_debug("Command to Execute: %s", cmd); - g_signal_connect_data (G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(activate_cb), cmd, (GClosureNotify)g_free, 0); - g_free (ad); -#endif - - if ((pixbuf = create_color_icon_pixbuf (appt->color))) - { - dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf); - g_clear_object (&pixbuf); - } - } - - - marks = g_variant_builder_end (&markeddays); - dbusmenu_menuitem_property_set_variant (self->calendar, CALENDAR_MENUITEM_PROP_MARKS, marks); - - g_slist_free_full (appointments, (GDestroyNotify)indicator_datetime_appt_free); - - self->updating_appointments = FALSE; - g_date_time_unref (end); - g_date_time_unref (begin); -} - -/* Looks for the time and date admin application and enables the - item we have one */ -static gboolean -check_for_timeadmin (gpointer gself) -{ - gchar * timeadmin; - IndicatorDatetimeService * self = gself; - - g_return_val_if_fail (self->settings != NULL, FALSE); - - timeadmin = g_find_program_in_path ("gnome-control-center"); - if (timeadmin != NULL) - { - g_debug ("Found the gnome-control-center application: %s", timeadmin); - dbusmenu_menuitem_property_set_bool (self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); - g_free(timeadmin); - } - else - { - g_debug ("Unable to find gnome-control-center app."); - dbusmenu_menuitem_property_set_bool (self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - } - - return G_SOURCE_REMOVE; -} - -/* Does the work to build the default menu, really calls out - to other functions but this is the core to clean up the - main function. */ -static void -build_menus (IndicatorDatetimeService * self, DbusmenuMenuitem * root) -{ - g_debug("Building Menus."); - if (self->date == NULL) { - self->date = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (self->date, DBUSMENU_MENUITEM_PROP_LABEL, _("No date yet…")); - dbusmenu_menuitem_property_set_bool(self->date, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - dbusmenu_menuitem_child_append(root, self->date); - - g_idle_add(update_datetime, self); - } - - if (self->calendar == NULL) { - self->calendar = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (self->calendar, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CALENDAR_MENUITEM_TYPE); - /* insensitive until we check for available apps */ - dbusmenu_menuitem_property_set_bool(self->calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - g_signal_connect (G_OBJECT(self->calendar), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (activate_cb), "evolution -c calendar"); - dbusmenu_menuitem_child_append(root, self->calendar); - - g_idle_add(check_for_calendar, self); - } - - if (!get_greeter_mode ()) { - self->locations_separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(self->locations_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_property_set_bool (self->locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_append(root, self->locations_separator); - - update_location_menu_items (self); - - g_signal_connect (self->conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), self); - g_signal_connect_swapped (self->conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (update_location_menu_items), self); - g_signal_connect_swapped (self->conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (update_location_menu_items), self); - g_signal_connect_swapped (self->conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (update_location_menu_items), self); - g_signal_connect_swapped (self->conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (update_appointment_menu_items), self); - - DbusmenuMenuitem * separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_append(root, separator); - - self->settings = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set (self->settings, DBUSMENU_MENUITEM_PROP_LABEL, _("Time & Date Settings…")); - /* insensitive until we check for available apps */ - dbusmenu_menuitem_property_set_bool(self->settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - g_signal_connect(G_OBJECT(self->settings), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), SETTINGS_APP_INVOCATION); - dbusmenu_menuitem_child_append(root, self->settings); - g_idle_add(check_for_timeadmin, self); - update_appointment_menu_items_soon (self); - } - - return; -} - -static void -on_clock_skew (gpointer self) -{ - /* tell the indicators to refresh */ - if (IS_DATETIME_INTERFACE (dbus)) - datetime_interface_update (DATETIME_INTERFACE(dbus)); - - /* update our day label */ - update_datetime (self); - day_timer_reset (self); -} - -static void -on_timezone_changed (gpointer self) -{ - update_location_menu_items (self); - - on_clock_skew (self); -} - -/* Execute at a given time, update and setup a new - timer to go again. */ -static gboolean -day_timer_func (gpointer self) -{ - day_timer_reset (self); - update_datetime (self); - - return G_SOURCE_REMOVE; -} - -/* Sets up the time to launch the timer to update the - date in the datetime entry */ -static void -day_timer_reset (IndicatorDatetimeService * self) -{ - GDateTime * now; - GDateTime * tomorrow; - GDateTime * new_day; - guint seconds_until_tomorrow; - - if (self->day_timer != 0) - { - g_source_remove (self->day_timer); - self->day_timer = 0; - } - - now = g_date_time_new_now_local (); - tomorrow = g_date_time_add_days (now, 1); - new_day = g_date_time_new_local (g_date_time_get_year (tomorrow), - g_date_time_get_month (tomorrow), - g_date_time_get_day_of_month (tomorrow), - 0, 0, 0); - seconds_until_tomorrow = (guint)(g_date_time_difference (new_day, now) / G_TIME_SPAN_SECOND); -g_message ("seconds until tomorrow is %u", seconds_until_tomorrow); - - self->day_timer = g_timeout_add_seconds (seconds_until_tomorrow + 15, - day_timer_func, - self); - - g_date_time_unref (new_day); - g_date_time_unref (tomorrow); - g_date_time_unref (now); -} - -static gboolean -skew_check_timer_func (gpointer self) -{ - static time_t prev_time = 0; - const time_t cur_time = time (NULL); /* FIXME: unmockable */ - const double diff_sec = fabs (difftime (cur_time, prev_time)); - - if (prev_time && (diff_sec > SKEW_DIFF_THRESHOLD_SEC)) - { - g_debug (G_STRLOC" clock skew detected (%.0f seconds)", diff_sec); - on_clock_skew (self); - } - - prev_time = cur_time; - return G_SOURCE_CONTINUE; -} - -static void -session_active_change_cb (GDBusProxy * proxy G_GNUC_UNUSED, - gchar * sender_name G_GNUC_UNUSED, - gchar * signal_name, - GVariant * parameters, - gpointer gself) -{ - IndicatorDatetimeService * self = gself; - - /* suspending / returning from suspend (true / false) */ - if (g_strcmp0(signal_name, "PrepareForSleep") == 0) - { - gboolean sleeping = FALSE; - g_variant_get (parameters, "(b)", &sleeping); - if (!sleeping) - { - g_debug ("System has been resumed; adjusting clock"); - on_clock_skew (self); - } - } -} - -/* for hooking into console kit signal on wake from suspend */ -static void -system_proxy_cb (GObject * object G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * error = NULL; - - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error); - - if (error != NULL) { - g_warning("Could not grab DBus proxy for logind: %s", error->message); - g_clear_error (&error); - return; - } - - g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), gself); -} - -/**** -***** -****/ - -static gboolean -get_greeter_mode (void) -{ - const gchar *var; - var = g_getenv("INDICATOR_GREETER_MODE"); - return (g_strcmp0(var, "1") == 0); -} - -/* Repsonds to the service object saying it's time to shutdown. - It stops the mainloop. */ -static void -service_shutdown (IndicatorService * service G_GNUC_UNUSED, - gpointer gmainloop) -{ - g_warning ("Shutting down service!"); - g_main_loop_quit (gmainloop); -} - -static void -on_use_geoclue_changed_cb (GSettings * settings, - gchar * key G_GNUC_UNUSED, - gpointer gself) -{ - IndicatorDatetimeService * self = gself; - const gboolean use_geoclue = g_settings_get_boolean (settings, "show-auto-detected-location"); - - if (self->geo_location && !use_geoclue) - { - g_signal_handlers_disconnect_by_func (self->geo_location, update_location_menu_items, self); - g_clear_object (&self->geo_location); - update_location_menu_items (self); - } - else if (use_geoclue && !self->geo_location) - { - self->geo_location = indicator_datetime_timezone_geoclue_new (); - g_signal_connect_swapped (self->geo_location, "notify::timezone", - G_CALLBACK(update_location_menu_items), self); - } -} - -/* Function to build everything up. Entry point from asm. */ -int -main (int argc, char ** argv) -{ - GMainLoop * mainloop; - IndicatorService * service; - DbusmenuServer * server; - struct IndicatorDatetimeService self; - - memset (&self, 0, sizeof(struct IndicatorDatetimeService)); - - gtk_init (&argc, &argv); - mainloop = g_main_loop_new (NULL, FALSE); - - /* acknowledging the service init and setting up the interface */ - service = indicator_service_new_version(SERVICE_NAME, SERVICE_VERSION); - g_signal_connect (service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, - G_CALLBACK(service_shutdown), mainloop); - - /* setting up i18n and gettext. apparently we need all of these. */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - /* set up GSettings */ - self.conf = g_settings_new (SETTINGS_INTERFACE); - g_signal_connect (self.conf, "changed::show-auto-detected-location", - G_CALLBACK(on_use_geoclue_changed_cb), &self); - - /* setup geoclue */ - on_use_geoclue_changed_cb (self.conf, NULL, &self); - - /* setup timezone watch */ - self.tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE); - g_signal_connect_swapped (self.tz_file, "notify::timezone", - G_CALLBACK(on_timezone_changed), &self); - - /* build our list of appointment calendar sources. - When a source changes, update our menu items. - When sources are added or removed, update our list and menu items. */ - self.planner = indicator_datetime_planner_eds_new (); - g_signal_connect_swapped (self.planner, "appointments-changed", - G_CALLBACK(update_appointment_menu_items_soon), &self); - - /* building the base menu */ - server = dbusmenu_server_new (MENU_OBJ); - root = dbusmenu_menuitem_new (); - dbusmenu_server_set_root (server, root); - - build_menus (&self, root); - - /* cache the timezone */ - update_location_menu_items (&self); - - /* setup dbus interface */ - dbus = g_object_new (DATETIME_INTERFACE_TYPE, NULL); - - /* set up the day timer */ - day_timer_reset (&self); - - /* set up the skew-check timer */ - g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC, - skew_check_timer_func, - &self); - - /* and watch for system resumes */ - 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", - NULL, /* FIXME: cancellable */ - system_proxy_cb, - &self); - - g_main_loop_run (mainloop); - g_main_loop_unref (mainloop); - - g_object_unref (self.conf); - g_object_unref (dbus); - g_object_unref (service); - g_object_unref (server); - g_object_unref (root); - g_object_unref (self.planner); - g_object_unref (self.geo_location); - g_object_unref (self.tz_file); - - return 0; -} diff --git a/src/datetime-service.xml b/src/datetime-service.xml deleted file mode 100644 index eda064f..0000000 --- a/src/datetime-service.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/indicator-datetime.c b/src/indicator-datetime.c deleted file mode 100644 index f7d1a78..0000000 --- a/src/indicator-datetime.c +++ /dev/null @@ -1,1535 +0,0 @@ -/* -An indicator to time and date related information in the menubar. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -/* GStuff */ -#include -#include -#include -#include -#include - -/* Indicator Stuff */ -#include -#include -#include - -/* DBusMenu */ -#include -#include -#include - -#include "utils.h" -#include "dbus-shared.h" -#include "settings-shared.h" - - -#define INDICATOR_DATETIME_TYPE (indicator_datetime_get_type ()) -#define INDICATOR_DATETIME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetime)) -#define INDICATOR_DATETIME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass)) -#define IS_INDICATOR_DATETIME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_DATETIME_TYPE)) -#define IS_INDICATOR_DATETIME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_DATETIME_TYPE)) -#define INDICATOR_DATETIME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass)) - -typedef struct _IndicatorDatetime IndicatorDatetime; -typedef struct _IndicatorDatetimeClass IndicatorDatetimeClass; -typedef struct _IndicatorDatetimePrivate IndicatorDatetimePrivate; - -struct _IndicatorDatetimeClass { - IndicatorObjectClass parent_class; -}; - -struct _IndicatorDatetime { - IndicatorObject parent; - IndicatorDatetimePrivate * priv; -}; - -struct _IndicatorDatetimePrivate { - GtkLabel * label; - guint timer; - - gchar * time_string; - - gboolean show_clock; - gint time_mode; - gboolean show_seconds; - gboolean show_date; - gboolean show_day; - gchar * custom_string; - gboolean custom_show_seconds; - - gboolean show_week_numbers; - gboolean show_calendar; - gint week_start; - - guint idle_measure; - gint max_width; - - IndicatorServiceManager * sm; - DbusmenuGtkMenu * menu; - - GCancellable * service_proxy_cancel; - GDBusProxy * service_proxy; - IdoCalendarMenuItem *ido_calendar; - - GList * timezone_items; - - GSettings * settings; - - GtkSizeGroup * indicator_right_group; -}; - -/* Enum for the properties so that they can be quickly - found and looked up. */ -enum { - PROP_0, - PROP_SHOW_CLOCK, - PROP_TIME_FORMAT, - PROP_SHOW_SECONDS, - PROP_SHOW_DAY, - PROP_SHOW_DATE, - PROP_CUSTOM_TIME_FORMAT, - PROP_SHOW_WEEK_NUMBERS, - PROP_SHOW_CALENDAR -}; - -typedef struct _indicator_item_t indicator_item_t; -struct _indicator_item_t { - IndicatorDatetime * self; - DbusmenuMenuitem * mi; - GtkWidget * gmi; - GtkWidget * icon; - GtkWidget * label; - GtkWidget * right; -}; - -#define PROP_SHOW_CLOCK_S "show-clock" -#define PROP_TIME_FORMAT_S "time-format" -#define PROP_SHOW_SECONDS_S "show-seconds" -#define PROP_SHOW_DAY_S "show-day" -#define PROP_SHOW_DATE_S "show-date" -#define PROP_CUSTOM_TIME_FORMAT_S "custom-time-format" -#define PROP_SHOW_WEEK_NUMBERS_S "show-week-numbers" -#define PROP_SHOW_CALENDAR_S "show-calendar" - -enum { - STRFTIME_MASK_NONE = 0, /* Hours or minutes as we always test those */ - STRFTIME_MASK_SECONDS = 1 << 0, /* Seconds count */ - STRFTIME_MASK_AMPM = 1 << 1, /* AM/PM counts */ - STRFTIME_MASK_WEEK = 1 << 2, /* Day of the week maters (Sat, Sun, etc.) */ - STRFTIME_MASK_DAY = 1 << 3, /* Day of the month counts (Feb 1st) */ - STRFTIME_MASK_MONTH = 1 << 4, /* Which month matters */ - STRFTIME_MASK_YEAR = 1 << 5, /* Which year matters */ - /* Last entry, combines all previous */ - STRFTIME_MASK_ALL = (STRFTIME_MASK_SECONDS | STRFTIME_MASK_AMPM | STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR) -}; - -GType indicator_datetime_get_type (void) G_GNUC_CONST; - -static void indicator_datetime_class_init (IndicatorDatetimeClass *klass); -static void indicator_datetime_init (IndicatorDatetime *self); -static void timezone_update_all_labels (IndicatorDatetime *self); -static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void indicator_datetime_dispose (GObject *object); -static void indicator_datetime_finalize (GObject *object); -static GtkLabel * get_label (IndicatorObject * io); -static GtkMenu * get_menu (IndicatorObject * io); -static const gchar * get_accessible_desc (IndicatorObject * io); -static const gchar * get_name_hint (IndicatorObject * io); -static gboolean bind_enum_get (GValue * value, GVariant * variant, gpointer user_data); -static gchar * generate_format_string_now (IndicatorDatetime * self); -static void update_label (IndicatorDatetime * io, GDateTime ** datetime); -static void guess_label_size (IndicatorDatetime * self); -static void setup_timer (IndicatorDatetime * self, GDateTime * datetime); -static void update_time (IndicatorDatetime * self); -static void receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data); -static void service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data); -static gint generate_strftime_bitmask (const char *time_str); -static void timezone_update_labels (indicator_item_t * mi_data); -static gboolean new_calendar_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data); -static gboolean new_appointment_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data); -static gboolean new_timezone_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data); - -/* Indicator Module Config */ -INDICATOR_SET_VERSION -INDICATOR_SET_TYPE(INDICATOR_DATETIME_TYPE) - -G_DEFINE_TYPE (IndicatorDatetime, indicator_datetime, INDICATOR_OBJECT_TYPE); - -static void -indicator_datetime_class_init (IndicatorDatetimeClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePrivate)); - - object_class->dispose = indicator_datetime_dispose; - object_class->finalize = indicator_datetime_finalize; - - object_class->set_property = set_property; - object_class->get_property = get_property; - - IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass); - - io_class->get_label = get_label; - io_class->get_menu = get_menu; - io_class->get_accessible_desc = get_accessible_desc; - io_class->get_name_hint = get_name_hint; - - g_object_class_install_property (object_class, - PROP_SHOW_CLOCK, - g_param_spec_boolean(PROP_SHOW_CLOCK_S, - "Whether to show the clock in the menu bar.", - "Shows indicator-datetime in the shell's menu bar.", - TRUE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_TIME_FORMAT, - g_param_spec_int(PROP_TIME_FORMAT_S, - "A choice of which format should be used on the panel", - "Chooses between letting the locale choose the time, 12-hour time, 24-time or using the custom string passed to g_date_time_format().", - SETTINGS_TIME_LOCALE, /* min */ - SETTINGS_TIME_CUSTOM, /* max */ - SETTINGS_TIME_LOCALE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_SHOW_SECONDS, - g_param_spec_boolean(PROP_SHOW_SECONDS_S, - "Whether to show seconds in the indicator.", - "Shows seconds along with the time in the indicator. Also effects refresh interval.", - FALSE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_SHOW_DAY, - g_param_spec_boolean(PROP_SHOW_DAY_S, - "Whether to show the day of the week in the indicator.", - "Shows the day of the week along with the time in the indicator.", - FALSE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_SHOW_DATE, - g_param_spec_boolean(PROP_SHOW_DATE_S, - "Whether to show the day and month in the indicator.", - "Shows the day and month along with the time in the indicator.", - FALSE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_CUSTOM_TIME_FORMAT, - g_param_spec_string(PROP_CUSTOM_TIME_FORMAT_S, - "The format that is used to show the time on the panel.", - "A format string in the form used to pass to g_date_time_format() to make a string for displaying on the panel.", - DEFAULT_TIME_FORMAT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_SHOW_WEEK_NUMBERS, - g_param_spec_boolean(PROP_SHOW_WEEK_NUMBERS_S, - "Whether to show the week numbers in the calendar.", - "Shows the week numbers in the monthly calendar in indicator-datetime's menu.", - FALSE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_SHOW_CALENDAR, - g_param_spec_boolean(PROP_SHOW_CALENDAR_S, - "Whether to show the calendar.", - "Shows the monthly calendar in indicator-datetime's menu.", - TRUE, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - return; -} - -static void -menu_visible_notify_cb(GtkWidget * menu, G_GNUC_UNUSED GParamSpec *pspec, gpointer user_data) -{ - IndicatorDatetime * self; - g_debug ("notify visible signal received"); - - self = INDICATOR_DATETIME (user_data); - g_assert (self != NULL); - - /* if the calendar widget's been created, set it to today's datë */ - if (self->priv->ido_calendar != NULL) { - GtkWidget * w; - GtkCalendar * calendar; - gint cur_y, cur_m, cur_d; - guint cal_y, cal_m, cal_d; - GDateTime * datetime = g_date_time_new_now_local (); - - g_date_time_get_ymd (datetime, &cur_y, &cur_m, &cur_d); - w = ido_calendar_menu_item_get_calendar (self->priv->ido_calendar); - calendar = GTK_CALENDAR(w); - g_return_if_fail (calendar != NULL); - - gtk_calendar_get_date (calendar, &cal_y, &cal_m, &cal_d); - if ((cur_y != cal_y) || (cur_m-1 != cal_m)) - gtk_calendar_select_month (calendar, cur_m-1, cur_y); /* (cur_m is 1-based) */ - if (cur_d != cal_d) - gtk_calendar_select_day (calendar, cur_d); - - g_date_time_unref (datetime); - } - - /* Update in case date was changed outside of indicator-datetime */ - update_label(self, NULL); - timezone_update_all_labels(self); - - // Make sure the day-selected signal is sent so the menu updates - may duplicate - /*GVariant *variant = g_variant_new_uint32((guint)curtime); - guint timestamp = (guint)time(NULL); - dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(self->priv->ido_calendar), "day-selected", variant, timestamp);*/ -} - -static void -indicator_datetime_init (IndicatorDatetime *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_DATETIME_TYPE, - IndicatorDatetimePrivate); - - self->priv->label = NULL; - self->priv->timer = 0; - - self->priv->idle_measure = 0; - self->priv->max_width = 0; - - self->priv->show_clock = TRUE; - self->priv->time_mode = SETTINGS_TIME_LOCALE; - self->priv->show_seconds = FALSE; - self->priv->show_date = FALSE; - self->priv->show_day = FALSE; - self->priv->custom_string = g_strdup(DEFAULT_TIME_FORMAT); - self->priv->custom_show_seconds = FALSE; - - self->priv->time_string = generate_format_string_now(self); - - self->priv->service_proxy = NULL; - - self->priv->sm = NULL; - self->priv->menu = NULL; - - self->priv->settings = g_settings_new(SETTINGS_INTERFACE); - if (self->priv->settings != NULL) { - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_CLOCK_S, - self, - PROP_SHOW_CLOCK_S, - G_SETTINGS_BIND_GET); - g_settings_bind_with_mapping(self->priv->settings, - SETTINGS_TIME_FORMAT_S, - self, - PROP_TIME_FORMAT_S, - G_SETTINGS_BIND_GET, - bind_enum_get, - NULL, NULL, NULL); /* set mapping, userdata and destroy func */ - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_SECONDS_S, - self, - PROP_SHOW_SECONDS_S, - G_SETTINGS_BIND_GET); - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_DAY_S, - self, - PROP_SHOW_DAY_S, - G_SETTINGS_BIND_GET); - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_DATE_S, - self, - PROP_SHOW_DATE_S, - G_SETTINGS_BIND_GET); - g_settings_bind(self->priv->settings, - SETTINGS_CUSTOM_TIME_FORMAT_S, - self, - PROP_CUSTOM_TIME_FORMAT_S, - G_SETTINGS_BIND_GET); - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_WEEK_NUMBERS_S, - self, - PROP_SHOW_WEEK_NUMBERS_S, - G_SETTINGS_BIND_GET); - g_settings_bind(self->priv->settings, - SETTINGS_SHOW_CALENDAR_S, - self, - PROP_SHOW_CALENDAR_S, - G_SETTINGS_BIND_GET); - } else { - g_warning("Unable to get settings for '" SETTINGS_INTERFACE "'"); - } - - self->priv->sm = indicator_service_manager_new_version(SERVICE_NAME, SERVICE_VERSION); - self->priv->indicator_right_group = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL)); - - self->priv->menu = dbusmenu_gtkmenu_new(SERVICE_NAME, MENU_OBJ); - - g_signal_connect(self->priv->menu, "notify::visible", G_CALLBACK(menu_visible_notify_cb), self); - - DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(self->priv->menu); - dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), DBUSMENU_CALENDAR_MENUITEM_TYPE, new_calendar_item, self, NULL); - dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), APPOINTMENT_MENUITEM_TYPE, new_appointment_item, self, NULL); - dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), TIMEZONE_MENUITEM_TYPE, new_timezone_item, self, NULL); - - self->priv->service_proxy_cancel = g_cancellable_new(); - - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - SERVICE_NAME, - SERVICE_OBJ, - SERVICE_IFACE, - self->priv->service_proxy_cancel, - service_proxy_cb, - self); - - return; -} - -/* Callback from trying to create the proxy for the serivce, this - could include starting the service. Sometime it'll fail and - we'll try to start that dang service again! */ -static void -service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - - IndicatorDatetime * self = INDICATOR_DATETIME(user_data); - g_return_if_fail(self != NULL); - IndicatorDatetimePrivate * priv = self->priv; - - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error); - - g_clear_object (&priv->service_proxy_cancel); - - if (error != NULL) { - g_warning("Could not grab DBus proxy for %s: %s", SERVICE_NAME, error->message); - g_error_free(error); - return; - } - - /* Okay, we're good to grab the proxy at this point, we're - sure that it's ours. */ - priv->service_proxy = proxy; - - g_signal_connect(proxy, "g-signal", G_CALLBACK(receive_signal), self); - - return; -} - -static void -indicator_datetime_dispose (GObject *object) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(object); - IndicatorDatetimePrivate * priv = self->priv; - - if (priv->timer != 0) { - g_source_remove(priv->timer); - priv->timer = 0; - } - - if (priv->idle_measure != 0) { - g_source_remove(priv->idle_measure); - priv->idle_measure = 0; - } - - g_clear_object (&priv->label); - g_clear_object (&priv->menu); - g_clear_object (&priv->sm); - g_clear_object (&priv->settings); - g_clear_object (&priv->service_proxy); - g_clear_object (&priv->indicator_right_group); - g_clear_object (&priv->ido_calendar); - g_clear_object (&priv->service_proxy_cancel); - - G_OBJECT_CLASS (indicator_datetime_parent_class)->dispose (object); - return; -} - -static void -indicator_datetime_finalize (GObject *object) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(object); - - if (self->priv->time_string != NULL) { - g_free(self->priv->time_string); - self->priv->time_string = NULL; - } - - if (self->priv->custom_string != NULL) { - g_free(self->priv->custom_string); - self->priv->custom_string = NULL; - } - - G_OBJECT_CLASS (indicator_datetime_parent_class)->finalize (object); - return; -} - -/* Turns a string GVariant into an int value */ -static gboolean -bind_enum_get (GValue * value, GVariant * variant, gpointer user_data) -{ - const gchar * str = g_variant_get_string(variant, NULL); - gint output = 0; - - if (g_strcmp0(str, "locale-default") == 0) { - output = SETTINGS_TIME_LOCALE; - } else if (g_strcmp0(str, "12-hour") == 0) { - output = SETTINGS_TIME_12_HOUR; - } else if (g_strcmp0(str, "24-hour") == 0) { - output = SETTINGS_TIME_24_HOUR; - } else if (g_strcmp0(str, "custom") == 0) { - output = SETTINGS_TIME_CUSTOM; - } else { - return FALSE; - } - - g_value_set_int(value, output); - return TRUE; -} - -static void -timezone_update_all_labels (IndicatorDatetime * self) -{ - IndicatorDatetimePrivate *priv = self->priv; - - g_list_foreach(priv->timezone_items, (GFunc)timezone_update_labels, NULL); -} - -/* Sets a property on the object */ -static void -set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(object); - gboolean update = FALSE; - - switch(prop_id) { - case PROP_SHOW_CLOCK: { - if (g_value_get_boolean(value) != self->priv->show_clock) { - self->priv->show_clock = g_value_get_boolean(value); - if (self->priv->label != NULL) { - gtk_widget_set_visible (GTK_WIDGET (self->priv->label), self->priv->show_clock); - } - } - break; - } - case PROP_TIME_FORMAT: { - gint newval = g_value_get_int(value); - if (newval != self->priv->time_mode) { - update = TRUE; - self->priv->time_mode = newval; - setup_timer(self, NULL); - } - break; - } - case PROP_SHOW_SECONDS: { - if (g_value_get_boolean(value) != self->priv->show_seconds) { - self->priv->show_seconds = !self->priv->show_seconds; - if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) { - update = TRUE; - setup_timer(self, NULL); - } - } - break; - } - case PROP_SHOW_DAY: { - if (g_value_get_boolean(value) != self->priv->show_day) { - self->priv->show_day = !self->priv->show_day; - if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) { - update = TRUE; - } - } - break; - } - case PROP_SHOW_DATE: { - if (g_value_get_boolean(value) != self->priv->show_date) { - self->priv->show_date = !self->priv->show_date; - if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) { - update = TRUE; - } - } - break; - } - case PROP_CUSTOM_TIME_FORMAT: { - const gchar * newstr = g_value_get_string(value); - if (g_strcmp0(newstr, self->priv->custom_string) != 0) { - if (self->priv->custom_string != NULL) { - g_free(self->priv->custom_string); - self->priv->custom_string = NULL; - } - self->priv->custom_string = g_strdup(newstr); - gint time_mask = generate_strftime_bitmask(newstr); - self->priv->custom_show_seconds = (time_mask & STRFTIME_MASK_SECONDS); - if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) { - update = TRUE; - setup_timer(self, NULL); - } - } - break; - } - case PROP_SHOW_WEEK_NUMBERS: { - if (g_value_get_boolean(value) != self->priv->show_week_numbers) { - GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar); - if (g_value_get_boolean(value) == TRUE) - flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS; - else - flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS; - ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags); - self->priv->show_week_numbers = g_value_get_boolean(value); - } - break; - } - case PROP_SHOW_CALENDAR: { - if (g_value_get_boolean(value) != self->priv->show_calendar) { - self->priv->show_calendar = g_value_get_boolean(value); - if (self->priv->ido_calendar != NULL) { - gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar); - } - } - break; - } - default: { - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - return; - } - } - - if (!update) { - return; - } - - /* Get the new format string */ - gchar * newformat = generate_format_string_now(self); - - /* check to ensure the format really changed */ - if (g_strcmp0(self->priv->time_string, newformat) == 0) { - g_free(newformat); - return; - } - - /* Okay now process the change */ - if (self->priv->time_string != NULL) { - g_free(self->priv->time_string); - self->priv->time_string = NULL; - } - self->priv->time_string = newformat; - - /* And update everything */ - update_label(self, NULL); - timezone_update_all_labels(self); - guess_label_size(self); - - return; -} - -/* Gets a property from the object */ -static void -get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(object); - - switch(prop_id) { - case PROP_SHOW_CLOCK: - g_value_set_boolean(value, self->priv->show_clock); - break; - case PROP_TIME_FORMAT: - g_value_set_int(value, self->priv->time_mode); - break; - case PROP_SHOW_SECONDS: - g_value_set_boolean(value, self->priv->show_seconds); - break; - case PROP_SHOW_DAY: - g_value_set_boolean(value, self->priv->show_day); - break; - case PROP_SHOW_DATE: - g_value_set_boolean(value, self->priv->show_date); - break; - case PROP_CUSTOM_TIME_FORMAT: - g_value_set_string(value, self->priv->custom_string); - break; - case PROP_SHOW_WEEK_NUMBERS: - g_value_set_boolean(value, self->priv->show_week_numbers); - break; - case PROP_SHOW_CALENDAR: - g_value_set_boolean(value, self->priv->show_calendar); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - return; - } - - return; -} - -/* Looks at the size of the label, if it grew beyond what we - thought was the max, make sure it doesn't shrink again. */ -static gboolean -idle_measure (gpointer data) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(data); - self->priv->idle_measure = 0; - - GtkAllocation allocation; - gtk_widget_get_allocation(GTK_WIDGET(self->priv->label), &allocation); - - if (allocation.width > self->priv->max_width) { - if (self->priv->max_width != 0) { - g_warning("Guessed wrong. We thought the max would be %d but we're now at %d", self->priv->max_width, allocation.width); - } - self->priv->max_width = allocation.width; - gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1); - } - - return FALSE; -} - -/* Updates the accessible description */ -static void -update_accessible_description (IndicatorDatetime * io) -{ - GList * entries = indicator_object_get_entries(INDICATOR_OBJECT(io)); - IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data; - - entry->accessible_desc = get_accessible_desc(INDICATOR_OBJECT(io)); - - g_signal_emit(G_OBJECT(io), - INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID, - 0, - entry, - TRUE); - - g_list_free(entries); - - return; -} - -/* Updates the label to be the current time. */ -static void -set_label_to_time_in_zone (IndicatorDatetime * self, GtkLabel * label, - GTimeZone * tz, const gchar * format, - GDateTime ** datetime) -{ - gboolean unref_tz = FALSE; - if (tz == NULL) { - gchar * zone = read_timezone (); - if (zone == NULL) - return; - tz = g_time_zone_new(zone); - unref_tz = TRUE; - g_free (zone); - } - - GDateTime * datetime_now; - datetime_now = g_date_time_new_now(tz); - - gchar * timestr; - if (format == NULL) { - gchar * format_for_time = generate_format_string_at_time(datetime_now); - timestr = g_date_time_format(datetime_now, format_for_time); - g_free(format_for_time); - } - else { - timestr = g_date_time_format(datetime_now, format); - if (timestr == NULL) { - g_warning ("The custom date format is not valid, check the\n" - "g_date_time_format() documentation for the supported\n" - "format specifiers "); - timestr = g_strdup ("Date format not supported"); - } - } - - gboolean use_markup = FALSE; - if (pango_parse_markup(timestr, -1, 0, NULL, NULL, NULL, NULL)) - use_markup = TRUE; - - if (use_markup) - gtk_label_set_markup(label, timestr); - else - gtk_label_set_text(label, timestr); - - g_free(timestr); - - if (datetime) - *datetime = datetime_now; - else - g_date_time_unref(datetime_now); - - if (unref_tz) - g_time_zone_unref(tz); - - return; -} - -/* Updates the label to be the current time. */ -static void -update_label (IndicatorDatetime * io, GDateTime ** datetime) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(io); - - if (self->priv->label == NULL) return; - - set_label_to_time_in_zone(self, self->priv->label, NULL, self->priv->time_string, datetime); - - if (self->priv->idle_measure == 0) { - self->priv->idle_measure = g_idle_add(idle_measure, io); - } - - update_accessible_description(io); - - return; -} - -/* Update the time right now. Usually the result of a timezone switch. */ -static void -update_time (IndicatorDatetime * self) -{ - GDateTime * dt = NULL; - update_label(self, &dt); - timezone_update_all_labels(self); - if (dt != NULL) { - setup_timer(self, dt); - g_date_time_unref(dt); - } - return; -} - -/* Receives all signals from the service, routed to the appropriate functions */ -static void -receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, - GVariant * parameters, gpointer user_data) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(user_data); - - if (g_strcmp0(signal_name, "UpdateTime") == 0) { - update_time(self); - } - - return; -} - -/* Runs every minute and updates the time */ -gboolean -timer_func (gpointer user_data) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(user_data); - self->priv->timer = 0; - GDateTime * dt = NULL; - update_label(self, &dt); - timezone_update_all_labels(self); - if (dt != NULL) { - setup_timer(self, dt); - g_date_time_unref(dt); - } - return FALSE; -} - -/* Configure the timer to run the next time through */ -static void -setup_timer (IndicatorDatetime * self, GDateTime * datetime) -{ - gboolean unref = FALSE; - - if (self->priv->timer != 0) { - g_source_remove(self->priv->timer); - self->priv->timer = 0; - } - - if (self->priv->show_seconds || - (self->priv->time_mode == SETTINGS_TIME_CUSTOM && self->priv->custom_show_seconds)) { - self->priv->timer = g_timeout_add_full(G_PRIORITY_HIGH, 999, timer_func, self, NULL); - } else { - if (datetime == NULL) { - datetime = g_date_time_new_now_local(); - unref = TRUE; - } - - /* Plus 2 so we're just after the minute, don't want to be early. */ - gint seconds = (gint)g_date_time_get_seconds(datetime); - self->priv->timer = g_timeout_add_seconds(60 - seconds + 2, timer_func, self); - - if (unref) { - g_date_time_unref(datetime); - } - } - - return; -} - -/* Does a quick meausre of how big the string is in - pixels with a Pango layout */ -static gint -measure_string (GtkStyle * style, PangoContext * context, const gchar * string) -{ - PangoLayout * layout = pango_layout_new(context); - - if (pango_parse_markup(string, -1, 0, NULL, NULL, NULL, NULL)) - pango_layout_set_markup(layout, string, -1); - else - pango_layout_set_text(layout, string, -1); - - pango_layout_set_font_description(layout, style->font_desc); - - gint width; - pango_layout_get_pixel_size(layout, &width, NULL); - g_object_unref(layout); - return width; -} - -/* Format for the table of strftime() modifiers to what - we need to check when determining the length */ -typedef struct _strftime_type_t strftime_type_t; -struct _strftime_type_t { - char character; - gint mask; -}; - -/* A table taken from the man page of strftime to what the different - characters can effect. These are worst case in that we need to - test the length based on all these things to ensure that we have - a reasonable string lenght measurement. */ -const static strftime_type_t strftime_type[] = { - {'a', STRFTIME_MASK_WEEK}, - {'A', STRFTIME_MASK_WEEK}, - {'b', STRFTIME_MASK_MONTH}, - {'B', STRFTIME_MASK_MONTH}, - {'c', STRFTIME_MASK_ALL}, /* We don't know, so we have to assume all */ - {'C', STRFTIME_MASK_YEAR}, - {'d', STRFTIME_MASK_MONTH}, - {'D', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY}, - {'e', STRFTIME_MASK_DAY}, - {'F', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY}, - {'G', STRFTIME_MASK_YEAR}, - {'g', STRFTIME_MASK_YEAR}, - {'h', STRFTIME_MASK_MONTH}, - {'j', STRFTIME_MASK_DAY}, - {'m', STRFTIME_MASK_MONTH}, - {'p', STRFTIME_MASK_AMPM}, - {'P', STRFTIME_MASK_AMPM}, - {'r', STRFTIME_MASK_AMPM}, - {'s', STRFTIME_MASK_SECONDS}, - {'S', STRFTIME_MASK_SECONDS}, - {'T', STRFTIME_MASK_SECONDS}, - {'u', STRFTIME_MASK_WEEK}, - {'U', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH}, - {'V', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH}, - {'w', STRFTIME_MASK_DAY}, - {'W', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH}, - {'x', STRFTIME_MASK_YEAR | STRFTIME_MASK_MONTH | STRFTIME_MASK_DAY | STRFTIME_MASK_WEEK}, - {'X', STRFTIME_MASK_SECONDS}, - {'y', STRFTIME_MASK_YEAR}, - {'Y', STRFTIME_MASK_YEAR}, - /* Last one */ - {0, 0} -}; - -#define FAT_NUMBER 8 - -/* Looks through the characters in the format string to - ensure that we can figure out which of the things we - need to check in determining the length. */ -static gint -generate_strftime_bitmask (const char *time_str) -{ - gint retval = 0; - glong strlength = g_utf8_strlen(time_str, -1); - gint i; - g_debug("Evaluating bitmask for '%s'", time_str); - - for (i = 0; i < strlength; i++) { - if (time_str[i] == '%' && i + 1 < strlength) { - gchar evalchar = time_str[i + 1]; - - /* If we're using alternate formats we need to skip those characters */ - if (evalchar == 'E' || evalchar == 'O') { - if (i + 2 < strlength) { - evalchar = time_str[i + 2]; - } else { - continue; - } - } - - /* Let's look at that character in the table */ - int j; - for (j = 0; strftime_type[j].character != 0; j++) { - if (strftime_type[j].character == evalchar) { - retval |= strftime_type[j].mask; - break; - } - } - } - } - - return retval; -} - -/* Build an array up of all the time values that we want to check - for length to ensure we're in a good place */ -static void -build_timeval_array (GArray * timevals, gint mask) -{ - struct tm mytm = {0}; - - /* Sun 12/28/8888 00:00 */ - mytm.tm_hour = 0; - mytm.tm_mday = 28; - mytm.tm_mon = 11; - mytm.tm_year = 8888 - 1900; - mytm.tm_wday = 0; - mytm.tm_yday = 363; - g_array_append_val(timevals, mytm); - - if (mask & STRFTIME_MASK_AMPM) { - /* Sun 12/28/8888 12:00 */ - mytm.tm_hour = 12; - g_array_append_val(timevals, mytm); - } - - /* NOTE: Ignoring year 8888 should handle it */ - - if (mask & STRFTIME_MASK_MONTH) { - gint oldlen = timevals->len; - gint i, j; - for (i = 0; i < oldlen; i++) { - for (j = 0; j < 11; j++) { - struct tm localval = g_array_index(timevals, struct tm, i); - localval.tm_mon = j; - /* Not sure if I need to adjust yday & wday, hope not */ - g_array_append_val(timevals, localval); - } - } - } - - /* Doing these together as it seems like just slightly more - coverage on the numerical days, but worth it. */ - if (mask & (STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY)) { - gint oldlen = timevals->len; - gint i, j; - for (i = 0; i < oldlen; i++) { - for (j = 22; j < 28; j++) { - struct tm localval = g_array_index(timevals, struct tm, i); - - gint diff = 28 - j; - - localval.tm_mday = j; - localval.tm_wday = localval.tm_wday - diff; - if (localval.tm_wday < 0) { - localval.tm_wday += 7; - } - localval.tm_yday = localval.tm_yday - diff; - - g_array_append_val(timevals, localval); - } - } - } - - return; -} - -/* Try to get a good guess at what a maximum width of the entire - string would be. */ -static void -guess_label_size (IndicatorDatetime * self) -{ - /* This is during startup. */ - if (self->priv->label == NULL) return; - - GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(self->priv->label)); - PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(self->priv->label)); - gint * max_width = &(self->priv->max_width); - gint posibilitymask = generate_strftime_bitmask(self->priv->time_string); - - /* Reset max width */ - *max_width = 0; - - /* Build the array of possibilities that we want to test */ - GArray * timevals = g_array_new(FALSE, TRUE, sizeof(struct tm)); - build_timeval_array(timevals, posibilitymask); - - g_debug("Checking against %d possible times", timevals->len); - gint check_time; - for (check_time = 0; check_time < timevals->len; check_time++) { - struct tm * timeval = &g_array_index(timevals, struct tm, check_time); - GDateTime * dt = g_date_time_new_local(timeval->tm_year, timeval->tm_mon, timeval->tm_mday, timeval->tm_hour, timeval->tm_min, timeval->tm_sec); - gchar * timestr = g_date_time_format(dt, self->priv->time_string); - - gint length = measure_string(style, context, timestr); - - g_free(timestr); - g_date_time_unref(dt); - - if (length > *max_width) { - *max_width = length; - } - } - - g_array_free(timevals, TRUE); - - gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1); - g_debug("Guessing max time width: %d", self->priv->max_width); - - return; -} - -/* React to the style changing, which could mean an font - update. */ -static void -style_changed (GtkWidget * widget, GtkStyle * oldstyle, gpointer data) -{ - g_debug("New style for time label"); - IndicatorDatetime * self = INDICATOR_DATETIME(data); - guess_label_size(self); - update_label(self, NULL); - timezone_update_all_labels(self); - return; -} - -/* Respond to changes in the screen to update the text gravity */ -static void -update_text_gravity (GtkWidget *widget, GdkScreen *previous_screen, gpointer data) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(data); - if (self->priv->label == NULL) return; - - PangoLayout *layout; - PangoContext *context; - - layout = gtk_label_get_layout (GTK_LABEL(self->priv->label)); - context = pango_layout_get_context(layout); - pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO); -} - -static gchar * -generate_format_string_now (IndicatorDatetime * self) -{ - if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) { - return g_strdup(self->priv->custom_string); - } - else { - return generate_format_string_full(self->priv->show_day, - self->priv->show_date); - } -} - -static void -timezone_update_labels (indicator_item_t * mi_data) -{ - const gchar * zone = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_ZONE); - const gchar * name = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_NAME); - - gtk_label_set_text(GTK_LABEL(mi_data->label), name); - - /* Show current time in that zone on the right */ - GTimeZone * tz = g_time_zone_new(zone); - set_label_to_time_in_zone(mi_data->self, GTK_LABEL(mi_data->right), tz, NULL, NULL); - g_time_zone_unref(tz); -} - -/* Whenever we have a property change on a DbusmenuMenuitem - we need to be responsive to that. */ -static void -indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, indicator_item_t * mi_data) -{ - if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_LABEL)) { - /* Set the main label */ - gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL)); - } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_RIGHT)) { - /* Set the right label */ - gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL)); - } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_ICON)) { - /* We don't use the value here, which is probably less efficient, - but it's easier to use the easy function. And since th value - is already cached, shouldn't be a big deal really. */ - GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(mi, APPOINTMENT_MENUITEM_PROP_ICON); - if (pixbuf != NULL) { - /* If we've got a pixbuf we need to make sure it's of a reasonable - size to fit in the menu. If not, rescale it. */ - GdkPixbuf * resized_pixbuf; - gint width, height; - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); - if (gdk_pixbuf_get_width(pixbuf) > width || - gdk_pixbuf_get_height(pixbuf) > height) { - g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height); - resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf, - width, - height, - GDK_INTERP_BILINEAR); - } else { - g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); - resized_pixbuf = pixbuf; - } - gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf); - /* The other pixbuf should be free'd by the dbusmenu. */ - if (resized_pixbuf != pixbuf) { - g_object_unref(resized_pixbuf); - } - } - } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_ZONE)) { - timezone_update_labels(mi_data); - } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_NAME)) { - timezone_update_labels(mi_data); - } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_RADIO)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi), g_variant_get_boolean(value)); - } - return; -} -// Properties for marking and unmarking the calendar -static void -calendar_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, IdoCalendarMenuItem * mi_data) -{ - g_debug("Changing calendar property: %s", prop); - if (!g_strcmp0(prop, CALENDAR_MENUITEM_PROP_MARKS)) { - ido_calendar_menu_item_clear_marks (IDO_CALENDAR_MENU_ITEM (mi_data)); - - if (value != NULL) { - GVariantIter *iter; - gint day; - - g_debug("\tMarks: %s", g_variant_print(value, FALSE)); - - g_variant_get (value, "ai", &iter); - while (g_variant_iter_loop (iter, "i", &day)) { - ido_calendar_menu_item_mark_day (IDO_CALENDAR_MENU_ITEM (mi_data), day); - } - g_variant_iter_free (iter); - } else { - g_debug("\tMarks: "); - } - } - return; -} - -/* We have a small little menuitem type that handles all - of the fun stuff for indicators. Mostly this is the - shifting over and putting the icon in with some right - side text that'll be determined by the service. - Copied verbatim from an old revision (including comments) of indicator-messages -*/ -static gboolean -new_appointment_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data) -{ - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE); - /* Note: not checking parent, it's reasonable for it to be NULL */ - IndicatorDatetime * self = INDICATOR_DATETIME(user_data); - - indicator_item_t * mi_data = g_new0(indicator_item_t, 1); - - mi_data->gmi = gtk_menu_item_new(); - - GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); - - /* Icon, probably someone's face or avatar on an IM */ - mi_data->icon = gtk_image_new(); - GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, APPOINTMENT_MENUITEM_PROP_ICON); - - if (pixbuf != NULL) { - /* If we've got a pixbuf we need to make sure it's of a reasonable - size to fit in the menu. If not, rescale it. */ - GdkPixbuf * resized_pixbuf; - gint width, height; - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); - if (gdk_pixbuf_get_width(pixbuf) > width || - gdk_pixbuf_get_height(pixbuf) > height) { - g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height); - resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf, - width, - height, - GDK_INTERP_BILINEAR); - } else { - g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); - resized_pixbuf = pixbuf; - } - - gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf); - - /* The other pixbuf should be free'd by the dbusmenu. */ - if (resized_pixbuf != pixbuf) { - g_object_unref(resized_pixbuf); - } - } - gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0); - gtk_widget_show(mi_data->icon); - - /* Label, probably a username, chat room or mailbox name */ - mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_LABEL)); - gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5); - - GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(mi_data->label)); - PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(mi_data->label)); - gint length = measure_string(style, context, "MMMMMMMMMMMMMMM"); // 15 char wide string max - gtk_widget_set_size_request(GTK_WIDGET(mi_data->label), length, -1); // Set the min size in pixels - - gtk_label_set_ellipsize(GTK_LABEL(mi_data->label), PANGO_ELLIPSIZE_END); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0); - gtk_widget_show(mi_data->label); - - /* Usually either the time or the count on the individual - item. */ - mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_RIGHT)); - gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right); - gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0); - gtk_widget_show(mi_data->right); - - gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox); - gtk_widget_show(hbox); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent); - - g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data); - return TRUE; -} - -static void -month_changed_cb (IdoCalendarMenuItem *ido, - gpointer user_data) -{ - guint d,m,y; - DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data); - ido_calendar_menu_item_get_date(ido, &y, &m, &d); - struct tm date = {0}; - date.tm_mday = d; - date.tm_mon = m; - date.tm_year = y - 1900; - guint selecteddate = (guint)mktime(&date); - g_debug("Got month changed signal: %s", asctime(&date)); - GVariant *variant = g_variant_new_uint32(selecteddate); - guint timestamp = (guint)time(NULL); - dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "month-changed", variant, timestamp); -} - -static void -day_selected_cb (IdoCalendarMenuItem *ido, - gpointer user_data) -{ - guint d,m,y; - DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data); - ido_calendar_menu_item_get_date(ido, &y, &m, &d); - struct tm date = {0}; - date.tm_mday = d; - date.tm_mon = m; - date.tm_year = y - 1900; - guint selecteddate = (guint)mktime(&date); - g_debug("Got day selected signal: %s", asctime(&date)); - GVariant *variant = g_variant_new_uint32(selecteddate); - guint timestamp = (guint)time(NULL); - dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected", variant, timestamp); -} - -static void -day_selected_double_click_cb (IdoCalendarMenuItem *ido, - gpointer user_data) -{ - guint d,m,y; - DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data); - ido_calendar_menu_item_get_date(ido, &y, &m, &d); - struct tm date = {0}; - date.tm_mday = d; - date.tm_mon = m; - date.tm_year = y - 1900; - guint selecteddate = (guint)mktime(&date); - g_debug("Got day selected double click signal: %s", asctime(&date)); - GVariant *variant = g_variant_new_uint32(selecteddate); - guint timestamp = (guint)time(NULL); - dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected-double-click", variant, timestamp); -} - -static gboolean -new_calendar_item (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_debug("New calendar item"); - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE); - /* Note: not checking parent, it's reasonable for it to be NULL */ - - IndicatorDatetime *self = INDICATOR_DATETIME(user_data); - - IdoCalendarMenuItem *ido = IDO_CALENDAR_MENU_ITEM (ido_calendar_menu_item_new ()); - self->priv->ido_calendar = ido; - - GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar); - if (self->priv->show_week_numbers == TRUE) - flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS; - else - flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS; - ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags); - - gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(ido), parent); - - g_signal_connect_after(ido, "month-changed", G_CALLBACK(month_changed_cb), (gpointer)newitem); - g_signal_connect_after(ido, "day-selected", G_CALLBACK(day_selected_cb), (gpointer)newitem); - g_signal_connect_after(ido, "day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), (gpointer)newitem); - - g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(calendar_prop_change_cb), ido); - - /* Run the current values through prop changed */ - GVariant * propval = NULL; - - propval = dbusmenu_menuitem_property_get_variant(newitem, CALENDAR_MENUITEM_PROP_MARKS); - if (propval != NULL) { - calendar_prop_change_cb(newitem, CALENDAR_MENUITEM_PROP_MARKS, propval, ido); - } - - return TRUE; -} - -static void -timezone_toggled_cb (GtkCheckMenuItem *checkmenuitem, DbusmenuMenuitem * dbusitem) -{ - /* Make sure that the displayed radio-active setting is always - consistent with the dbus menuitem */ - gtk_check_menu_item_set_active(checkmenuitem, - dbusmenu_menuitem_property_get_bool(dbusitem, TIMEZONE_MENUITEM_PROP_RADIO)); -} - -static void -timezone_destroyed_cb (indicator_item_t * mi_data, DbusmenuMenuitem * dbusitem) -{ - IndicatorDatetime *self = INDICATOR_DATETIME (mi_data->self); - IndicatorDatetimePrivate *priv = self->priv; - - priv->timezone_items = g_list_remove(priv->timezone_items, mi_data); - g_signal_handlers_disconnect_by_func(G_OBJECT(mi_data->gmi), G_CALLBACK(timezone_toggled_cb), dbusitem); - g_free(mi_data); -} - -static gboolean -new_timezone_item(DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE); - /* Note: not checking parent, it's reasonable for it to be NULL */ - - IndicatorDatetime * self = INDICATOR_DATETIME(user_data); - IndicatorDatetimePrivate *priv = self->priv; - - // Menu item with a radio button and a right aligned time - indicator_item_t * mi_data = g_new0(indicator_item_t, 1); - - priv->timezone_items = g_list_prepend(priv->timezone_items, mi_data); - - mi_data->self = self; - mi_data->mi = newitem; - mi_data->gmi = gtk_check_menu_item_new(); - - gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(mi_data->gmi), TRUE); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi), - dbusmenu_menuitem_property_get_bool(newitem, TIMEZONE_MENUITEM_PROP_RADIO)); - - GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); - - /* Label, probably a username, chat room or mailbox name */ - mi_data->label = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0); - gtk_widget_show(mi_data->label); - - /* Usually either the time or the count on the individual - item. */ - mi_data->right = gtk_label_new(""); - gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right); - gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0); - gtk_widget_show(mi_data->right); - - timezone_update_labels(mi_data); - - gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox); - gtk_widget_show(hbox); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent); - - g_signal_connect(G_OBJECT(mi_data->gmi), "toggled", G_CALLBACK(timezone_toggled_cb), newitem); - g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data); - g_object_weak_ref(G_OBJECT(newitem), (GWeakNotify)timezone_destroyed_cb, mi_data); - - return TRUE; -} - -/* Grabs the label. Creates it if it doesn't - exist already */ -static GtkLabel * -get_label (IndicatorObject * io) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(io); - - /* If there's not a label, we'll build ourselves one */ - if (self->priv->label == NULL) { - self->priv->label = GTK_LABEL(gtk_label_new("Time")); - gtk_label_set_justify (GTK_LABEL(self->priv->label), GTK_JUSTIFY_CENTER); - g_object_ref(G_OBJECT(self->priv->label)); - g_signal_connect(G_OBJECT(self->priv->label), "style-set", G_CALLBACK(style_changed), self); - g_signal_connect(G_OBJECT(self->priv->label), "screen-changed", G_CALLBACK(update_text_gravity), self); - guess_label_size(self); - update_label(self, NULL); - gtk_widget_set_visible(GTK_WIDGET (self->priv->label), self->priv->show_clock); - } - - if (self->priv->timer == 0) { - setup_timer(self, NULL); - } - - return self->priv->label; -} - -static GtkMenu * -get_menu (IndicatorObject * io) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(io); - - return GTK_MENU(self->priv->menu); -} - -static const gchar * -get_accessible_desc (IndicatorObject * io) -{ - IndicatorDatetime * self = INDICATOR_DATETIME(io); - const gchar * name; - - if (self->priv->label != NULL) { - name = gtk_label_get_text(self->priv->label); - return name; - } - return NULL; -} - -static const gchar * -get_name_hint (IndicatorObject * io) -{ - return PACKAGE_NAME; -} -- cgit v1.2.3 From ba84cae2fe41496a89cc964bc5bc0bf13c9796ed Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:04:14 -0500 Subject: remove build dependencies on dbusmenu, libindicator --- configure.ac | 33 ++++++--------------------------- debian/control | 3 --- src/Makefile.am | 37 +------------------------------------ 3 files changed, 7 insertions(+), 66 deletions(-) diff --git a/configure.ac b/configure.ac index e9acb8c..22a6ee5 100644 --- a/configure.ac +++ b/configure.ac @@ -38,48 +38,27 @@ AS_IF([test "x$enable_deprecations" = xno], [CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGSEAL_ENABLE -DGTK_DISABLE_SINGLE_INCLUDES"] ) -AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums]) -AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal]) - PKG_PROG_PKG_CONFIG ########################### # Dependencies ########################### -INDICATOR_REQUIRED_VERSION=0.3.19 -DBUSMENUGLIB_REQUIRED_VERSION=0.1.1 -DBUSMENUGTK_REQUIRED_VERSION=0.5.90 +GLIB_REQUIRED_VERSION=2.35.4 GIO_REQUIRED_VERSION=2.25.11 -INDICATOR_DISPLAY_OBJECTS=0.2.2 GEOCLUE_REQUIRED_VERSION=0.12.0 +ICAL_REQUIRED_VERSION=0.48 ECAL_REQUIRED_VERSION=3.5 EDS_REQUIRED_VERSION=3.5 -ICAL_REQUIRED_VERSION=0.48 -CAIRO_REQUIRED_VERSION=1.10 -GDK_REQUIRED_VERSION=2.22 -GLIB_REQUIRED_VERSION=2.35.4 + GTK3_REQUIRED_VERSION=3.1.4 -PKG_CHECK_MODULES(INDICATOR, indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION - glib-2.0 >= $GLIB_REQUIRED_VERSION - dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION - dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION - libido3-0.1 >= $INDICATOR_DISPLAY_OBJECTS) - -PKG_CHECK_MODULES(SERVICE, indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION - glib-2.0 >= $GLIB_REQUIRED_VERSION - dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION - dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION - libido3-0.1 >= $INDICATOR_DISPLAY_OBJECTS +PKG_CHECK_MODULES(SERVICE, glib-2.0 >= $GLIB_REQUIRED_VERSION gio-2.0 >= $GIO_REQUIRED_VERSION geoclue >= $GEOCLUE_REQUIRED_VERSION - libecal-1.2 >= $ECAL_REQUIRED_VERSION libical >= $ICAL_REQUIRED_VERSION - libedataserver-1.2 >= EDS_REQUIRED_VERSION - libedataserverui-3.0 >= EDS_REQUIRED_VERSION - cairo >= CAIRO_REQUIRED_VERSION - gdk-3.0 >= GDK_REQUIRED_VERSION) + libecal-1.2 >= $ECAL_REQUIRED_VERSION + libedataserver-1.2 >= EDS_REQUIRED_VERSION) ########################### # Control Center panel diff --git a/debian/control b/debian/control index 41bdb0f..01fc757 100644 --- a/debian/control +++ b/debian/control @@ -9,9 +9,6 @@ Build-Depends: debhelper (>= 9), gnome-common, libxorg-gtest-dev, libgtest-dev, - libindicator3-dev (>= 0.3.90), - libdbusmenu-glib-dev (>= 0.5.90), - libdbusmenu-gtk3-dev (>= 0.5.90), libglib2.0-dev (>= 2.35.4), libido3-0.1-dev (>= 0.2.90), libgeoclue-dev (>= 0.12.0), diff --git a/src/Makefile.am b/src/Makefile.am index fee2245..31639af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,9 +7,6 @@ endif libexec_PROGRAMS = indicator-datetime-service indicator_datetime_service_SOURCES = \ - datetime-interface.c \ - datetime-interface.h \ - gen-datetime-service.xml.c \ planner.c \ planner.h \ planner-eds.c \ @@ -27,6 +24,7 @@ indicator_datetime_service_SOURCES = \ utils.h \ dbus-shared.h \ settings-shared.h + indicator_datetime_service_CFLAGS = \ -Wall \ -Wextra -Wno-missing-field-initializers \ @@ -40,28 +38,6 @@ indicator_datetime_service_LDADD = \ indicator_datetime_service_LDFLAGS = \ $(COVERAGE_LDFLAGS) -datetimelibdir = $(INDICATORDIR) -datetimelib_LTLIBRARIES = libdatetime.la -libdatetime_la_SOURCES = \ - gen-datetime-service.xml.h \ - dbus-shared.h \ - settings-shared.h \ - utils.c \ - utils.h \ - indicator-datetime.c -libdatetime_la_CFLAGS = \ - $(INDICATOR_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - -Wall -Werror \ - -DTIMEZONE_FILE="\"/etc/timezone\"" \ - -DG_LOG_DOMAIN=\"Indicator-Datetime\" -libdatetime_la_LIBADD = \ - $(INDICATOR_LIBS) -libdatetime_la_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - -module \ - -avoid-version - if BUILD_CCPANEL libindicator_datetime_la_SOURCES =\ datetime-prefs.c \ @@ -93,14 +69,3 @@ gen-%.xml.c: %.xml gen-%.xml.h: %.xml @echo "Building $@ from $<" @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@ - -BUILT_SOURCES = \ - gen-datetime-service.xml.c \ - gen-datetime-service.xml.h - - -CLEANFILES = \ - $(BUILT_SOURCES) - -EXTRA_DIST = \ - datetime-service.xml -- cgit v1.2.3 From 18f93e6ec8aab4923e192be4d74c83b56626c6d2 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:04:43 -0500 Subject: add the .indicator file --- data/datetime.indicator | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 data/datetime.indicator diff --git a/data/datetime.indicator b/data/datetime.indicator new file mode 100644 index 0000000..055ea8e --- /dev/null +++ b/data/datetime.indicator @@ -0,0 +1,9 @@ +[Indicator Service] +Name=indicator-datetime +ObjectPath=/com/canonical/indicator/datetime + +[desktop] +ObjectPath=/com/canonical/indicator/datetime/desktop + +[greeter] +ObjectPath=/com/canonical/indicator/datetime/greeter -- cgit v1.2.3 From 0fa8088983a1295b275f1b23af1f5c69fee7dd24 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:05:17 -0500 Subject: update documentation about the widgets and GActions --- README | 75 ++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/README b/README index ebeed8b..c6a8780 100644 --- a/README +++ b/README @@ -1,43 +1,39 @@ ACTIONS ======= - * "aboutToShow" - Description: notifies the indicator that its menu is about to be shown. - Workitems that are expensive on startup (such as loading EDS to query - it for appointments, or loading GeoClue to guess our current location) - may be deferred until this is called. - So, calendarDay's state may not be fully populated until this is called. - FIXME: aboutToShow should be spec'ed once for all indicators, not ad-hoc - - * "activateSettings" + * "activate-settings" Description: opens a page for changing indicator-datetime's settings - State: NULL - Parameter: NULL - - * "activatePlanner" - Description: opens up the appointment editor. - State: NULL - Parameter: uint64 (a time_t hinting which day/time to show in the planner, - or 0 for the current day) - - * "calendarDay" - Description: set which month/day should be given focus in the indicator's - calendar and to use as a starting point when listing upcoming - appointments in the indicator. - Client code implementing the calendar widget should call this - when the user clicks to a different day, month, or year. - State: a dictionary containing these key value pairs: - "calendarDay": string ("YYYY-MM-DD"). Used by the calendar menuitem - to know which year/month should be visible and which - day should have the cursor. - "daysWithAppointments": an array of day-of-month ints. Used by the - calendar menuitem to mark appointment days. - Parameter: uint64 (a time_t specifying which day to give focus to) - - * "location" - Description: Sets the user's current location. Called by location menuitems. - State: string containing a location and a timezone - Parameter: string containing a location and a timezone + State: None + Parameter: None + + * "activate-planner" + Description: opens up a calendar appointment editor. + State: None + Parameter: int64, a time_t hinting which day/time to show in the planner, + or 0 for the current day + + * "set-location" + Description: Set the current location. This will try to set the current + timezone to the new location's timezone. + State: None + Parameter: a timezone id string followed by a space and location name. + Example: "America/Chicago Oklahoma City" + + * "calendar" + Description: set which month/day should be given focus in the indicator's + calendar. The planner will look for appointments from this + day to the end of the same month. + Client code implementing the calendar view should call this + when the user clicks on a new day, month, or year. + State: a dictionary containing these key value/pairs: + "appointment-days": an array of day-of-month ints. Used by the + calendar menuitem to mark appointment days. + "calendar-day": int64, a time_t. Used by the calendar menuitem + to know which year/month should be visible + and which day should have the cursor. + "show-week-numbers": if true, show week numbers in the calendar. + Parameter: int64, a time_t specifying which year/month should be visible + and which day should have the cursor. CUSTOM MENUITEMS @@ -47,14 +43,15 @@ CUSTOM MENUITEMS - x-canonical-type s "com.canonical.indicator.calendar" * Appointment + - label s short summary of the appointment - x-canonical-type s "com.canonical.indicator.appointment" - x-canonical-color s color of the appt's type, to give a visual cue - - label s short summary of the appointment - - x-canonical-right-label s the date of the appointment + - x-canonical-time x the date of the appointment + - x-canonical-time-format s strftime format string * Location - - x-canonical-type s "com.canonical.indicator.location" - label s the location's name, eg "Oklahoma City" + - x-canonical-type s "com.canonical.indicator.location" - x-canonical-timezone s timezone that the location is in - x-canonical-time-format s strftime format string -- cgit v1.2.3 From fd12257bf524c53b0bcfba1ce96c28ae612d206f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:07:20 -0500 Subject: land the service implementation --- src/service.c | 1357 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/service.h | 4 +- 2 files changed, 1290 insertions(+), 71 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 + * Ted Gould * * 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 . */ -#include +#include "config.h" + +#include /* strstr() */ #include #include +#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; isettings, 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; isettings, 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; imessage); + 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; imenus[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); isettings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + + for (i=0, n=G_N_ELEMENTS(calendar_settings); isettings, gstr->str, + G_CALLBACK(rebuild_calendar_section_soon), self); + } + + for (i=0, n=G_N_ELEMENTS(appointment_settings); isettings, gstr->str, + G_CALLBACK(rebuild_appointments_section_soon), self); + } + + for (i=0, n=G_N_ELEMENTS(location_settings); isettings, 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); isettings, 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; diff --git a/src/service.h b/src/service.h index 594d7fe..47dd937 100644 --- a/src/service.h +++ b/src/service.h @@ -26,10 +26,8 @@ G_BEGIN_DECLS /* standard GObject macros */ -#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type()) #define INDICATOR_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeService)) -#define INDICATOR_DATETIME_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeServiceClass)) -#define INDICATOR_DATETIME_SERVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeServiceClass)) +#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type()) #define INDICATOR_IS_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_SERVICE)) typedef struct _IndicatorDatetimeService IndicatorDatetimeService; -- cgit v1.2.3 From 06776754f024c45f55063ac6f0df8ab0cb78defe Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 16:22:28 -0500 Subject: add TODO to show status --- TODO | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..dd4a89f --- /dev/null +++ b/TODO @@ -0,0 +1,9 @@ +SHORT TERM TODO: + - due dilligence in a profiler + - need to install the .indicator file during "make install" + - remove shared-dbus: prefs panel needs to look for running indicator in a new way + - fix broken indicator test + +LONG TERM TODO: + - test coverage + - lots of the service is unmockable b/c of calls to time(NULL), g_date_time_new_local(), etc -- cgit v1.2.3 From 30dc0c769a9f048fd93a3c5f3cbff0aa8c923377 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 15 Jun 2013 19:28:02 -0500 Subject: in service.c, fix virtual finalize() function chaining up --- src/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.c b/src/service.c index 7287336..bb71a33 100644 --- a/src/service.c +++ b/src/service.c @@ -1678,7 +1678,7 @@ my_finalize (GObject * o) g_clear_pointer (&p->skew_time, g_date_time_unref); - G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o); + G_OBJECT_CLASS (indicator_datetime_service_parent_class)->finalize (o); } /*** -- cgit v1.2.3 From 4ec7af235c35dbcea1daaf497c6307213b4d9d9c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 15 Jun 2013 19:28:36 -0500 Subject: in src/Makefile.am, remove unnecessary gen-%.xml.[ch] rules --- src/Makefile.am | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 31639af..7143c6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,13 +59,3 @@ libindicator_datetime_la_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ -module -avoid-version endif - -gen-%.xml.c: %.xml - @echo "Building $@ from $<" - @echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@ - @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@ - @echo ";" >> $@ - -gen-%.xml.h: %.xml - @echo "Building $@ from $<" - @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@ -- cgit v1.2.3 From 49ee32fefe869a778f39f88a936f546ef2c68737 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 16 Jun 2013 23:35:54 -0500 Subject: in planner.c, fix virtual finalize() function chaining up --- src/planner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/planner.c b/src/planner.c index f16a05a..1643651 100644 --- a/src/planner.c +++ b/src/planner.c @@ -106,7 +106,7 @@ my_finalize (GObject * o) g_free (self->priv->timezone); - G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->dispose (o); + G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->finalize (o); } /*** -- cgit v1.2.3 From 72f23220db542cb8e41e9492528e79082c916804 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 09:06:02 -0500 Subject: in service.c, replace all calls to time(NULL) and g_date_time_new_now_local() with a call to the new 'indicator_datetime_service_get_localtime()' func. This is currently a passthrough to _new_now_local(), but we'll need this control point to override the system time in unit testing --- src/datetime-prefs-locations.c | 9 ++-- src/service.c | 111 ++++++++++++++++++++--------------------- src/service.h | 2 + src/utils.c | 6 +-- src/utils.h | 2 +- 5 files changed, 63 insertions(+), 67 deletions(-) diff --git a/src/datetime-prefs-locations.c b/src/datetime-prefs-locations.c index 33fd660..4bbf053 100644 --- a/src/datetime-prefs-locations.c +++ b/src/datetime-prefs-locations.c @@ -25,6 +25,7 @@ with this program. If not, see . #endif #include +#include /* time_t */ #include #include #include @@ -416,10 +417,9 @@ update_times (GtkWidget * dlg) g_signal_handlers_block_by_func (store, save_when_idle, dlg); - GDateTime * now = g_date_time_new_now_local (); - GtkTreeIter iter; if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { + GDateTime * now = g_date_time_new_now_local (); do { gchar * strzone; @@ -428,7 +428,7 @@ update_times (GtkWidget * dlg) if (strzone && *strzone) { GTimeZone * tz = g_time_zone_new (strzone); GDateTime * now_tz = g_date_time_to_timezone (now, tz); - gchar * format = generate_format_string_at_time (now_tz); + gchar * format = generate_format_string_at_time (now, now_tz); gchar * time_str = g_date_time_format (now_tz, format); gchar * old_time_str; @@ -444,10 +444,9 @@ update_times (GtkWidget * dlg) } g_free (strzone); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + g_date_time_unref (now); } - g_date_time_unref (now); - g_signal_handlers_unblock_by_func (store, save_when_idle, dlg); return TRUE; diff --git a/src/service.c b/src/service.c index bb71a33..55905aa 100644 --- a/src/service.c +++ b/src/service.c @@ -211,18 +211,15 @@ rebuild_settings_section_soon (IndicatorDatetimeService * self) */ static guint -calculate_seconds_until_next_fifteen_minutes (void) +calculate_seconds_until_next_fifteen_minutes (GDateTime * now) { 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); @@ -242,7 +239,6 @@ calculate_seconds_until_next_fifteen_minutes (void) g_date_time_unref (start_of_next); g_date_time_unref (next); - g_date_time_unref (now); return seconds; } @@ -267,13 +263,16 @@ on_timezone_timer (gpointer gself) static void start_timezone_timer (IndicatorDatetimeService * self) { + GDateTime * now; + guint seconds; priv_t * p = self->priv; indicator_clear_timer (&p->timezone_timer); - p->timezone_timer = g_timeout_add_seconds (calculate_seconds_until_next_fifteen_minutes(), - on_timezone_timer, - self); + now = indicator_datetime_service_get_localtime (self); + seconds = calculate_seconds_until_next_fifteen_minutes (now); + p->timezone_timer = g_timeout_add_seconds (seconds, on_timezone_timer, self); + g_date_time_unref (now); } /*** @@ -288,15 +287,13 @@ start_timezone_timer (IndicatorDatetimeService * self) */ static guint -calculate_milliseconds_until_next_minute (void) +calculate_milliseconds_until_next_minute (GDateTime * now) { - 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), @@ -310,25 +307,19 @@ calculate_milliseconds_until_next_minute (void) 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) +calculate_milliseconds_until_next_second (GDateTime * now) { - 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; } @@ -355,6 +346,7 @@ start_header_timer (IndicatorDatetimeService * self) guint interval_msec; gboolean header_shows_seconds = FALSE; priv_t * p = self->priv; + GDateTime * now = indicator_datetime_service_get_localtime (self); indicator_clear_timer (&p->header_timer); @@ -368,9 +360,9 @@ start_header_timer (IndicatorDatetimeService * self) } if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second (); + interval_msec = calculate_milliseconds_until_next_second (now); else - interval_msec = calculate_milliseconds_until_next_minute (); + interval_msec = calculate_milliseconds_until_next_minute (now); interval_msec += 50; /* add a small margin to ensure the callback fires /after/ next is reached */ @@ -380,6 +372,8 @@ start_header_timer (IndicatorDatetimeService * self) on_header_timer, self, NULL); + + g_date_time_unref (now); } /** @@ -407,8 +401,8 @@ on_local_time_jumped (IndicatorDatetimeService * self) static gboolean skew_timer_func (gpointer gself) { - GDateTime * now = g_date_time_new_now_local (); IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); + GDateTime * now = indicator_datetime_service_get_localtime (self); priv_t * p = self->priv; /* check for clock skew: has too much time passed since the last check? */ @@ -490,15 +484,15 @@ create_header_state (IndicatorDatetimeService * self) gchar * fmt; gchar * str; gboolean visible; - GDateTime * now_local; + GDateTime * now; 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); + now = indicator_datetime_service_get_localtime (self); + str = g_date_time_format (now, fmt); if (str == NULL) { str = g_strdup (_("Unsupported date format")); @@ -511,7 +505,7 @@ create_header_state (IndicatorDatetimeService * self) g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (visible)); /* cleanup */ - g_date_time_unref (now_local); + g_date_time_unref (now); g_free (str); g_free (fmt); return g_variant_builder_end (&b); @@ -530,10 +524,10 @@ 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 + if (p->calendar_date) date = g_date_time_new_from_unix_local ((gint64)p->calendar_date); + else + date = indicator_datetime_service_get_localtime (self); return date; } @@ -626,26 +620,19 @@ create_calendar_section (IndicatorDatetimeService * self) { char * label; GMenuItem * menu_item; - GDateTime * date_time; + GDateTime * now; 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); - } + now = indicator_datetime_service_get_localtime (self); + label = g_date_time_format (now, _("%A, %e %B %Y")); 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); + g_date_time_unref (now); /* create the calendar menuitem */ if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CALENDAR_S)) @@ -706,7 +693,7 @@ get_upcoming_appointments (IndicatorDatetimeService * self) } static char * -get_appointment_time_format (struct IndicatorDatetimeAppt * appt) +get_appointment_time_format (struct IndicatorDatetimeAppt * appt, GDateTime * now) { char * fmt; gboolean full_day = g_date_time_difference (appt->end, appt->begin) == G_TIME_SPAN_DAY; @@ -721,7 +708,7 @@ get_appointment_time_format (struct IndicatorDatetimeAppt * appt) } else { - fmt = generate_format_string_at_time (appt->begin); + fmt = generate_format_string_at_time (now, appt->begin); } return fmt; @@ -738,13 +725,14 @@ create_appointments_section (IndicatorDatetimeService * self) GSList * l; GSList * appts; GMenuItem * menu_item; + GDateTime * now = indicator_datetime_service_get_localtime (self); /* 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); + char * fmt = get_appointment_time_format (appt, now); const gint64 unix_time = g_date_time_to_unix (appt->begin); menu_item = g_menu_item_new (appt->summary, NULL); @@ -773,6 +761,7 @@ create_appointments_section (IndicatorDatetimeService * self) g_object_unref (menu_item); /* cleanup */ + g_date_time_unref (now); g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free); } @@ -846,7 +835,7 @@ set_detect_location_enabled (IndicatorDatetimeService * self, gboolean enabled) for pruning duplicates and sorting. */ struct TimeLocation { - gint32 offset; + GTimeSpan offset; gchar * zone; gchar * name; gboolean visible; @@ -865,17 +854,15 @@ time_location_free (struct TimeLocation * loc) static struct TimeLocation* time_location_new (const char * zone, const char * name, - gboolean visible, - time_t now) + gboolean visible) { 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); + loc->offset = g_date_time_get_utc_offset (loc->local_time); g_time_zone_unref (tz); return loc; } @@ -884,7 +871,10 @@ static int time_location_compare (const struct TimeLocation * a, const struct TimeLocation * b) { - int ret = a->offset - b->offset; /* primary key */ + int ret = 0; + + if (!ret && (a->offset != b->offset)) /* primary key */ + ret = (a->offset < b->offset) ? -1 : 1; if (!ret) ret = g_strcmp0 (a->name, b->name); /* secondary key */ @@ -899,10 +889,9 @@ static GSList* locations_add (GSList * locations, const char * zone, const char * name, - gboolean visible, - time_t now) + gboolean visible) { - struct TimeLocation * loc = time_location_new (zone, name, visible, now); + struct TimeLocation * loc = time_location_new (zone, name, visible); if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare)) { @@ -927,9 +916,9 @@ create_locations_section (IndicatorDatetimeService * self) GSList * locations = NULL; gchar ** user_locations; gboolean visible; - const time_t now = time (NULL); - priv_t * p = self->priv; IndicatorDatetimeTimezone * detected_timezones[2]; + 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)); @@ -953,7 +942,7 @@ create_locations_section (IndicatorDatetimeService * self) if (tz && *tz) { gchar * name = get_current_zone_name (tz); - locations = locations_add (locations, tz, name, visible, now); + locations = locations_add (locations, tz, name, visible); g_free (name); } } @@ -970,7 +959,7 @@ create_locations_section (IndicatorDatetimeService * self) gchar * zone; gchar * name; split_settings_location (user_locations[i], &zone, &name); - locations = locations_add (locations, zone, name, visible, now); + locations = locations_add (locations, zone, name, visible); g_free (name); g_free (zone); } @@ -994,7 +983,7 @@ create_locations_section (IndicatorDatetimeService * self) detailed_action = g_strdup_printf ("indicator.set-location::%s %s", loc->zone, loc->name); - fmt = generate_format_string_at_time (loc->local_time); + fmt = generate_format_string_at_time (now, loc->local_time); menu_item = g_menu_item_new (label, detailed_action); g_menu_item_set_attribute (menu_item, "x-canonical-type", @@ -1012,6 +1001,7 @@ create_locations_section (IndicatorDatetimeService * self) } } + g_date_time_unref (now); g_slist_free_full (locations, (GDestroyNotify)time_location_free); return G_MENU_MODEL (menu); } @@ -1857,3 +1847,12 @@ indicator_datetime_service_new (gboolean replace) 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 (); +} + diff --git a/src/service.h b/src/service.h index 47dd937..b8c9bcd 100644 --- a/src/service.h +++ b/src/service.h @@ -64,6 +64,8 @@ GType indicator_datetime_service_get_type (void); IndicatorDatetimeService * indicator_datetime_service_new (gboolean replace); +GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service); + G_END_DECLS #endif /* __INDICATOR_DATETIME_SERVICE_H__ */ diff --git a/src/utils.c b/src/utils.c index 07a2022..fa54008 100644 --- a/src/utils.c +++ b/src/utils.c @@ -226,7 +226,7 @@ generate_format_string_full (gboolean show_day, gboolean show_date) } gchar * -generate_format_string_at_time (GDateTime * time) +generate_format_string_at_time (GDateTime * now, GDateTime * time) { /* This is a bit less free-form than for the main "now" time label. */ /* If it is today, just the time should be shown (e.g. “3:55 PM”) @@ -236,8 +236,6 @@ generate_format_string_at_time (GDateTime * time) gboolean show_day = FALSE; gboolean show_date = FALSE; - GDateTime * now = g_date_time_new_now_local(); - /* First, are we same day? */ gint time_year, time_month, time_day; gint now_year, now_month, now_day; @@ -272,8 +270,6 @@ generate_format_string_at_time (GDateTime * time) g_date_time_unref(future_bound); } - g_date_time_unref (now); - return generate_format_string_full(show_day, show_date); } diff --git a/src/utils.h b/src/utils.h index c2bc0c5..8dc2964 100644 --- a/src/utils.h +++ b/src/utils.h @@ -31,7 +31,7 @@ gboolean is_locale_12h (void); void split_settings_location (const gchar * location, gchar ** zone, gchar ** name); gchar * get_current_zone_name (const gchar * location); gchar * generate_format_string_full (gboolean show_day, gboolean show_date); -gchar * generate_format_string_at_time (GDateTime * time); +gchar * generate_format_string_at_time (GDateTime * now, GDateTime * time); G_END_DECLS -- cgit v1.2.3 From d368216efb7bb5befbf8a41d99098ca130c610ab Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 09:16:17 -0500 Subject: in service.c, use GDateTime internally instead of time_t --- src/service.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/service.c b/src/service.c index 55905aa..e9b4c1a 100644 --- a/src/service.c +++ b/src/service.c @@ -119,7 +119,7 @@ struct _IndicatorDatetimeServicePrivate /* 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; + GDateTime * calendar_date; GSimpleActionGroup * actions; GSimpleAction * header_action; @@ -525,7 +525,7 @@ get_calendar_date (IndicatorDatetimeService * self) priv_t * p = self->priv; if (p->calendar_date) - date = g_date_time_new_from_unix_local ((gint64)p->calendar_date); + date = g_date_time_ref (p->calendar_date); else date = indicator_datetime_service_get_localtime (self); @@ -1240,8 +1240,7 @@ on_activate_planner (GSimpleAction * a G_GNUC_UNUSED, if (p->planner != NULL) { - const time_t t = g_variant_get_int64 (param); - + const gint64 t = g_variant_get_int64 (param); if (t) { GDateTime * date_time = g_date_time_new_from_unix_local (t); @@ -1262,14 +1261,16 @@ on_calendar_action_activated (GSimpleAction * action G_GNUC_UNUSED, { IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); priv_t * p = self->priv; - const time_t calendar_date = (time_t) g_variant_get_int64 (state); + gint64 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); - } + /* update calendar_date */ + g_clear_pointer (&p->calendar_date, g_date_time_unref); + if (t) + p->calendar_date = g_date_time_new_from_unix_local (t); + + /* sync the menuitems and action states */ + update_calendar_action_state (self); + rebuild_appointments_section_soon (self); } @@ -1667,6 +1668,7 @@ my_finalize (GObject * o) priv_t * p = self->priv; g_clear_pointer (&p->skew_time, g_date_time_unref); + g_clear_pointer (&p->calendar_date, g_date_time_unref); G_OBJECT_CLASS (indicator_datetime_service_parent_class)->finalize (o); } -- cgit v1.2.3 From 556eb6bdee7b2c5d3aa5676963fd77749edf4c9e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 09:31:56 -0500 Subject: copyediting: better variable names, function names, comments --- src/service.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/service.c b/src/service.c index e9b4c1a..74bddbf 100644 --- a/src/service.c +++ b/src/service.c @@ -54,10 +54,10 @@ enum { PROP_0, PROP_REPLACE, - PROP_LAST + LAST_PROP }; -static GParamSpec * properties[PROP_LAST]; +static GParamSpec * properties[LAST_PROP]; enum { @@ -203,7 +203,7 @@ rebuild_settings_section_soon (IndicatorDatetimeService * self) * 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. + * might have changed. We do that by updating when either zone's day changes. * * Since not all UTC offsets are evenly divisible by hours * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour @@ -1007,10 +1007,10 @@ create_locations_section (IndicatorDatetimeService * self) } /*** -**** +**** SET LOCATION ***/ -struct settimezone_data +struct setlocation_data { IndicatorDatetimeService * service; char * timezone_id; @@ -1018,7 +1018,7 @@ struct settimezone_data }; static void -settimezone_data_free (struct settimezone_data * data) +setlocation_data_free (struct setlocation_data * data) { g_free (data->timezone_id); g_free (data->name); @@ -1032,7 +1032,7 @@ on_datetime1_set_timezone_response (GObject * object, { GError * err; GVariant * answers; - struct settimezone_data * data = gdata; + struct setlocation_data * data = gdata; err = NULL; answers = g_dbus_proxy_call_finish (G_DBUS_PROXY(object), res, &err); @@ -1055,7 +1055,7 @@ on_datetime1_set_timezone_response (GObject * object, g_variant_unref (answers); } - settimezone_data_free (data); + setlocation_data_free (data); } static void @@ -1065,7 +1065,7 @@ on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, { GError * err; GDBusProxy * proxy; - struct settimezone_data * data = gdata; + struct setlocation_data * data = gdata; err = NULL; proxy = g_dbus_proxy_new_for_bus_finish (res, &err); @@ -1073,7 +1073,7 @@ on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, { g_warning ("Could not grab DBus proxy for timedated: %s", err->message); g_error_free (err); - settimezone_data_free (data); + setlocation_data_free (data); } else { @@ -1091,18 +1091,18 @@ on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, } static void -indicator_datetime_service_set_location (IndicatorDatetimeService * self, - const char * timezone_id, - const char * name) +indicator_datetime_service_set_location (IndicatorDatetimeService * self, + const char * timezone_id, + const char * name) { priv_t * p = self->priv; - struct settimezone_data * data; + struct setlocation_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 = g_new0 (struct setlocation_data, 1); data->timezone_id = g_strdup (timezone_id); data->name = g_strdup (name); data->service = self; @@ -1217,18 +1217,16 @@ execute_command (const gchar * cmd) } } -#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) { - execute_command (SETTINGS_APP_INVOCATION); +#ifdef HAVE_CCPANEL + execute_command ("gnome-control-center indicator-datetime"); +#else + execute_command ("gnome-control-center datetime"); +#endif } static void @@ -1833,7 +1831,7 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, PROP_LAST, properties); + g_object_class_install_properties (object_class, LAST_PROP, properties); } /*** -- cgit v1.2.3 From 1d015b51af186c0f71ff8164321feeef1cf63c19 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 09:33:23 -0500 Subject: make indicator_datetime_service_set_calendar_date() public. This is another one we'll need for unit tests --- src/service.c | 42 ++++++++++++++++++++++++++++++++---------- src/service.h | 4 ++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/service.c b/src/service.c index 74bddbf..cc0b17d 100644 --- a/src/service.c +++ b/src/service.c @@ -1257,18 +1257,19 @@ on_calendar_action_activated (GSimpleAction * action G_GNUC_UNUSED, GVariant * state, gpointer gself) { + gint64 unix_time; IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - priv_t * p = self->priv; - gint64 t = g_variant_get_int64 (state); - /* update calendar_date */ - g_clear_pointer (&p->calendar_date, g_date_time_unref); - if (t) - p->calendar_date = g_date_time_new_from_unix_local (t); - - /* sync the menuitems and action states */ - update_calendar_action_state (self); - rebuild_appointments_section_soon (self); + if ((unix_time = g_variant_get_int64 (state))) + { + GDateTime * date = g_date_time_new_from_unix_local (unix_time); + indicator_datetime_service_set_calendar_date (self, date); + g_date_time_unref (date); + } + else /* unset */ + { + indicator_datetime_service_set_calendar_date (self, NULL); + } } @@ -1856,3 +1857,24 @@ indicator_datetime_service_get_localtime (IndicatorDatetimeService * self G_GNUC return g_date_time_new_now_local (); } +void +indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, + GDateTime * date) +{ + gboolean dirty; + priv_t * p = self->priv; + + dirty = !date || !p->calendar_date || g_date_time_compare (date, p->calendar_date); + + /* update calendar_date */ + g_clear_pointer (&p->calendar_date, g_date_time_unref); + if (date != NULL) + p->calendar_date = g_date_time_ref (date); + + /* sync the menuitems and action states */ + if (dirty) + { + update_calendar_action_state (self); + rebuild_appointments_section_soon (self); + } +} diff --git a/src/service.h b/src/service.h index b8c9bcd..53d4281 100644 --- a/src/service.h +++ b/src/service.h @@ -66,6 +66,10 @@ IndicatorDatetimeService * indicator_datetime_service_new (gboolean replace); GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service); +void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, + GDateTime * date); + + G_END_DECLS #endif /* __INDICATOR_DATETIME_SERVICE_H__ */ -- cgit v1.2.3 From d1d65249d12ca309a3df7fac7614b866372b072f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 18 Jun 2013 01:05:53 -0500 Subject: rename the indicator bus file as 'com.canonical.indicator.datetime'; add it to data/Makefile --- configure.ac | 18 ---------- data/Makefile.am | 63 ++++++++++++++++++++--------------- data/com.canonical.indicator.datetime | 9 +++++ data/datetime.indicator | 9 ----- 4 files changed, 46 insertions(+), 53 deletions(-) create mode 100644 data/com.canonical.indicator.datetime delete mode 100644 data/datetime.indicator diff --git a/configure.ac b/configure.ac index 22a6ee5..315facd 100644 --- a/configure.ac +++ b/configure.ac @@ -115,23 +115,6 @@ AC_SUBST(COVERAGE_LDFLAGS) with_localinstall="no" AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no) -########################### -# Indicator Info -########################### - -AS_IF([test "x$with_localinstall" = "xyes"], - [ - INDICATORDIR="${libdir}/indicators/2/" - INDICATORICONSDIR="${datadir}/libindicate/icons/" - ], - [ - INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4` - INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator3-0.4` - ]) - -AC_SUBST(INDICATORDIR) -AC_SUBST(INDICATORICONSDIR) - ########################### # Control Center Info ########################### @@ -229,7 +212,6 @@ AC_MSG_NOTICE([ Date and Time Indicator Configuration: Prefix: $prefix - Indicator Dir: $INDICATORDIR CC Panel: $have_ccpanel CC Panel Dir: $CCPANELDIR Unit Tests: $enable_tests diff --git a/data/Makefile.am b/data/Makefile.am index 6d4dd72..e0019e6 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,34 +1,45 @@ -#SUBDIRS = icons +BUILT_SOURCES= +CLEANFILES= +EXTRA_DIST= -gsettings_SCHEMAS = \ - com.canonical.indicator.datetime.gschema.xml -@GSETTINGS_RULES@ +# +# the indicator bus file +# -dbus_servicesdir = $(DBUSSERVICEDIR) -dbus_services_DATA = indicator-datetime.service +indicatorsdir = $(prefix)/share/unity/indicators +indicators_DATA = com.canonical.indicator.datetime -%.service: %.service.in - sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ +# +# the gsettings +# -EXTRA_DIST = \ - $(gsettings_SCHEMAS) \ - indicator-datetime.service.in +gsettings_SCHEMAS = com.canonical.indicator.datetime.gschema.xml +@GSETTINGS_RULES@ +EXTRA_DIST += $(gsettings_SCHEMAS) -CLEANFILES = indicator-datetime.service +# +# the dbus service file +# +dbus_servicesdir = $(DBUSSERVICEDIR) +dbus_services_DATA = indicator-datetime.service +dbus_services_in = $(dbus_services_DATA:.service=.service.in) +$(dbus_services_DATA): $(dbus_services_in) + $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ +BUILT_SOURCES += $(dbus_services_DATA) +CLEANFILES += $(dbus_services_DATA) +EXTRA_DIST += $(dbus_services_in) + +# +# the gnome-control-center panel +# + +EXTRA_DIST += datetime-dialog.ui if BUILD_CCPANEL -pkgdata_DATA = datetime-dialog.ui - -@INTLTOOL_DESKTOP_RULE@ -desktopdir = $(datadir)/applications -desktop_DATA = indicator-datetime-preferences.desktop - -EXTRA_DIST += \ - indicator-datetime-preferences.desktop - -CLEANFILES += indicator-datetime-preferences.desktop + pkgdata_DATA = datetime-dialog.ui + @INTLTOOL_DESKTOP_RULE@ + desktopdir = $(datadir)/applications + desktop_DATA = indicator-datetime-preferences.desktop + EXTRA_DIST += $(desktop_DATA) $(desktop_DATA:.desktop=.desktop.in) + CLEANFILES += $(desktop_DATA) endif - -EXTRA_DIST += \ - datetime-dialog.ui \ - indicator-datetime-preferences.desktop.in diff --git a/data/com.canonical.indicator.datetime b/data/com.canonical.indicator.datetime new file mode 100644 index 0000000..055ea8e --- /dev/null +++ b/data/com.canonical.indicator.datetime @@ -0,0 +1,9 @@ +[Indicator Service] +Name=indicator-datetime +ObjectPath=/com/canonical/indicator/datetime + +[desktop] +ObjectPath=/com/canonical/indicator/datetime/desktop + +[greeter] +ObjectPath=/com/canonical/indicator/datetime/greeter diff --git a/data/datetime.indicator b/data/datetime.indicator deleted file mode 100644 index 055ea8e..0000000 --- a/data/datetime.indicator +++ /dev/null @@ -1,9 +0,0 @@ -[Indicator Service] -Name=indicator-datetime -ObjectPath=/com/canonical/indicator/datetime - -[desktop] -ObjectPath=/com/canonical/indicator/datetime/desktop - -[greeter] -ObjectPath=/com/canonical/indicator/datetime/greeter -- cgit v1.2.3 From c18a48d1c81cef6bf874caae86f6230e64d7a13a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 18 Jun 2013 21:30:46 -0500 Subject: remove TODO --- TODO | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index dd4a89f..0000000 --- a/TODO +++ /dev/null @@ -1,9 +0,0 @@ -SHORT TERM TODO: - - due dilligence in a profiler - - need to install the .indicator file during "make install" - - remove shared-dbus: prefs panel needs to look for running indicator in a new way - - fix broken indicator test - -LONG TERM TODO: - - test coverage - - lots of the service is unmockable b/c of calls to time(NULL), g_date_time_new_local(), etc -- cgit v1.2.3 From 07232499cbb7b051dbd0752ee58c3fa2c48d91af Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 18 Jun 2013 23:13:16 -0500 Subject: sync po/POTFILES.in with the source code changes in src/ --- po/POTFILES.in | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index d22e5f9..d9b2f98 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,8 +1,7 @@ -src/indicator-datetime.c -src/datetime-service.c src/datetime-prefs.c src/datetime-prefs-locations.c -src/utils.c +src/service.c src/settings-shared.h +src/utils.c [type: gettext/glade]data/datetime-dialog.ui -data/indicator-datetime-preferences.desktop.in +data/gnome-indicator-datetime-panel.desktop.in -- cgit v1.2.3 From fff2cf6b39b1ba5b2b8dc48fd44238f039c53328 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 18 Jun 2013 23:27:13 -0500 Subject: fix build breakage in tests/ --- tests/Makefile.am | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index f9676c3..dce6076 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,15 +23,14 @@ AM_CXXFLAGS = $(GTEST_CXXFLAGS) ### TEST_LIBS = \ - $(top_builddir)/src/.libs/libdatetime.so \ libgtest.a \ - $(INDICATOR_LIBS) \ + $(SERVICE_LIBS) \ $(COVERAGE_LDFLAGS) \ $(XORG_GTEST_LDFLAGS) TEST_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(INDICATOR_CFLAGS) + $(SERVICE_CFLAGS) BUILT_SOURCES += gschemas.compiled CLEANFILES += gschemas.compiled -- cgit v1.2.3 From 8efd873ac7026024f71134dd1dea7a9b54eafa60 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 19 Jun 2013 00:31:06 -0500 Subject: ensure datetime-dialog.ui makes it into the distribution --- data/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Makefile.am b/data/Makefile.am index 3180cbe..1a33447 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST= # indicatorsdir = $(prefix)/share/unity/indicators -indicators_DATA = com.canonical.indicator.datetime +dist_indicators_DATA = com.canonical.indicator.datetime # # the gsettings @@ -42,4 +42,4 @@ if BUILD_CCPANEL EXTRA_DIST += $(desktop_DATA) CLEANFILES += $(desktop_DATA) endif -EXTRA_DIST += $(desktop_DATA:.desktop=.desktop.in) +EXTRA_DIST += datetime-dialog.ui $(desktop_DATA:.desktop=.desktop.in) -- cgit v1.2.3 From 3bd2f55923c0555b2f5438b2169ea0030aca2fe1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 19 Jun 2013 00:46:20 -0500 Subject: put the service on the production busname --- src/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.c b/src/service.c index cc0b17d..e6a1209 100644 --- a/src/service.c +++ b/src/service.c @@ -32,7 +32,7 @@ #include "settings-shared.h" #include "utils.h" -#define BUS_NAME "datetime.indicator" +#define BUS_NAME "com.canonical.indicator.datetime" #define BUS_PATH "/com/canonical/indicator/datetime" #define SKEW_CHECK_INTERVAL_SEC 10 -- cgit v1.2.3 From 22fb11bd6b8b4dca696e16baf6619c50399abd14 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 19 Jun 2013 01:26:32 -0500 Subject: in the gnome control center datetime panel, use g_bus_watch_name() to see if com.canonical.indicator.datetime is available. --- src/datetime-prefs.c | 55 ++++++++++++++++++++++++++++------------------------ src/dbus-shared.h | 22 ++------------------- src/service.c | 4 +--- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/src/datetime-prefs.c b/src/datetime-prefs.c index d7f0def..ef0a553 100644 --- a/src/datetime-prefs.c +++ b/src/datetime-prefs.c @@ -58,6 +58,7 @@ struct _IndicatorDatetimePanel struct _IndicatorDatetimePanelPrivate { + guint name_watch_id; GtkBuilder * builder; GDBusProxy * proxy; GtkWidget * auto_radio; @@ -286,33 +287,30 @@ proxy_ready (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self) } } +#define WIG(name) GTK_WIDGET (gtk_builder_get_object(self->priv->builder, name)) + static void -service_name_owner_changed (GDBusProxy * proxy, GParamSpec *pspec, gpointer user_data) +set_show_clock_check_sensitive (IndicatorDatetimePanel * self, + gboolean sensitive) { - GtkWidget * widget = GTK_WIDGET (user_data); - gchar * owner = g_dbus_proxy_get_name_owner (proxy); - - gtk_widget_set_sensitive (widget, (owner != NULL)); - - g_free (owner); + gtk_widget_set_sensitive (WIG("showClockCheck"), sensitive); } static void -service_proxy_ready (GObject *object, GAsyncResult *res, gpointer user_data) +on_bus_name_appeared (GDBusConnection * connection G_GNUC_UNUSED, + const char * name G_GNUC_UNUSED, + const char * name_owner, + gpointer self) { - GError * error = NULL; - - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - - if (error != NULL) { - g_critical("Could not grab DBus proxy for indicator-datetime-service: %s", error->message); - g_error_free(error); - return; - } + set_show_clock_check_sensitive (self, name_owner && *name_owner); +} - /* And now, do initial proxy configuration */ - g_signal_connect (proxy, "notify::g-name-owner", G_CALLBACK (service_name_owner_changed), user_data); - service_name_owner_changed (proxy, NULL, user_data); +static void +on_bus_name_vanished (GDBusConnection * connection G_GNUC_UNUSED, + const char * name G_GNUC_UNUSED, + gpointer self) +{ + set_show_clock_check_sensitive (self, FALSE); } static gboolean @@ -656,7 +654,6 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self) GSettings * conf = g_settings_new (SETTINGS_INTERFACE); -#define WIG(name) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, name)) /* Add policykit button */ GtkWidget * polkit_button = gtk_lock_button_new (NULL); @@ -745,10 +742,13 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self) but that doesn't yet claim a name on the bus. Presumably the service would have been started by any such indicator, so this will at least tell us if there *was* a datetime module run this session. */ - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, - SERVICE_NAME, SERVICE_OBJ, SERVICE_IFACE, - NULL, (GAsyncReadyCallback)service_proxy_ready, - WIG ("showClockCheck")); + self->priv->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_bus_name_appeared, + on_bus_name_vanished, + self, + NULL); #undef WIG @@ -772,6 +772,11 @@ indicator_datetime_panel_dispose (GObject * object) priv->loc_dlg = NULL; } + if (priv->name_watch_id != 0) { + g_bus_unwatch_name (priv->name_watch_id); + priv->name_watch_id = 0; + } + if (priv->save_time_id) { g_source_remove (priv->save_time_id); priv->save_time_id = 0; diff --git a/src/dbus-shared.h b/src/dbus-shared.h index 9e3a781..24319e3 100644 --- a/src/dbus-shared.h +++ b/src/dbus-shared.h @@ -19,24 +19,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#define BUS_NAME "com.canonical.indicator.datetime" +#define BUS_PATH "/com/canonical/indicator/datetime" -#define SERVICE_NAME "com.canonical.indicator.datetime" -#define SERVICE_IFACE "com.canonical.indicator.datetime.service" -#define SERVICE_OBJ "/com/canonical/indicator/datetime/service" -#define SERVICE_VERSION 1 - -#define MENU_OBJ "/com/canonical/indicator/datetime/menu" - -#define DBUSMENU_CALENDAR_MENUITEM_TYPE "x-canonical-calendar-item" - -#define CALENDAR_MENUITEM_PROP_MARKS "calendar-marks" - -#define APPOINTMENT_MENUITEM_TYPE "appointment-item" -#define APPOINTMENT_MENUITEM_PROP_LABEL "appointment-label" -#define APPOINTMENT_MENUITEM_PROP_ICON "appointment-icon" -#define APPOINTMENT_MENUITEM_PROP_RIGHT "appointment-time" - -#define TIMEZONE_MENUITEM_TYPE "timezone-item" -#define TIMEZONE_MENUITEM_PROP_ZONE "timezone-zone" -#define TIMEZONE_MENUITEM_PROP_NAME "timezone-name" -#define TIMEZONE_MENUITEM_PROP_RADIO "timezone-radio" diff --git a/src/service.c b/src/service.c index e6a1209..5c8f966 100644 --- a/src/service.c +++ b/src/service.c @@ -25,6 +25,7 @@ #include #include +#include "dbus-shared.h" #include "planner-eds.h" #include "timezone-file.h" #include "timezone-geoclue.h" @@ -32,9 +33,6 @@ #include "settings-shared.h" #include "utils.h" -#define BUS_NAME "com.canonical.indicator.datetime" -#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) -- cgit v1.2.3 From 42cfd74dc245913286cfd2c3ac0cc6caf2d25bff Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 19 Jun 2013 08:57:27 -0500 Subject: in configure.ac, add AM_PROG_AR to silence an autotools warning in the tests/ directory caused by making static local copies of libgtest --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 315facd..8933d17 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,7 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX AC_HEADER_STDC +AM_PROG_AR # Initialize libtool LT_PREREQ([2.2]) -- cgit v1.2.3 From ee30bb04d1ee4842fd20bec5c673215df5a878c5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 19 Jun 2013 16:53:04 -0500 Subject: in our async callbacks, don't call g_warning() when we get a G_IO_ERROR_CANCELLED --- src/datetime-prefs.c | 9 ++++++--- src/service.c | 12 +++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/datetime-prefs.c b/src/datetime-prefs.c index ef0a553..1eaaf1e 100644 --- a/src/datetime-prefs.c +++ b/src/datetime-prefs.c @@ -176,7 +176,8 @@ polkit_perm_ready (GObject *source_object, GAsyncResult *res, gpointer user_data GPermission * permission = polkit_permission_new_finish (res, &error); if (error != NULL) { - g_warning ("Could not get permission object: %s", error->message); + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Could not get permission object: %s", error->message); g_error_free (error); return; } @@ -192,7 +193,8 @@ dbus_set_answered (GObject *object, GAsyncResult *res, gpointer command) GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error); if (error != NULL) { - g_warning("Could not set '%s' using timedated: %s", (gchar *)command, error->message); + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Could not set '%s' using timedated: %s", (gchar *)command, error->message); g_error_free(error); return; } @@ -247,7 +249,8 @@ proxy_ready (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self) self->priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (error != NULL) { - g_critical("Could not grab DBus proxy for timedated: %s", error->message); + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_critical("Could not grab DBus proxy for timedated: %s", error->message); g_error_free(error); return; } diff --git a/src/service.c b/src/service.c index 5c8f966..5694ffe 100644 --- a/src/service.c +++ b/src/service.c @@ -1036,7 +1036,9 @@ on_datetime1_set_timezone_response (GObject * object, 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); + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Could not set new timezone: %s", err->message); + g_error_free (err); } else @@ -1069,7 +1071,9 @@ on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, 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); + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Could not grab DBus proxy for timedated: %s", err->message); + g_error_free (err); setlocation_data_free (data); } @@ -1422,7 +1426,9 @@ on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED, if (err != NULL) { - g_warning ("Could not grab DBus proxy for logind: %s", err->message); + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Could not grab DBus proxy for logind: %s", err->message); + g_error_free (err); } else -- cgit v1.2.3 From 51bd7465ac148a34ec37cb8760daf3b282b5ed6a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 20 Jun 2013 13:44:21 -0500 Subject: remove the --replace command-line argument and property as we're using upstart for that --- src/main.c | 37 ++------------------- src/service.c | 102 +++++++--------------------------------------------------- src/service.h | 2 +- 3 files changed, 14 insertions(+), 127 deletions(-) diff --git a/src/main.c b/src/main.c index aa29456..c75b2d7 100644 --- a/src/main.c +++ b/src/main.c @@ -31,37 +31,6 @@ **** ***/ -static gboolean replace = FALSE; - -static void -parse_command_line (int * argc, char *** argv) -{ - GError * error; - GOptionContext * option_context; - - static GOptionEntry entries[] = - { - { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the currently-running service", NULL }, - { NULL } - }; - - error = NULL; - option_context = g_option_context_new ("- indicator-datetime service"); - g_option_context_add_main_entries (option_context, entries, GETTEXT_PACKAGE); - if (!g_option_context_parse (option_context, argc, argv, &error)) - { - g_print ("option parsing failed: %s\n", error->message); - g_error_free (error); - exit (EXIT_FAILURE); - } - - g_option_context_free (option_context); -} - -/*** -**** -***/ - static void on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) { @@ -70,7 +39,7 @@ on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) } int -main (int argc, char ** argv) +main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) { GMainLoop * loop; IndicatorDatetimeService * service; @@ -80,10 +49,8 @@ main (int argc, char ** argv) bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain (GETTEXT_PACKAGE); - parse_command_line (&argc, &argv); - /* run */ - service = indicator_datetime_service_new (replace); + service = indicator_datetime_service_new (); loop = g_main_loop_new (NULL, FALSE); g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST, G_CALLBACK(on_name_lost), loop); diff --git a/src/service.c b/src/service.c index 5694ffe..558b03a 100644 --- a/src/service.c +++ b/src/service.c @@ -48,15 +48,6 @@ enum static guint signals[LAST_SIGNAL] = { 0 }; -enum -{ - PROP_0, - PROP_REPLACE, - LAST_PROP -}; - -static GParamSpec * properties[LAST_PROP]; - enum { SECTION_HEADER = (1<<0), @@ -124,8 +115,6 @@ struct _IndicatorDatetimeServicePrivate GSimpleAction * calendar_action; GDBusProxy * login1_manager; - - gboolean replace; }; typedef IndicatorDatetimeServicePrivate priv_t; @@ -1544,67 +1533,6 @@ on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, **** GObject virtual functions ***/ -static void -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 (p->replace) - owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; - - 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 -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o); - - switch (property_id) - { - case PROP_REPLACE: - g_value_set_boolean (value, self->priv->replace); - 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_REPLACE: - self->priv->replace = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - static void my_dispose (GObject * o) { @@ -1799,6 +1727,15 @@ indicator_datetime_service_init (IndicatorDatetimeService * self) skew_timer_func, self); + p->own_id = g_bus_own_name (G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + NULL, + on_name_lost, + self, + NULL); + on_local_time_jumped (self); g_string_free (gstr, TRUE); @@ -1811,9 +1748,6 @@ 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; g_type_class_add_private (klass, sizeof (IndicatorDatetimeServicePrivate)); @@ -1825,18 +1759,6 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - properties[PROP_0] = NULL; - - properties[PROP_REPLACE] = g_param_spec_boolean ("replace", - "Replace Service", - "Replace existing service", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, LAST_PROP, properties); } /*** @@ -1844,11 +1766,9 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass) ***/ IndicatorDatetimeService * -indicator_datetime_service_new (gboolean replace) +indicator_datetime_service_new (void) { - GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, - "replace", replace, - NULL); + GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, NULL); return INDICATOR_DATETIME_SERVICE (o); } diff --git a/src/service.h b/src/service.h index 53d4281..b142882 100644 --- a/src/service.h +++ b/src/service.h @@ -62,7 +62,7 @@ struct _IndicatorDatetimeServiceClass GType indicator_datetime_service_get_type (void); -IndicatorDatetimeService * indicator_datetime_service_new (gboolean replace); +IndicatorDatetimeService * indicator_datetime_service_new (void); GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service); -- cgit v1.2.3