1 /* gtd-task-list-eds.c
2 *
3 * Copyright (C) 2015-2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define G_LOG_DOMAIN "GtdTaskListEds"
20
21 #include "e-source-gnome-todo.h"
22 #include "gtd-debug.h"
23 #include "gtd-eds-autoptr.h"
24 #include "gtd-provider-eds.h"
25 #include "gtd-task-eds.h"
26 #include "gtd-task-list-eds.h"
27
28 #include <glib/gi18n.h>
29
30 struct _GtdTaskListEds
31 {
32 GtdTaskList parent;
33
34 ECalClient *client;
35 ECalClientView *client_view;
36 ESource *source;
37
38 GCancellable *cancellable;
39 };
40
41 typedef struct
42 {
43 GtdProvider *provider;
44 ESource *source;
45 ECalClient *client;
46 } NewTaskListData;
47
48 static void on_client_objects_modified_for_migration_cb (GObject *object,
49 GAsyncResult *result,
50 gpointer user_data);
51
52 G_DEFINE_TYPE (GtdTaskListEds, gtd_task_list_eds, GTD_TYPE_TASK_LIST)
53
54 enum
55 {
56 PROP_0,
57 PROP_CLIENT,
58 PROP_SOURCE,
59 N_PROPS
60 };
61
62
63 /*
64 * Auxiliary methods
65 */
66
67 static void
new_task_list_data_free(gpointer data)68 new_task_list_data_free (gpointer data)
69 {
70 NewTaskListData *list_data = data;
71
72 if (!list_data)
73 return;
74
75 g_clear_object (&list_data->provider);
76 g_clear_object (&list_data->source);
77 g_clear_object (&list_data->client);
78 g_free (list_data);
79 }
80
81 static void
update_changed_tasks(GtdTaskListEds * self,GHashTable * changed_tasks)82 update_changed_tasks (GtdTaskListEds *self,
83 GHashTable *changed_tasks)
84 {
85 g_autoptr (GSList) components = NULL;
86 GHashTableIter iter;
87 GtdProvider *provider;
88 GtdTask *task;
89 guint n_changed_tasks;
90
91 GTD_ENTRY;
92
93 n_changed_tasks = g_hash_table_size (changed_tasks);
94 provider = gtd_task_list_get_provider (GTD_TASK_LIST (self));
95
96 /* Nothing changed, list is ready */
97 if (n_changed_tasks == 0)
98 {
99 gtd_object_pop_loading (GTD_OBJECT (provider));
100 g_signal_emit_by_name (provider, "list-added", self);
101 GTD_RETURN ();
102 }
103
104 GTD_TRACE_MSG ("%u task(s) changed", n_changed_tasks);
105
106 g_hash_table_iter_init (&iter, changed_tasks);
107 while (g_hash_table_iter_next (&iter, (gpointer *) &task, NULL))
108 {
109 ICalComponent *ical_comp;
110 ECalComponent *comp;
111
112 comp = gtd_task_eds_get_component (GTD_TASK_EDS (task));
113 ical_comp = e_cal_component_get_icalcomponent (comp);
114
115 components = g_slist_prepend (components, ical_comp);
116 }
117
118 e_cal_client_modify_objects (self->client,
119 components,
120 E_CAL_OBJ_MOD_THIS,
121 E_CAL_OPERATION_FLAG_NONE,
122 self->cancellable,
123 on_client_objects_modified_for_migration_cb,
124 self);
125
126 GTD_EXIT;
127 }
128
129 static void
migrate_to_v1(GtdTaskListEds * self,GHashTable * changed_tasks)130 migrate_to_v1 (GtdTaskListEds *self,
131 GHashTable *changed_tasks)
132 {
133 GListModel *model;
134 guint n_tasks;
135 guint i;
136
137 model = G_LIST_MODEL (self);
138 n_tasks = g_list_model_get_n_items (model);
139
140 for (i = 0; i < n_tasks; i++)
141 {
142 g_autoptr (GtdTask) task = NULL;
143
144 task = g_list_model_get_item (model, i);
145
146 /* Don't notify to avoid carpet-bombing GtdTaskList */
147 g_object_freeze_notify (G_OBJECT (task));
148
149 gtd_task_set_position (task, i);
150
151 g_hash_table_add (changed_tasks, task);
152 }
153
154 for (i = 0; i < n_tasks; i++)
155 {
156 g_autoptr (GtdTask) task = NULL;
157
158 task = g_list_model_get_item (model, i);
159 g_object_thaw_notify (G_OBJECT (task));
160 }
161 }
162
163 struct
164 {
165 guint api_version;
166 void (* migrate) (GtdTaskListEds *self,
167 GHashTable *changed_tasks);
168 }
169 migration_vtable[] =
170 {
171 { 0, migrate_to_v1 },
172 };
173
174 static void
maybe_migrate_todo_api_version(GtdTaskListEds * self)175 maybe_migrate_todo_api_version (GtdTaskListEds *self)
176 {
177 g_autoptr (GHashTable) changed_tasks = NULL;
178 ESourceGnomeTodo *gnome_todo_extension;
179 gboolean api_version_changed;
180 guint api_version;
181 guint i;
182
183 GTD_ENTRY;
184
185 /*
186 * Ensure the type so that it is available for introspection when
187 * calling e_source_get_extension().
188 */
189 g_type_ensure (E_TYPE_SOURCE_GNOME_TODO);
190
191 api_version_changed = FALSE;
192 gnome_todo_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_GNOME_TODO);
193 api_version = e_source_gnome_todo_get_api_version (gnome_todo_extension);
194 changed_tasks = g_hash_table_new (g_direct_hash, g_direct_equal);
195
196 g_debug ("%s: GNOME To Do API version %u",
197 gtd_task_list_get_name (GTD_TASK_LIST (self)),
198 api_version);
199
200 for (i = 0; i < G_N_ELEMENTS (migration_vtable); i++)
201 {
202 guint new_api_version = i + 1;
203
204 if (api_version > migration_vtable[i].api_version)
205 continue;
206
207 g_debug (" Migrating task list to GNOME To Do API v%u", new_api_version);
208
209 migration_vtable[i].migrate (self, changed_tasks);
210
211 e_source_gnome_todo_set_api_version (gnome_todo_extension, new_api_version);
212 api_version_changed = TRUE;
213 }
214
215 if (api_version_changed)
216 {
217 g_debug ("Saving new API version");
218
219 e_source_write (self->source, NULL, NULL, NULL);
220 }
221
222 update_changed_tasks (self, changed_tasks);
223
224 GTD_EXIT;
225 }
226
227
228 /*
229 * Callbacks
230 */
231
232 static gboolean
new_task_list_in_idle_cb(gpointer data)233 new_task_list_in_idle_cb (gpointer data)
234 {
235 g_autoptr (GtdTaskListEds) list_eds = NULL;
236 g_autoptr (GTask) task = data;
237 NewTaskListData *list_data;
238
239 list_data = g_task_get_task_data (task);
240 list_eds = g_object_new (GTD_TYPE_TASK_LIST_EDS,
241 "provider", list_data->provider,
242 "source", list_data->source,
243 "client", list_data->client,
244 NULL);
245
246 g_task_return_pointer (task, g_steal_pointer (&list_eds), NULL);
247
248 return G_SOURCE_REMOVE;
249 }
250
251 static void
on_client_objects_modified_for_migration_cb(GObject * object,GAsyncResult * result,gpointer user_data)252 on_client_objects_modified_for_migration_cb (GObject *object,
253 GAsyncResult *result,
254 gpointer user_data)
255 {
256 g_autoptr (GError) error = NULL;
257 GtdProvider *provider;
258 GtdTaskListEds *self;
259
260 GTD_ENTRY;
261
262 self = GTD_TASK_LIST_EDS (user_data);
263 provider = gtd_task_list_get_provider (GTD_TASK_LIST (self));
264
265 e_cal_client_modify_objects_finish (self->client, result, &error);
266
267 if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
268 g_warning ("Error migrating tasks to new API version: %s", error->message);
269
270 gtd_object_pop_loading (GTD_OBJECT (provider));
271 g_signal_emit_by_name (provider, "list-added", self);
272
273 GTD_EXIT;
274 }
275
276 static void
on_view_objects_added_cb(ECalClientView * view,const GSList * objects,GtdTaskList * self)277 on_view_objects_added_cb (ECalClientView *view,
278 const GSList *objects,
279 GtdTaskList *self)
280 {
281 g_autoptr (ECalClient) client = NULL;
282 GSList *l;
283
284 GTD_ENTRY;
285
286 client = e_cal_client_view_ref_client (view);
287
288 for (l = (GSList*) objects; l; l = l->next)
289 {
290 g_autoptr (ECalComponent) component = NULL;
291 GtdTask *task;
292 const gchar *uid;
293
294 component = e_cal_component_new_from_icalcomponent (i_cal_component_clone (l->data));
295 uid = e_cal_component_get_uid (component);
296
297 task = gtd_task_list_get_task_by_id (self, uid);
298
299 /* If the task already exists, we must instead update its component */
300 if (task)
301 {
302 gtd_task_eds_set_component (GTD_TASK_EDS (task), component);
303
304 gtd_task_list_update_task (self, task);
305
306 GTD_TRACE_MSG ("Updated task '%s' to tasklist '%s'",
307 gtd_task_get_title (task),
308 gtd_task_list_get_name (self));
309
310 continue;
311 }
312
313 /* Add the new task */
314 task = gtd_task_eds_new (component);
315 gtd_task_set_list (task, self);
316
317 gtd_task_list_add_task (self, task);
318
319 GTD_TRACE_MSG ("Added task '%s' (%s) to tasklist '%s'",
320 gtd_task_get_title (task),
321 gtd_object_get_uid (GTD_OBJECT (task)),
322 gtd_task_list_get_name (self));
323 }
324
325 GTD_EXIT;
326 }
327
328 static void
on_view_objects_modified_cb(ECalClientView * view,const GSList * objects,GtdTaskList * self)329 on_view_objects_modified_cb (ECalClientView *view,
330 const GSList *objects,
331 GtdTaskList *self)
332 {
333 g_autoptr (ECalClient) client = NULL;
334 GSList *l;
335
336 GTD_ENTRY;
337
338 client = e_cal_client_view_ref_client (view);
339
340 for (l = (GSList*) objects; l; l = l->next)
341 {
342 g_autoptr (ECalComponent) component = NULL;
343 GtdTask *task;
344 const gchar *uid;
345
346 component = e_cal_component_new_from_icalcomponent (i_cal_component_clone (l->data));
347 uid = e_cal_component_get_uid (component);
348
349 task = gtd_task_list_get_task_by_id (self, uid);
350
351 if (!task)
352 continue;
353
354 gtd_task_eds_set_component (GTD_TASK_EDS (task), component);
355
356 GTD_TRACE_MSG ("Updated task '%s' from tasklist '%s'",
357 gtd_task_get_title (GTD_TASK (task)),
358 gtd_task_list_get_name (self));
359 }
360
361 GTD_EXIT;
362 }
363
364 static void
on_view_objects_removed_cb(ECalClientView * view,const GSList * uids,GtdTaskList * self)365 on_view_objects_removed_cb (ECalClientView *view,
366 const GSList *uids,
367 GtdTaskList *self)
368 {
369 GSList *l;
370
371 GTD_ENTRY;
372
373 for (l = (GSList*) uids; l; l = l->next)
374 {
375 ECalComponentId *id;
376 GtdTask *task;
377
378 id = l->data;
379 task = gtd_task_list_get_task_by_id (self, e_cal_component_id_get_uid (id));
380
381 if (!task)
382 continue;
383
384 gtd_task_list_remove_task (self, task);
385
386 GTD_TRACE_MSG ("Removed task '%s' from tasklist '%s'",
387 gtd_task_get_title (task),
388 gtd_task_list_get_name (self));
389 }
390
391 GTD_EXIT;
392 }
393
394 static void
on_view_completed_cb(ECalClientView * view,const GError * error,GtdTaskList * self)395 on_view_completed_cb (ECalClientView *view,
396 const GError *error,
397 GtdTaskList *self)
398 {
399 gtd_object_pop_loading (GTD_OBJECT (gtd_manager_get_default ()));
400 gtd_object_pop_loading (GTD_OBJECT (self));
401
402 if (error)
403 {
404 g_warning ("Error fetching tasks from list: %s", error->message);
405
406 gtd_manager_emit_error_message (gtd_manager_get_default (),
407 _("Error fetching tasks from list"),
408 error->message,
409 NULL,
410 NULL);
411 return;
412 }
413
414 maybe_migrate_todo_api_version (GTD_TASK_LIST_EDS (self));
415 }
416
417 static void
on_client_view_acquired_cb(GObject * client,GAsyncResult * result,gpointer user_data)418 on_client_view_acquired_cb (GObject *client,
419 GAsyncResult *result,
420 gpointer user_data)
421 {
422 g_autoptr (GError) error = NULL;
423 GtdTaskListEds *self;
424
425 self = GTD_TASK_LIST_EDS (user_data);
426
427 e_cal_client_get_view_finish (E_CAL_CLIENT (client), result, &self->client_view, &error);
428
429 if (error)
430 {
431 g_warning ("Error fetching tasks from list: %s", error->message);
432
433 gtd_manager_emit_error_message (gtd_manager_get_default (),
434 _("Error fetching tasks from list"),
435 error->message,
436 NULL,
437 NULL);
438 return;
439 }
440
441 g_debug ("ECalClientView for tasklist '%s' successfully acquired",
442 gtd_task_list_get_name (GTD_TASK_LIST (self)));
443
444 g_signal_connect (self->client_view, "objects-added", G_CALLBACK (on_view_objects_added_cb), self);
445 g_signal_connect (self->client_view, "objects-removed", G_CALLBACK (on_view_objects_removed_cb), self);
446 g_signal_connect (self->client_view, "objects-modified", G_CALLBACK (on_view_objects_modified_cb), self);
447 g_signal_connect (self->client_view, "complete", G_CALLBACK (on_view_completed_cb), self);
448
449 gtd_object_push_loading (GTD_OBJECT (self));
450
451 e_cal_client_view_start (self->client_view, &error);
452
453 if (error)
454 {
455 g_warning ("Error starting view: %s", error->message);
456
457 gtd_manager_emit_error_message (gtd_manager_get_default (),
458 _("Error fetching tasks from list"),
459 error->message,
460 NULL,
461 NULL);
462 }
463 }
464
465 static void
on_source_removable_changed_cb(GtdTaskListEds * list)466 on_source_removable_changed_cb (GtdTaskListEds *list)
467 {
468 gtd_task_list_set_is_removable (GTD_TASK_LIST (list),
469 e_source_get_removable (list->source) ||
470 e_source_get_remote_deletable (list->source));
471 }
472
473 static void
on_source_selectable_selected_changed_cb(ESourceSelectable * selectable,GParamSpec * pspec,GtdTaskListEds * self)474 on_source_selectable_selected_changed_cb (ESourceSelectable *selectable,
475 GParamSpec *pspec,
476 GtdTaskListEds *self)
477 {
478 g_debug ("%s (%s): ESourceSelectable:selected changed, notifying...",
479 e_source_get_uid (self->source),
480 e_source_get_display_name (self->source));
481
482 g_object_notify (G_OBJECT (self), "archived");
483 }
484
485 static void
on_save_task_list_finished_cb(GObject * source,GAsyncResult * result,gpointer user_data)486 on_save_task_list_finished_cb (GObject *source,
487 GAsyncResult *result,
488 gpointer user_data)
489 {
490 GtdTaskListEds *list;
491 GError *error;
492
493 list = user_data;
494 error = NULL;
495
496 gtd_object_pop_loading (GTD_OBJECT (list));
497
498 e_source_write_finish (E_SOURCE (source), result, &error);
499
500 if (error)
501 {
502 g_warning ("%s: %s: %s",
503 G_STRFUNC,
504 "Error saving task list",
505 error->message);
506 g_clear_error (&error);
507 }
508 }
509
510 static void
save_task_list(GtdTaskListEds * list)511 save_task_list (GtdTaskListEds *list)
512 {
513 if (e_source_get_writable (list->source))
514 {
515 if (!list->cancellable)
516 list->cancellable = g_cancellable_new ();
517
518 gtd_object_push_loading (GTD_OBJECT (list));
519
520 e_source_write (list->source,
521 list->cancellable,
522 on_save_task_list_finished_cb,
523 list);
524 }
525 }
526
527 static gboolean
color_to_string(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)528 color_to_string (GBinding *binding,
529 const GValue *from_value,
530 GValue *to_value,
531 gpointer user_data)
532 {
533 GdkRGBA *color;
534 gchar *color_str;
535
536 color = g_value_get_boxed (from_value);
537 color_str = gdk_rgba_to_string (color);
538
539 g_value_set_string (to_value, color_str);
540
541 g_free (color_str);
542
543 return TRUE;
544 }
545
546 static gboolean
string_to_color(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)547 string_to_color (GBinding *binding,
548 const GValue *from_value,
549 GValue *to_value,
550 gpointer user_data)
551 {
552 GdkRGBA color;
553
554 if (!gdk_rgba_parse (&color, g_value_get_string (from_value)))
555 gdk_rgba_parse (&color, "#ffffff"); /* calendar default colour */
556
557 g_value_set_boxed (to_value, &color);
558
559 return TRUE;
560 }
561
562
563 /*
564 * GtdTaskList overrides
565 */
566
567 static gboolean
gtd_task_list_eds_get_archived(GtdTaskList * list)568 gtd_task_list_eds_get_archived (GtdTaskList *list)
569 {
570 ESourceSelectable *selectable;
571 GtdTaskListEds *self;
572
573 self = GTD_TASK_LIST_EDS (list);
574 selectable = e_source_get_extension (self->source, E_SOURCE_EXTENSION_TASK_LIST);
575
576 return !e_source_selectable_get_selected (selectable);
577 }
578
579 static void
gtd_task_list_eds_set_archived(GtdTaskList * list,gboolean archived)580 gtd_task_list_eds_set_archived (GtdTaskList *list,
581 gboolean archived)
582 {
583 ESourceSelectable *selectable;
584 GtdTaskListEds *self;
585
586 GTD_ENTRY;
587
588 self = GTD_TASK_LIST_EDS (list);
589 selectable = e_source_get_extension (self->source, E_SOURCE_EXTENSION_TASK_LIST);
590
591 g_signal_handlers_block_by_func (selectable, on_source_selectable_selected_changed_cb, self);
592
593 e_source_selectable_set_selected (selectable, !archived);
594
595 g_signal_handlers_unblock_by_func (selectable, on_source_selectable_selected_changed_cb, self);
596
597 GTD_EXIT;
598 }
599
600
601 /*
602 * GtdObject overrides
603 */
604
605 static const gchar*
gtd_task_list_eds_get_uid(GtdObject * object)606 gtd_task_list_eds_get_uid (GtdObject *object)
607 {
608 GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
609
610 return e_source_get_uid (self->source);
611 }
612
613 static void
gtd_task_list_eds_set_uid(GtdObject * object,const gchar * uid)614 gtd_task_list_eds_set_uid (GtdObject *object,
615 const gchar *uid)
616 {
617 g_assert_not_reached ();
618 }
619
620
621 /*
622 * GObject overrides
623 */
624
625 static void
gtd_task_list_eds_finalize(GObject * object)626 gtd_task_list_eds_finalize (GObject *object)
627 {
628 GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
629
630 g_cancellable_cancel (self->cancellable);
631
632 g_clear_object (&self->cancellable);
633 g_clear_object (&self->client);
634 g_clear_object (&self->client_view);
635 g_clear_object (&self->source);
636
637 G_OBJECT_CLASS (gtd_task_list_eds_parent_class)->finalize (object);
638 }
639
640 static void
gtd_task_list_eds_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)641 gtd_task_list_eds_get_property (GObject *object,
642 guint prop_id,
643 GValue *value,
644 GParamSpec *pspec)
645 {
646 GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
647
648 switch (prop_id)
649 {
650 case PROP_CLIENT:
651 g_value_set_object (value, self->client);
652 break;
653
654 case PROP_SOURCE:
655 g_value_set_object (value, self->source);
656 break;
657
658 default:
659 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
660 }
661 }
662
663 static void
gtd_task_list_eds_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)664 gtd_task_list_eds_set_property (GObject *object,
665 guint prop_id,
666 const GValue *value,
667 GParamSpec *pspec)
668 {
669 GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
670
671 switch (prop_id)
672 {
673 case PROP_CLIENT:
674 if (!g_set_object (&self->client, g_value_get_object (value)) || !self->client)
675 return;
676
677 e_cal_client_get_view (self->client,
678 "#t",
679 self->cancellable,
680 on_client_view_acquired_cb,
681 self);
682
683 g_object_notify (object, "client");
684 break;
685
686 case PROP_SOURCE:
687 gtd_task_list_eds_set_source (self, g_value_get_object (value));
688 break;
689
690 default:
691 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
692 }
693 }
694
695 static void
gtd_task_list_eds_class_init(GtdTaskListEdsClass * klass)696 gtd_task_list_eds_class_init (GtdTaskListEdsClass *klass)
697 {
698 GObjectClass *object_class = G_OBJECT_CLASS (klass);
699 GtdObjectClass *gtd_object_class = GTD_OBJECT_CLASS (klass);
700 GtdTaskListClass *task_list_class = GTD_TASK_LIST_CLASS (klass);
701
702 task_list_class->get_archived = gtd_task_list_eds_get_archived;
703 task_list_class->set_archived = gtd_task_list_eds_set_archived;
704
705 gtd_object_class->get_uid = gtd_task_list_eds_get_uid;
706 gtd_object_class->set_uid = gtd_task_list_eds_set_uid;
707
708 object_class->finalize = gtd_task_list_eds_finalize;
709 object_class->get_property = gtd_task_list_eds_get_property;
710 object_class->set_property = gtd_task_list_eds_set_property;
711
712
713 /**
714 * GtdTaskListEds::client:
715 *
716 * The #ECalClient of this #GtdTaskListEds
717 */
718 g_object_class_install_property (object_class,
719 PROP_CLIENT,
720 g_param_spec_object ("client",
721 "ECalClient of this list",
722 "The ECalClient of this list",
723 E_TYPE_CAL_CLIENT,
724 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
725
726 /**
727 * GtdTaskListEds::source:
728 *
729 * The #ESource of this #GtdTaskListEds
730 */
731 g_object_class_install_property (object_class,
732 PROP_SOURCE,
733 g_param_spec_object ("source",
734 "ESource of this list",
735 "The ESource of this list",
736 E_TYPE_SOURCE,
737 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
738 }
739
740 static void
gtd_task_list_eds_init(GtdTaskListEds * self)741 gtd_task_list_eds_init (GtdTaskListEds *self)
742 {
743 }
744
745 void
gtd_task_list_eds_new(GtdProvider * provider,ESource * source,ECalClient * client,GAsyncReadyCallback callback,GCancellable * cancellable,gpointer user_data)746 gtd_task_list_eds_new (GtdProvider *provider,
747 ESource *source,
748 ECalClient *client,
749 GAsyncReadyCallback callback,
750 GCancellable *cancellable,
751 gpointer user_data)
752 {
753 g_autoptr (GTask) task = NULL;
754 NewTaskListData *data;
755
756 data = g_new (NewTaskListData, 1);
757 data->provider = g_object_ref (provider);
758 data->source = g_object_ref (source);
759 data->client = g_object_ref (client);
760
761 task = g_task_new (NULL, cancellable, callback, user_data);
762 g_task_set_task_data (task, data, new_task_list_data_free);
763 g_task_set_source_tag (task, gtd_task_list_eds_new);
764
765 g_idle_add (new_task_list_in_idle_cb, g_steal_pointer (&task));
766 }
767
768 GtdTaskListEds*
gtd_task_list_eds_new_finish(GAsyncResult * result,GError ** error)769 gtd_task_list_eds_new_finish (GAsyncResult *result,
770 GError **error)
771 {
772 g_return_val_if_fail (G_IS_TASK (result), NULL);
773 g_return_val_if_fail (!error || !*error, NULL);
774
775 return g_task_propagate_pointer (G_TASK (result), error);
776 }
777
778 ESource*
gtd_task_list_eds_get_source(GtdTaskListEds * list)779 gtd_task_list_eds_get_source (GtdTaskListEds *list)
780 {
781 g_return_val_if_fail (GTD_IS_TASK_LIST_EDS (list), NULL);
782
783 return list->source;
784 }
785
786 void
gtd_task_list_eds_set_source(GtdTaskListEds * self,ESource * source)787 gtd_task_list_eds_set_source (GtdTaskListEds *self,
788 ESource *source)
789 {
790 ESourceSelectable *selectable;
791 ESourceRefresh *refresh;
792 GdkRGBA color;
793 gboolean is_inbox;
794
795 g_return_if_fail (GTD_IS_TASK_LIST_EDS (self));
796
797 if (!g_set_object (&self->source, source))
798 return;
799
800 is_inbox = g_str_equal (e_source_get_uid (source), GTD_PROVIDER_EDS_INBOX_ID);
801
802 /* Setup color */
803 selectable = E_SOURCE_SELECTABLE (e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST));
804
805 if (!gdk_rgba_parse (&color, e_source_selectable_get_color (selectable)))
806 gdk_rgba_parse (&color, "#ffffff"); /* calendar default color */
807
808 gtd_task_list_set_color (GTD_TASK_LIST (self), &color);
809
810 g_object_bind_property_full (self,
811 "color",
812 selectable,
813 "color",
814 G_BINDING_BIDIRECTIONAL,
815 color_to_string,
816 string_to_color,
817 self,
818 NULL);
819
820 g_signal_connect_object (selectable,
821 "notify::selected",
822 G_CALLBACK (on_source_selectable_selected_changed_cb),
823 self,
824 0);
825
826 /* Setup tasklist name */
827 if (is_inbox)
828 gtd_task_list_set_name (GTD_TASK_LIST (self), _("Inbox"));
829 else
830 gtd_task_list_set_name (GTD_TASK_LIST (self), e_source_get_display_name (source));
831
832 g_object_bind_property (source,
833 "display-name",
834 self,
835 "name",
836 G_BINDING_BIDIRECTIONAL);
837
838 /* Save the task list every time something changes */
839 g_signal_connect_swapped (source,
840 "notify",
841 G_CALLBACK (save_task_list),
842 self);
843
844 /* Update ::is-removable property */
845 gtd_task_list_set_is_removable (GTD_TASK_LIST (self),
846 e_source_get_removable (source) ||
847 e_source_get_remote_deletable (source));
848
849 g_signal_connect_swapped (source,
850 "notify::removable",
851 G_CALLBACK (on_source_removable_changed_cb),
852 self);
853
854 g_signal_connect_swapped (source,
855 "notify::remote-deletable",
856 G_CALLBACK (on_source_removable_changed_cb),
857 self);
858
859 /* Refresh timeout */
860 refresh = e_source_get_extension (source, E_SOURCE_EXTENSION_REFRESH);
861 e_source_refresh_set_enabled (refresh, TRUE);
862 e_source_refresh_set_interval_minutes (refresh, 5);
863
864 e_source_write (source, NULL, NULL, NULL);
865
866 g_object_notify (G_OBJECT (self), "source");
867 }
868
869 ECalClient*
gtd_task_list_eds_get_client(GtdTaskListEds * self)870 gtd_task_list_eds_get_client (GtdTaskListEds *self)
871 {
872 g_return_val_if_fail (GTD_IS_TASK_LIST_EDS (self), NULL);
873
874 return self->client;
875 }
876