Merge lp:~charlesk/indicator-datetime/lp-1204532 into lp:~indicator-applet-developers/indicator-datetime/trunk.13.10
- lp-1204532
- Merge into trunk.13.10
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 |
Related bugs: |
|
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_
2. Make indicator_
3. In the service, respond to the "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:/
PS Jenkins bot (ps-jenkins) wrote : | # |
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:254
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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_
* In on_appointments
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.
Charles Kerr (charlesk) wrote : | # |
> * For decrementing the subtask_count you should probably use g_atomic_
Fixed r255
> * In on_appointments
get_calendar_date() doesn't return NULL, and the arguments to g_date_
> 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
Ted Gould (ted) wrote : | # |
On Thu, 2013-09-05 at 21:35 +0000, Charles Kerr wrote:
> > * In on_appointments
>
> get_calendar_date() doesn't return NULL, and the arguments to g_date_
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 :-)
Marco Trevisan (Treviño) (3v1n0) wrote : | # |
FYI, I can confirm that this fixes #1204532
Marco Trevisan (Treviño) (3v1n0) wrote : | # |
Mh, I don't see the calendar widget in it though...
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.
Charles Kerr (charlesk) wrote : | # |
> the menu refreshes, but the 5 appointments stay the same.
Fixed r256
> * In on_appointments
Fixed r257
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:254
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:254
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
deb: http://
Click here to trigger a rebuild:
http://
Ted Gould (ted) wrote : | # |
Odd to me Jenkins kept reporting 254 as the revision. Running again at 257.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:257
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:257
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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).
Ted Gould (ted) wrote : | # |
This is good for me now. Thanks!
Marco Trevisan (Treviño) (3v1n0) wrote : | # |
Sorry I only had a configuration issue... Calendar is working as it should here now.
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
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 | } |
PASSED: Continuous integration, rev:251 jenkins. qa.ubuntu. com/job/ indicator- datetime- ci/88/ jenkins. qa.ubuntu. com/job/ indicator- datetime- saucy-amd64- ci/42 jenkins. qa.ubuntu. com/job/ indicator- datetime- saucy-armhf- ci/43 jenkins. qa.ubuntu. com/job/ indicator- datetime- saucy-armhf- ci/43/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ indicator- datetime- ci/88/rebuild
http://