aboutsummaryrefslogtreecommitdiff
path: root/src/planner-eds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/planner-eds.c')
-rw-r--r--src/planner-eds.c482
1 files changed, 386 insertions, 96 deletions
diff --git a/src/planner-eds.c b/src/planner-eds.c
index 276058d..876fdfc 100644
--- a/src/planner-eds.c
+++ b/src/planner-eds.c
@@ -19,8 +19,6 @@
#include "config.h"
-#include <gio/gio.h> /* GFile, GFileMonitor */
-
#include <libical/ical.h>
#include <libical/icaltime.h>
#include <libecal/libecal.h>
@@ -30,6 +28,8 @@
struct _IndicatorDatetimePlannerEdsPriv
{
+ GSList * sources;
+ GCancellable * cancellable;
ESourceRegistry * source_registry;
};
@@ -39,44 +39,195 @@ G_DEFINE_TYPE (IndicatorDatetimePlannerEds,
indicator_datetime_planner_eds,
INDICATOR_TYPE_DATETIME_PLANNER)
+G_DEFINE_QUARK ("source-client", source_client)
+
/***
****
+**** my_get_appointments() helpers
+****
***/
-void
-indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt)
+/* whole-task data that all the subtasks can see */
+struct appointment_task_data
+{
+ /* a ref to the planner's cancellable */
+ GCancellable * cancellable;
+
+ /* how many subtasks are still running on */
+ int subtask_count;
+
+ /* the list of appointments to be returned */
+ GSList * appointments;
+};
+
+static struct appointment_task_data *
+appointment_task_data_new (GCancellable * cancellable)
+{
+ struct appointment_task_data * data;
+
+ data = g_slice_new0 (struct appointment_task_data);
+ data->cancellable = g_object_ref (cancellable);
+ return data;
+}
+
+static void
+appointment_task_data_free (gpointer gdata)
+{
+ struct appointment_task_data * data = gdata;
+
+ g_object_unref (data->cancellable);
+
+ g_slice_free (struct appointment_task_data, data);
+}
+
+static void
+appointment_task_done (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ g_task_return_pointer (task, data->appointments, NULL);
+ g_object_unref (task);
+}
+
+static void
+appointment_task_decrement_subtasks (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ if (g_atomic_int_dec_and_test (&data->subtask_count))
+ appointment_task_done (task);
+}
+
+static void
+appointment_task_increment_subtasks (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ g_atomic_int_inc (&data->subtask_count);
+}
+
+/**
+*** get-the-appointment's-uri subtasks
+**/
+
+struct appointment_uri_subtask_data
+{
+ /* The parent task */
+ GTask * task;
+
+ /* The appointment whose uri we're looking for.
+ This pointer is owned by the Task and isn't reffed/unreffed by the subtask */
+ struct IndicatorDatetimeAppt * appt;
+};
+
+static void
+appointment_uri_subtask_done (struct appointment_uri_subtask_data * subdata)
{
- if (appt != NULL)
+ GTask * task = subdata->task;
+
+ /* free the subtask data */
+ g_slice_free (struct appointment_uri_subtask_data, subdata);
+
+ appointment_task_decrement_subtasks (task);
+}
+
+static struct appointment_uri_subtask_data *
+appointment_uri_subtask_data_new (GTask * task, struct IndicatorDatetimeAppt * appt)
+{
+ struct appointment_uri_subtask_data * subdata;
+
+ appointment_task_increment_subtasks (task);
+
+ subdata = g_slice_new0 (struct appointment_uri_subtask_data);
+ subdata->task = task;
+ subdata->appt = appt;
+ return subdata;
+}
+
+static void
+on_appointment_uris_ready (GObject * client,
+ GAsyncResult * res,
+ gpointer gsubdata)
+{
+ GSList * uris;
+ GError * error;
+ struct appointment_uri_subtask_data * subdata = gsubdata;
+
+ uris = NULL;
+ error = NULL;
+ e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Error getting appointment uris: %s", error->message);
+
+ g_error_free (error);
+ }
+ else if (uris != NULL)
{
- g_date_time_unref (appt->end);
- g_date_time_unref (appt->begin);
- g_free (appt->color);
- g_free (appt->summary);
- g_free (appt);
+ struct IndicatorDatetimeAppt * appt = subdata->appt;
+ appt->url = g_strdup (uris->data); /* copy the first URL */
+ g_debug ("found url '%s' for appointment '%s'", appt->url, appt->summary);
+ e_client_util_free_string_slist (uris);
}
+
+ appointment_uri_subtask_done (subdata);
}
-/***
-**** my_get_appointments() helpers
-***/
+/**
+*** enumerate-the-components subtasks
+**/
-struct my_get_appointments_data
+/* data struct for the enumerate-components subtask */
+struct appointment_component_subtask_data
{
- ESource * source;
- GSList * appointments;
+ /* The parent task */
+ GTask * task;
+
+ /* The client we're walking through. The subtask owns a ref to this */
+ ECalClient * client;
- /* ensure that recurring events don't get multiple IndicatorDatetimeAppts */
- GHashTable * added;
+ /* The appointment's color coding. The subtask owns this string */
+ gchar * color;
};
+static void
+on_appointment_component_subtask_done (gpointer gsubdata)
+{
+ struct appointment_component_subtask_data * subdata = gsubdata;
+ GTask * task = subdata->task;
+
+ /* free the subtask data */
+ g_free (subdata->color);
+ g_object_unref (subdata->client);
+ g_slice_free (struct appointment_component_subtask_data, subdata);
+
+ appointment_task_decrement_subtasks (task);
+}
+
+static struct appointment_component_subtask_data *
+appointment_component_subtask_data_new (GTask * task, ECalClient * client, const gchar * color)
+{
+ struct appointment_component_subtask_data * subdata;
+
+ appointment_task_increment_subtasks (task);
+
+ subdata = g_slice_new0 (struct appointment_component_subtask_data);
+ subdata->task = task;
+ subdata->client = g_object_ref (client);
+ subdata->color = g_strdup (color);
+ return subdata;
+}
+
static gboolean
my_get_appointments_foreach (ECalComponent * component,
time_t begin,
time_t end,
- gpointer gdata)
+ gpointer gsubdata)
{
const ECalComponentVType vtype = e_cal_component_get_vtype (component);
- struct my_get_appointments_data * data = gdata;
+ struct appointment_component_subtask_data * subdata = gsubdata;
+ struct appointment_task_data * data = g_task_get_task_data (subdata->task);
if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))
{
@@ -87,7 +238,6 @@ my_get_appointments_foreach (ECalComponent * component,
e_cal_component_get_status (component, &status);
if ((uid != NULL) &&
- (!g_hash_table_contains (data->added, uid)) &&
(status != ICAL_STATUS_COMPLETED) &&
(status != ICAL_STATUS_CANCELLED))
{
@@ -96,8 +246,9 @@ my_get_appointments_foreach (ECalComponent * component,
GSList * recur_list;
ECalComponentText text;
struct IndicatorDatetimeAppt * appt;
+ struct appointment_uri_subtask_data * uri_subdata;
- appt = g_new0 (struct IndicatorDatetimeAppt, 1);
+ appt = g_slice_new0 (struct IndicatorDatetimeAppt);
/* Determine whether this is a recurring event.
NB: icalrecurrencetype supports complex recurrence patterns;
@@ -117,40 +268,50 @@ my_get_appointments_foreach (ECalComponent * component,
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->color = g_strdup (subdata->color);
appt->is_event = vtype == E_CAL_COMPONENT_EVENT;
appt->summary = g_strdup (text.value);
+ appt->uid = g_strdup (uid);
alarm_uids = e_cal_component_get_alarm_uids (component);
appt->has_alarms = alarm_uids != NULL;
cal_obj_uid_list_free (alarm_uids);
data->appointments = g_slist_prepend (data->appointments, appt);
- g_hash_table_add (data->added, g_strdup(uid));
+
+ /* start a new subtask to get the associated URIs */
+ uri_subdata = appointment_uri_subtask_data_new (subdata->task, appt);
+ e_cal_client_get_attachment_uris (subdata->client,
+ uid,
+ NULL,
+ data->cancellable,
+ on_appointment_uris_ready,
+ uri_subdata);
}
}
return G_SOURCE_CONTINUE;
}
-
/***
**** IndicatorDatetimePlanner virtual funcs
***/
-static GSList *
+static void
my_get_appointments (IndicatorDatetimePlanner * planner,
GDateTime * begin_datetime,
- GDateTime * end_datetime)
+ GDateTime * end_datetime,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GList * l;
- GList * sources;
+ GSList * l;
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);
+ GTask * task;
+ gboolean subtasks_added;
p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv;
@@ -172,60 +333,55 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
*** walk through the sources to build the appointment list
**/
- data.source = NULL;
- data.appointments = NULL;
- data.added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ task = g_task_new (planner, p->cancellable, callback, user_data);
+ g_task_set_task_data (task,
+ appointment_task_data_new (p->cancellable),
+ appointment_task_data_free);
- sources = e_source_registry_list_sources (p->source_registry, E_SOURCE_EXTENSION_CALENDAR);
- for (l=sources; l!=NULL; l=l->next)
+ subtasks_added = FALSE;
+ for (l=p->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);
- }
- }
+ ECalClient * client;
+ const char * color;
+ struct appointment_component_subtask_data * subdata;
+
+ source = l->data;
+ client = g_object_get_qdata (l->data, source_client_quark());
+ if (client == NULL)
+ continue;
+
+ if (default_timezone != NULL)
+ e_cal_client_set_default_timezone (client, default_timezone);
+
+ /* start a new subtask to enumerate all the components in this client. */
+ color = e_source_selectable_get_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
+ subdata = appointment_component_subtask_data_new (task, client, color);
+ subtasks_added = TRUE;
+ e_cal_client_generate_instances (client,
+ begin,
+ end,
+ p->cancellable,
+ my_get_appointments_foreach,
+ subdata,
+ on_appointment_component_subtask_done);
}
- g_list_free_full (sources, g_object_unref);
+ if (!subtasks_added)
+ appointment_task_done (task);
+}
- g_debug ("%s EDS get_appointments returning %d appointments", G_STRLOC, g_slist_length (data.appointments));
- g_hash_table_destroy (data.added);
- return data.appointments;
+static GSList *
+my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
+ GAsyncResult * res,
+ GError ** error)
+{
+ return g_task_propagate_pointer (G_TASK(res), error);
}
-gboolean
+static gboolean
my_is_configured (IndicatorDatetimePlanner * planner)
{
- GList * sources;
- gboolean have_sources;
IndicatorDatetimePlannerEds * self;
/* confirm that it's installed... */
@@ -238,10 +394,7 @@ my_is_configured (IndicatorDatetimePlanner * planner)
/* 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;
+ return self->priv->sources != NULL;
}
static void
@@ -279,6 +432,148 @@ my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
}
/***
+**** Source / Client Wrangling
+***/
+
+static void
+on_client_connected (GObject * unused G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ EClient * client;
+
+ error = NULL;
+ client = e_cal_client_connect_finish (res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ /* we've got a new connected ECalClient, so store it & notify clients */
+
+ g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)),
+ source_client_quark(),
+ client,
+ g_object_unref);
+
+ indicator_datetime_planner_emit_appointments_changed (gself);
+ }
+}
+
+static void
+on_source_enabled (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ e_cal_client_connect (source,
+ E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
+ p->cancellable,
+ on_client_connected,
+ self);
+}
+
+static void
+on_source_added (ESourceRegistry * registry,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ p->sources = g_slist_prepend (p->sources, g_object_ref(source));
+
+ if (e_source_get_enabled (source))
+ on_source_enabled (registry, source, gself);
+}
+
+static void
+on_source_disabled (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source,
+ gpointer gself)
+{
+ ECalClient * client;
+
+ /* If this source has a connected ECalClient, remove it & notify clients */
+ if ((client = g_object_steal_qdata (G_OBJECT(source), source_client_quark())))
+ {
+ g_object_unref (client);
+ indicator_datetime_planner_emit_appointments_changed (gself);
+ }
+}
+
+static void
+on_source_removed (ESourceRegistry * registry,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ on_source_disabled (registry, source, gself);
+
+ p->sources = g_slist_remove (p->sources, source);
+ g_object_unref (source);
+}
+
+static void
+on_source_changed (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source G_GNUC_UNUSED,
+ gpointer gself)
+{
+ indicator_datetime_planner_emit_appointments_changed (gself);
+}
+
+static void
+on_source_registry_ready (GObject * source_object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ ESourceRegistry * r;
+
+ error = NULL;
+ r = e_source_registry_new_finish (res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ IndicatorDatetimePlannerEds * self;
+ priv_t * p;
+ GList * l;
+ GList * sources;
+
+ self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ p = self->priv;
+
+ g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self);
+ g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self);
+ g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self);
+ g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self);
+ g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self);
+
+ p->source_registry = r;
+
+ sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR);
+ for (l=sources; l!=NULL; l=l->next)
+ on_source_added (r, l->data, self);
+ g_list_free_full (sources, g_object_unref);
+ }
+}
+
+/***
**** GObject virtual funcs
***/
@@ -288,6 +583,12 @@ my_dispose (GObject * o)
IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o);
priv_t * p = self->priv;
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel (p->cancellable);
+ g_clear_object (&p->cancellable);
+ }
+
if (p->source_registry != NULL)
{
g_signal_handlers_disconnect_by_func (p->source_registry,
@@ -301,7 +602,7 @@ my_dispose (GObject * o)
}
/***
-**** Insantiation
+**** Instantiation
***/
static void
@@ -318,6 +619,7 @@ indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * kl
planner_class->activate = my_activate;
planner_class->activate_time = my_activate_time;
planner_class->get_appointments = my_get_appointments;
+ planner_class->get_appointments_finish = my_get_appointments_finish;
g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv));
}
@@ -326,7 +628,6 @@ 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,
@@ -334,22 +635,11 @@ indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self)
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);
- }
+ p->cancellable = g_cancellable_new ();
+
+ e_source_registry_new (p->cancellable,
+ on_source_registry_ready,
+ self);
}
/***