1 /* gtd-task-list.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 "GtdTaskList"
20
21 #include "gtd-debug.h"
22 #include "gtd-provider.h"
23 #include "gtd-task.h"
24 #include "gtd-task-list.h"
25
26 #include <glib/gi18n.h>
27
28 /**
29 * SECTION:gtd-task-list
30 * @short_description:a list of tasks
31 * @title:GtdTaskList
32 * @stability:Unstable
33 * @see_also:#GtdTask
34 *
35 * A #GtdTaskList represents a task list, and contains a list of tasks, a
36 * color, a name and the provider who generated it.
37 *
38 * Only a #GtdProvider can create a #GtdTaskList. Equally, a #GtdTaskList
39 * is only valid when associated with a #GtdProvider.
40 *
41 * It implements #GListModel, and can be used as the model for #GtkListBox.
42 */
43
44 typedef struct
45 {
46 GtdProvider *provider;
47 GdkRGBA *color;
48
49 GHashTable *task_to_uid;
50 GHashTable *tasks;
51 GSequence *sorted_tasks;
52 guint n_tasks;
53
54 guint freeze_counter;
55
56 gchar *name;
57 gboolean removable;
58 gboolean archived;
59 } GtdTaskListPrivate;
60
61
62 static gint compare_tasks_cb (gconstpointer a,
63 gconstpointer b,
64 gpointer user_data);
65
66 static void task_changed_cb (GtdTask *task,
67 GParamSpec *pspec,
68 GtdTaskList *self);
69
70 static void g_list_model_iface_init (GListModelInterface *iface);
71
72
73 G_DEFINE_TYPE_WITH_CODE (GtdTaskList, gtd_task_list, GTD_TYPE_OBJECT,
74 G_ADD_PRIVATE (GtdTaskList)
75 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
76
77 enum
78 {
79 TASK_ADDED,
80 TASK_REMOVED,
81 TASK_UPDATED,
82 NUM_SIGNALS
83 };
84
85 enum
86 {
87 PROP_0,
88 PROP_ARCHIVED,
89 PROP_COLOR,
90 PROP_IS_REMOVABLE,
91 PROP_NAME,
92 PROP_PROVIDER,
93 N_PROPS
94 };
95
96 static guint signals[NUM_SIGNALS] = { 0, };
97 static GParamSpec *properties[N_PROPS] = { NULL, };
98
99
100 /*
101 * Auxiliary functions
102 */
103
104 static void
update_task_uid(GtdTaskList * self,GtdTask * task)105 update_task_uid (GtdTaskList *self,
106 GtdTask *task)
107 {
108 GtdTaskListPrivate *priv;
109 GSequenceIter *iter;
110 const gchar *old_uid;
111 gchar *new_uid;
112
113 priv = gtd_task_list_get_instance_private (self);
114
115 g_debug ("Updating uid of task '%s'", gtd_task_get_title (task));
116
117 new_uid = g_strdup (gtd_object_get_uid (GTD_OBJECT (task)));
118
119 old_uid = g_hash_table_lookup (priv->task_to_uid, task);
120 iter = g_hash_table_lookup (priv->tasks, old_uid);
121
122 g_assert (g_sequence_get (iter) == task);
123
124 g_hash_table_remove (priv->tasks, old_uid);
125
126 g_hash_table_insert (priv->task_to_uid, task, new_uid);
127 g_hash_table_insert (priv->tasks, new_uid, iter);
128 }
129
130 static guint
add_task(GtdTaskList * self,GtdTask * task)131 add_task (GtdTaskList *self,
132 GtdTask *task)
133 {
134 GtdTaskListPrivate *priv;
135 GSequenceIter *iter;
136 gchar *uid;
137
138 priv = gtd_task_list_get_instance_private (self);
139
140 uid = g_strdup (gtd_object_get_uid (GTD_OBJECT (task)));
141 iter = g_sequence_insert_sorted (priv->sorted_tasks,
142 g_object_ref (task),
143 compare_tasks_cb,
144 NULL);
145
146 g_hash_table_insert (priv->task_to_uid, task, uid);
147 g_hash_table_insert (priv->tasks, uid, iter);
148
149 g_signal_connect (task, "notify", G_CALLBACK (task_changed_cb), self);
150
151 priv->n_tasks++;
152
153 g_signal_emit (self, signals[TASK_ADDED], 0, task);
154
155 return g_sequence_iter_get_position (iter);
156 }
157
158 static guint
remove_task(GtdTaskList * self,GtdTask * task)159 remove_task (GtdTaskList *self,
160 GtdTask *task)
161 {
162 GtdTaskListPrivate *priv;
163 GSequenceIter *iter;
164 const gchar *uid;
165 guint position;
166
167 priv = gtd_task_list_get_instance_private (self);
168
169 g_signal_handlers_disconnect_by_func (task, task_changed_cb, self);
170
171 uid = gtd_object_get_uid (GTD_OBJECT (task));
172 iter = g_hash_table_lookup (priv->tasks, uid);
173 position = g_sequence_iter_get_position (iter);
174
175 g_hash_table_remove (priv->task_to_uid, task);
176 g_hash_table_remove (priv->tasks, uid);
177
178 g_sequence_remove (iter);
179
180 priv->n_tasks--;
181
182 g_signal_emit (self, signals[TASK_REMOVED], 0, task);
183
184 return position;
185 }
186
187
188 /*
189 * Callbacks
190 */
191
192 static void
on_task_updated_cb(GObject * object,GAsyncResult * result,gpointer user_data)193 on_task_updated_cb (GObject *object,
194 GAsyncResult *result,
195 gpointer user_data)
196 {
197 g_autoptr (GError) error = NULL;
198
199 gtd_provider_update_task_finish (GTD_PROVIDER (object), result, &error);
200
201 if (error)
202 {
203 g_warning ("Error updating task: %s", error->message);
204 return;
205 }
206 }
207
208 static gint
compare_tasks_cb(gconstpointer a,gconstpointer b,gpointer user_data)209 compare_tasks_cb (gconstpointer a,
210 gconstpointer b,
211 gpointer user_data)
212 {
213 return gtd_task_compare ((GtdTask*) a, (GtdTask*) b);
214 }
215
216 static void
task_changed_cb(GtdTask * task,GParamSpec * pspec,GtdTaskList * self)217 task_changed_cb (GtdTask *task,
218 GParamSpec *pspec,
219 GtdTaskList *self)
220 {
221 GtdTaskListPrivate *priv;
222 GSequenceIter *iter;
223 guint old_position;
224 guint new_position;
225
226 GTD_ENTRY;
227
228 priv = gtd_task_list_get_instance_private (self);
229
230 if (g_strcmp0 (g_param_spec_get_name (pspec), "loading") == 0)
231 GTD_RETURN ();
232
233 if (g_strcmp0 (g_param_spec_get_name (pspec), "uid") == 0)
234 {
235 update_task_uid (self, task);
236 GTD_RETURN ();
237 }
238
239 /* Don't update when the list is frozen */
240 if (priv->freeze_counter > 0)
241 GTD_RETURN ();
242
243 iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task)));
244
245 old_position = g_sequence_iter_get_position (iter);
246 g_sequence_sort_changed (iter, compare_tasks_cb, NULL);
247 new_position = g_sequence_iter_get_position (iter);
248
249 if (old_position != new_position)
250 {
251 GTD_TRACE_MSG ("Old position: %u, New position: %u", old_position, new_position);
252
253 g_list_model_items_changed (G_LIST_MODEL (self), old_position, 1, 0);
254 g_list_model_items_changed (G_LIST_MODEL (self), new_position, 0, 1);
255 }
256
257 GTD_EXIT;
258 }
259
260
261 /*
262 * GListModel iface
263 */
264
265 static GType
gtd_list_model_get_type(GListModel * model)266 gtd_list_model_get_type (GListModel *model)
267 {
268 return GTD_TYPE_TASK;
269 }
270
271 static guint
gtd_list_model_get_n_items(GListModel * model)272 gtd_list_model_get_n_items (GListModel *model)
273 {
274 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (GTD_TASK_LIST (model));
275 return priv->n_tasks;
276 }
277
278 static gpointer
gtd_list_model_get_item(GListModel * model,guint i)279 gtd_list_model_get_item (GListModel *model,
280 guint i)
281 {
282 GtdTaskListPrivate *priv;
283 GSequenceIter *iter;
284 GtdTask *task;
285
286 priv = gtd_task_list_get_instance_private (GTD_TASK_LIST (model));
287 iter = g_sequence_get_iter_at_pos (priv->sorted_tasks, i);
288 task = g_sequence_get (iter);
289
290 return g_object_ref (task);
291 }
292
293 static void
g_list_model_iface_init(GListModelInterface * iface)294 g_list_model_iface_init (GListModelInterface *iface)
295 {
296 iface->get_item_type = gtd_list_model_get_type;
297 iface->get_n_items = gtd_list_model_get_n_items;
298 iface->get_item = gtd_list_model_get_item;
299 }
300
301
302 /*
303 * GtdTaskList overrides
304 */
305
306 static gboolean
gtd_task_list_real_get_archived(GtdTaskList * self)307 gtd_task_list_real_get_archived (GtdTaskList *self)
308 {
309 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
310
311 return priv->archived;
312 }
313
314 static void
gtd_task_list_real_set_archived(GtdTaskList * self,gboolean archived)315 gtd_task_list_real_set_archived (GtdTaskList *self,
316 gboolean archived)
317 {
318 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
319
320 priv->archived = archived;
321 }
322
323
324 /*
325 * GObject overrides
326 */
327
328 static void
gtd_task_list_finalize(GObject * object)329 gtd_task_list_finalize (GObject *object)
330 {
331 GtdTaskList *self = (GtdTaskList*) object;
332 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
333
334 g_clear_object (&priv->provider);
335
336 g_clear_pointer (&priv->color, gdk_rgba_free);
337 g_clear_pointer (&priv->name, g_free);
338 g_clear_pointer (&priv->sorted_tasks, g_sequence_free);
339 g_clear_pointer (&priv->tasks, g_hash_table_destroy);
340 g_clear_pointer (&priv->task_to_uid, g_hash_table_destroy);
341
342 G_OBJECT_CLASS (gtd_task_list_parent_class)->finalize (object);
343 }
344
345 static void
gtd_task_list_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)346 gtd_task_list_get_property (GObject *object,
347 guint prop_id,
348 GValue *value,
349 GParamSpec *pspec)
350 {
351 GtdTaskList *self = GTD_TASK_LIST (object);
352 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
353
354 switch (prop_id)
355 {
356 case PROP_ARCHIVED:
357 g_value_set_boolean (value, gtd_task_list_get_archived (self));
358 break;
359
360 case PROP_COLOR:
361 {
362 GdkRGBA *color = gtd_task_list_get_color (self);
363 g_value_set_boxed (value, color);
364 gdk_rgba_free (color);
365 break;
366 }
367
368 case PROP_IS_REMOVABLE:
369 g_value_set_boolean (value, gtd_task_list_is_removable (self));
370 break;
371
372 case PROP_NAME:
373 g_value_set_string (value, priv->name);
374 break;
375
376 case PROP_PROVIDER:
377 g_value_set_object (value, priv->provider);
378 break;
379
380 default:
381 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382 }
383 }
384
385 static void
gtd_task_list_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)386 gtd_task_list_set_property (GObject *object,
387 guint prop_id,
388 const GValue *value,
389 GParamSpec *pspec)
390 {
391 GtdTaskList *self = GTD_TASK_LIST (object);
392
393 switch (prop_id)
394 {
395 case PROP_ARCHIVED:
396 gtd_task_list_set_archived (self, g_value_get_boolean (value));
397 break;
398
399 case PROP_COLOR:
400 gtd_task_list_set_color (self, g_value_get_boxed (value));
401 break;
402
403 case PROP_IS_REMOVABLE:
404 gtd_task_list_set_is_removable (self, g_value_get_boolean (value));
405 break;
406
407 case PROP_NAME:
408 gtd_task_list_set_name (self, g_value_get_string (value));
409 break;
410
411 case PROP_PROVIDER:
412 gtd_task_list_set_provider (self, g_value_get_object (value));
413 break;
414
415 default:
416 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 }
418 }
419
420 static void
gtd_task_list_class_init(GtdTaskListClass * klass)421 gtd_task_list_class_init (GtdTaskListClass *klass)
422 {
423 GObjectClass *object_class = G_OBJECT_CLASS (klass);
424
425 object_class->finalize = gtd_task_list_finalize;
426 object_class->get_property = gtd_task_list_get_property;
427 object_class->set_property = gtd_task_list_set_property;
428
429 klass->get_archived = gtd_task_list_real_get_archived;
430 klass->set_archived = gtd_task_list_real_set_archived;
431
432 /**
433 * GtdTaskList::archived:
434 *
435 * Whether the task list is archived or not.
436 */
437 properties[PROP_ARCHIVED] = g_param_spec_boolean ("archived",
438 "Whether the list is archived",
439 "Whether the list is archived or not",
440 FALSE,
441 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
442 /**
443 * GtdTaskList::color:
444 *
445 * The color of the list.
446 */
447 properties[PROP_COLOR] = g_param_spec_boxed ("color",
448 "Color of the list",
449 "The color of the list",
450 GDK_TYPE_RGBA,
451 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
452
453 /**
454 * GtdTaskList::is-removable:
455 *
456 * Whether the task list can be removed from the system.
457 */
458 properties[PROP_IS_REMOVABLE] = g_param_spec_boolean ("is-removable",
459 "Whether the task list is removable",
460 "Whether the task list can be removed from the system",
461 FALSE,
462 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
463
464 /**
465 * GtdTaskList::name:
466 *
467 * The display name of the list.
468 */
469 properties[PROP_NAME] = g_param_spec_string ("name",
470 "Name of the list",
471 "The name of the list",
472 NULL,
473 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
474
475 /**
476 * GtdTaskList::provider:
477 *
478 * The data provider of the list.
479 */
480 properties[PROP_PROVIDER] = g_param_spec_object ("provider",
481 "Provider of the list",
482 "The provider that handles the list",
483 GTD_TYPE_PROVIDER,
484 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
485
486 g_object_class_install_properties (object_class, N_PROPS, properties);
487
488 /**
489 * GtdTaskList::task-added:
490 * @list: a #GtdTaskList
491 * @task: a #GtdTask
492 *
493 * The ::task-added signal is emmited after a #GtdTask
494 * is added to the list.
495 */
496 signals[TASK_ADDED] = g_signal_new ("task-added",
497 GTD_TYPE_TASK_LIST,
498 G_SIGNAL_RUN_LAST,
499 G_STRUCT_OFFSET (GtdTaskListClass, task_added),
500 NULL,
501 NULL,
502 NULL,
503 G_TYPE_NONE,
504 1,
505 GTD_TYPE_TASK);
506
507 /**
508 * GtdTaskList::task-removed:
509 * @list: a #GtdTaskList
510 * @task: a #GtdTask
511 *
512 * The ::task-removed signal is emmited after a #GtdTask
513 * is removed from the list.
514 */
515 signals[TASK_REMOVED] = g_signal_new ("task-removed",
516 GTD_TYPE_TASK_LIST,
517 G_SIGNAL_RUN_LAST,
518 G_STRUCT_OFFSET (GtdTaskListClass, task_removed),
519 NULL,
520 NULL,
521 NULL,
522 G_TYPE_NONE,
523 1,
524 GTD_TYPE_TASK);
525
526 /**
527 * GtdTaskList::task-updated:
528 * @list: a #GtdTaskList
529 * @task: a #GtdTask
530 *
531 * The ::task-updated signal is emmited after a #GtdTask
532 * in the list is updated.
533 */
534 signals[TASK_UPDATED] = g_signal_new ("task-updated",
535 GTD_TYPE_TASK_LIST,
536 G_SIGNAL_RUN_LAST,
537 G_STRUCT_OFFSET (GtdTaskListClass, task_updated),
538 NULL,
539 NULL,
540 NULL,
541 G_TYPE_NONE,
542 1,
543 GTD_TYPE_TASK);
544 }
545
546 static void
gtd_task_list_init(GtdTaskList * self)547 gtd_task_list_init (GtdTaskList *self)
548 {
549 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
550
551 priv->task_to_uid = g_hash_table_new (g_str_hash, g_str_equal);
552 priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
553 priv->sorted_tasks = g_sequence_new (g_object_unref);
554 }
555
556 /**
557 * gtd_task_list_new:
558 * @provider: (nullable): a #GtdProvider
559 *
560 * Creates a new list.
561 *
562 * Returns: (transfer full): the new #GtdTaskList
563 */
564 GtdTaskList *
gtd_task_list_new(GtdProvider * provider)565 gtd_task_list_new (GtdProvider *provider)
566 {
567 return g_object_new (GTD_TYPE_TASK_LIST,
568 "provider", provider,
569 NULL);
570 }
571
572 /**
573 * gtd_task_list_get_color:
574 * @list: a #GtdTaskList
575 *
576 * Retrieves the color of %list. It is guarantee that it always returns a
577 * color, given a valid #GtdTaskList.
578 *
579 * Returns: (transfer full): the color of %list. Free with %gdk_rgba_free after use.
580 */
581 GdkRGBA*
gtd_task_list_get_color(GtdTaskList * list)582 gtd_task_list_get_color (GtdTaskList *list)
583 {
584 GtdTaskListPrivate *priv;
585 GdkRGBA rgba;
586
587 g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL);
588
589 priv = gtd_task_list_get_instance_private (list);
590
591 if (!priv->color)
592 {
593 gdk_rgba_parse (&rgba, "#ffffff");
594 priv->color = gdk_rgba_copy (&rgba);
595 }
596
597 return gdk_rgba_copy (priv->color);
598 }
599
600 /**
601 * gtd_task_list_set_color:
602 * @list: a #GtdTaskList
603 * #color: a #GdkRGBA
604 *
605 * sets the color of @list.
606 */
607 void
gtd_task_list_set_color(GtdTaskList * list,const GdkRGBA * color)608 gtd_task_list_set_color (GtdTaskList *list,
609 const GdkRGBA *color)
610 {
611 GtdTaskListPrivate *priv;
612 GdkRGBA *current_color;
613
614 g_return_if_fail (GTD_IS_TASK_LIST (list));
615
616 priv = gtd_task_list_get_instance_private (list);
617 current_color = gtd_task_list_get_color (list);
618
619 if (!gdk_rgba_equal (current_color, color))
620 {
621 g_clear_pointer (&priv->color, gdk_rgba_free);
622 priv->color = gdk_rgba_copy (color);
623
624 g_object_notify (G_OBJECT (list), "color");
625 }
626
627 gdk_rgba_free (current_color);
628 }
629
630 /**
631 * gtd_task_list_get_name:
632 * @list: a #GtdTaskList
633 *
634 * Retrieves the user-visible name of @list, or %NULL.
635 *
636 * Returns: (transfer none): the internal name of @list. Do not free
637 * after use.
638 */
639 const gchar*
gtd_task_list_get_name(GtdTaskList * list)640 gtd_task_list_get_name (GtdTaskList *list)
641 {
642 GtdTaskListPrivate *priv;
643
644 g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL);
645
646 priv = gtd_task_list_get_instance_private (list);
647
648 return priv->name;
649 }
650
651 /**
652 * gtd_task_list_set_name:
653 * @list: a #GtdTaskList
654 * @name: (nullable): the name of @list
655 *
656 * Sets the @list name to @name.
657 */
658 void
gtd_task_list_set_name(GtdTaskList * list,const gchar * name)659 gtd_task_list_set_name (GtdTaskList *list,
660 const gchar *name)
661 {
662 GtdTaskListPrivate *priv;
663
664 g_assert (GTD_IS_TASK_LIST (list));
665
666 priv = gtd_task_list_get_instance_private (list);
667
668 if (g_strcmp0 (priv->name, name) != 0)
669 {
670 g_free (priv->name);
671 priv->name = g_strdup (name);
672
673 g_object_notify (G_OBJECT (list), "name");
674 }
675 }
676
677 /**
678 * gtd_task_list_get_provider:
679 * @list: a #GtdTaskList
680 *
681 * Retrieves the #GtdProvider who owns this list.
682 *
683 * Returns: (transfer none): a #GtdProvider
684 */
685 GtdProvider*
gtd_task_list_get_provider(GtdTaskList * list)686 gtd_task_list_get_provider (GtdTaskList *list)
687 {
688 GtdTaskListPrivate *priv;
689
690 g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL);
691
692 priv = gtd_task_list_get_instance_private (list);
693
694 return priv->provider;
695 }
696
697 /**
698 * gtd_task_list_set_provider:
699 * @self: a #GtdTaskList
700 * @provider: (nullable): a #GtdProvider, or %NULL
701 *
702 * Sets the provider of this tasklist.
703 */
704 void
gtd_task_list_set_provider(GtdTaskList * self,GtdProvider * provider)705 gtd_task_list_set_provider (GtdTaskList *self,
706 GtdProvider *provider)
707 {
708 GtdTaskListPrivate *priv;
709
710 g_assert (GTD_IS_TASK_LIST (self));
711
712 priv = gtd_task_list_get_instance_private (self);
713
714 if (g_set_object (&priv->provider, provider))
715 g_object_notify (G_OBJECT (self), "provider");
716 }
717
718 /**
719 * gtd_task_list_add_task:
720 * @list: a #GtdTaskList
721 * @task: a #GtdTask
722 *
723 * Adds @task to @list.
724 */
725 void
gtd_task_list_add_task(GtdTaskList * self,GtdTask * task)726 gtd_task_list_add_task (GtdTaskList *self,
727 GtdTask *task)
728 {
729 guint position;
730
731 g_assert (GTD_IS_TASK_LIST (self));
732 g_assert (GTD_IS_TASK (task));
733 g_assert (!gtd_task_list_contains (self, task));
734
735 position = add_task (self, task);
736 g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
737 }
738
739 /**
740 * gtd_task_list_update_task:
741 * @list: a #GtdTaskList
742 * @task: a #GtdTask
743 *
744 * Updates @task at @list.
745 */
746 void
gtd_task_list_update_task(GtdTaskList * self,GtdTask * task)747 gtd_task_list_update_task (GtdTaskList *self,
748 GtdTask *task)
749 {
750 GtdTaskListPrivate *priv;
751 GSequenceIter *iter;
752
753 g_return_if_fail (GTD_IS_TASK_LIST (self));
754 g_return_if_fail (GTD_IS_TASK (task));
755
756 priv = gtd_task_list_get_instance_private (self);
757
758 g_return_if_fail (gtd_task_list_contains (self, task));
759
760 iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task)));
761
762 g_list_model_items_changed (G_LIST_MODEL (self),
763 g_sequence_iter_get_position (iter),
764 1,
765 1);
766
767 g_signal_emit (self, signals[TASK_UPDATED], 0, task);
768 }
769
770 /**
771 * gtd_task_list_remove_task:
772 * @list: a #GtdTaskList
773 * @task: a #GtdTask
774 *
775 * Removes @task from @list if it's inside the list.
776 */
777 void
gtd_task_list_remove_task(GtdTaskList * list,GtdTask * task)778 gtd_task_list_remove_task (GtdTaskList *list,
779 GtdTask *task)
780 {
781 guint position;
782
783 g_assert (GTD_IS_TASK_LIST (list));
784 g_assert (GTD_IS_TASK (task));
785 g_assert (gtd_task_list_contains (list, task));
786
787 position = remove_task (list, task);
788 g_list_model_items_changed (G_LIST_MODEL (list), position, 1, 0);
789 }
790
791 /**
792 * gtd_task_list_contains:
793 * @list: a #GtdTaskList
794 * @task: a #GtdTask
795 *
796 * Checks if @task is inside @list.
797 *
798 * Returns: %TRUE if @list contains @task, %FALSE otherwise
799 */
800 gboolean
gtd_task_list_contains(GtdTaskList * list,GtdTask * task)801 gtd_task_list_contains (GtdTaskList *list,
802 GtdTask *task)
803 {
804 GtdTaskListPrivate *priv;
805
806 g_assert (GTD_IS_TASK_LIST (list));
807 g_assert (GTD_IS_TASK (task));
808
809 priv = gtd_task_list_get_instance_private (list);
810
811 return g_hash_table_contains (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task)));
812 }
813
814 /**
815 * gtd_task_list_get_is_removable:
816 * @list: a #GtdTaskList
817 *
818 * Retrieves whether @list can be removed or not.
819 *
820 * Returns: %TRUE if the @list can be removed, %FALSE otherwise
821 */
822 gboolean
gtd_task_list_is_removable(GtdTaskList * list)823 gtd_task_list_is_removable (GtdTaskList *list)
824 {
825 GtdTaskListPrivate *priv;
826
827 g_return_val_if_fail (GTD_IS_TASK_LIST (list), FALSE);
828
829 priv = gtd_task_list_get_instance_private (list);
830
831 return priv->removable;
832 }
833
834 /**
835 * gtd_task_list_set_is_removable:
836 * @list: a #GtdTaskList
837 * @is_removable: %TRUE if @list can be deleted, %FALSE otherwise
838 *
839 * Sets whether @list can be deleted or not.
840 */
841 void
gtd_task_list_set_is_removable(GtdTaskList * list,gboolean is_removable)842 gtd_task_list_set_is_removable (GtdTaskList *list,
843 gboolean is_removable)
844 {
845 GtdTaskListPrivate *priv;
846
847 g_return_if_fail (GTD_IS_TASK_LIST (list));
848
849 priv = gtd_task_list_get_instance_private (list);
850
851 if (priv->removable != is_removable)
852 {
853 priv->removable = is_removable;
854
855 g_object_notify (G_OBJECT (list), "is-removable");
856 }
857 }
858
859 /**
860 * gtd_task_list_get_task_by_id:
861 * @list: a #GtdTaskList
862 * @id: the id of the task
863 *
864 * Retrieves a task from @self with the given @id.
865 *
866 * Returns: (transfer none)(nullable): a #GtdTask, or %NULL
867 */
868 GtdTask*
gtd_task_list_get_task_by_id(GtdTaskList * self,const gchar * id)869 gtd_task_list_get_task_by_id (GtdTaskList *self,
870 const gchar *id)
871 {
872 GtdTaskListPrivate *priv;
873 GSequenceIter *iter;
874
875 g_return_val_if_fail (GTD_IS_TASK_LIST (self), NULL);
876
877 priv = gtd_task_list_get_instance_private (self);
878 iter = g_hash_table_lookup (priv->tasks, id);
879
880 if (!iter)
881 return NULL;
882
883 return g_sequence_get (iter);
884 }
885
886 /**
887 * gtd_task_list_move_task_to_position:
888 * @self: a #GtdTaskList
889 * @task: a #GtdTask
890 * @new_position: the new position of @task inside @self
891 *
892 * Moves @task to @new_position, and repositions the elements
893 * in between as well.
894 *
895 * @task must belog to @self.
896 */
897 void
gtd_task_list_move_task_to_position(GtdTaskList * self,GtdTask * task,guint new_position)898 gtd_task_list_move_task_to_position (GtdTaskList *self,
899 GtdTask *task,
900 guint new_position)
901 {
902
903 GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
904 GSequenceIter *new_position_iter;
905 GSequenceIter *iter;
906 guint old_position;
907 guint length;
908 guint start;
909 guint i;
910
911
912 g_return_if_fail (GTD_IS_TASK_LIST (self));
913 g_return_if_fail (GTD_IS_TASK (task));
914 g_return_if_fail (gtd_task_list_contains (self, task));
915 g_return_if_fail (g_list_model_get_n_items (G_LIST_MODEL (self)) >= new_position);
916
917 iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task)));
918 old_position = g_sequence_iter_get_position (iter);
919
920 if (old_position == new_position)
921 return;
922
923 GTD_TRACE_MSG ("Moving task '%s' (%s) from %u to %u",
924 gtd_task_get_title (task),
925 gtd_object_get_uid (GTD_OBJECT (task)),
926 old_position,
927 new_position);
928
929 /* Update the GSequence */
930 new_position_iter = new_position < old_position ?
931 g_sequence_get_iter_at_pos (priv->sorted_tasks, new_position) :
932 g_sequence_get_iter_at_pos (priv->sorted_tasks, new_position + 1);
933 g_sequence_move (iter, new_position_iter);
934
935 /* Update the 'position' property of all tasks in between */
936 priv->freeze_counter++;
937
938 length = ABS ((gint) new_position - (gint64) old_position) + 1;
939 start = MIN (old_position, new_position);
940 iter = g_sequence_get_iter_at_pos (priv->sorted_tasks, start);
941
942 for (i = 0; i < length; i++)
943 {
944 GtdTask *aux = g_sequence_get (iter);
945
946 g_signal_handlers_block_by_func (aux, task_changed_cb, self);
947 gtd_task_set_position (aux, start + i);
948 g_signal_handlers_unblock_by_func (aux, task_changed_cb, self);
949
950 gtd_provider_update_task (priv->provider, aux, NULL, on_task_updated_cb, self);
951
952 iter = g_sequence_iter_next (iter);
953 }
954
955 g_list_model_items_changed (G_LIST_MODEL (self), old_position, 1, 0);
956 g_list_model_items_changed (G_LIST_MODEL (self), new_position, 0, 1);
957
958 priv->freeze_counter--;
959 }
960
961 /**
962 * gtd_task_list_get_archived:
963 * @self: a #GtdTaskList
964 *
965 * Retrieves whether @self is archived or not. Archived task lists
966 * are hidden by default, and new tasks cannot be added.
967 *
968 * Returns: %TRUE if @self is archived, %FALSE otherwise.
969 */
970 gboolean
gtd_task_list_get_archived(GtdTaskList * self)971 gtd_task_list_get_archived (GtdTaskList *self)
972 {
973 g_return_val_if_fail (GTD_IS_TASK_LIST (self), FALSE);
974
975 return GTD_TASK_LIST_GET_CLASS (self)->get_archived (self);
976 }
977
978 /**
979 * gtd_task_list_set_archived:
980 * @self: a #GtdTaskList
981 * @archived: whether @self is archived or not
982 *
983 * Sets the "archive" property of @self to @archived.
984 */
985 void
gtd_task_list_set_archived(GtdTaskList * self,gboolean archived)986 gtd_task_list_set_archived (GtdTaskList *self,
987 gboolean archived)
988 {
989 gboolean was_archived;
990
991 g_return_if_fail (GTD_IS_TASK_LIST (self));
992
993 was_archived = gtd_task_list_get_archived (self);
994
995 if (archived == was_archived)
996 return;
997
998 GTD_TASK_LIST_GET_CLASS (self)->set_archived (self, archived);
999
1000 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ARCHIVED]);
1001 }
1002
1003 /**
1004 * gtd_task_list_is_inbox:
1005 * @self: a #GtdTaskList
1006 *
1007 * Retrieves whether @self is the inbox task list of its provider.
1008 *
1009 * Returns: %TRUE if @self is the inbox of it's provider, %FALSE otherwise.
1010 */
1011 gboolean
gtd_task_list_is_inbox(GtdTaskList * self)1012 gtd_task_list_is_inbox (GtdTaskList *self)
1013 {
1014 GtdTaskListPrivate *priv;
1015
1016 g_return_val_if_fail (GTD_IS_TASK_LIST (self), FALSE);
1017
1018 priv = gtd_task_list_get_instance_private (self);
1019
1020 return self == gtd_provider_get_inbox (priv->provider);
1021 }
1022