1 /* gtd-task.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 "GtdTask"
20 
21 #include "gtd-debug.h"
22 #include "gtd-task.h"
23 #include "gtd-task-list.h"
24 
25 #include <glib/gi18n.h>
26 
27 /**
28  * SECTION:gtd-task
29  * @short_description: a task
30  * @title:GtdTask
31  * @stability:Unstable
32  * @see_also:#GtdTaskList
33  *
34  * A #GtdTask is an object that represents a task. All #GtdTasks
35  * must be inside a #GtdTaskList.
36  */
37 
38 typedef struct
39 {
40   gchar           *description;
41   GtdTaskList     *list;
42 
43   GDateTime       *creation_date;
44   GDateTime       *completion_date;
45   GDateTime       *due_date;
46 
47   gchar           *title;
48 
49   gint32           priority;
50   gint64           position;
51   gboolean         complete;
52   gboolean         important;
53 } GtdTaskPrivate;
54 
55 G_DEFINE_TYPE_WITH_PRIVATE (GtdTask, gtd_task, GTD_TYPE_OBJECT)
56 
57 enum
58 {
59   PROP_0,
60   PROP_COMPLETE,
61   PROP_DESCRIPTION,
62   PROP_CREATION_DATE,
63   PROP_DUE_DATE,
64   PROP_IMPORTANT,
65   PROP_LIST,
66   PROP_POSITION,
67   PROP_TITLE,
68   LAST_PROP
69 };
70 
71 static void
task_list_weak_notified(gpointer data,GObject * where_the_object_was)72 task_list_weak_notified (gpointer  data,
73                          GObject  *where_the_object_was)
74 {
75   GtdTask *task = GTD_TASK (data);
76   GtdTaskPrivate *priv = gtd_task_get_instance_private (task);
77   priv->list = NULL;
78 }
79 
80 /*
81  * GtdTask default implementations
82  */
83 
84 static GDateTime*
gtd_task_real_get_completion_date(GtdTask * self)85 gtd_task_real_get_completion_date (GtdTask *self)
86 {
87   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
88 
89   return priv->completion_date ? g_date_time_ref (priv->completion_date) : NULL;
90 }
91 
92 static void
gtd_task_real_set_completion_date(GtdTask * self,GDateTime * dt)93 gtd_task_real_set_completion_date (GtdTask   *self,
94                                    GDateTime *dt)
95 {
96   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
97 
98   g_clear_pointer (&priv->completion_date, g_date_time_unref);
99   priv->completion_date = dt ? g_date_time_ref (dt) : NULL;
100 }
101 
102 static gboolean
gtd_task_real_get_complete(GtdTask * self)103 gtd_task_real_get_complete (GtdTask *self)
104 {
105   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
106 
107   return priv->complete;
108 }
109 
110 static void
gtd_task_real_set_complete(GtdTask * self,gboolean complete)111 gtd_task_real_set_complete (GtdTask  *self,
112                             gboolean  complete)
113 {
114   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
115   GDateTime *dt;
116 
117   dt = complete ? g_date_time_new_now_local () : NULL;
118   gtd_task_real_set_completion_date (self, dt);
119 
120   priv->complete = complete;
121 }
122 
123 static GDateTime*
gtd_task_real_get_creation_date(GtdTask * self)124 gtd_task_real_get_creation_date (GtdTask *self)
125 {
126   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
127 
128   return priv->creation_date ? g_date_time_ref (priv->creation_date) : NULL;
129 }
130 
131 static void
gtd_task_real_set_creation_date(GtdTask * self,GDateTime * dt)132 gtd_task_real_set_creation_date (GtdTask   *self,
133                                  GDateTime *dt)
134 {
135   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
136 
137   g_clear_pointer (&priv->creation_date, g_date_time_unref);
138   priv->creation_date = dt ? g_date_time_ref (dt) : NULL;
139 }
140 
141 static const gchar*
gtd_task_real_get_description(GtdTask * self)142 gtd_task_real_get_description (GtdTask *self)
143 {
144   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
145 
146   return priv->description ? priv->description : "";
147 }
148 
149 static void
gtd_task_real_set_description(GtdTask * self,const gchar * description)150 gtd_task_real_set_description (GtdTask     *self,
151                                const gchar *description)
152 {
153   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
154 
155   g_clear_pointer (&priv->description, g_free);
156   priv->description = g_strdup (description);
157 }
158 
159 static GDateTime*
gtd_task_real_get_due_date(GtdTask * self)160 gtd_task_real_get_due_date (GtdTask *self)
161 {
162   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
163 
164   return priv->due_date ? g_date_time_ref (priv->due_date) : NULL;
165 }
166 
167 static void
gtd_task_real_set_due_date(GtdTask * self,GDateTime * due_date)168 gtd_task_real_set_due_date (GtdTask   *self,
169                             GDateTime *due_date)
170 {
171   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
172 
173   g_clear_pointer (&priv->due_date, g_date_time_unref);
174 
175   if (due_date)
176     priv->due_date = g_date_time_ref (due_date);
177 }
178 
179 static gboolean
gtd_task_real_get_important(GtdTask * self)180 gtd_task_real_get_important (GtdTask *self)
181 {
182   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
183 
184   return priv->important;
185 }
186 
187 static void
gtd_task_real_set_important(GtdTask * self,gboolean important)188 gtd_task_real_set_important (GtdTask  *self,
189                              gboolean  important)
190 {
191   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
192 
193   if (priv->important == important)
194     return;
195 
196   priv->important = important;
197 }
198 
199 static gint64
gtd_task_real_get_position(GtdTask * self)200 gtd_task_real_get_position (GtdTask *self)
201 {
202   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
203 
204   return priv->position;
205 }
206 
207 static void
gtd_task_real_set_position(GtdTask * self,gint64 position)208 gtd_task_real_set_position (GtdTask *self,
209                             gint64   position)
210 {
211   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
212 
213   priv->position = position;
214 }
215 
216 static const gchar*
gtd_task_real_get_title(GtdTask * self)217 gtd_task_real_get_title (GtdTask *self)
218 {
219   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
220 
221   return priv->title;
222 }
223 
224 static void
gtd_task_real_set_title(GtdTask * self,const gchar * title)225 gtd_task_real_set_title (GtdTask     *self,
226                          const gchar *title)
227 {
228   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
229 
230   g_clear_pointer (&priv->title, g_free);
231   priv->title = title ? g_strdup (title) : NULL;
232 }
233 
234 
235 /*
236  * GObject overrides
237  */
238 
239 static void
gtd_task_finalize(GObject * object)240 gtd_task_finalize (GObject *object)
241 {
242   GtdTask *self = (GtdTask*) object;
243   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
244 
245   if (priv->list)
246     g_object_weak_unref (G_OBJECT (priv->list), task_list_weak_notified, self);
247 
248   priv->list = NULL;
249   g_free (priv->description);
250 
251   G_OBJECT_CLASS (gtd_task_parent_class)->finalize (object);
252 }
253 
254 static void
gtd_task_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)255 gtd_task_get_property (GObject    *object,
256                        guint       prop_id,
257                        GValue     *value,
258                        GParamSpec *pspec)
259 {
260   GtdTask *self = GTD_TASK (object);
261   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
262   GDateTime *date;
263 
264   switch (prop_id)
265     {
266     case PROP_COMPLETE:
267       g_value_set_boolean (value, gtd_task_get_complete (self));
268       break;
269 
270     case PROP_CREATION_DATE:
271       g_value_set_boxed (value, gtd_task_get_creation_date (self));
272       break;
273 
274     case PROP_DESCRIPTION:
275       g_value_set_string (value, gtd_task_get_description (self));
276       break;
277 
278     case PROP_DUE_DATE:
279       date = gtd_task_get_due_date (self);
280       g_value_set_boxed (value, date);
281       g_clear_pointer (&date, g_date_time_unref);
282       break;
283 
284     case PROP_IMPORTANT:
285       g_value_set_boolean (value, gtd_task_get_important (self));
286       break;
287 
288     case PROP_LIST:
289       g_value_set_object (value, priv->list);
290       break;
291 
292     case PROP_POSITION:
293       g_value_set_int64 (value, gtd_task_get_position (self));
294       break;
295 
296     case PROP_TITLE:
297       g_value_set_string (value, gtd_task_get_title (self));
298       break;
299 
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302     }
303 }
304 
305 static void
gtd_task_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)306 gtd_task_set_property (GObject      *object,
307                        guint         prop_id,
308                        const GValue *value,
309                        GParamSpec   *pspec)
310 {
311   GtdTask *self = GTD_TASK (object);
312 
313   switch (prop_id)
314     {
315     case PROP_COMPLETE:
316       gtd_task_set_complete (self, g_value_get_boolean (value));
317       break;
318 
319     case PROP_CREATION_DATE:
320       gtd_task_set_creation_date (self, g_value_get_boxed (value));
321       break;
322 
323     case PROP_DESCRIPTION:
324       gtd_task_set_description (self, g_value_get_string (value));
325       break;
326 
327     case PROP_DUE_DATE:
328       gtd_task_set_due_date (self, g_value_get_boxed (value));
329       break;
330 
331     case PROP_IMPORTANT:
332       gtd_task_set_important (self, g_value_get_boolean (value));
333       break;
334 
335     case PROP_LIST:
336       gtd_task_set_list (self, g_value_get_object (value));
337       break;
338 
339     case PROP_POSITION:
340       gtd_task_set_position (self, g_value_get_int64 (value));
341       break;
342 
343     case PROP_TITLE:
344       gtd_task_set_title (self, g_value_get_string (value));
345       break;
346 
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349     }
350 }
351 
352 static void
gtd_task_class_init(GtdTaskClass * klass)353 gtd_task_class_init (GtdTaskClass *klass)
354 {
355   GObjectClass *object_class = G_OBJECT_CLASS (klass);
356 
357   klass->get_complete = gtd_task_real_get_complete;
358   klass->set_complete = gtd_task_real_set_complete;
359   klass->get_creation_date = gtd_task_real_get_creation_date;
360   klass->set_creation_date = gtd_task_real_set_creation_date;
361   klass->get_completion_date = gtd_task_real_get_completion_date;
362   klass->set_completion_date = gtd_task_real_set_completion_date;
363   klass->get_description = gtd_task_real_get_description;
364   klass->set_description = gtd_task_real_set_description;
365   klass->get_due_date = gtd_task_real_get_due_date;
366   klass->set_due_date = gtd_task_real_set_due_date;
367   klass->get_important = gtd_task_real_get_important;
368   klass->set_important = gtd_task_real_set_important;
369   klass->get_position = gtd_task_real_get_position;
370   klass->set_position = gtd_task_real_set_position;
371   klass->get_title = gtd_task_real_get_title;
372   klass->set_title = gtd_task_real_set_title;
373 
374   object_class->finalize = gtd_task_finalize;
375   object_class->get_property = gtd_task_get_property;
376   object_class->set_property = gtd_task_set_property;
377 
378   /**
379    * GtdTask::complete:
380    *
381    * @TRUE if the task is marked as complete or @FALSE otherwise. Usually
382    * represented by a checkbox at user interfaces.
383    */
384   g_object_class_install_property (
385         object_class,
386         PROP_COMPLETE,
387         g_param_spec_boolean ("complete",
388                               "Whether the task is completed or not",
389                               "Whether the task is marked as completed by the user",
390                               FALSE,
391                               G_PARAM_READWRITE));
392 
393   /**
394    * GtdTask::creation-date:
395    *
396    * The @GDateTime that represents the time in which the task was created.
397    */
398   g_object_class_install_property (
399         object_class,
400         PROP_CREATION_DATE,
401         g_param_spec_boxed ("creation-date",
402                             "Creation date of the task",
403                             "The day the task was created.",
404                             G_TYPE_DATE_TIME,
405                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
406 
407   /**
408    * GtdTask::description:
409    *
410    * Description of the task.
411    */
412   g_object_class_install_property (
413         object_class,
414         PROP_DESCRIPTION,
415         g_param_spec_string ("description",
416                              "Description of the task",
417                              "Optional string describing the task",
418                              NULL,
419                              G_PARAM_READWRITE));
420 
421   /**
422    * GtdTask::due-date:
423    *
424    * The @GDateTime that represents the time in which the task should
425    * be completed before.
426    */
427   g_object_class_install_property (
428         object_class,
429         PROP_DUE_DATE,
430         g_param_spec_boxed ("due-date",
431                             "End date of the task",
432                             "The day the task is supposed to be completed",
433                             G_TYPE_DATE_TIME,
434                             G_PARAM_READWRITE));
435 
436   /**
437    * GtdTask::important:
438    *
439    * @TRUE if the task is important, @FALSE otherwise.
440    */
441   g_object_class_install_property (
442         object_class,
443         PROP_IMPORTANT,
444         g_param_spec_boolean ("important",
445                               "Whether the task is important or not",
446                               "Whether the task is important or not",
447                               FALSE,
448                               G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
449 
450   /**
451    * GtdTask::list:
452    *
453    * The @GtdTaskList that contains this task.
454    */
455   g_object_class_install_property (
456         object_class,
457         PROP_LIST,
458         g_param_spec_object ("list",
459                              "List of the task",
460                              "The list that owns this task",
461                              GTD_TYPE_TASK_LIST,
462                              G_PARAM_READWRITE));
463 
464   /**
465    * GtdTask::position:
466    *
467    * Position of the task, -1 if not set.
468    */
469   g_object_class_install_property (
470         object_class,
471         PROP_POSITION,
472         g_param_spec_int64 ("position",
473                             "Position of the task",
474                             "The position of the task. -1 means no position, and tasks will be sorted alphabetically.",
475                             -1,
476                             G_MAXINT64,
477                             0,
478                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
479 
480   /**
481    * GtdTask::title:
482    *
483    * The title of the task, usually the task name.
484    */
485   g_object_class_install_property (
486         object_class,
487         PROP_TITLE,
488         g_param_spec_string ("title",
489                              "Title of the task",
490                              "The title of the task",
491                              NULL,
492                              G_PARAM_READWRITE));
493 }
494 
495 static void
gtd_task_init(GtdTask * self)496 gtd_task_init (GtdTask *self)
497 {
498   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
499 
500   priv->position = -1;
501 }
502 
503 /**
504  * gtd_task_new:
505  *
506  * Creates a new #GtdTask
507  *
508  * Returns: (transfer full): a #GtdTask
509  */
510 GtdTask *
gtd_task_new(void)511 gtd_task_new (void)
512 {
513   return g_object_new (GTD_TYPE_TASK, NULL);
514 }
515 
516 /**
517  * gtd_task_get_complete:
518  * @self: a #GtdTask
519  *
520  * Retrieves whether the task is complete or not.
521  *
522  * Returns: %TRUE if the task is complete, %FALSE otherwise
523  */
524 gboolean
gtd_task_get_complete(GtdTask * self)525 gtd_task_get_complete (GtdTask *self)
526 {
527   g_return_val_if_fail (GTD_IS_TASK (self), FALSE);
528 
529   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (self))->get_complete (self);
530 }
531 
532 /**
533  * gtd_task_set_complete:
534  * @task: a #GtdTask
535  * @complete: the new value
536  *
537  * Updates the complete state of @task.
538  */
539 void
gtd_task_set_complete(GtdTask * task,gboolean complete)540 gtd_task_set_complete (GtdTask  *task,
541                        gboolean  complete)
542 {
543   g_return_if_fail (GTD_IS_TASK (task));
544 
545   if (gtd_task_get_complete (task) == complete)
546     return;
547 
548   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_complete (task, complete);
549 
550   g_object_notify (G_OBJECT (task), "complete");
551 }
552 
553 /**
554  * gtd_task_get_creation_date:
555  * @task: a #GtdTask
556  *
557  * Returns the #GDateTime that represents the task's creation date.
558  * The value is referenced for thread safety. Returns %NULL if
559  * no date is set.
560  *
561  * Returns: (transfer full): the internal #GDateTime referenced
562  * for thread safety, or %NULL. Unreference it after use.
563  */
564 GDateTime*
gtd_task_get_creation_date(GtdTask * task)565 gtd_task_get_creation_date (GtdTask *task)
566 {
567   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
568 
569   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_creation_date (task);
570 }
571 
572 /**
573  * gtd_task_set_creation_date:
574  * @task: a #GtdTask
575  *
576  * Sets the creation date of @task.
577  */
578 void
gtd_task_set_creation_date(GtdTask * task,GDateTime * dt)579 gtd_task_set_creation_date (GtdTask   *task,
580                             GDateTime *dt)
581 {
582   g_return_if_fail (GTD_IS_TASK (task));
583 
584   if (gtd_task_get_creation_date (task) == dt)
585     return;
586 
587   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_creation_date (task, dt);
588 
589   g_object_notify (G_OBJECT (task), "complete");
590 }
591 
592 /**
593  * gtd_task_get_completion_date:
594  * @task: a #GtdTask
595  *
596  * Returns the #GDateTime that represents the task's completion date.
597  * Returns %NULL if no date is set.
598  *
599  * Returns: (transfer full)(nullable): the internal #GDateTime or %NULL.
600  * Unreference it after use.
601  */
602 GDateTime*
gtd_task_get_completion_date(GtdTask * task)603 gtd_task_get_completion_date (GtdTask *task)
604 {
605   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
606 
607   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_completion_date (task);
608 }
609 
610 /**
611  * gtd_task_get_description:
612  * @task: a #GtdTask
613  *
614  * Retrieves the description of the task.
615  *
616  * Returns: (transfer none): the description of @task
617  */
618 const gchar*
gtd_task_get_description(GtdTask * task)619 gtd_task_get_description (GtdTask *task)
620 {
621   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
622 
623   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_description (task);
624 }
625 
626 /**
627  * gtd_task_set_description:
628  * @task: a #GtdTask
629  * @description: (nullable): the new description, or %NULL
630  *
631  * Updates the description of @task. The string is not stripped off of
632  * spaces to preserve user data.
633  */
634 void
gtd_task_set_description(GtdTask * task,const gchar * description)635 gtd_task_set_description (GtdTask     *task,
636                           const gchar *description)
637 {
638   GtdTaskPrivate *priv;
639 
640   g_assert (GTD_IS_TASK (task));
641   g_assert (g_utf8_validate (description, -1, NULL));
642 
643   priv = gtd_task_get_instance_private (task);
644 
645   if (g_strcmp0 (priv->description, description) == 0)
646     return;
647 
648   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_description (task, description);
649 
650   g_object_notify (G_OBJECT (task), "description");
651 }
652 
653 /**
654  * gtd_task_get_due_date:
655  * @task: a #GtdTask
656  *
657  * Returns the #GDateTime that represents the task's due date.
658  * The value is referenced for thread safety. Returns %NULL if
659  * no date is set.
660  *
661  * Returns: (transfer full): the internal #GDateTime referenced
662  * for thread safety, or %NULL. Unreference it after use.
663  */
664 GDateTime*
gtd_task_get_due_date(GtdTask * task)665 gtd_task_get_due_date (GtdTask *task)
666 {
667   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
668 
669   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_due_date (task);
670 }
671 
672 /**
673  * gtd_task_set_due_date:
674  * @task: a #GtdTask
675  * @dt: (nullable): a #GDateTime
676  *
677  * Updates the internal @GtdTask::due-date property.
678  */
679 void
gtd_task_set_due_date(GtdTask * task,GDateTime * dt)680 gtd_task_set_due_date (GtdTask   *task,
681                        GDateTime *dt)
682 {
683   g_autoptr (GDateTime) current_dt = NULL;
684 
685   g_assert (GTD_IS_TASK (task));
686 
687   current_dt = gtd_task_get_due_date (task);
688 
689   /* Don't do anything if the date is equal */
690   if (current_dt == dt || (current_dt && dt && g_date_time_equal (current_dt, dt)))
691     return;
692 
693   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_due_date (task, dt);
694 
695   g_object_notify (G_OBJECT (task), "due-date");
696 }
697 
698 /**
699  * gtd_task_get_important:
700  * @self: a #GtdTask
701  *
702  * Retrieves whether @self is @important or not.
703  *
704  * Returns: %TRUE if @self is important, %FALSE otherwise
705  */
706 gboolean
gtd_task_get_important(GtdTask * self)707 gtd_task_get_important (GtdTask *self)
708 {
709   g_return_val_if_fail (GTD_IS_TASK (self), FALSE);
710 
711   return GTD_TASK_GET_CLASS (self)->get_important (self);
712 }
713 
714 /**
715  * gtd_task_set_important:
716  * @self: a #GtdTask
717  * @important: whether @self is important or not
718  *
719  * Sets whether @self is @important or not.
720  */
721 void
gtd_task_set_important(GtdTask * self,gboolean important)722 gtd_task_set_important (GtdTask  *self,
723                         gboolean  important)
724 {
725   g_return_if_fail (GTD_IS_TASK (self));
726 
727   important = !!important;
728 
729   GTD_TASK_GET_CLASS (self)->set_important (self, important);
730   g_object_notify (G_OBJECT (self), "important");
731 }
732 
733 /**
734  * gtd_task_get_list:
735  *
736  * Returns a weak reference to the #GtdTaskList that
737  * owns the given @task.
738  *
739  * Returns: (transfer none): a weak reference to the
740  * #GtdTaskList that owns @task. Do not free after
741  * usage.
742  */
743 GtdTaskList*
gtd_task_get_list(GtdTask * task)744 gtd_task_get_list (GtdTask *task)
745 {
746   GtdTaskPrivate *priv;
747 
748   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
749 
750   priv = gtd_task_get_instance_private (task);
751 
752   return priv->list;
753 }
754 
755 /**
756  * gtd_task_set_list:
757  * @task: a #GtdTask
758  * @list: (nullable): a #GtdTaskList
759  *
760  * Sets the parent #GtdTaskList of @task.
761  */
762 void
gtd_task_set_list(GtdTask * task,GtdTaskList * list)763 gtd_task_set_list (GtdTask     *task,
764                    GtdTaskList *list)
765 {
766   GtdTaskPrivate *priv;
767 
768   g_assert (GTD_IS_TASK (task));
769   g_assert (GTD_IS_TASK_LIST (list));
770 
771   priv = gtd_task_get_instance_private (task);
772 
773   if (priv->list == list)
774     return;
775 
776   if (priv->list)
777     g_object_weak_unref (G_OBJECT (priv->list), task_list_weak_notified, task);
778 
779   priv->list = list;
780   g_object_weak_ref (G_OBJECT (priv->list), task_list_weak_notified, task);
781   g_object_notify (G_OBJECT (task), "list");
782 }
783 
784 /**
785  * gtd_task_get_position:
786  * @task: a #GtdTask
787  *
788  * Returns the position of @task inside the parent #GtdTaskList,
789  * or -1 if not set.
790  *
791  * Returns: the position of the task, or -1
792  */
793 gint64
gtd_task_get_position(GtdTask * self)794 gtd_task_get_position (GtdTask *self)
795 {
796   g_return_val_if_fail (GTD_IS_TASK (self), -1);
797 
798   return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (self))->get_position (self);
799 }
800 
801 /**
802  * gtd_task_set_position:
803  * @task: a #GtdTask
804  * @position: the priority of @task, or -1
805  *
806  * Sets the @task position inside the parent #GtdTaskList. It
807  * is up to the interface to handle two or more #GtdTask with
808  * the same position value.
809  */
810 void
gtd_task_set_position(GtdTask * self,gint64 position)811 gtd_task_set_position (GtdTask *self,
812                        gint64   position)
813 {
814   g_return_if_fail (GTD_IS_TASK (self));
815 
816   if (gtd_task_get_position (self) == position)
817     return;
818 
819   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (self))->set_position (self, position);
820 
821   g_object_notify (G_OBJECT (self), "position");
822 }
823 
824 /**
825  * gtd_task_get_title:
826  * @task: a #GtdTask
827  *
828  * Retrieves the title of the task, or %NULL.
829  *
830  * Returns: (transfer none): the title of @task, or %NULL
831  */
832 const gchar*
gtd_task_get_title(GtdTask * task)833 gtd_task_get_title (GtdTask *task)
834 {
835   const gchar *title;
836 
837   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
838 
839   title = GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_title (task);
840 
841   return title ? title : "";
842 }
843 
844 /**
845  * gtd_task_set_title:
846  * @task: a #GtdTask
847  * @title: (nullable): the new title, or %NULL
848  *
849  * Updates the title of @task. The string is stripped off of
850  * leading spaces.
851  */
852 void
gtd_task_set_title(GtdTask * task,const gchar * title)853 gtd_task_set_title (GtdTask     *task,
854                     const gchar *title)
855 {
856   const gchar *current_title;
857 
858   g_return_if_fail (GTD_IS_TASK (task));
859   g_return_if_fail (g_utf8_validate (title, -1, NULL));
860 
861   current_title = gtd_task_get_title (task);
862 
863   if (g_strcmp0 (current_title, title) == 0)
864     return;
865 
866   GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_title (task, title);
867 
868   g_object_notify (G_OBJECT (task), "title");
869 }
870 
871 /**
872  * gtd_task_compare:
873  * @t1: (nullable): a #GtdTask
874  * @t2: (nullable): a #GtdTask
875  *
876  * Compare @t1 and @t2.
877  *
878  * Returns: %-1 if @t1 comes before @t2, %1 for the opposite, %0 if they're equal
879  */
880 gint
gtd_task_compare(GtdTask * t1,GtdTask * t2)881 gtd_task_compare (GtdTask *t1,
882                   GtdTask *t2)
883 {
884   GDateTime *dt1;
885   GDateTime *dt2;
886   gchar *txt1;
887   gchar *txt2;
888   gint retval;
889 
890   if (!t1 && !t2)
891     return  0;
892   if (!t1)
893     return  1;
894   if (!t2)
895     return -1;
896 
897   /*
898    * The custom position overrides any comparison we can make. To keep compatibility,
899    * for now, we only compare by position if both tasks have a custom position set.
900    */
901   if (gtd_task_get_position (t1) != -1 && gtd_task_get_position (t2) != -1)
902     {
903       retval = gtd_task_get_position (t1) - gtd_task_get_position (t2);
904 
905       if (retval != 0)
906         return retval;
907     }
908 
909   /* Compare by due date */
910   dt1 = gtd_task_get_due_date (t1);
911   dt2 = gtd_task_get_due_date (t2);
912 
913   if (!dt1 && !dt2)
914     retval =  0;
915   else if (!dt1)
916     retval =  1;
917   else if (!dt2)
918     retval = -1;
919   else
920     retval = g_date_time_compare (dt1, dt2);
921 
922   if (dt1)
923     g_date_time_unref (dt1);
924   if (dt2)
925     g_date_time_unref (dt2);
926 
927   if (retval != 0)
928     return retval;
929 
930   /* Compare by creation date */
931   dt1 = gtd_task_get_creation_date (t1);
932   dt2 = gtd_task_get_creation_date (t2);
933 
934   if (!dt1 && !dt2)
935     retval =  0;
936   else if (!dt1)
937     retval =  1;
938   else if (!dt2)
939     retval = -1;
940   else
941     retval = g_date_time_compare (dt1, dt2);
942 
943   g_clear_pointer (&dt1, g_date_time_unref);
944   g_clear_pointer (&dt2, g_date_time_unref);
945 
946   if (retval != 0)
947     return retval;
948 
949   /* If they're equal up to now, compare by title */
950   txt1 = txt2 = NULL;
951 
952   txt1 = g_utf8_casefold (gtd_task_get_title (t1), -1);
953   txt2 = g_utf8_casefold (gtd_task_get_title (t2), -1);
954 
955   retval = g_strcmp0 (txt1, txt2);
956 
957   g_free (txt1);
958   g_free (txt2);
959 
960   return retval;
961 }
962 
963 /**
964  * gtd_task_get_provider:
965  * @self: a #GtdTaskList
966  *
967  * Utility function to retrieve the data provider that backs this
968  * task. Notice that this is exactly the same as writting:
969  *
970  * |[<!-- language="C" -->
971  * GtdTaskList *list;
972  * GtdProvider *provider;
973  *
974  * list = gtd_task_get_list (task);
975  * provider = gtd_task_list_get_provider (list);
976  * ]|
977  *
978  * Returns: (transfer none)(nullable): the #GtdProvider of this task's list.
979  */
980 GtdProvider*
gtd_task_get_provider(GtdTask * self)981 gtd_task_get_provider (GtdTask *self)
982 {
983   GtdTaskPrivate *priv;
984 
985   g_return_val_if_fail (GTD_IS_TASK (self), NULL);
986 
987   priv = gtd_task_get_instance_private (self);
988 
989   if (priv->list)
990     return gtd_task_list_get_provider (priv->list);
991 
992   return NULL;
993 }
994