Merge lp:~charlesk/indicator-datetime/lp-1204532 into lp:~indicator-applet-developers/indicator-datetime/trunk.13.10

Proposed by Charles Kerr
Status: Merged
Approved by: Ted Gould
Approved revision: 257
Merged at revision: 250
Proposed branch: lp:~charlesk/indicator-datetime/lp-1204532
Merge into: lp:~indicator-applet-developers/indicator-datetime/trunk.13.10
Diff against target: 1014 lines (+477/-176)
4 files modified
src/planner-eds.c (+261/-80)
src/planner.c (+29/-2)
src/planner.h (+33/-7)
src/service.c (+154/-87)
To merge this branch: bzr merge lp:~charlesk/indicator-datetime/lp-1204532
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+184155@code.launchpad.net

Commit message

Make the EDS planner nonblocking.

Description of the change

Make the EDS planner nonblocking.

Summary of changes:

 1. Instead of synchronously creating short-term EDS sources & clients in indicator_datetime_planner_get_appointments(), load them asynchronously when the planner object is instantiated and store references to them. When a connection changes or is added or removed, notify the datetime service via planner's "appointments-changed" signal.

 2. Make indicator_datetime_planner_get_appointments() nonblocking.

 3. In the service, respond to the "appointments-changed" signal by asynchronously fetching appointments, then updating the appropriate menu sections (ie, calendar & appointments).

This branch should make headway on three bug tickets:

 1. bug #1204532, which is straightforward: when EDS is misconfigured, datetime's sync calls to it can block, hanging datetime

 2. bug #1210864, which is exacerbated by indicator-datetime frequently cycling through EDS sources & clients. This patch removes that churn.

 3. bug #1207060, which is currently the top bug at <https://errors.ubuntu.com/?release=Ubuntu%2013.10&user=indicator-applet-developers&period=month>. That ticket's trace is vague; however, it looks like a dangling pointer issue in our EDS code. Eliminating the EDS churn mentioned above may solve this. (Note, this is a wishy-washy assessment. I'd feel more confident on this ticket if I could reproduce the crash or if the stacktrace were more informative.)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
252. By Charles Kerr

use GSlice for allocating/freeing indicator appt structs

253. By Charles Kerr

use GSlice for allocating/freeing service's setlocation_data structs

254. By Charles Kerr

use GSlice for allocating/freeing service's TimeLocation structs

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

A could little comments from the code review. I'm going to run this and look at it as well. Complex, but good!

* For decrementing the subtask_count you should probably use g_atomic_int_dec_and_test().

* In on_appointments_change() you should probably handle the case of a GDateTime being NULL. I think that the indicator functions are fine, but the g_datetime_unref() is not.

Revision history for this message
Ted Gould (ted) wrote :

Installed on my machine it is much faster to get the service up and time displayed, which is awesome. It doesn't seem like clicking on days in the calendar work though. It looks like the menu refreshes, but the 5 appointments stay the same.

review: Needs Fixing
Revision history for this message
Charles Kerr (charlesk) wrote :

> * For decrementing the subtask_count you should probably use g_atomic_int_dec_and_test().

Fixed r255

> * In on_appointments_change() you should probably handle the case of a GDateTime being NULL. I think that the indicator functions are fine, but the g_datetime_unref() is not.

get_calendar_date() doesn't return NULL, and the arguments to g_date_time_new_local() all look sane. I don't see the NULL case you're wanting to safeguard against?

> the menu refreshes, but the 5 appointments stay the same.

Oog. in progress.

255. By Charles Kerr

in planner-eds, use g_atomic_int for enc/dec of the subtask_count

Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2013-09-05 at 21:35 +0000, Charles Kerr wrote:

> > * In on_appointments_change() you should probably handle the case of a GDateTime being NULL. I think that the indicator functions are fine, but the g_datetime_unref() is not.
>
> get_calendar_date() doesn't return NULL, and the arguments to g_date_time_new_local() all look sane. I don't see the NULL case you're wanting to safeguard against?

Yeah, I don't know. It was happening in one of the other bugs I fixed.
Not sure if it's bad system time or what. What's weirder is that it was
the begin time... no clue. Be safe out there :-)

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

FYI, I can confirm that this fixes #1204532

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Mh, I don't see the calendar widget in it though...

Revision history for this message
Charles Kerr (charlesk) wrote :

Ted, I can confirm your report of the appointments section not updating properly when the calendar date changes.

Trevino, just to confirm, you're looking at the desktop? There's no calendar widget in datetime indicator's phone menu. I'm seeing it on the desktop though.

256. By Charles Kerr

fix 'upcoming appointment' section menuitems issue reported by ted

257. By Charles Kerr

