diff options
-rw-r--r-- | README | 61 | ||||
-rw-r--r-- | debian/changelog | 28 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rwxr-xr-x | debian/rules | 2 | ||||
-rw-r--r-- | src/Makefile.am | 19 | ||||
-rw-r--r-- | src/datetime-interface.c | 2 | ||||
-rw-r--r-- | src/datetime-service.c | 1550 | ||||
-rw-r--r-- | src/location-geoclue.h | 61 | ||||
-rw-r--r-- | src/location.h | 68 | ||||
-rw-r--r-- | src/main.c | 96 | ||||
-rw-r--r-- | src/planner-eds.c | 330 | ||||
-rw-r--r-- | src/planner-eds.h | 60 | ||||
-rw-r--r-- | src/planner.c | 232 | ||||
-rw-r--r-- | src/planner.h | 138 | ||||
-rw-r--r-- | src/service.c | 638 | ||||
-rw-r--r-- | src/service.h | 71 | ||||
-rw-r--r-- | src/timezone-file.c | 240 | ||||
-rw-r--r-- | src/timezone-file.h | 58 | ||||
-rw-r--r-- | src/timezone-geoclue.c (renamed from src/location-geoclue.c) | 85 | ||||
-rw-r--r-- | src/timezone-geoclue.h | 57 | ||||
-rw-r--r-- | src/timezone.c (renamed from src/location.c) | 30 | ||||
-rw-r--r-- | src/timezone.h | 75 |
22 files changed, 2818 insertions, 1085 deletions
@@ -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 + + diff --git a/debian/changelog b/debian/changelog index 3223098..7d949a1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,31 @@ +indicator-datetime (12.10.3daily13.05.06.1-0ubuntu1) saucy; urgency=low + + [ Sebastien Bacher ] + * [FFE] Use systemd-services rather than ubuntu-system-service + systemdcompatibility code (LP: #1153567) + + [ Iain Lane ] + * Stop using ConsoleKit (LP: #1156613) + + [ Mathieu Trudel-Lapierre ] + * [FFE] Use systemd-services rather than ubuntu-system-service + systemdcompatibility code (LP: #1153567) + + [ Ubuntu daily release ] + * Automatic snapshot from revision 216 + + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 06 May 2013 19:23:23 +0000 + +indicator-datetime (12.10.3daily13.05.02-0ubuntu1) saucy; urgency=low + + [ Charles Kerr ] + * g_critical hit when datetime indicator first shown (LP: #1175392) + + [ Ubuntu daily release ] + * Automatic snapshot from revision 213 + + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 02 May 2013 22:59:08 +0000 + indicator-datetime (12.10.3daily13.03.26-0ubuntu1) raring; urgency=low * Use systemd's service backend, ffe lp: #1153567 diff --git a/debian/control b/debian/control index 7645232..41bdb0f 100644 --- a/debian/control +++ b/debian/control @@ -41,7 +41,7 @@ Depends: ${shlibs:Depends}, gnome-control-center, geoclue-ubuntu-geoip | geoclue-provider, systemd-services, - systemd-shim + systemd-shim, Recommends: indicator-applet | indicator-renderer, evolution-data-server, Description: Simple clock diff --git a/debian/rules b/debian/rules index f98cf93..a350b13 100755 --- a/debian/rules +++ b/debian/rules @@ -9,7 +9,7 @@ override_dh_autoreconf: NOCONFIGURE=1 dh_autoreconf ./autogen.sh override_dh_auto_configure: - dh_auto_configure -- --disable-static + dh_auto_configure -- --disable-static --disable-silent-rules override_dh_install: find debian/indicator-datetime -name \*.la -delete diff --git a/src/Makefile.am b/src/Makefile.am index f9b8562..fee2245 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,17 +10,26 @@ indicator_datetime_service_SOURCES = \ datetime-interface.c \ datetime-interface.h \ gen-datetime-service.xml.c \ - datetime-service.c \ - location.c \ - location.h \ - location-geoclue.c \ - location-geoclue.h \ + planner.c \ + planner.h \ + planner-eds.c \ + planner-eds.h \ + service.c \ + service.h \ + main.c \ + timezone.c \ + timezone.h \ + timezone-file.c \ + timezone-file.h \ + timezone-geoclue.c \ + timezone-geoclue.h \ utils.c \ utils.h \ dbus-shared.h \ settings-shared.h indicator_datetime_service_CFLAGS = \ -Wall \ + -Wextra -Wno-missing-field-initializers \ -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 ff22db5..1a29949 100644 --- a/src/datetime-service.c +++ b/src/datetime-service.c @@ -7,16 +7,16 @@ Copyright 2010 Canonical Ltd. Authors: Ted Gould <ted@canonical.com> -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published +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 <http://www.gnu.org/licenses/>. */ @@ -27,26 +27,21 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <gtk/gtk.h> #include <gdk/gdk.h> #include <glib/gi18n.h> -#include <gio/gio.h> -#include <math.h> +#include <math.h> /* fabs() */ #include <libdbusmenu-gtk/menuitem.h> #include <libdbusmenu-glib/server.h> #include <libdbusmenu-glib/client.h> #include <libdbusmenu-glib/menuitem.h> -#include <time.h> -#include <libecal/libecal.h> -#include <libical/ical.h> -#include <libedataserver/libedataserver.h> -// Other users of ecal seem to also include these, not sure why they should be included by the above -#include <libical/icaltime.h> #include <cairo/cairo.h> #include "datetime-interface.h" #include "dbus-shared.h" -#include "location-geoclue.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 */ @@ -62,118 +57,120 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #define SETTINGS_APP_INVOCATION "gnome-control-center datetime" #endif -static gboolean update_appointment_menu_items (gpointer user_data); -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 * appointments[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 IndicatorDatetimeLocation * geo_location = NULL; - - -/* Our 2 important timezones */ -static gchar * current_timezone = NULL; - -struct comp_instance { - ECalComponent *comp; - time_t start; - time_t end; - ESource *source; -}; +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; + 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 */ 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(G_OBJECT(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); } /*** @@ -182,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_location_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); @@ -196,19 +193,22 @@ 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 (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 (conf, SETTINGS_LOCATIONS_S); - if (user_locations != NULL) { - gint i; + 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; i<location_count; i++) { gchar * zone; @@ -223,7 +223,7 @@ update_location_menu_items (void) } /* finally create menuitems for each location */ - gint offset = dbusmenu_menuitem_get_position (locations_separator, root)+1; + gint offset = dbusmenu_menuitem_get_position (self->locations_separator, root)+1; GSList * l; gboolean have_visible_location = FALSE; for (l=locations; l!=NULL; l=l->next) { @@ -238,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); @@ -247,32 +247,11 @@ 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); } -/* 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) +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); @@ -287,7 +266,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; @@ -307,7 +286,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); @@ -334,29 +313,27 @@ 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 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; } @@ -383,172 +360,193 @@ activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED, } static gboolean -update_appointment_menu_items_idle (gpointer user_data) +update_appointment_menu_items_idle (gpointer gself) { - update_appointment_menu_items(user_data); - return FALSE; + update_appointment_menu_items (gself); + + return G_SOURCE_REMOVE; } static void -hide_all_appointments (void) +update_appointment_menu_items_soon (IndicatorDatetimeService * self) { - int i; + g_idle_add (update_appointment_menu_items_idle, self); +} - for (i=0; i<MAX_APPOINTMENT_MENUITEMS; i++) { - if (appointments[i]) { - dbusmenu_menuitem_property_set_bool(appointments[i], DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); - dbusmenu_menuitem_property_set_bool(appointments[i], DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } - } +static void +hide_all_appointments (IndicatorDatetimeService * self) +{ + int i; + + for (i=0; i<MAX_APPOINTMENT_MENUITEMS; i++) + { + if (self->appointment_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, GVariant *variant, guint timestamp) +month_changed_cb (DbusmenuMenuitem * menuitem, + gchar * name G_GNUC_UNUSED, + GVariant * variant, + 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); - - g_idle_add(update_appointment_menu_items_idle, NULL); - 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, GVariant *variant, guint timestamp) +day_selected_cb (DbusmenuMenuitem * menuitem, + gchar * name G_GNUC_UNUSED, + GVariant * variant, + 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); + + if (start_tm.tm_mon != new_tm.tm_mon) + dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS); + } - start_time_appointments = new_time; + self->start_time_appointments = new_time; - g_debug("Received day-selected with timestamp: %d -> %s",(int)start_time_appointments, ctime(&start_time_appointments)); - g_idle_add(update_appointment_menu_items_idle, NULL); + 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); - - 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); + time_t evotime; + GDateTime * dt; + IndicatorDatetimeService * self = gself; - g_free (cmd); - g_free (ad); - - return TRUE; + 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 self) +{ + update_appointment_menu_items (self); + return G_SOURCE_CONTINUE; +} static void -start_ecal_timer(void) +start_ecal_timer (IndicatorDatetimeService * self) { - 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 (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) { - g_source_remove(ecaltimer); - ecaltimer = 0; - } + if (self->ecaltimer != 0) + { + g_source_remove (self->ecaltimer); + self->ecaltimer = 0; + } } + static gboolean -idle_start_ecal_timer (gpointer data) +idle_start_ecal_timer (gpointer gself) { - start_ecal_timer(); - return FALSE; + start_ecal_timer (gself); + + return G_SOURCE_REMOVE; } static void -show_events_changed (void) +show_events_changed (IndicatorDatetimeService * self) { - 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 (self->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 (self->add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, b); + dbusmenu_menuitem_property_set_bool (self->events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, b); - /* see if there are any calendar sources */ - return appointment_sources > 0; + 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 user_data) +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 () && calendar_app_is_usable()) { + 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; i<MAX_APPOINTMENT_MENUITEMS; i++) { @@ -556,631 +554,452 @@ 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; + self->appointment_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; } -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); + + pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + } - return TRUE; /* tell eds to keep iterating */ + 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. + * 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 (IndicatorDatetimeService * self) { - // 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; - - 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; + 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; - highlightdays = highlightdays + 7; // Minimum of 7 days ahead - t2 = t1 + (time_t) (highlightdays * 24 * 60 * 60); + self->updating_appointments = TRUE; - // clear any previous comp_instances - g_list_free_full (comp_instances, (GDestroyNotify)comp_instance_free); - comp_instances = NULL; - - // 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; + 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); - 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")); - - 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 = 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); - // 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 (self->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; + 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 user_data) +check_for_timeadmin (gpointer gself) { - g_return_val_if_fail (settings != NULL, FALSE); - - 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); - } + gchar * timeadmin; + IndicatorDatetimeService * self = gself; - return FALSE; -} + g_return_val_if_fail (self->settings != NULL, FALSE); -static void -show_locations_changed (void) -{ - /* Re-calculate */ - update_location_menu_items(); -} + 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); + } -static void -time_format_changed (void) -{ - update_appointment_menu_items(NULL); + 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_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 (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); + 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); } -/* Run when the timezone file changes */ static void -timezone_changed (GFileMonitor * monitor, GFile * file, GFile * otherfile, GFileMonitorEvent event, gpointer user_data) +on_timezone_changed (gpointer self) { - update_current_timezone(); - on_clock_skew(); - return; -} + update_location_menu_items (self); -/* 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 (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 user_data) +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); - 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 -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 gself) { - // Just returned from suspend - if (g_strcmp0(signal_name, "SystemIdleHintChanged") == 0) { - gboolean idle = FALSE; - g_variant_get(parameters, "(b)", &idle); - if (!idle) { - on_clock_skew (); - } - } - return; + 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, GAsyncResult * res, gpointer user_data) +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 ConsoleKit: %s", error->message); + 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), user_data); + g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), gself); } /**** @@ -1197,77 +1016,33 @@ 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) -{ - g_warning("Shutting down service!"); - g_main_loop_quit(mainloop); - 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) +service_shutdown (IndicatorService * service G_GNUC_UNUSED, + gpointer gmainloop) { - update_appointment_menu_items (user_data); + g_warning ("Shutting down service!"); + g_main_loop_quit (gmainloop); } static void -init_appointment_sources (ESourceRegistry *registry) +on_use_geoclue_changed_cb (GSettings * settings, + gchar * key G_GNUC_UNUSED, + gpointer gself) { - GList * l; + IndicatorDatetimeService * self = gself; + const gboolean use_geoclue = g_settings_get_boolean (settings, "show-auto-detected-location"); - 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, - gpointer user_data G_GNUC_UNUSED) -{ - const gboolean use_geoclue = g_settings_get_boolean (conf, "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_location_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); } } @@ -1275,88 +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. */ - 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); - - /* 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_current_timezone(); - - /* Setup geoclue */ - on_use_geoclue_changed_cb (conf, NULL, NULL); - - /* Setup dbus interface */ - dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL); - - /* Setup timezone watch */ - build_timezone(dbus); - - /* 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.ConsoleKit", - "/org/freedesktop/ConsoleKit/Manager", - "org.freedesktop.ConsoleKit.Manager", - NULL, system_proxy_cb, dbus); - - 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_clear_object (&geo_location); - - 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; } diff --git a/src/location-geoclue.h b/src/location-geoclue.h deleted file mode 100644 index 7b65917..0000000 --- a/src/location-geoclue.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ -#define __INDICATOR_DATETIME_LOCATION_GEOCLUE__H__ - -#include <glib.h> -#include <glib-object.h> - -#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.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 <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __INDICATOR_DATETIME_LOCATION__H__ -#define __INDICATOR_DATETIME_LOCATION__H__ - -#include <glib.h> -#include <glib-object.h> - -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/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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> /* exit() */ + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#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/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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gio/gio.h> /* GFile, GFileMonitor */ + +#include <libical/ical.h> +#include <libical/icaltime.h> +#include <libecal/libecal.h> +#include <libedataserver/libedataserver.h> + +#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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_PLANNER__H__ +#define __INDICATOR_DATETIME_PLANNER__H__ + +#include <glib.h> +#include <glib-object.h> /* parent class */ +#include <gdk/gdk.h> /* 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__ */ 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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <locale.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#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 && profile<N_PROFILES); + g_assert (self->priv->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; i<n; ++i) + { + g_menu_append_section (submenu, NULL, sections[i]); + g_object_unref (sections[i]); + } + + /* add submenu to the header */ + header = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_attribute (header, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_submenu (header, G_MENU_MODEL (submenu)); + g_object_unref (submenu); + + /* add header to the menu */ + menu = g_menu_new (); + g_menu_append_item (menu, header); + g_object_unref (header); + + self->priv->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; i<N_PROFILES; ++i) + { + char * path = g_strdup_printf ("%s/%s", BUS_PATH, menu_names[i]); + struct ProfileMenuInfo * menu = &p->menus[i]; + + if (menu->menu == NULL) + create_menu (self, i); + + if ((id = g_dbus_connection_export_menu_model (connection, + path, + G_MENU_MODEL (menu->menu), + &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; i<N_PROFILES; ++i) + { + guint * id = &self->priv->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; i<N_PROFILES; ++i) + g_clear_object (&p->menus[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 <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_SERVICE_H__ +#define __INDICATOR_DATETIME_SERVICE_H__ + +#include <glib.h> +#include <glib-object.h> + +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__ */ diff --git a/src/timezone-file.c b/src/timezone-file.c new file mode 100644 index 0000000..2adf2ca --- /dev/null +++ b/src/timezone-file.c @@ -0,0 +1,240 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gio/gio.h> /* GFile, GFileMonitor */ + +#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 ("%s Unable to read timezone file '%s': %s", G_STRLOC, p->filename, err->message); + g_error_free (err); + } + else + { + g_strstrip (new_timezone); + + 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); + } +} + +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 ("%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 ("%s Monitoring timezone file '%s'", G_STRLOC, 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..b02abe1 --- /dev/null +++ b/src/timezone-file.h @@ -0,0 +1,58 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE_FILE__H__ +#define __INDICATOR_DATETIME_TIMEZONE_FILE__H__ + +#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 IndicatorDatetimeTimezone which uses a local file, + * such as /etc/timezone, to determine the 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/location-geoclue.c b/src/timezone-geoclue.c index 87358fb..08f272d 100644 --- a/src/location-geoclue.c +++ b/src/timezone-geoclue.c @@ -19,15 +19,12 @@ #include "config.h" -#include <glib.h> -#include <glib/gi18n-lib.h> - #include <geoclue/geoclue-master.h> #include <geoclue/geoclue-master-client.h> -#include "location-geoclue.h" +#include "timezone-geoclue.h" -struct _IndicatorDatetimeLocationGeocluePriv +struct _IndicatorDatetimeTimezoneGeocluePriv { GeoclueMaster * master; GeoclueMasterClient * client; @@ -35,20 +32,20 @@ struct _IndicatorDatetimeLocationGeocluePriv gchar * timezone; }; -typedef IndicatorDatetimeLocationGeocluePriv priv_t; +typedef IndicatorDatetimeTimezoneGeocluePriv priv_t; -G_DEFINE_TYPE (IndicatorDatetimeLocationGeoclue, - indicator_datetime_location_geoclue, - INDICATOR_TYPE_DATETIME_LOCATION) +G_DEFINE_TYPE (IndicatorDatetimeTimezoneGeoclue, + indicator_datetime_timezone_geoclue, + INDICATOR_TYPE_DATETIME_TIMEZONE) -static void geo_restart (IndicatorDatetimeLocationGeoclue * self); +static void geo_restart (IndicatorDatetimeTimezoneGeoclue * self); /*** **** ***/ static void -set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) +set_timezone (IndicatorDatetimeTimezoneGeoclue * self, const gchar * timezone) { priv_t * p = self->priv; @@ -56,15 +53,15 @@ set_timezone (IndicatorDatetimeLocationGeoclue * self, const gchar * timezone) { g_free (p->timezone); p->timezone = g_strdup (timezone); - indicator_datetime_location_notify_timezone (INDICATOR_DATETIME_LOCATION(self)); + indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self)); } } 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) { @@ -75,14 +72,14 @@ on_address_changed (GeoclueAddress * address, } else { - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); + 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, +on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED, GeoclueAddress * address, GError * error, gpointer gself) @@ -94,7 +91,7 @@ on_address_created (GeoclueMasterClient * master, } else { - priv_t * p = INDICATOR_DATETIME_LOCATION_GEOCLUE(gself)->priv; + priv_t * p = INDICATOR_DATETIME_TIMEZONE_GEOCLUE(gself)->priv; g_assert (p->address == NULL); p->address = g_object_ref (address); @@ -105,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) { @@ -115,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, @@ -128,13 +127,9 @@ 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 { - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (gself); + IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); priv_t * p = self->priv; g_clear_object (&p->client); @@ -154,7 +149,7 @@ on_client_created (GeoclueMaster * master, } static void -geo_start (IndicatorDatetimeLocationGeoclue * self) +geo_start (IndicatorDatetimeTimezoneGeoclue * self) { priv_t * p = self->priv; @@ -164,7 +159,7 @@ geo_start (IndicatorDatetimeLocationGeoclue * self) } static void -geo_stop (IndicatorDatetimeLocationGeoclue * self) +geo_stop (IndicatorDatetimeTimezoneGeoclue * self) { priv_t * p = self->priv; @@ -184,7 +179,7 @@ geo_stop (IndicatorDatetimeLocationGeoclue * self) } static void -geo_restart (IndicatorDatetimeLocationGeoclue * self) +geo_restart (IndicatorDatetimeTimezoneGeoclue * self) { geo_stop (self); geo_start (self); @@ -195,54 +190,54 @@ geo_restart (IndicatorDatetimeLocationGeoclue * self) ***/ static const char * -my_get_timezone (IndicatorDatetimeLocation * self) +my_get_timezone (IndicatorDatetimeTimezone * self) { - return INDICATOR_DATETIME_LOCATION_GEOCLUE(self)->priv->timezone; + return INDICATOR_DATETIME_TIMEZONE_GEOCLUE(self)->priv->timezone; } static void my_dispose (GObject * o) { - geo_stop (INDICATOR_DATETIME_LOCATION_GEOCLUE (o)); + geo_stop (INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o)); - G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->dispose (o); + G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->dispose (o); } static void my_finalize (GObject * o) { - IndicatorDatetimeLocationGeoclue * self = INDICATOR_DATETIME_LOCATION_GEOCLUE (o); + IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o); priv_t * p = self->priv; g_free (p->timezone); - G_OBJECT_CLASS (indicator_datetime_location_geoclue_parent_class)->finalize (o); + G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->finalize (o); } static void -indicator_datetime_location_geoclue_class_init (IndicatorDatetimeLocationGeoclueClass * klass) +indicator_datetime_timezone_geoclue_class_init (IndicatorDatetimeTimezoneGeoclueClass * klass) { GObjectClass * object_class; - IndicatorDatetimeLocationClass * location_class; + IndicatorDatetimeTimezoneClass * 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 = INDICATOR_DATETIME_TIMEZONE_CLASS (klass); location_class->get_timezone = my_get_timezone; - g_type_class_add_private (klass, sizeof (IndicatorDatetimeLocationGeocluePriv)); + g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneGeocluePriv)); } static void -indicator_datetime_location_geoclue_init (IndicatorDatetimeLocationGeoclue * self) +indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * self) { priv_t * p; p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, - IndicatorDatetimeLocationGeocluePriv); + INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, + IndicatorDatetimeTimezoneGeocluePriv); self->priv = p; geo_start (self); @@ -252,10 +247,10 @@ indicator_datetime_location_geoclue_init (IndicatorDatetimeLocationGeoclue * sel **** Public ***/ -IndicatorDatetimeLocation * -indicator_datetime_location_geoclue_new (void) +IndicatorDatetimeTimezone * +indicator_datetime_timezone_geoclue_new (void) { - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_LOCATION_GEOCLUE, NULL); + gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, NULL); - return INDICATOR_DATETIME_LOCATION (o); + return INDICATOR_DATETIME_TIMEZONE (o); } diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h new file mode 100644 index 0000000..059bd81 --- /dev/null +++ b/src/timezone-geoclue.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ +#define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ + +#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 IndicatorDatetimeTimezone which uses GeoClue to determine the timezone. + */ +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/location.c b/src/timezone.c index 12e25c3..546a3e3 100644 --- a/src/location.c +++ b/src/timezone.c @@ -17,10 +17,10 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "location.h" +#include "timezone.h" -G_DEFINE_TYPE (IndicatorDatetimeLocation, - indicator_datetime_location, +G_DEFINE_TYPE (IndicatorDatetimeTimezone, + indicator_datetime_timezone, G_TYPE_OBJECT) enum @@ -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, @@ -38,12 +38,12 @@ my_get_property (GObject * o, GValue * value, GParamSpec * pspec) { - IndicatorDatetimeLocation * self = INDICATOR_DATETIME_LOCATION (o); + IndicatorDatetimeTimezone * self = INDICATOR_DATETIME_TIMEZONE (o); switch (property_id) { case PROP_TIMEZONE: - g_value_set_string (value, indicator_datetime_location_get_timezone (self)); + g_value_set_string (value, indicator_datetime_timezone_get_timezone (self)); break; default: @@ -54,12 +54,12 @@ my_get_property (GObject * o, static void my_dispose (GObject * object) { - G_OBJECT_CLASS (indicator_datetime_location_parent_class)->dispose (object); + G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->dispose (object); } static void /* cppcheck-suppress unusedFunction */ -indicator_datetime_location_class_init (IndicatorDatetimeLocationClass * klass) +indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass) { GObjectClass * object_class; const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; @@ -70,8 +70,6 @@ indicator_datetime_location_class_init (IndicatorDatetimeLocationClass * klass) klass->get_timezone = NULL; - properties[PROP_0] = NULL; - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", "Timezone", "Timezone", @@ -82,7 +80,7 @@ indicator_datetime_location_class_init (IndicatorDatetimeLocationClass * klass) } static void -indicator_datetime_location_init (IndicatorDatetimeLocation * self G_GNUC_UNUSED) +indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self G_GNUC_UNUSED) { } @@ -91,17 +89,17 @@ indicator_datetime_location_init (IndicatorDatetimeLocation * self G_GNUC_UNUSED ***/ const char * -indicator_datetime_location_get_timezone (IndicatorDatetimeLocation * self) +indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone * self) { - g_return_val_if_fail (INDICATOR_IS_DATETIME_LOCATION (self), NULL); + g_return_val_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self), NULL); - return INDICATOR_DATETIME_LOCATION_GET_CLASS (self)->get_timezone (self); + return INDICATOR_DATETIME_TIMEZONE_GET_CLASS (self)->get_timezone (self); } void -indicator_datetime_location_notify_timezone (IndicatorDatetimeLocation * self) +indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone * self) { - g_return_if_fail (INDICATOR_IS_DATETIME_LOCATION (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..cadeb6f --- /dev/null +++ b/src/timezone.h @@ -0,0 +1,75 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_DATETIME_TIMEZONE__H__ +#define __INDICATOR_DATETIME_TIMEZONE__H__ + +#include <glib.h> +#include <glib-object.h> /* parent class */ + +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); + +#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone" + +/** + * 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 +{ + /*< private >*/ + GObject parent; +}; + +struct _IndicatorDatetimeTimezoneClass +{ + GObjectClass parent_class; + + /* virtual functions */ + const char * (*get_timezone) (IndicatorDatetimeTimezone * self); +}; + +/*** +**** +***/ + +const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *); + +void indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone *); + +G_END_DECLS + +#endif /* __INDICATOR_DATETIME_TIMEZONE__H__ */ |