aboutsummaryrefslogtreecommitdiff
path: root/src/planner-eds.c
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-10-17 03:39:12 +0000
committerTarmac <Unknown>2013-10-17 03:39:12 +0000
commite38b6293bb37557d27efd052c82ee44d70996077 (patch)
treeffcb823fe27b30a448422a7ef72f3727a81d4469 /src/planner-eds.c
parent3cb74c566f21cead559308c94f3b3995e35e6fa2 (diff)
parent05c1039f4b9f0014371bb767089755317ea2d322 (diff)
downloadayatana-indicator-datetime-e38b6293bb37557d27efd052c82ee44d70996077.tar.gz
ayatana-indicator-datetime-e38b6293bb37557d27efd052c82ee44d70996077.tar.bz2
ayatana-indicator-datetime-e38b6293bb37557d27efd052c82ee44d70996077.zip
== Changes to planner-eds:
The get-appointments GTask has a new task subtype for pulling an ECalComponent's uris asynchronously. When get_appointments() is called, create one GTask. We add subtasks to it for each client we know of for calling e_cal_client_generate_instances(). What's new is that for each ECalComponent we find in generate_instances(), we add another new subtask that tries to get the uris for that component. == Testing changes: Make "planner" a property in IndicatorDatetimeService so that we can swap in different appointment planners at runtime. This is for unit testing purposes. Add a mechanism for testing snap decisions without an EDS backend. == Service changes: Every time the appointment list changes, walk through it to find the alarm that will occur the soonest. Set a timer to wake up at that time. When the timer is reached, pop up a snap decision for each alarm set to that time. If the user clicks "OK", dispatch the URL associated with that alarm. Made the appointment menuitems clickable, they now dispatch the appointment's URL. Fixes: https://bugs.launchpad.net/bugs/1233176. Approved by PS Jenkins bot, Ted Gould.
Diffstat (limited to 'src/planner-eds.c')
-rw-r--r--src/planner-eds.c219
1 files changed, 164 insertions, 55 deletions
diff --git a/src/planner-eds.c b/src/planner-eds.c
index f121a32..876fdfc 100644
--- a/src/planner-eds.c
+++ b/src/planner-eds.c
@@ -43,78 +43,180 @@ 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
{
- if (appt != NULL)
- {
- g_date_time_unref (appt->end);
- g_date_time_unref (appt->begin);
- g_free (appt->color);
- g_free (appt->summary);
- g_slice_free (struct IndicatorDatetimeAppt, appt);
- }
-}
-
-/***
-**** my_get_appointments() helpers
-***/
+ /* a ref to the planner's cancellable */
+ GCancellable * cancellable;
-struct get_appointments_task_data
-{
/* how many subtasks are still running on */
int subtask_count;
/* the list of appointments to be returned */
GSList * appointments;
-
- /* ensure that recurring events don't get multiple IndicatorDatetimeAppts */
- GHashTable * added;
};
+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
-get_appointments_task_data_free (gpointer gdata)
+appointment_task_data_free (gpointer gdata)
{
- struct get_appointments_task_data * data = gdata;
- g_hash_table_unref (data->added);
- g_slice_free (struct get_appointments_task_data, data);
+ struct appointment_task_data * data = gdata;
+
+ g_object_unref (data->cancellable);
+
+ g_slice_free (struct appointment_task_data, data);
}
static void
-on_all_subtasks_done (GTask * task)
+appointment_task_done (GTask * task)
{
- struct get_appointments_task_data * data = g_task_get_task_data (task);
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
g_task_return_pointer (task, data->appointments, NULL);
g_object_unref (task);
}
-struct get_appointments_subtask_data
+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;
- gchar * color;
+ /* 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
-on_subtask_done (gpointer gsubdata)
+appointment_uri_subtask_done (struct appointment_uri_subtask_data * subdata)
+{
+ 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)
{
- struct get_appointments_subtask_data * subdata;
+ 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)
+ {
+ 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);
+}
+
+/**
+*** enumerate-the-components subtasks
+**/
+
+/* data struct for the enumerate-components subtask */
+struct appointment_component_subtask_data
+{
+ /* The parent task */
GTask * task;
- struct get_appointments_task_data * data;
- subdata = gsubdata;
- task = subdata->task;
+ /* The client we're walking through. The subtask owns a ref to this */
+ ECalClient * client;
+
+ /* 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_slice_free (struct get_appointments_subtask_data, subdata);
+ g_object_unref (subdata->client);
+ g_slice_free (struct appointment_component_subtask_data, subdata);
- /* poke the task */
- data = g_task_get_task_data (task);
- if (g_atomic_int_dec_and_test (&data->subtask_count))
- on_all_subtasks_done (task);
+ 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
@@ -124,8 +226,8 @@ my_get_appointments_foreach (ECalComponent * component,
gpointer gsubdata)
{
const ECalComponentVType vtype = e_cal_component_get_vtype (component);
- struct get_appointments_subtask_data * subdata = gsubdata;
- struct get_appointments_task_data * data = g_task_get_task_data (subdata->task);
+ 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))
{
@@ -136,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))
{
@@ -145,6 +246,7 @@ my_get_appointments_foreach (ECalComponent * component,
GSList * recur_list;
ECalComponentText text;
struct IndicatorDatetimeAppt * appt;
+ struct appointment_uri_subtask_data * uri_subdata;
appt = g_slice_new0 (struct IndicatorDatetimeAppt);
@@ -169,13 +271,22 @@ my_get_appointments_foreach (ECalComponent * component,
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);
}
}
@@ -197,7 +308,6 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
priv_t * p;
const char * str;
icaltimezone * default_timezone;
- struct get_appointments_task_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;
@@ -223,17 +333,18 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
*** walk through the sources to build the appointment list
**/
- data = g_slice_new0 (struct get_appointments_task_data);
- 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, data, get_appointments_task_data_free);
+ g_task_set_task_data (task,
+ appointment_task_data_new (p->cancellable),
+ appointment_task_data_free);
subtasks_added = FALSE;
for (l=p->sources; l!=NULL; l=l->next)
{
ESource * source;
ECalClient * client;
- struct get_appointments_subtask_data * subdata;
+ const char * color;
+ struct appointment_component_subtask_data * subdata;
source = l->data;
client = g_object_get_qdata (l->data, source_client_quark());
@@ -243,11 +354,9 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
if (default_timezone != NULL)
e_cal_client_set_default_timezone (client, default_timezone);
- subdata = g_slice_new (struct get_appointments_subtask_data);
- subdata->task = task;
- subdata->color = e_source_selectable_dup_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
-
- g_atomic_int_inc (&data->subtask_count);
+ /* 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,
@@ -255,11 +364,11 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
p->cancellable,
my_get_appointments_foreach,
subdata,
- on_subtask_done);
+ on_appointment_component_subtask_done);
}
if (!subtasks_added)
- on_all_subtasks_done (task);
+ appointment_task_done (task);
}
static GSList *
@@ -270,7 +379,7 @@ my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
return g_task_propagate_pointer (G_TASK(res), error);
}
-gboolean
+static gboolean
my_is_configured (IndicatorDatetimePlanner * planner)
{
IndicatorDatetimePlannerEds * self;