in update_appointment_lists(), safeguard against NULL GDateTimes.

Revision history for this message
Charles Kerr (charlesk) wrote :

> the menu refreshes, but the 5 appointments stay the same.

Fixed r256

> * In on_appointments_change() you should probably handle the case of a GDateTime being NULL. I think that the indicator functions are fine, but the g_datetime_unref() is not.

Fixed r257

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

Odd to me Jenkins kept reporting 254 as the revision. Running again at 257.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Charles: I'm talking about the desktop... I only see the day and the "Add event…" menu item. But no calendar widget.

I don't know if this may be due to the fact that one of my calendars seems to be lazy and the reply to the call doesn't arrive (same issue that lead to bug #1204532).

Revision history for this message
Ted Gould (ted) wrote :

This is good for me now. Thanks!

review: Approve
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Sorry I only had a configuration issue... Calendar is working as it should here now.

Revision history for this message
Charles Kerr (charlesk) wrote :

Ted, thanks for catching that regression -- it shouldn't've slipped through. I've started a new side-branch to increase test coverage in datetime.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/planner-eds.c'
2--- src/planner-eds.c 2013-07-26 02:06:25 +0000
3+++ src/planner-eds.c 2013-09-05 23:00:57 +0000
4@@ -19,8 +19,6 @@
5
6 #include "config.h"
7
8-#include <gio/gio.h> /* GFile, GFileMonitor */
9-
10 #include <libical/ical.h>
11 #include <libical/icaltime.h>
12 #include <libecal/libecal.h>
13@@ -30,6 +28,8 @@
14
15 struct _IndicatorDatetimePlannerEdsPriv
16 {
17+ GSList * sources;
18+ GCancellable * cancellable;
19 ESourceRegistry * source_registry;
20 };
21
22@@ -39,6 +39,8 @@
23 indicator_datetime_planner_eds,
24 INDICATOR_TYPE_DATETIME_PLANNER)
25
26+G_DEFINE_QUARK ("source-client", source_client)
27+
28 /***
29 ****
30 ***/
31@@ -52,7 +54,7 @@
32 g_date_time_unref (appt->begin);
33 g_free (appt->color);
34 g_free (appt->summary);
35- g_free (appt);
36+ g_slice_free (struct IndicatorDatetimeAppt, appt);
37 }
38 }
39
40@@ -60,23 +62,70 @@
41 **** my_get_appointments() helpers
42 ***/
43
44-struct my_get_appointments_data
45+struct get_appointments_task_data
46 {
47- ESource * source;
48+ /* how many subtasks are still running on */
49+ int subtask_count;
50+
51+ /* the list of appointments to be returned */
52 GSList * appointments;
53
54 /* ensure that recurring events don't get multiple IndicatorDatetimeAppts */
55 GHashTable * added;
56 };
57
58+static void
59+get_appointments_task_data_free (gpointer gdata)
60+{
61+ struct get_appointments_task_data * data = gdata;
62+ g_hash_table_unref (data->added);
63+ g_slice_free (struct get_appointments_task_data, data);
64+}
65+
66+static void
67+on_all_subtasks_done (GTask * task)
68+{
69+ struct get_appointments_task_data * data = g_task_get_task_data (task);
70+ g_task_return_pointer (task, data->appointments, NULL);
71+ g_object_unref (task);
72+}
73+
74+struct get_appointments_subtask_data
75+{
76+ GTask * task;
77+
78+ gchar * color;
79+};
80+
81+static void
82+on_subtask_done (gpointer gsubdata)
83+{
84+ struct get_appointments_subtask_data * subdata;
85+ GTask * task;
86+ struct get_appointments_task_data * data;
87+
88+ subdata = gsubdata;
89+ task = subdata->task;
90+
91+ /* free the subtask data */
92+ g_free (subdata->color);
93+ g_slice_free (struct get_appointments_subtask_data, subdata);
94+
95+ /* poke the task */
96+ data = g_task_get_task_data (task);
97+ if (g_atomic_int_dec_and_test (&data->subtask_count))
98+ on_all_subtasks_done (task);
99+}
100+
101 static gboolean
102 my_get_appointments_foreach (ECalComponent * component,
103 time_t begin,
104 time_t end,
105- gpointer gdata)
106+ gpointer gsubdata)
107 {
108 const ECalComponentVType vtype = e_cal_component_get_vtype (component);
109- struct my_get_appointments_data * data = gdata;
110+ struct get_appointments_subtask_data * subdata = gsubdata;
111+ struct get_appointments_task_data * data = g_task_get_task_data (subdata->task);
112
113 if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))
114 {
115@@ -97,7 +146,7 @@
116 ECalComponentText text;
117 struct IndicatorDatetimeAppt * appt;
118
119- appt = g_new0 (struct IndicatorDatetimeAppt, 1);
120+ appt = g_slice_new0 (struct IndicatorDatetimeAppt);
121
122 /* Determine whether this is a recurring event.
123 NB: icalrecurrencetype supports complex recurrence patterns;
124@@ -117,7 +166,7 @@
125
126 appt->begin = g_date_time_new_from_unix_local (begin);
127 appt->end = g_date_time_new_from_unix_local (end);
128- appt->color = e_source_selectable_dup_color (e_source_get_extension (data->source, E_SOURCE_EXTENSION_CALENDAR));
129+ appt->color = g_strdup (subdata->color);
130 appt->is_event = vtype == E_CAL_COMPONENT_EVENT;
131 appt->summary = g_strdup (text.value);
132
133@@ -133,24 +182,26 @@
134 return G_SOURCE_CONTINUE;
135 }
136
137-
138 /***
139 **** IndicatorDatetimePlanner virtual funcs
140 ***/
141
142-static GSList *
143+static void
144 my_get_appointments (IndicatorDatetimePlanner * planner,
145 GDateTime * begin_datetime,
146- GDateTime * end_datetime)
147+ GDateTime * end_datetime,
148+ GAsyncReadyCallback callback,
149+ gpointer user_data)
150 {
151- GList * l;
152- GList * sources;
153+ GSList * l;
154 priv_t * p;
155 const char * str;
156 icaltimezone * default_timezone;
157- struct my_get_appointments_data data;
158+ struct get_appointments_task_data * data;
159 const int64_t begin = g_date_time_to_unix (begin_datetime);
160 const int64_t end = g_date_time_to_unix (end_datetime);
161+ GTask * task;
162+ gboolean subtasks_added;
163
164 p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv;
165
166@@ -172,60 +223,56 @@
167 *** walk through the sources to build the appointment list
168 **/
169
170- data.source = NULL;
171- data.appointments = NULL;
172- data.added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
173+ data = g_slice_new0 (struct get_appointments_task_data);
174+ data->added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
175+ task = g_task_new (planner, p->cancellable, callback, user_data);
176+ g_task_set_task_data (task, data, get_appointments_task_data_free);
177
178- sources = e_source_registry_list_sources (p->source_registry, E_SOURCE_EXTENSION_CALENDAR);
179- for (l=sources; l!=NULL; l=l->next)
180+ subtasks_added = FALSE;
181+ for (l=p->sources; l!=NULL; l=l->next)
182 {
183- GError * err;
184 ESource * source;
185- ECalClient * ecc;
186-
187- source = E_SOURCE (l->data);
188- if (e_source_get_enabled (source))
189- {
190- err = NULL;
191- ecc = e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, &err);
192- if (err != NULL)
193- {
194- g_warning ("Can't create ecal client: %s", err->message);
195- g_error_free (err);
196- }
197- else
198- {
199- if (!e_client_open_sync (E_CLIENT (ecc), TRUE, NULL, &err))
200- {
201- g_debug ("Failed to open ecal client: %s", err->message);
202- g_error_free (err);
203- }
204- else
205- {
206- if (default_timezone != NULL)
207- e_cal_client_set_default_timezone (ecc, default_timezone);
208-
209- data.source = source;
210- e_cal_client_generate_instances_sync (ecc, begin, end, my_get_appointments_foreach, &data);
211- }
212-
213- g_object_unref (ecc);
214- }
215- }
216+ ECalClient * client;
217+ struct get_appointments_subtask_data * subdata;
218+
219+ source = l->data;
220+ client = g_object_get_qdata (l->data, source_client_quark());
221+ if (client == NULL)
222+ continue;
223+
224+ if (default_timezone != NULL)
225+ e_cal_client_set_default_timezone (client, default_timezone);
226+
227+ subdata = g_slice_new (struct get_appointments_subtask_data);
228+ subdata->task = task;
229+ subdata->color = e_source_selectable_dup_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
230+
231+ g_atomic_int_inc (&data->subtask_count);
232+ subtasks_added = TRUE;
233+ e_cal_client_generate_instances (client,
234+ begin,
235+ end,
236+ p->cancellable,
237+ my_get_appointments_foreach,
238+ subdata,
239+ on_subtask_done);
240 }
241
242- g_list_free_full (sources, g_object_unref);
243+ if (!subtasks_added)
244+ on_all_subtasks_done (task);
245+}
246
247- g_debug ("%s EDS get_appointments returning %d appointments", G_STRLOC, g_slist_length (data.appointments));
248- g_hash_table_destroy (data.added);
249- return data.appointments;
250+static GSList *
251+my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
252+ GAsyncResult * res,
253+ GError ** error)
254+{
255+ return g_task_propagate_pointer (G_TASK(res), error);
256 }
257
258 gboolean
259 my_is_configured (IndicatorDatetimePlanner * planner)
260 {
261- GList * sources;
262- gboolean have_sources;
263 IndicatorDatetimePlannerEds * self;
264
265 /* confirm that it's installed... */
266@@ -238,10 +285,7 @@
267
268 /* see if there are any calendar sources */
269 self = INDICATOR_DATETIME_PLANNER_EDS (planner);
270- sources = e_source_registry_list_sources (self->priv->source_registry, E_SOURCE_EXTENSION_CALENDAR);
271- have_sources = sources != NULL;
272- g_list_free_full (sources, g_object_unref);
273- return have_sources;
274+ return self->priv->sources != NULL;
275 }
276
277 static void
278@@ -279,6 +323,148 @@
279 }
280
281 /***
282+**** Source / Client Wrangling
283+***/
284+
285+static void
286+on_client_connected (GObject * unused G_GNUC_UNUSED,
287+ GAsyncResult * res,
288+ gpointer gself)
289+{
290+ GError * error;
291+ EClient * client;
292+
293+ error = NULL;
294+ client = e_cal_client_connect_finish (res, &error);
295+ if (error != NULL)
296+ {
297+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
298+ g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message);
299+
300+ g_error_free (error);
301+ }
302+ else
303+ {
304+ /* we've got a new connected ECalClient, so store it & notify clients */
305+
306+ g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)),
307+ source_client_quark(),
308+ client,
309+ g_object_unref);
310+
311+ indicator_datetime_planner_emit_appointments_changed (gself);
312+ }
313+}
314+
315+static void
316+on_source_enabled (ESourceRegistry * registry G_GNUC_UNUSED,
317+ ESource * source,
318+ gpointer gself)
319+{
320+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
321+ priv_t * p = self->priv;
322+
323+ e_cal_client_connect (source,
324+ E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
325+ p->cancellable,
326+ on_client_connected,
327+ self);
328+}
329+
330+static void
331+on_source_added (ESourceRegistry * registry,
332+ ESource * source,
333+ gpointer gself)
334+{
335+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
336+ priv_t * p = self->priv;
337+
338+ p->sources = g_slist_prepend (p->sources, g_object_ref(source));
339+
340+ if (e_source_get_enabled (source))
341+ on_source_enabled (registry, source, gself);
342+}
343+
344+static void
345+on_source_disabled (ESourceRegistry * registry G_GNUC_UNUSED,
346+ ESource * source,
347+ gpointer gself)
348+{
349+ ECalClient * client;
350+
351+ /* If this source has a connected ECalClient, remove it & notify clients */
352+ if ((client = g_object_steal_qdata (G_OBJECT(source), source_client_quark())))
353+ {
354+ g_object_unref (client);
355+ indicator_datetime_planner_emit_appointments_changed (gself);
356+ }
357+}
358+
359+static void
360+on_source_removed (ESourceRegistry * registry,
361+ ESource * source,
362+ gpointer gself)
363+{
364+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
365+ priv_t * p = self->priv;
366+
367+ on_source_disabled (registry, source, gself);
368+
369+ p->sources = g_slist_remove (p->sources, source);
370+ g_object_unref (source);
371+}
372+
373+static void
374+on_source_changed (ESourceRegistry * registry G_GNUC_UNUSED,
375+ ESource * source G_GNUC_UNUSED,
376+ gpointer gself)
377+{
378+ indicator_datetime_planner_emit_appointments_changed (gself);
379+}
380+
381+static void
382+on_source_registry_ready (GObject * source_object G_GNUC_UNUSED,
383+ GAsyncResult * res,
384+ gpointer gself)
385+{
386+ GError * error;
387+ ESourceRegistry * r;
388+
389+ error = NULL;
390+ r = e_source_registry_new_finish (res, &error);
391+ if (error != NULL)
392+ {
393+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
394+ g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message);
395+
396+ g_error_free (error);
397+ }
398+ else
399+ {
400+ IndicatorDatetimePlannerEds * self;
401+ priv_t * p;
402+ GList * l;
403+ GList * sources;
404+
405+ self = INDICATOR_DATETIME_PLANNER_EDS (gself);
406+ p = self->priv;
407+
408+ g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self);
409+ g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self);
410+ g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self);
411+ g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self);
412+ g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self);
413+
414+ p->source_registry = r;
415+
416+ sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR);
417+ for (l=sources; l!=NULL; l=l->next)
418+ on_source_added (r, l->data, self);
419+ g_list_free_full (sources, g_object_unref);
420+ }
421+}
422+
423+/***
424 **** GObject virtual funcs
425 ***/
426
427@@ -288,6 +474,12 @@
428 IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o);
429 priv_t * p = self->priv;
430
431+ if (p->cancellable != NULL)
432+ {
433+ g_cancellable_cancel (p->cancellable);
434+ g_clear_object (&p->cancellable);
435+ }
436+
437 if (p->source_registry != NULL)
438 {
439 g_signal_handlers_disconnect_by_func (p->source_registry,
440@@ -301,7 +493,7 @@
441 }
442
443 /***
444-**** Insantiation
445+**** Instantiation
446 ***/
447
448 static void
449@@ -318,6 +510,7 @@
450 planner_class->activate = my_activate;
451 planner_class->activate_time = my_activate_time;
452 planner_class->get_appointments = my_get_appointments;
453+ planner_class->get_appointments_finish = my_get_appointments_finish;
454
455 g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv));
456 }
457@@ -326,7 +519,6 @@
458 indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self)
459 {
460 priv_t * p;
461- GError * err;
462
463 p = G_TYPE_INSTANCE_GET_PRIVATE (self,
464 INDICATOR_TYPE_DATETIME_PLANNER_EDS,
465@@ -334,22 +526,11 @@
466
467 self->priv = p;
468
469- err = 0;
470- p->source_registry = e_source_registry_new_sync (NULL, &err);
471- if (err != NULL)
472- {
473- g_warning ("indicator-datetime cannot show EDS appointments: %s", err->message);
474- g_error_free (err);
475- }
476- else
477- {
478- gpointer o = p->source_registry;
479- g_signal_connect_swapped (o, "source-added", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
480- g_signal_connect_swapped (o, "source-removed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
481- g_signal_connect_swapped (o, "source-changed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
482- g_signal_connect_swapped (o, "source-disabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
483- g_signal_connect_swapped (o, "source-enabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
484- }
485+ p->cancellable = g_cancellable_new ();
486+
487+ e_source_registry_new (p->cancellable,
488+ on_source_registry_ready,
489+ self);
490 }
491
492 /***
493
494=== modified file 'src/planner.c'
495--- src/planner.c 2013-06-17 04:35:54 +0000
496+++ src/planner.c 2013-09-05 23:00:57 +0000
497@@ -178,17 +178,44 @@
498 return g_date_time_compare (a->begin, b->begin);
499 }
500
501+void
502+indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self,
503+ GDateTime * begin,
504+ GDateTime * end,
505+ GAsyncReadyCallback callback,
506+ gpointer user_data)
507+{
508+ IndicatorDatetimePlannerClass * klass;
509+
510+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
511+
512+ klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self);
513+ g_return_if_fail (klass->get_appointments != NULL);
514+ klass->get_appointments (self, begin, end, callback, user_data);
515+}
516+
517 GSList *
518-indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end)
519+indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self,
520+ GAsyncResult * res,
521+ GError ** error)
522 {
523+ IndicatorDatetimePlannerClass * klass;
524 GSList * appointments;
525
526 g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL);
527
528- appointments = INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->get_appointments (self, begin, end);
529+ klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self);
530+ g_return_val_if_fail (klass->get_appointments_finish != NULL, NULL);
531+ appointments = klass->get_appointments_finish (self, res, error);
532 return g_slist_sort (appointments, compare_appointments_by_start_time);
533 }
534
535+void
536+indicator_datetime_planner_free_appointments (GSList * l)
537+{
538+ g_slist_free_full (l, (GDestroyNotify)indicator_datetime_appt_free);
539+}
540+
541 gboolean
542 indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self)
543 {
544
545=== modified file 'src/planner.h'
546--- src/planner.h 2013-07-26 02:06:25 +0000
547+++ src/planner.h 2013-09-05 23:00:57 +0000
548@@ -22,6 +22,7 @@
549
550 #include <glib.h>
551 #include <glib-object.h> /* parent class */
552+#include <gio/gio.h>
553
554 G_BEGIN_DECLS
555
556@@ -70,7 +71,16 @@
557
558 /* virtual functions */
559
560- GSList* (*get_appointments) (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
561+ void (*get_appointments) (IndicatorDatetimePlanner * self,
562+ GDateTime * begin,
563+ GDateTime * end,
564+ GAsyncReadyCallback callback,
565+ gpointer user_data);
566+
567+ GSList* (*get_appointments_finish) (IndicatorDatetimePlanner * self,
568+ GAsyncResult * res,
569+ GError ** error);
570+
571
572 gboolean (*is_configured) (IndicatorDatetimePlanner * self);
573 void (*activate) (IndicatorDatetimePlanner * self);
574@@ -85,17 +95,33 @@
575
576 /**
577 * Get a list of appointments, sorted by start time.
578- *
579- * An easy way to free the list properly in one step is as follows:
580- *
581- * g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free);
582- *
583+ */
584+void indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self,
585+ GDateTime * begin,
586+ GDateTime * end,
587+ GAsyncReadyCallback callback,
588+ gpointer user_data);
589+
590+/**
591+ * Finishes the async call begun with indicator_datetime_planner_get_appointments()
592+ *
593+ * To free the list properly, use indicator_datetime_planner_free_appointments()
594 *
595 * Return value: (element-type IndicatorDatetimeAppt)
596 * (transfer full):
597 * list of appointments
598 */
599-GSList * indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
600+GSList * indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self,
601+ GAsyncResult * res,
602+ GError ** error);
603+
604+/**
605+ * Convenience function for freeing a GSList of IndicatorDatetimeAppt.
606+ *
607+ * Equivalent to g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free);
608+ */
609+void indicator_datetime_planner_free_appointments (GSList *);
610+
611
612 /**
613 * Returns false if the planner's backend is not configured.
614
615=== modified file 'src/service.c'
616--- src/service.c 2013-08-22 11:02:54 +0000
617+++ src/service.c 2013-09-05 23:00:57 +0000
618@@ -118,6 +118,15 @@
619 GSimpleAction * calendar_action;
620
621 GDBusProxy * login1_manager;
622+
623+ /* all the appointments in the selected calendar_date's month.
624+ Used when populating the 'appointment-days' entry in
625+ create_calendar_state() */
626+ GSList * calendar_appointments;
627+
628+ /* appointments over the next few weeks.
629+ Used when building SECTION_APPOINTMENTS */
630+ GSList * upcoming_appointments;
631 };
632
633 typedef IndicatorDatetimeServicePrivate priv_t;
634@@ -569,38 +578,6 @@
635 return date;
636 }
637
638-static GSList *
639-get_all_appointments_this_month (IndicatorDatetimeService * self)
640-{
641- GSList * appointments = NULL;
642- priv_t * p = self->priv;
643-
644- if (p->planner != NULL)
645- {
646- GDateTime * calendar_date;
647- GDateTime * begin;
648- GDateTime * end;
649- int y, m, d;
650-
651- calendar_date = get_calendar_date (self);
652- g_date_time_get_ymd (calendar_date, &y, &m, &d);
653- begin = g_date_time_new_local (y, m, 1,
654- 0, 0, 0);
655- end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y),
656- 23, 59, 0);
657-
658- appointments = indicator_datetime_planner_get_appointments (p->planner,
659- begin,
660- end);
661-
662- g_date_time_unref (end);
663- g_date_time_unref (begin);
664- g_date_time_unref (calendar_date);
665- }
666-
667- return appointments;
668-}
669-
670 static GVariant *
671 create_calendar_state (IndicatorDatetimeService * self)
672 {
673@@ -611,15 +588,13 @@
674 GVariantBuilder day_builder;
675 GDateTime * date;
676 GSList * l;
677- GSList * appts;
678 gboolean b;
679 priv_t * p = self->priv;
680
681 g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_DICTIONARY);
682
683 key = "appointment-days";
684- appts = get_all_appointments_this_month (self);
685- for (l=appts; l!=NULL; l=l->next)
686+ for (l=p->calendar_appointments; l!=NULL; l=l->next)
687 {
688 const struct IndicatorDatetimeAppt * appt = l->data;
689 days[g_date_time_get_day_of_month (appt->begin)] = TRUE;
690@@ -630,7 +605,6 @@
691 g_variant_builder_add (&day_builder, "i", i);
692 g_variant_builder_add (&dict_builder, "{sv}", key,
693 g_variant_builder_end (&day_builder));
694- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
695
696 key = "calendar-day";
697 date = get_calendar_date (self);
698@@ -726,38 +700,6 @@
699 ****
700 ***/
701
702-/* gets the next MAX_APPTS appointments */
703-static GSList *
704-get_upcoming_appointments (IndicatorDatetimeService * self)
705-{
706- const int MAX_APPTS = 5;
707- GSList * l;
708- GSList * appts = NULL;
709- priv_t * p = self->priv;
710-
711- if (p->planner != NULL)
712- {
713- GDateTime * begin = get_calendar_date (self);
714- GDateTime * end = g_date_time_add_months (begin, 1);
715-
716- appts = indicator_datetime_planner_get_appointments (p->planner,
717- begin,
718- end);
719-
720- g_date_time_unref (end);
721- g_date_time_unref (begin);
722- }
723-
724- /* truncate at MAX_APPTS */
725- if ((l = g_slist_nth (appts, MAX_APPTS-1)))
726- {
727- g_slist_free_full (l->next, (GDestroyNotify)indicator_datetime_appt_free);
728- l->next = NULL;
729- }
730-
731- return appts;
732-}
733-
734 static gboolean
735 service_has_alarms (IndicatorDatetimeService * self)
736 {
737@@ -765,7 +707,7 @@
738 GSList * appts;
739 GSList * l;
740
741- appts = get_upcoming_appointments (self);
742+ appts = self->priv->upcoming_appointments;
743 for (l=appts; l!=NULL; l=l->next)
744 {
745 struct IndicatorDatetimeAppt * appt = l->data;
746@@ -773,7 +715,6 @@
747 break;
748 }
749
750- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
751 return has_alarms;
752 }
753
754@@ -809,13 +750,15 @@
755 static void
756 add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse)
757 {
758+ const int MAX_APPTS = 5;
759 GDateTime * now = indicator_datetime_service_get_localtime (self);
760 GSList * appts;
761 GSList * l;
762+ int i;
763
764 /* build appointment menuitems */
765- appts = get_upcoming_appointments (self);
766- for (l=appts; l!=NULL; l=l->next)
767+ appts = self->priv->upcoming_appointments;
768+ for (l=appts, i=0; l!=NULL && i<MAX_APPTS; l=l->next, i++)
769 {
770 struct IndicatorDatetimeAppt * appt = l->data;
771 char * fmt = get_appointment_time_format (appt, now, terse);
772@@ -824,7 +767,7 @@
773
774 menu_item = g_menu_item_new (appt->summary, NULL);
775
776- if (!appt->has_alarms)
777+ if (appt->color && !appt->has_alarms)
778 g_menu_item_set_attribute (menu_item, "x-canonical-color",
779 "s", appt->color);
780
781@@ -845,7 +788,6 @@
782
783 /* cleanup */
784 g_date_time_unref (now);
785- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
786 }
787
788 static GMenuModel *
789@@ -966,7 +908,7 @@
790 g_date_time_unref (loc->local_time);
791 g_free (loc->name);
792 g_free (loc->zone);
793- g_free (loc);
794+ g_slice_free (struct TimeLocation, loc);
795 }
796
797 static struct TimeLocation*
798@@ -974,7 +916,7 @@
799 const char * name,
800 gboolean visible)
801 {
802- struct TimeLocation * loc = g_new (struct TimeLocation, 1);
803+ struct TimeLocation * loc = g_slice_new (struct TimeLocation);
804 GTimeZone * tz = g_time_zone_new (zone);
805 loc->zone = g_strdup (zone);
806 loc->name = g_strdup (name);
807@@ -1140,7 +1082,7 @@
808 {
809 g_free (data->timezone_id);
810 g_free (data->name);
811- g_free (data);
812+ g_slice_free (struct setlocation_data, data);
813 }
814
815 static void
816@@ -1224,7 +1166,7 @@
817 g_return_if_fail (name && *name);
818 g_return_if_fail (timezone_id && *timezone_id);
819
820- data = g_new0 (struct setlocation_data, 1);
821+ data = g_slice_new0 (struct setlocation_data);
822 data->timezone_id = g_strdup (timezone_id);
823 data->name = g_strdup (name);
824 data->service = self;
825@@ -1604,6 +1546,133 @@
826 }
827
828 /***
829+**** Appointments
830+***/
831+
832+static void
833+set_calendar_appointments (IndicatorDatetimeService * self,
834+ GSList * appointments)
835+{
836+ priv_t * p = self->priv;
837+
838+ /* repopulate the list */
839+ indicator_datetime_planner_free_appointments (p->calendar_appointments);
840+ p->calendar_appointments = appointments;
841+
842+ /* sync the menus/actions */
843+ update_calendar_action_state (self);
844+ rebuild_calendar_section_soon (self);
845+}
846+
847+static void
848+on_calendar_appointments_ready (GObject * source,
849+ GAsyncResult * res,
850+ gpointer self)
851+{
852+ IndicatorDatetimePlanner * planner;
853+ GError * error;
854+ GSList * appointments;
855+
856+ planner = INDICATOR_DATETIME_PLANNER (source);
857+ error = NULL;
858+ appointments = indicator_datetime_planner_get_appointments_finish (planner,
859+ res,
860+ &error);
861+
862+ if (error != NULL)
863+ {
864+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
865+ g_warning ("can't get this month's appointments: %s", error->message);
866+
867+ g_error_free (error);
868+ }
869+ else
870+ {
871+ set_calendar_appointments (INDICATOR_DATETIME_SERVICE (self),
872+ appointments);
873+ }
874+}
875+
876+static void
877+set_upcoming_appointments (IndicatorDatetimeService * self,
878+ GSList * appointments)
879+{
880+ priv_t * p = self->priv;
881+
882+ /* repopulate the list */
883+ indicator_datetime_planner_free_appointments (p->upcoming_appointments);
884+ p->upcoming_appointments = appointments;
885+
886+ /* sync the menus/actions */
887+ rebuild_appointments_section_soon (self);
888+}
889+
890+static void
891+on_upcoming_appointments_ready (GObject * source,
892+ GAsyncResult * res,
893+ gpointer self)
894+{
895+ IndicatorDatetimePlanner * planner;
896+ GError * error;
897+ GSList * appointments;
898+
899+ planner = INDICATOR_DATETIME_PLANNER (source);
900+ error = NULL;
901+ appointments = indicator_datetime_planner_get_appointments_finish (planner,
902+ res,
903+ &error);
904+
905+ if (error != NULL)
906+ {
907+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
908+ g_warning ("can't get upcoming appointments: %s", error->message);
909+
910+ g_error_free (error);
911+ }
912+ else
913+ {
914+ set_upcoming_appointments (INDICATOR_DATETIME_SERVICE (self),
915+ appointments);
916+ }
917+}
918+
919+static void
920+update_appointment_lists (IndicatorDatetimeService * self)
921+{
922+ IndicatorDatetimePlanner * planner;
923+ GDateTime * calendar_date;
924+ GDateTime * begin;
925+ GDateTime * end;
926+ int y, m, d;
927+
928+ planner = self->priv->planner;
929+ calendar_date = get_calendar_date (self);
930+
931+ /* get all the appointments in the calendar month */
932+ g_date_time_get_ymd (calendar_date, &y, &m, &d);
933+ begin = g_date_time_new_local (y, m, 1, 0, 0, 0);
934+ end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y), 23, 59, 0);
935+ if (begin && end)
936+ indicator_datetime_planner_get_appointments (planner, begin, end,
937+ on_calendar_appointments_ready,
938+ self);
939+ g_clear_pointer (&begin, g_date_time_unref);
940+ g_clear_pointer (&end, g_date_time_unref);
941+
942+ /* get the upcoming appointments */
943+ begin = g_date_time_ref (calendar_date);
944+ end = g_date_time_add_months (begin, 1);
945+ if (begin && end)
946+ indicator_datetime_planner_get_appointments (planner, begin, end,
947+ on_upcoming_appointments_ready,
948+ self);
949+ g_clear_pointer (&begin, g_date_time_unref);
950+ g_clear_pointer (&end, g_date_time_unref);
951+ g_clear_pointer (&calendar_date, g_date_time_unref);
952+}
953+
954+
955+/***
956 **** GDBus
957 ***/
958
959@@ -1642,9 +1711,6 @@
960 char * path = g_strdup_printf ("%s/%s", BUS_PATH, menu_names[i]);
961 struct ProfileMenuInfo * menu = &p->menus[i];
962
963- if (menu->menu == NULL)
964- create_menu (self, i);
965-
966 if ((id = g_dbus_connection_export_menu_model (connection,
967 path,
968 G_MENU_MODEL (menu->menu),
969@@ -1735,6 +1801,8 @@
970 g_signal_handlers_disconnect_by_data (p->planner, self);
971 g_clear_object (&p->planner);
972 }
973+ g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments);
974+ g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments);
975
976 if (p->login1_manager != NULL)
977 {
978@@ -1758,7 +1826,6 @@
979 for (i=0; i<N_PROFILES; ++i)
980 g_clear_object (&p->menus[i].menu);
981
982- g_clear_object (&p->planner);
983 g_clear_object (&p->calendar_action);
984 g_clear_object (&p->desktop_header_action);
985 g_clear_object (&p->phone_header_action);
986@@ -1841,7 +1908,7 @@
987 p->planner = indicator_datetime_planner_eds_new ();
988
989 g_signal_connect_swapped (p->planner, "appointments-changed",
990- G_CALLBACK(rebuild_calendar_section_soon), self);
991+ G_CALLBACK(update_appointment_lists), self);
992
993
994 /***
995@@ -1913,6 +1980,9 @@
996
997 on_local_time_jumped (self);
998
999+ for (i=0; i<N_PROFILES; ++i)
1000+ create_menu (self, i);
1001+
1002 g_string_free (gstr, TRUE);
1003 }
1004
1005@@ -1972,8 +2042,5 @@
1006
1007 /* sync the menuitems and action states */
1008 if (dirty)
1009- {
1010- update_calendar_action_state (self);
1011- rebuild_appointments_section_soon (self);
1012- }
1013+ update_appointment_lists (self);
1014 }

Subscribers

People subscribed via source and target branches