aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Terry <mike@mterry.name>2011-02-22 08:56:38 -0500
committerMichael Terry <mike@mterry.name>2011-02-22 08:56:38 -0500
commit8754d980f2992a2596465d46b5d18d35846060fa (patch)
tree865167f9cfcacf563704b04d35edc574c1cf6dfa
parent1e274bb18d9936c39d4c22e00ec895dc79178390 (diff)
parent124ef53b7bab308704974bd5a1efc5e40f5fc6c0 (diff)
downloadayatana-indicator-datetime-8754d980f2992a2596465d46b5d18d35846060fa.tar.gz
ayatana-indicator-datetime-8754d980f2992a2596465d46b5d18d35846060fa.tar.bz2
ayatana-indicator-datetime-8754d980f2992a2596465d46b5d18d35846060fa.zip
re-sync with trunk
-rw-r--r--configure.ac13
-rw-r--r--src/datetime-service.c229
-rw-r--r--src/dbus-shared.h3
-rw-r--r--src/indicator-datetime.c284
4 files changed, 411 insertions, 118 deletions
diff --git a/configure.ac b/configure.ac
index 2418f12..242033e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT(indicator-datetime, 0.1.91, ted@canonical.com)
+AC_INIT(indicator-datetime, 0.1.94, ted@canonical.com)
AC_COPYRIGHT([Copyright 2009,2010 Canonical])
AC_PREREQ(2.53)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(indicator-datetime, 0.1.91)
+AM_INIT_AUTOMAKE(indicator-datetime, 0.1.94)
AM_MAINTAINER_MODE
@@ -51,7 +51,7 @@ AC_ARG_WITH([gtk],
# Dependencies
###########################
-INDICATOR_REQUIRED_VERSION=0.3.0
+INDICATOR_REQUIRED_VERSION=0.3.19
DBUSMENUGLIB_REQUIRED_VERSION=0.1.1
DBUSMENUGTK_REQUIRED_VERSION=0.3.94
GIO_REQUIRED_VERSION=2.25.11
@@ -64,17 +64,20 @@ EDS_REQUIRED_VERSION=2.30
ICAL_REQUIRED_VERSION=0.44
CAIRO_REQUIRED_VERSION=1.10
GDK_REQUIRED_VERSION=2.22
+GLIB_REQUIRED_VERSION=2.26
GTK_REQUIRED_VERSION=2.12
GTK3_REQUIRED_VERSION=3.0
AS_IF([test "x$with_gtk" = x3],
[PKG_CHECK_MODULES(INDICATOR, indicator3 >= $INDICATOR_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION
dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
libido3-0.1 >= $INDICATOR_DISPLAY_OBJECTS)
],
[test "x$with_gtk" = x2],
[PKG_CHECK_MODULES(INDICATOR, indicator >= $INDICATOR_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION
dbusmenu-gtk-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
libido-0.1 >= $INDICATOR_DISPLAY_OBJECTS)
@@ -84,6 +87,7 @@ AS_IF([test "x$with_gtk" = x3],
AS_IF([test "x$with_gtk" = x3],
[PKG_CHECK_MODULES(SERVICE, indicator >= $INDICATOR_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION
dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
libido-0.1 >= $INDICATOR_DISPLAY_OBJECTS
@@ -93,11 +97,13 @@ AS_IF([test "x$with_gtk" = x3],
libecal-1.2 >= $ECAL_REQUIRED_VERSION
libical >= $ICAL_REQUIRED_VERSION
libedataserver-1.2 >= EDS_REQUIRED_VERSION
+ libedataserverui-1.2 >= EDS_REQUIRED_VERSION
cairo >= CAIRO_REQUIRED_VERSION
gdk-2.0 >= GDK_REQUIRED_VERSION)
],
[test "x$with_gtk" = x2],
[PKG_CHECK_MODULES(SERVICE, indicator >= $INDICATOR_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION
dbusmenu-gtk-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
libido-0.1 >= $INDICATOR_DISPLAY_OBJECTS
@@ -107,6 +113,7 @@ AS_IF([test "x$with_gtk" = x3],
libecal-1.2 >= $ECAL_REQUIRED_VERSION
libical >= $ICAL_REQUIRED_VERSION
libedataserver-1.2 >= EDS_REQUIRED_VERSION
+ libedataserverui-1.2 >= EDS_REQUIRED_VERSION
cairo >= CAIRO_REQUIRED_VERSION
gdk-2.0 >= GDK_REQUIRED_VERSION)
],
diff --git a/src/datetime-service.c b/src/datetime-service.c
index cc6c9d5..1f3eac6 100644
--- a/src/datetime-service.c
+++ b/src/datetime-service.c
@@ -42,6 +42,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <libical/ical.h>
#include <libecal/e-cal-time-util.h>
#include <libedataserver/e-source.h>
+#include <libedataserverui/e-passwords.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>
@@ -58,6 +59,8 @@ static void geo_create_client (GeoclueMaster * master, GeoclueMasterClient * cli
static gboolean update_appointment_menu_items (gpointer user_data);
static gboolean update_timezone_menu_items(gpointer user_data);
static void setup_timer (void);
+static void geo_client_invalid (GeoclueMasterClient * client, gpointer user_data);
+static void geo_address_change (GeoclueMasterClient * client, gchar * a, gchar * b, gchar * c, gchar * d, gpointer user_data);
static IndicatorService * service = NULL;
static GMainLoop * mainloop = NULL;
@@ -83,14 +86,10 @@ GSettings *conf;
static GeoclueMasterClient * geo_master = NULL;
static GeoclueAddress * geo_address = NULL;
-/* Our 3 important timezones */
-static const gchar * ecal_timezone = NULL;
+/* Our 2 important timezones */
static gchar * current_timezone = NULL;
static gchar * geo_timezone = NULL;
-static ECal * ecal = NULL;
-static icaltimezone * tzone;
-
/* Check to see if our timezones are the same */
static void
check_timezone_sync (void) {
@@ -143,9 +142,8 @@ check_timezone_sync (void) {
if (label != NULL) {
// TODO work out the current location name in a nice way
- dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_LABEL, label);
+ dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_ZONE, label);
// TODO work out the current time at that location
- dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_RIGHT, "+tzone");
dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
dbusmenu_menuitem_property_set_bool(current_location, TIMEZONE_MENUITEM_PROP_RADIO, TRUE);
} else {
@@ -153,22 +151,19 @@ check_timezone_sync (void) {
}
if (geo_timezone != NULL) {
// TODO work out the geo location name in a nice way
- dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_LABEL, geo_timezone);
+ dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_ZONE, geo_timezone);
// TODO work out the current time at that location
- dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_RIGHT, "+tzone");
dbusmenu_menuitem_property_set_bool (geo_location, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
}
} else {
// TODO work out the geo location name in a nice way
- dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_LABEL, geo_timezone);
+ dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_ZONE, geo_timezone);
// TODO work out the current time at that location
- dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_RIGHT, "+tzone");
dbusmenu_menuitem_property_set_bool(geo_location, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
// TODO work out the current location name in a nice way
- dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_LABEL, current_timezone);
+ dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_ZONE, current_timezone);
// TODO work out the current time at that location
- dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_RIGHT, "+tzone");
dbusmenu_menuitem_property_set_bool(current_location, TIMEZONE_MENUITEM_PROP_RADIO, TRUE);
dbusmenu_menuitem_property_set_bool(current_location, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
dbusmenu_menuitem_property_set_bool(locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
@@ -230,7 +225,7 @@ quick_set_tz_cb (OobsObject * obj, OobsResult result, gpointer user_data)
static void
quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data)
{
- const gchar * tz = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_LABEL);
+ const gchar * tz = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_ZONE);
g_debug("Quick setting timezone to: %s", tz);
@@ -303,7 +298,7 @@ check_for_calendar (gpointer user_data)
g_debug("Found the calendar application: %s", evo);
dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
-
+/*
GError *gerror = NULL;
// TODO: In reality we should iterate sources of calendar, but getting the local one doens't lag for > a minute
g_debug("Setting up ecal.");
@@ -327,13 +322,13 @@ check_for_calendar (gpointer user_data)
ecal = NULL;
}
- /* This timezone represents the timezone of the calendar, this might be different to the current UTC offset.
+ This timezone represents the timezone of the calendar, this might be different to the current UTC offset.
* this means we'll have some geoclue interaction going on, and possibly the user will be involved in setting
* their location manually, case in point: trains have satellite links which often geoclue to sweden,
* this shouldn't automatically set the location and mess up all the appointments for the user.
- */
+
if (ecal) ecal_timezone = icaltimezone_get_tzid(tzone);
-
+ */
DbusmenuMenuitem * separator = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
dbusmenu_menuitem_child_add_position(root, separator, 3);
@@ -358,6 +353,7 @@ check_for_calendar (gpointer user_data)
return FALSE;
}
+
static gboolean
update_timezone_menu_items(gpointer user_data) {
g_debug("Updating timezone menu items");
@@ -402,8 +398,7 @@ update_timezone_menu_items(gpointer user_data) {
g_debug("Adding timezone in update_timezones %s", locations[i]);
item = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, TIMEZONE_MENUITEM_TYPE);
- dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_LABEL, locations[i]);
- dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_RIGHT, "+tzone");
+ dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_ZONE, locations[i]);
dbusmenu_menuitem_property_set_bool (item, TIMEZONE_MENUITEM_PROP_RADIO, FALSE);
dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
@@ -417,6 +412,35 @@ update_timezone_menu_items(gpointer user_data) {
return FALSE;
}
+// Authentication function
+static gchar *
+auth_func (ECal *ecal, const gchar *prompt, const gchar *key, gpointer user_data) {
+ gboolean remember; // TODO: Is this useful? Should we be storing it somewhere?
+ ESource *source = e_cal_get_source (ecal);
+ gchar *auth_domain = e_source_get_duped_property (source, "auth-domain");
+
+ const gchar *component_name;
+ if (auth_domain) component_name = auth_domain;
+ else component_name = "Calendar";
+
+ gchar *password = e_passwords_get_password (component_name, key);
+
+ if (password == NULL) {
+ password = e_passwords_ask_password (
+ _("Enter password"),
+ component_name, key, prompt,
+ E_PASSWORDS_REMEMBER_FOREVER |
+ E_PASSWORDS_SECRET |
+ E_PASSWORDS_ONLINE,
+ &remember, NULL);
+ }
+
+ g_free (auth_domain);
+
+ return password;
+}
+
+
// Compare function for g_list_sort of ECalComponent objects
static gint
compare_appointment_items (ECalComponent *a,
@@ -426,8 +450,13 @@ compare_appointment_items (ECalComponent *a,
struct tm tm_a, tm_b;
time_t t_a, t_b;
gint retval = 0;
-
+
+ if (a == NULL || b == NULL) return retval;
+
ECalComponentVType vtype = e_cal_component_get_vtype (a);
+
+ if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO) return -1;
+
if (vtype == E_CAL_COMPONENT_EVENT)
e_cal_component_get_dtstart (a, &datetime_a);
else
@@ -436,6 +465,8 @@ compare_appointment_items (ECalComponent *a,
t_a = mktime(&tm_a);
vtype = e_cal_component_get_vtype (b);
+ if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO) return 1;
+
if (vtype == E_CAL_COMPONENT_EVENT)
e_cal_component_get_dtstart (b, &datetime_b);
else
@@ -459,15 +490,17 @@ compare_appointment_items (ECalComponent *a,
*/
static gboolean
update_appointment_menu_items (gpointer user_data) {
- if (!ecal) return FALSE;
// 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.
time_t t1, t2;
gchar *query, *is, *ie, *ad;
GList *objects = NULL, *l;
+ GList *allobjects = NULL;
+ GSList *g;
GError *gerror = NULL;
gint i;
gint width, height;
+ ESourceList * sources = NULL;
time(&t1);
time(&t2);
@@ -476,19 +509,6 @@ update_appointment_menu_items (gpointer user_data) {
is = isodate_from_time_t(t1);
ie = isodate_from_time_t(t2);
- // FIXME can we put a limit on the number of results? Or if not complete, or is event/todo? Or sort it by date?
- query = g_strdup_printf("(occur-in-time-range? (make-time\"%s\") (make-time\"%s\"))", is, ie);
-
-
- // FIXME iterate the query for all sources, kill global ecal
- g_debug("Getting objects with query: %s", query);
- if (!e_cal_get_object_list_as_comp(ecal, query, &objects, &gerror)) {
- g_debug("Failed to get objects\n");
- g_free(ecal);
- ecal = NULL;
- return FALSE;
- }
- g_debug("Number of objects returned: %d", g_list_length(objects));
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
/* Remove all of the previous appointments */
@@ -505,11 +525,56 @@ update_appointment_menu_items (gpointer user_data) {
}
// TODO Remove all highlights from the calendar widget
+
+ // FIXME can we put a limit on the number of results? Or if not complete, or is event/todo? Or sort it by date?
+ query = g_strdup_printf("(occur-in-time-range? (make-time\"%s\") (make-time\"%s\"))", is, ie);
+
+ if (!e_cal_get_sources(&sources, E_CAL_SOURCE_TYPE_EVENT, &gerror)) {
+ g_debug("Failed to get ecal sources\n");
+ return FALSE;
+ }
+
+ // iterate the query for all sources
+ for (g = e_source_list_peek_groups (sources); g; g = g->next) {
+ ESourceGroup *group = E_SOURCE_GROUP (g->data);
+ GSList *s;
+
+ for (s = e_source_group_peek_sources (group); s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ g_signal_connect (G_OBJECT(source), "changed", G_CALLBACK (update_appointment_menu_items), NULL);
+ ECal *ecal = e_cal_new(source, E_CAL_SOURCE_TYPE_EVENT);
+ e_cal_set_auth_func (ecal, (ECalAuthFunc) auth_func, NULL);
+ //icaltimezone * tzone;
+
+ if (!e_cal_open(ecal, FALSE, &gerror)) {
+ g_debug("Failed to get ecal sources %s", gerror->message);
+ g_error_free(gerror);
+ gerror = NULL;
+ continue;
+ }
+
+ g_debug("Getting objects with query: %s", query);
+ if (!e_cal_get_object_list_as_comp(ecal, query, &objects, &gerror)) {
+ g_debug("Failed to get objects\n");
+ g_free(ecal);
+ return FALSE;
+ }
+ g_debug("Number of objects returned: %d", g_list_length(objects));
+
+ if (allobjects == NULL) {
+ allobjects = objects;
+ } else if (objects != NULL) {
+ allobjects = g_list_concat(allobjects, objects);
+ g_object_unref(objects);
+ }
+ }
+ }
// Sort the list see above FIXME regarding queries
- objects = g_list_sort(objects, (GCompareFunc) compare_appointment_items);
+ g_debug("Sorting objects list");
+ allobjects = g_list_sort(allobjects, (GCompareFunc) compare_appointment_items);
i = 0;
- for (l = objects; l; l = l->next) {
+ for (l = allobjects; l; l = l->next) {
ECalComponent *ecalcomp = l->data;
ECalComponentText valuetext;
ECalComponentDateTime datetime;
@@ -522,6 +587,7 @@ update_appointment_menu_items (gpointer user_data) {
struct tm tmp_tm;
DbusmenuMenuitem * item;
+ g_debug("Start Object");
ECalComponentVType vtype = e_cal_component_get_vtype (ecalcomp);
// See above FIXME regarding query result
@@ -540,7 +606,6 @@ update_appointment_menu_items (gpointer user_data) {
dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- g_debug("Start Object");
// Label text
e_cal_component_get_summary (ecalcomp, &valuetext);
summary = g_strdup (valuetext.value);
@@ -586,7 +651,7 @@ update_appointment_menu_items (gpointer user_data) {
g_debug("Appointment time: %s", right);
g_debug("Appointment timezone: %s", datetime.tzid);
g_debug("Appointment timezone: %s", icaltimezone_get_tzid(appointment_zone)); // These two should be the same
- g_debug("Calendar timezone: %s", ecal_timezone);
+ //g_debug("Calendar timezone: %s", ecal_timezone);
dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right);
@@ -604,15 +669,18 @@ update_appointment_menu_items (gpointer user_data) {
g_debug("Command to Execute: %s", cmd);
- ESource *source = e_cal_get_source (ecal);
+ // FIXME This is now more difficult to get right with more sources, as we need to keep track
+ // of which ecal or source goes with each ECalComponent :/
+
+ //ESource *source = e_cal_get_source (ecal);
//e_source_get_color (source, &source_color); api has been changed
- const gchar *color_spec = e_source_peek_color_spec(source);
- GdkColor color;
+ const gchar *color_spec = NULL; //e_source_peek_color_spec(source);
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) {
+ GdkColor color;
gdk_color_parse (color_spec, &color);
cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
@@ -634,7 +702,7 @@ update_appointment_menu_items (gpointer user_data) {
if (i == 4) break; // See above FIXME regarding query result limit
i++;
}
- g_list_free(objects);
+ g_object_unref(allobjects);
g_debug("End of objects");
return TRUE;
}
@@ -694,7 +762,7 @@ build_menus (DbusmenuMenuitem * root)
geo_location = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set (geo_location, DBUSMENU_MENUITEM_PROP_TYPE, TIMEZONE_MENUITEM_TYPE);
- dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_LABEL, "Updating location information...");
+ dbusmenu_menuitem_property_set (geo_location, TIMEZONE_MENUITEM_PROP_ZONE, "");
dbusmenu_menuitem_property_set_bool (geo_location, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
dbusmenu_menuitem_property_set_bool (geo_location, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
g_signal_connect(G_OBJECT(geo_location), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(quick_set_tz), NULL);
@@ -702,14 +770,13 @@ build_menus (DbusmenuMenuitem * root)
current_location = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set (current_location, DBUSMENU_MENUITEM_PROP_TYPE, TIMEZONE_MENUITEM_TYPE);
- dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_LABEL, "Current Timezone");
+ dbusmenu_menuitem_property_set (current_location, TIMEZONE_MENUITEM_PROP_ZONE, "");
dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
g_signal_connect(G_OBJECT(current_location), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(quick_set_tz), NULL);
dbusmenu_menuitem_child_append(root, current_location);
check_timezone_sync();
- //g_signal_connect(root, DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, G_CALLBACK(update_timezone_menu_items), NULL);
DbusmenuMenuitem * separator = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
@@ -815,6 +882,41 @@ geo_address_cb (GeoclueAddress * address, int timestamp, GHashTable * addy_data,
return;
}
+/* Clean up the reference we kept to the address and make sure to
+ drop the signals incase someone else has one. */
+static void
+geo_address_clean (void)
+{
+ if (geo_address == NULL) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func(G_OBJECT(geo_address), geo_address_cb, NULL);
+ g_object_unref(G_OBJECT(geo_address));
+
+ geo_address = NULL;
+
+ return;
+}
+
+/* Clean up and remove all signal handlers from the client as we
+ unreference it as well. */
+static void
+geo_client_clean (void)
+{
+ if (geo_master == NULL) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func(G_OBJECT(geo_master), geo_client_invalid, NULL);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(geo_master), geo_address_change, NULL);
+ g_object_unref(G_OBJECT(geo_master));
+
+ geo_master = NULL;
+
+ return;
+}
+
/* Callback from creating the address */
static void
geo_create_address (GeoclueMasterClient * master, GeoclueAddress * address, GError * error, gpointer user_data)
@@ -824,6 +926,12 @@ geo_create_address (GeoclueMasterClient * master, GeoclueAddress * address, GErr
return;
}
+ /* We shouldn't have created a new address if we already had one
+ so this is a warning. But, it really is only a mem-leak so we
+ don't need to error out. */
+ g_warn_if_fail(geo_address == NULL);
+ geo_address_clean();
+
g_debug("Created Geoclue Address");
geo_address = address;
g_object_ref(G_OBJECT(geo_address));
@@ -851,10 +959,12 @@ geo_client_invalid (GeoclueMasterClient * client, gpointer user_data)
{
g_warning("Master client invalid, rebuilding.");
- if (geo_master != NULL) {
- g_object_unref(G_OBJECT(geo_master));
- }
- geo_master = NULL;
+ /* Client changes we can assume the address is now invalid so we
+ need to unreference the one we had. */
+ geo_address_clean();
+
+ /* And our master client is invalid */
+ geo_client_clean();
GeoclueMaster * master = geoclue_master_get_default();
geoclue_master_create_client_async(master, geo_create_client, NULL);
@@ -875,10 +985,9 @@ geo_address_change (GeoclueMasterClient * client, gchar * a, gchar * b, gchar *
{
g_warning("Address provider changed. Let's change");
- if (geo_address != NULL) {
- g_object_unref(G_OBJECT(geo_address));
- }
- geo_address = NULL;
+ /* If the address is supposed to have changed we need to drop the old
+ address before starting to get the new one. */
+ geo_address_clean();
geoclue_master_client_create_address_async(geo_master, geo_create_address, NULL);
@@ -899,8 +1008,17 @@ geo_create_client (GeoclueMaster * master, GeoclueMasterClient * client, gchar *
g_debug("Created Geoclue client at: %s", path);
geo_master = client;
+
+ if (geo_master != NULL) {
+ g_warning(_("Unable to get a GeoClue client! Geolocation based timezone support will not be available."));
+ return;
+ }
+
g_object_ref(G_OBJECT(geo_master));
+ /* New client, make sure we don't have an address hanging on */
+ geo_address_clean();
+
geoclue_master_client_set_requirements_async(geo_master,
GEOCLUE_ACCURACY_LEVEL_REGION,
0,
@@ -980,5 +1098,8 @@ main (int argc, char ** argv)
g_object_unref(G_OBJECT(server));
g_object_unref(G_OBJECT(root));
+ geo_address_clean();
+ geo_client_clean();
+
return 0;
}
diff --git a/src/dbus-shared.h b/src/dbus-shared.h
index d13cb32..bad8354 100644
--- a/src/dbus-shared.h
+++ b/src/dbus-shared.h
@@ -35,6 +35,5 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define APPOINTMENT_MENUITEM_PROP_RIGHT "appointment-time"
#define TIMEZONE_MENUITEM_TYPE "timezone-item"
-#define TIMEZONE_MENUITEM_PROP_LABEL "timezone-label"
+#define TIMEZONE_MENUITEM_PROP_ZONE "timezone-zone"
#define TIMEZONE_MENUITEM_PROP_RADIO "timezone-radio"
-#define TIMEZONE_MENUITEM_PROP_RIGHT "timezone-time"
diff --git a/src/indicator-datetime.c b/src/indicator-datetime.c
index e27158c..5e73612 100644
--- a/src/indicator-datetime.c
+++ b/src/indicator-datetime.c
@@ -87,6 +87,8 @@ struct _IndicatorDatetimePrivate {
GDBusProxy * service_proxy;
IdoCalendarMenuItem *ido_calendar;
+ GList * timezone_items;
+
GSettings * settings;
};
@@ -103,6 +105,8 @@ enum {
typedef struct _indicator_item_t indicator_item_t;
struct _indicator_item_t {
+ IndicatorDatetime * self;
+ DbusmenuMenuitem * mi;
GtkWidget * gmi;
GtkWidget * icon;
GtkWidget * label;
@@ -157,16 +161,19 @@ static void indicator_datetime_dispose (GObject *object);
static void indicator_datetime_finalize (GObject *object);
static GtkLabel * get_label (IndicatorObject * io);
static GtkMenu * get_menu (IndicatorObject * io);
+static const gchar * get_accessible_desc (IndicatorObject * io);
static GVariant * bind_enum_set (const GValue * value, const GVariantType * type, gpointer user_data);
static gboolean bind_enum_get (GValue * value, GVariant * variant, gpointer user_data);
-static gchar * generate_format_string (IndicatorDatetime * self);
-static struct tm * update_label (IndicatorDatetime * io);
+static gchar * generate_format_string_now (IndicatorDatetime * self);
+static gchar * generate_format_string_at_time (IndicatorDatetime * self, GDateTime * time);
+static void update_label (IndicatorDatetime * io, GDateTime ** datetime);
static void guess_label_size (IndicatorDatetime * self);
-static void setup_timer (IndicatorDatetime * self, struct tm * ltime);
+static void setup_timer (IndicatorDatetime * self, GDateTime * datetime);
static void update_time (IndicatorDatetime * self);
static void receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data);
static void service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data);
static gint generate_strftime_bitmask (const char *time_str);
+static void timezone_update_labels (indicator_item_t * mi_data);
/* Indicator Module Config */
INDICATOR_SET_VERSION
@@ -193,6 +200,7 @@ indicator_datetime_class_init (IndicatorDatetimeClass *klass)
io_class->get_label = get_label;
io_class->get_menu = get_menu;
+ io_class->get_accessible_desc = get_accessible_desc;
g_object_class_install_property (object_class,
PROP_TIME_FORMAT,
@@ -253,7 +261,7 @@ indicator_datetime_init (IndicatorDatetime *self)
self->priv->custom_string = g_strdup(DEFAULT_TIME_FORMAT);
self->priv->custom_show_seconds = FALSE;
- self->priv->time_string = generate_format_string(self);
+ self->priv->time_string = generate_format_string_now(self);
self->priv->service_proxy = NULL;
@@ -450,6 +458,13 @@ bind_enum_get (GValue * value, GVariant * variant, gpointer user_data)
return TRUE;
}
+static void
+timezone_update_all_labels (IndicatorDatetime * self)
+{
+ IndicatorDatetimePrivate *priv = INDICATOR_DATETIME_GET_PRIVATE(self);
+ g_list_foreach(priv->timezone_items, (GFunc)timezone_update_labels, NULL);
+}
+
/* Sets a property on the object */
static void
set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
@@ -519,7 +534,7 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
}
/* Get the new format string */
- gchar * newformat = generate_format_string(self);
+ gchar * newformat = generate_format_string_now(self);
/* check to ensure the format really changed */
if (g_strcmp0(self->priv->time_string, newformat) == 0) {
@@ -535,7 +550,8 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
self->priv->time_string = newformat;
/* And update everything */
- update_label(self);
+ update_label(self, NULL);
+ timezone_update_all_labels(self);
guess_label_size(self);
return;
@@ -593,54 +609,95 @@ idle_measure (gpointer data)
return FALSE;
}
-/* Updates the label to be the current time. */
-static struct tm *
-update_label (IndicatorDatetime * io)
+/* Updates the accessible description */
+static void
+update_accessible_description (IndicatorDatetime * io)
{
- IndicatorDatetime * self = INDICATOR_DATETIME(io);
+ GList * entries = indicator_object_get_entries(INDICATOR_OBJECT(io));
+ IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
- if (self->priv->label == NULL) return NULL;
+ entry->accessible_desc = get_accessible_desc(INDICATOR_OBJECT(io));
- gchar longstr[256];
- time_t t;
- struct tm *ltime;
- gboolean use_markup;
+ g_signal_emit(G_OBJECT(io),
+ INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
+ 0,
+ entry,
+ TRUE);
- t = time(NULL);
- ltime = localtime(&t);
- if (ltime == NULL) {
- g_debug("Error getting local time");
- gtk_label_set_label(self->priv->label, _("Error getting time"));
- return NULL;
- }
+ g_list_free(entries);
- strftime(longstr, 256, self->priv->time_string, ltime);
-
- gchar * utf8 = g_locale_to_utf8(longstr, -1, NULL, NULL, NULL);
+ return;
+}
+
+/* Updates the label to be the current time. */
+static void
+set_label_to_time_in_zone (IndicatorDatetime * self, GtkLabel * label,
+ GTimeZone * tz, const gchar * format,
+ GDateTime ** datetime)
+{
+ GDateTime * datetime_now;
+ if (tz == NULL)
+ datetime_now = g_date_time_new_now_local();
+ else
+ datetime_now = g_date_time_new_now(tz);
+
+ gchar * timestr;
+ if (format == NULL) {
+ gchar * format_for_time = generate_format_string_at_time(self, datetime_now);
+ timestr = g_date_time_format(datetime_now, format_for_time);
+ g_free(format_for_time);
+ }
+ else {
+ timestr = g_date_time_format(datetime_now, format);
+ }
- if (pango_parse_markup(utf8, -1, 0, NULL, NULL, NULL, NULL))
+ gboolean use_markup = FALSE;
+ if (pango_parse_markup(timestr, -1, 0, NULL, NULL, NULL, NULL))
use_markup = TRUE;
if (use_markup)
- gtk_label_set_markup(self->priv->label, utf8);
+ gtk_label_set_markup(label, timestr);
else
- gtk_label_set_text(self->priv->label, utf8);
+ gtk_label_set_text(label, timestr);
- g_free(utf8);
+ g_free(timestr);
+
+ if (datetime)
+ *datetime = datetime_now;
+ else
+ g_date_time_unref(datetime_now);
+
+ return;
+}
+
+/* Updates the label to be the current time. */
+static void
+update_label (IndicatorDatetime * io, GDateTime ** datetime)
+{
+ IndicatorDatetime * self = INDICATOR_DATETIME(io);
+
+ if (self->priv->label == NULL) return;
+
+ set_label_to_time_in_zone(self, self->priv->label, NULL, self->priv->time_string, datetime);
if (self->priv->idle_measure == 0) {
self->priv->idle_measure = g_idle_add(idle_measure, io);
}
- return ltime;
+ update_accessible_description(io);
+
+ return;
}
/* Update the time right now. Usually the result of a timezone switch. */
static void
update_time (IndicatorDatetime * self)
{
- struct tm * ltime = update_label(self);
- setup_timer(self, ltime);
+ GDateTime * dt;
+ update_label(self, &dt);
+ timezone_update_all_labels(self);
+ setup_timer(self, dt);
+ g_date_time_unref(dt);
return;
}
@@ -664,15 +721,20 @@ timer_func (gpointer user_data)
{
IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
self->priv->timer = 0;
- struct tm * ltime = update_label(self);
- setup_timer(self, ltime);
+ GDateTime * dt;
+ update_label(self, &dt);
+ timezone_update_all_labels(self);
+ setup_timer(self, dt);
+ g_date_time_unref(dt);
return FALSE;
}
/* Configure the timer to run the next time through */
static void
-setup_timer (IndicatorDatetime * self, struct tm * ltime)
+setup_timer (IndicatorDatetime * self, GDateTime * datetime)
{
+ gboolean unref = FALSE;
+
if (self->priv->timer != 0) {
g_source_remove(self->priv->timer);
self->priv->timer = 0;
@@ -682,14 +744,18 @@ setup_timer (IndicatorDatetime * self, struct tm * ltime)
(self->priv->time_mode == SETTINGS_TIME_CUSTOM && self->priv->custom_show_seconds)) {
self->priv->timer = g_timeout_add_seconds(1, timer_func, self);
} else {
- if (ltime == NULL) {
- time_t t;
- t = time(NULL);
- ltime = localtime(&t);
+ if (datetime == NULL) {
+ datetime = g_date_time_new_now_local();
+ unref = TRUE;
}
/* Plus 2 so we're just after the minute, don't want to be early. */
- self->priv->timer = g_timeout_add_seconds(60 - ltime->tm_sec + 2, timer_func, self);
+ gint seconds = (gint)g_date_time_get_seconds(datetime);
+ self->priv->timer = g_timeout_add_seconds(60 - seconds + 2, timer_func, self);
+
+ if (unref) {
+ g_date_time_unref(datetime);
+ }
}
return;
@@ -916,7 +982,8 @@ style_changed (GtkWidget * widget, GtkStyle * oldstyle, gpointer data)
g_debug("New style for time label");
IndicatorDatetime * self = INDICATOR_DATETIME(data);
guess_label_size(self);
- update_label(self);
+ update_label(self, NULL);
+ timezone_update_all_labels(self);
return;
}
@@ -973,12 +1040,8 @@ update_text_gravity (GtkWidget *widget, GdkScreen *previous_screen, gpointer dat
/* Tries to figure out what our format string should be. Lots
of translator comments in here. */
static gchar *
-generate_format_string (IndicatorDatetime * self)
+generate_format_string_full (IndicatorDatetime * self, gboolean show_day, gboolean show_date)
{
- if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
- return g_strdup(self->priv->custom_string);
- }
-
gboolean twelvehour = TRUE;
if (self->priv->time_mode == SETTINGS_TIME_LOCALE) {
@@ -1011,20 +1074,20 @@ generate_format_string (IndicatorDatetime * self)
/* If there's no date or day let's just leave now and
not worry about the rest of this code */
- if (!self->priv->show_date && !self->priv->show_day) {
+ if (!show_date && !show_day) {
return g_strdup(time_string);
}
const gchar * date_string = NULL;
- if (self->priv->show_date && self->priv->show_day) {
+ if (show_date && show_day) {
/* TRANSLATORS: This is a format string passed to strftime to represent
the day of the week, the month and the day of the month. */
date_string = T_("%a %b %e");
- } else if (self->priv->show_date) {
+ } else if (show_date) {
/* TRANSLATORS: This is a format string passed to strftime to represent
the month and the day of the month. */
date_string = T_("%b %e");
- } else if (self->priv->show_day) {
+ } else if (show_day) {
/* TRANSLATORS: This is a format string passed to strftime to represent
the day of the week. */
date_string = T_("%a");
@@ -1039,6 +1102,83 @@ generate_format_string (IndicatorDatetime * self)
return g_strdup_printf(T_("%s, %s"), date_string, time_string);
}
+static gchar *
+generate_format_string_now (IndicatorDatetime * self)
+{
+ if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
+ return g_strdup(self->priv->custom_string);
+ }
+ else {
+ return generate_format_string_full(self,
+ self->priv->show_day,
+ self->priv->show_date);
+ }
+}
+
+static gchar *
+generate_format_string_at_time (IndicatorDatetime * self, GDateTime * time)
+{
+ /* This is a bit less free-form than for the main "now" time label. */
+ /* If it is today, just the time should be shown (e.g. “3:55 PM”)
+ If it is a different day this week, the day and time should be shown (e.g. “Wed 3:55 PM”)
+ If it is after this week, the day, date, and time should be shown (e.g. “Wed 21 Apr 3:55 PM”).
+ In addition, when presenting the times of upcoming events, the time should be followed by the timezone if it is different from the one the computer is currently set to. For example, “Wed 3:55 PM UTC−5”. */
+ gboolean show_day = FALSE;
+ gboolean show_date = FALSE;
+
+ GDateTime * now = g_date_time_new_now_local();
+
+ /* First, are we same day? */
+ gint time_year, time_month, time_day;
+ gint now_year, now_month, now_day;
+ g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
+ g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
+
+ if (time_year != now_year ||
+ time_month != now_month ||
+ time_day != now_day) {
+ /* OK, different days so we must at least show the day. */
+ show_day = TRUE;
+
+ /* Is it this week? */
+ /* Here, we define "is this week" as yesterday, today, or the next five days */
+ GDateTime * past = g_date_time_add_days(now, -1);
+ GDateTime * future = g_date_time_add_days(now, 5);
+ GDateTime * past_bound = g_date_time_new_local(g_date_time_get_year(past),
+ g_date_time_get_month(past),
+ g_date_time_get_day_of_month(past),
+ 0, 0, 0.0);
+ GDateTime * future_bound = g_date_time_new_local(g_date_time_get_year(future),
+ g_date_time_get_month(future),
+ g_date_time_get_day_of_month(future),
+ 23, 59, 59.9);
+ if (g_date_time_compare(time, past_bound) < 0 ||
+ g_date_time_compare(time, future_bound) > 0) {
+ show_date = TRUE;
+ }
+ g_date_time_unref(past);
+ g_date_time_unref(future);
+ g_date_time_unref(past_bound);
+ g_date_time_unref(future_bound);
+ }
+
+ return generate_format_string_full(self, show_day, show_date);
+}
+
+static void
+timezone_update_labels (indicator_item_t * mi_data)
+{
+ const gchar * zone_name = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_ZONE);
+
+ /* TODO: Make zone name a little more user friendly */
+ gtk_label_set_text(GTK_LABEL(mi_data->label), zone_name);
+
+ /* Show current time in that zone on the right */
+ GTimeZone * tz = g_time_zone_new(zone_name);
+ set_label_to_time_in_zone(mi_data->self, GTK_LABEL(mi_data->right), tz, NULL, NULL);
+ g_time_zone_unref(tz);
+}
+
/* Whenever we have a property change on a DbusmenuMenuitem
we need to be responsive to that. */
static void
@@ -1078,12 +1218,8 @@ indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value,
g_object_unref(resized_pixbuf);
}
}
- } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_LABEL)) {
- /* Set the main label */
- gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_RIGHT)) {
- /* Set the right label */
- gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL));
+ } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_ZONE)) {
+ timezone_update_labels(mi_data);
} else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_RADIO)) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi), g_variant_get_boolean(value));
} else {
@@ -1209,6 +1345,8 @@ timezone_toggled_cb (GtkCheckMenuItem *checkmenuitem, DbusmenuMenuitem * dbusite
static void
timezone_destroyed_cb (DbusmenuMenuitem * dbusitem, indicator_item_t * mi_data)
{
+ IndicatorDatetimePrivate *priv = INDICATOR_DATETIME_GET_PRIVATE(mi_data->self);
+ priv->timezone_items = g_list_remove(priv->timezone_items, mi_data);
g_signal_handlers_disconnect_by_func(G_OBJECT(mi_data->gmi), G_CALLBACK(timezone_toggled_cb), dbusitem);
g_free(mi_data);
}
@@ -1223,9 +1361,22 @@ new_timezone_item(DbusmenuMenuitem * newitem,
g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
/* Note: not checking parent, it's reasonable for it to be NULL */
+ IndicatorObject *io = g_object_get_data (G_OBJECT (client), "indicator");
+ if (io == NULL) {
+ g_warning ("found no indicator to attach the timezone to");
+ return FALSE;
+ }
+
+ IndicatorDatetime *self = INDICATOR_DATETIME(io);
+ IndicatorDatetimePrivate *priv = INDICATOR_DATETIME_GET_PRIVATE(self);
+
// Menu item with a radio button and a right aligned time
indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
+ priv->timezone_items = g_list_prepend(priv->timezone_items, mi_data);
+
+ mi_data->self = self;
+ mi_data->mi = newitem;
mi_data->gmi = gtk_check_menu_item_new();
gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(mi_data->gmi), TRUE);
@@ -1235,19 +1386,21 @@ new_timezone_item(DbusmenuMenuitem * newitem,
GtkWidget * hbox = gtk_hbox_new(FALSE, 4);
/* Label, probably a username, chat room or mailbox name */
- mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, TIMEZONE_MENUITEM_PROP_LABEL));
+ mi_data->label = gtk_label_new("");
gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
gtk_widget_show(mi_data->label);
/* Usually either the time or the count on the individual
item. */
- mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, TIMEZONE_MENUITEM_PROP_RIGHT));
+ mi_data->right = gtk_label_new("");
gtk_size_group_add_widget(indicator_right_group, mi_data->right);
gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0);
gtk_widget_show(mi_data->right);
+ timezone_update_labels(mi_data);
+
gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox);
gtk_widget_show(hbox);
@@ -1275,7 +1428,7 @@ get_label (IndicatorObject * io)
g_signal_connect(G_OBJECT(self->priv->label), "style-set", G_CALLBACK(style_changed), self);
g_signal_connect(G_OBJECT(self->priv->label), "screen-changed", G_CALLBACK(update_text_gravity), self);
guess_label_size(self);
- update_label(self);
+ update_label(self, NULL);
gtk_widget_show(GTK_WIDGET(self->priv->label));
}
@@ -1304,3 +1457,16 @@ get_menu (IndicatorObject * io)
return GTK_MENU(self->priv->menu);
}
+
+static const gchar *
+get_accessible_desc (IndicatorObject * io)
+{
+ IndicatorDatetime * self = INDICATOR_DATETIME(io);
+ const gchar * name;
+
+ if (self->priv->label != NULL) {
+ name = gtk_label_get_text(self->priv->label);
+ return name;
+ }
+ return NULL;
+}