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