[gnome-todo] task: Move ECalComponent to EDS plugin



commit a52d9f34f0116b0cdf7b5258f1a0e9980ad2f90d
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sat Sep 23 11:33:19 2017 -0300

    task: Move ECalComponent to EDS plugin
    
    This was the last big remaining Evolution-Data-Server
    bit in GNOME To Do's core codebase. Now, everything
    related to EDS is in EDS plugin, and the core of To Do
    is truly binding-friendly.

 plugins/eds/gtd-provider-eds.c           |   11 +-
 plugins/eds/gtd-task-eds.c               |  600 ++++++++++++++++++++++++++++++
 plugins/eds/gtd-task-eds.h               |   39 ++
 plugins/eds/gtd-task-list-eds.c          |  116 ++++++
 plugins/eds/meson.build                  |   13 +-
 plugins/todo-txt/gtd-provider-todo-txt.c |   18 +-
 plugins/todo-txt/gtd-todo-txt-parser.c   |    2 +-
 plugins/todoist/gtd-provider-todoist.c   |    7 +-
 src/gtd-dnd-row.c                        |    2 -
 src/gtd-edit-pane.c                      |    7 +-
 src/gtd-new-task-row.c                   |    3 +-
 src/gtd-task-list.c                      |  129 ++-----
 src/gtd-task-list.h                      |    3 +
 src/gtd-task.c                           |  593 ++++++++----------------------
 src/gtd-task.h                           |   34 ++-
 15 files changed, 978 insertions(+), 599 deletions(-)
---
diff --git a/plugins/eds/gtd-provider-eds.c b/plugins/eds/gtd-provider-eds.c
index b7b33e7..b9a2a88 100644
--- a/plugins/eds/gtd-provider-eds.c
+++ b/plugins/eds/gtd-provider-eds.c
@@ -17,6 +17,7 @@
  */
 
 #include "gtd-provider-eds.h"
+#include "gtd-task-eds.h"
 #include "gtd-task-list-eds.h"
 
 #include <glib/gi18n.h>
@@ -121,7 +122,7 @@ gtd_provider_eds_fill_task_list (GObject      *client,
         {
           GtdTask *task;
 
-          task = gtd_task_new (l->data);
+          task = gtd_task_eds_new (l->data);
           gtd_task_set_list (task, list);
 
           gtd_task_list_save_task (list, task);
@@ -879,7 +880,7 @@ gtd_provider_eds_create_task (GtdProviderEds *provider,
   tasklist = GTD_TASK_LIST_EDS (gtd_task_get_list (task));
   source = gtd_task_list_eds_get_source (tasklist);
   client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
+  component = gtd_task_eds_get_component (GTD_TASK_EDS (task));
 
   /* Temporary data for async operation */
   data = task_data_new (provider, (gpointer) task);
@@ -912,7 +913,9 @@ gtd_provider_eds_update_task (GtdProviderEds *provider,
   tasklist = GTD_TASK_LIST_EDS (gtd_task_get_list (task));
   source = gtd_task_list_eds_get_source (tasklist);
   client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
+  component = gtd_task_eds_get_component (GTD_TASK_EDS (task));
+
+  e_cal_component_commit_sequence (component);
 
   /* Temporary data for async operation */
   data = task_data_new (provider, (gpointer) task);
@@ -948,7 +951,7 @@ gtd_provider_eds_remove_task (GtdProviderEds *provider,
   tasklist = GTD_TASK_LIST_EDS (gtd_task_get_list (task));
   source = gtd_task_list_eds_get_source (tasklist);
   client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
+  component = gtd_task_eds_get_component (GTD_TASK_EDS (task));
   id = e_cal_component_get_id (component);
 
   /* Temporary data for async operation */
diff --git a/plugins/eds/gtd-task-eds.c b/plugins/eds/gtd-task-eds.c
new file mode 100644
index 0000000..20eb636
--- /dev/null
+++ b/plugins/eds/gtd-task-eds.c
@@ -0,0 +1,600 @@
+/* gtd-task-eds.c
+ *
+ * Copyright (C) 2017 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-task-eds.h"
+
+struct _GtdTaskEds
+{
+  GtdTask             parent;
+
+  ECalComponent      *component;
+
+  gchar              *description;
+};
+
+G_DEFINE_TYPE (GtdTaskEds, gtd_task_eds, GTD_TYPE_TASK)
+
+enum
+{
+  PROP_0,
+  PROP_COMPONENT,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+
+/*
+ * Auxiliary methods
+ */
+
+static GDateTime*
+convert_icaltime (const icaltimetype *date)
+{
+  GDateTime *dt;
+
+  if (!date)
+    return NULL;
+
+  dt = g_date_time_new_utc (date->year,
+                            date->month,
+                            date->day,
+                            date->is_date ? 0 : date->hour,
+                            date->is_date ? 0 : date->minute,
+                            date->is_date ? 0 : date->second);
+
+  return dt;
+}
+
+static void
+set_description (GtdTaskEds  *self,
+                 const gchar *description)
+{
+  ECalComponentText text;
+  GSList note;
+
+  text.value = description && *description ? description : "";
+  text.altrep = NULL;
+
+  note.data = &text;
+  note.next = NULL;
+
+  g_clear_pointer (&self->description, g_free);
+  self->description = g_strdup (description);
+
+  e_cal_component_set_description_list (self->component, &note);
+}
+
+static void
+setup_description (GtdTaskEds *self)
+{
+  g_autofree gchar *desc = NULL;
+  GSList *text_list;
+  GSList *l;
+
+  /* concatenates the multiple descriptions a task may have */
+  e_cal_component_get_description_list (self->component, &text_list);
+
+  for (l = text_list; l != NULL; l = l->next)
+    {
+      if (l->data != NULL)
+        {
+          ECalComponentText *text;
+          gchar *carrier;
+
+          text = l->data;
+
+          if (desc)
+            {
+              carrier = g_strconcat (desc,
+                                     "\n",
+                                     text->value,
+                                     NULL);
+              g_free (desc);
+              desc = carrier;
+            }
+          else
+            {
+              desc = g_strdup (text->value);
+            }
+        }
+    }
+
+  set_description (self, desc);
+
+  e_cal_component_free_text_list (text_list);
+}
+
+
+/*
+ * GtdObject overrides
+ */
+
+static const gchar*
+gtd_task_eds_get_uid (GtdObject *object)
+{
+  GtdTaskEds *self;
+  const gchar *uid;
+
+  g_return_val_if_fail (GTD_IS_TASK (object), NULL);
+
+  self = GTD_TASK_EDS (object);
+
+  if (self->component)
+    e_cal_component_get_uid (self->component, &uid);
+  else
+    uid = NULL;
+
+  return uid;
+}
+
+static void
+gtd_task_eds_set_uid (GtdObject   *object,
+                      const gchar *uid)
+{
+  GtdTaskEds *self;
+  const gchar *current_uid;
+
+  g_return_if_fail (GTD_IS_TASK (object));
+
+  self = GTD_TASK_EDS (object);
+
+  if (!self->component)
+    return;
+
+  e_cal_component_get_uid (self->component, &current_uid);
+
+  if (g_strcmp0 (current_uid, uid) != 0)
+    {
+      e_cal_component_set_uid (self->component, uid);
+
+      g_object_notify (G_OBJECT (object), "uid");
+    }
+}
+
+
+/*
+ * GtdTask overrides
+ */
+
+static gboolean
+gtd_task_eds_get_complete (GtdTask *task)
+{
+  GtdTaskEds *self;
+  icaltimetype *dt;
+  gboolean completed;
+
+  g_return_val_if_fail (GTD_IS_TASK_EDS (task), FALSE);
+
+  self = GTD_TASK_EDS (task);
+
+  e_cal_component_get_completed (self->component, &dt);
+  completed = (dt != NULL);
+
+  g_clear_pointer (&dt, e_cal_component_free_icaltimetype);
+
+  return completed;
+}
+
+static void
+gtd_task_eds_set_complete (GtdTask  *task,
+                           gboolean  complete)
+{
+  icalproperty_status status;
+  icaltimetype *dt;
+  GtdTaskEds *self;
+  gint percent;
+
+  self = GTD_TASK_EDS (task);
+
+  if (complete)
+    {
+      GDateTime *now = g_date_time_new_now_utc ();
+
+      percent = 100;
+      status = ICAL_STATUS_COMPLETED;
+
+      dt = g_new0 (icaltimetype, 1);
+      dt->year = g_date_time_get_year (now);
+      dt->month = g_date_time_get_month (now);
+      dt->day = g_date_time_get_day_of_month (now);
+      dt->hour = g_date_time_get_hour (now);
+      dt->minute = g_date_time_get_minute (now);
+      dt->second = g_date_time_get_seconds (now);
+      dt->is_date = 0;
+      dt->is_utc = 1;
+
+      /* convert timezone
+       *
+       * FIXME: This does not do anything until we have an ical
+       * timezone associated with the task
+       */
+      icaltimezone_convert_time (dt,
+                                 NULL,
+                                 icaltimezone_get_utc_timezone ());
+      g_date_time_unref (now);
+    }
+  else
+    {
+      dt = NULL;
+      percent = 0;
+      status = ICAL_STATUS_NEEDSACTION;
+    }
+
+  e_cal_component_set_percent_as_int (self->component, percent);
+  e_cal_component_set_status (self->component, status);
+  e_cal_component_set_completed (self->component, dt);
+
+  if (dt)
+    e_cal_component_free_icaltimetype (dt);
+}
+
+static GDateTime*
+gtd_task_eds_get_creation_date (GtdTask *task)
+{
+  icaltimetype *idt;
+  GtdTaskEds *self;
+  GDateTime *dt;
+
+  self = GTD_TASK_EDS (task);
+  idt = NULL;
+  dt = NULL;
+
+  e_cal_component_get_created (self->component, &idt);
+
+  if (idt)
+    dt = convert_icaltime (idt);
+
+  g_clear_pointer (&idt, e_cal_component_free_icaltimetype);
+
+  return dt;
+}
+
+static const gchar*
+gtd_task_eds_get_description (GtdTask *task)
+{
+  GtdTaskEds *self = GTD_TASK_EDS (task);
+
+  return self->description ? self->description : "";
+}
+
+static void
+gtd_task_eds_set_description (GtdTask     *task,
+                              const gchar *description)
+{
+  set_description (GTD_TASK_EDS (task), description);
+}
+
+static GDateTime*
+gtd_task_eds_get_due_date (GtdTask *task)
+{
+  ECalComponentDateTime comp_dt;
+  GtdTaskEds *self;
+  GDateTime *date;
+
+  g_return_val_if_fail (GTD_IS_TASK_EDS (task), NULL);
+
+  self = GTD_TASK_EDS (task);
+
+  e_cal_component_get_due (self->component, &comp_dt);
+
+  date = convert_icaltime (comp_dt.value);
+  e_cal_component_free_datetime (&comp_dt);
+
+  return date;
+}
+
+static void
+gtd_task_eds_set_due_date (GtdTask   *task,
+                           GDateTime *dt)
+{
+  GtdTaskEds *self;
+  GDateTime *current_dt;
+
+  g_assert (GTD_IS_TASK_EDS (task));
+
+  self = GTD_TASK_EDS (task);
+
+  current_dt = gtd_task_get_due_date (task);
+
+  if (dt != current_dt)
+    {
+      ECalComponentDateTime comp_dt;
+      icaltimetype *idt;
+
+      comp_dt.value = NULL;
+      comp_dt.tzid = NULL;
+      idt = NULL;
+
+      if (!current_dt ||
+          (current_dt &&
+           dt &&
+           g_date_time_compare (current_dt, dt) != 0))
+        {
+          idt = g_new0 (icaltimetype, 1);
+
+          g_date_time_ref (dt);
+
+          /* Copy the given dt */
+          idt->year = g_date_time_get_year (dt);
+          idt->month = g_date_time_get_month (dt);
+          idt->day = g_date_time_get_day_of_month (dt);
+          idt->hour = g_date_time_get_hour (dt);
+          idt->minute = g_date_time_get_minute (dt);
+          idt->second = g_date_time_get_seconds (dt);
+          idt->is_date = (idt->hour == 0 &&
+                          idt->minute == 0 &&
+                          idt->second == 0);
+
+          comp_dt.tzid = g_strdup ("UTC");
+
+          comp_dt.value = idt;
+
+          e_cal_component_set_due (self->component, &comp_dt);
+
+          e_cal_component_free_datetime (&comp_dt);
+
+          g_date_time_unref (dt);
+        }
+      else if (!dt)
+        {
+          idt = NULL;
+          comp_dt.tzid = NULL;
+
+          e_cal_component_set_due (self->component, NULL);
+        }
+    }
+
+  g_clear_pointer (&current_dt, g_date_time_unref);
+}
+
+static gint32
+gtd_task_eds_get_priority (GtdTask *task)
+{
+  g_autofree gint *priority = NULL;
+  GtdTaskEds *self;
+
+  g_assert (GTD_IS_TASK_EDS (task));
+
+  self = GTD_TASK_EDS (task);
+
+  e_cal_component_get_priority (self->component, &priority);
+
+  if (!priority)
+    return -1;
+
+  return *priority;
+}
+
+static void
+gtd_task_eds_set_priority (GtdTask *task,
+                           gint32   priority)
+{
+  GtdTaskEds *self;
+
+  g_assert (GTD_IS_TASK_EDS (task));
+
+  self = GTD_TASK_EDS (task);
+
+  e_cal_component_set_priority (self->component, &priority);
+}
+
+static const gchar*
+gtd_task_eds_get_title (GtdTask *task)
+{
+  ECalComponentText summary;
+  GtdTaskEds *self;
+
+  g_return_val_if_fail (GTD_IS_TASK_EDS (task), NULL);
+
+  self = GTD_TASK_EDS (task);
+
+  e_cal_component_get_summary (self->component, &summary);
+
+  return summary.value;
+}
+
+static void
+gtd_task_eds_set_title (GtdTask     *task,
+                        const gchar *title)
+{
+  ECalComponentText new_summary;
+  GtdTaskEds *self;
+
+  g_return_if_fail (GTD_IS_TASK_EDS (task));
+  g_return_if_fail (g_utf8_validate (title, -1, NULL));
+
+  self = GTD_TASK_EDS (task);
+
+  new_summary.value = title;
+  new_summary.altrep = NULL;
+
+  e_cal_component_set_summary (self->component, &new_summary);
+}
+
+
+static void
+gtd_task_eds_subtask_added (GtdTask *task,
+                            GtdTask *subtask)
+{
+  g_autoptr (GList) subtasks = NULL;
+  ECalComponentId *id;
+  ECalComponent *comp;
+  icalcomponent *ical_comp;
+  icalproperty *property;
+  GtdTaskEds *subtask_self;
+  GtdTaskEds *self;
+
+  self = GTD_TASK_EDS (task);
+  subtask_self = GTD_TASK_EDS (subtask);
+  subtasks = gtd_task_get_subtasks (task);
+
+  /* Hook with parent's :subtask_added */
+  GTD_TASK_CLASS (gtd_task_eds_parent_class)->subtask_added (task, subtask);
+
+  id = e_cal_component_get_id (self->component);
+  comp = subtask_self->component;
+  ical_comp = e_cal_component_get_icalcomponent (comp);
+  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
+
+  if (property)
+    icalproperty_set_relatedto (property, id->uid);
+  else
+    icalcomponent_add_property (ical_comp, icalproperty_new_relatedto (id->uid));
+
+  e_cal_component_free_id (id);
+}
+
+static void
+gtd_task_eds_subtask_removed (GtdTask *task,
+                              GtdTask *subtask)
+{
+  g_autoptr (GList) subtasks = NULL;
+  icalcomponent *ical_comp;
+  icalproperty *property;
+  GtdTaskEds *subtask_self;
+
+  subtask_self = GTD_TASK_EDS (subtask);
+  subtasks = gtd_task_get_subtasks (task);
+
+  /* Hook with parent's :subtask_removed */
+  GTD_TASK_CLASS (gtd_task_eds_parent_class)->subtask_removed (task, subtask);
+
+  /* Remove the parent link from the subtask's component */
+  ical_comp = e_cal_component_get_icalcomponent (subtask_self->component);
+  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
+
+  if (!property)
+    return;
+
+  icalcomponent_remove_property (ical_comp, property);
+}
+
+/*
+ * GObject overrides
+ */
+
+static void
+gtd_task_eds_finalize (GObject *object)
+{
+  GtdTaskEds *self = (GtdTaskEds *)object;
+
+  g_clear_object (&self->component);
+
+  G_OBJECT_CLASS (gtd_task_eds_parent_class)->finalize (object);
+}
+
+static void
+gtd_task_eds_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GtdTaskEds *self = GTD_TASK_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_COMPONENT:
+      g_value_set_object (value, self->component);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_task_eds_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  GtdTaskEds *self = GTD_TASK_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_COMPONENT:
+      if (g_set_object (&self->component, g_value_get_object (value)))
+        {
+          setup_description (self);
+          g_object_notify_by_pspec (object, properties[PROP_COMPONENT]);
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_task_eds_class_init (GtdTaskEdsClass *klass)
+{
+  GtdObjectClass *gtd_object_class = GTD_OBJECT_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtdTaskClass *task_class = GTD_TASK_CLASS (klass);
+
+  object_class->finalize = gtd_task_eds_finalize;
+  object_class->get_property = gtd_task_eds_get_property;
+  object_class->set_property = gtd_task_eds_set_property;
+
+  task_class->get_complete = gtd_task_eds_get_complete;
+  task_class->set_complete = gtd_task_eds_set_complete;
+  task_class->get_creation_date = gtd_task_eds_get_creation_date;
+  task_class->get_description = gtd_task_eds_get_description;
+  task_class->set_description = gtd_task_eds_set_description;
+  task_class->get_due_date = gtd_task_eds_get_due_date;
+  task_class->set_due_date = gtd_task_eds_set_due_date;
+  task_class->get_priority = gtd_task_eds_get_priority;
+  task_class->set_priority = gtd_task_eds_set_priority;
+  task_class->get_title = gtd_task_eds_get_title;
+  task_class->set_title = gtd_task_eds_set_title;
+  task_class->subtask_added = gtd_task_eds_subtask_added;
+  task_class->subtask_removed = gtd_task_eds_subtask_removed;
+
+  gtd_object_class->get_uid = gtd_task_eds_get_uid;
+  gtd_object_class->set_uid = gtd_task_eds_set_uid;
+
+  properties[PROP_COMPONENT] = g_param_spec_object ("component",
+                                                    "Component",
+                                                    "Component",
+                                                    E_TYPE_CAL_COMPONENT,
+                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gtd_task_eds_init (GtdTaskEds *self)
+{
+}
+
+GtdTask*
+gtd_task_eds_new (ECalComponent *component)
+{
+  return g_object_new (GTD_TYPE_TASK_EDS,
+                       "component", component,
+                       NULL);
+}
+
+ECalComponent*
+gtd_task_eds_get_component (GtdTaskEds *self)
+{
+  g_return_val_if_fail (GTD_IS_TASK_EDS (self), NULL);
+
+  return self->component;
+}
diff --git a/plugins/eds/gtd-task-eds.h b/plugins/eds/gtd-task-eds.h
new file mode 100644
index 0000000..3009672
--- /dev/null
+++ b/plugins/eds/gtd-task-eds.h
@@ -0,0 +1,39 @@
+/* gtd-task-eds.h
+ *
+ * Copyright (C) 2017 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_TASK_EDS_H
+#define GTD_TASK_EDS_H
+
+#include "gnome-todo.h"
+
+#include <libecal/libecal.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_TASK_EDS (gtd_task_eds_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdTaskEds, gtd_task_eds, GTD, TASK_EDS, GtdTask)
+
+GtdTask*             gtd_task_eds_new                            (ECalComponent      *component);
+
+ECalComponent*       gtd_task_eds_get_component                  (GtdTaskEds         *self);
+
+G_END_DECLS
+
+#endif /* GTD_TASK_EDS_H */
+
diff --git a/plugins/eds/gtd-task-list-eds.c b/plugins/eds/gtd-task-list-eds.c
index 677c6a0..a9594b8 100644
--- a/plugins/eds/gtd-task-list-eds.c
+++ b/plugins/eds/gtd-task-list-eds.c
@@ -16,6 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "gtd-task-eds.h"
 #include "gtd-task-list-eds.h"
 
 #include <glib/gi18n.h>
@@ -26,9 +27,17 @@ struct _GtdTaskListEds
 
   ESource            *source;
 
+  GPtrArray           *pending_subtasks;
+
   GCancellable       *cancellable;
 };
 
+typedef struct
+{
+  GtdTask            *child;
+  gchar              *parent_uid;
+} PendingSubtaskData;
+
 G_DEFINE_TYPE (GtdTaskListEds, gtd_task_list_eds, GTD_TYPE_TASK_LIST)
 
 enum {
@@ -37,6 +46,90 @@ enum {
   N_PROPS
 };
 
+
+/*
+ * Auxiliary methods
+ */
+
+static PendingSubtaskData*
+pending_subtask_data_new (GtdTask     *child,
+                          const gchar *parent_uid)
+{
+  PendingSubtaskData *data;
+
+  data = g_new0 (PendingSubtaskData, 1);
+  data->child = child;
+  data->parent_uid = g_strdup (parent_uid);
+
+  return data;
+}
+
+static void
+pending_subtask_data_free (PendingSubtaskData *data)
+{
+  g_free (data->parent_uid);
+  g_free (data);
+}
+
+static void
+setup_parent_task (GtdTaskListEds *self,
+                   GtdTask        *task)
+{
+  ECalComponent *component;
+  icalcomponent *ical_comp;
+  icalproperty *property;
+  GtdTask *parent_task;
+  const gchar *parent_uid;
+
+  component = gtd_task_eds_get_component (GTD_TASK_EDS (task));
+  ical_comp = e_cal_component_get_icalcomponent (component);
+  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
+
+  if (!property)
+    return;
+
+  parent_uid = icalproperty_get_relatedto (property);
+  parent_task = gtd_task_list_get_task_by_id (GTD_TASK_LIST (self), parent_uid);
+
+  if (parent_task)
+    {
+      gtd_task_add_subtask (parent_task, task);
+    }
+  else
+    {
+      PendingSubtaskData *data;
+
+      data = pending_subtask_data_new (task, parent_uid);
+
+      g_ptr_array_add (self->pending_subtasks, data);
+    }
+}
+
+static void
+process_pending_subtasks (GtdTaskListEds *self,
+                          GtdTask        *task)
+{
+  const gchar *uid;
+  guint i;
+
+  uid = gtd_object_get_uid (GTD_OBJECT (task));
+
+  for (i = 0; i < self->pending_subtasks->len; i++)
+    {
+      PendingSubtaskData *data;
+
+      data = g_ptr_array_index (self->pending_subtasks, i);
+
+      if (g_strcmp0 (uid, data->parent_uid) == 0)
+        {
+          gtd_task_add_subtask (task, data->child);
+          g_ptr_array_remove (self->pending_subtasks, data);
+          i--;
+        }
+    }
+}
+
+
 static void
 source_removable_changed (GtdTaskListEds *list)
 {
@@ -123,6 +216,25 @@ string_to_color (GBinding     *binding,
   return TRUE;
 }
 
+
+/*
+ * GtdTaskList overrides
+ */
+static void
+gtd_task_list_eds_task_added (GtdTaskList *list,
+                              GtdTask     *task)
+{
+  GtdTaskListEds *self = GTD_TASK_LIST_EDS (list);
+
+  process_pending_subtasks (self, task);
+  setup_parent_task (self, task);
+}
+
+
+/*
+ * GObject overrides
+ */
+
 static void
 gtd_task_list_eds_finalize (GObject *object)
 {
@@ -178,6 +290,9 @@ static void
 gtd_task_list_eds_class_init (GtdTaskListEdsClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtdTaskListClass *task_list_class = GTD_TASK_LIST_CLASS (klass);
+
+  task_list_class->task_added = gtd_task_list_eds_task_added;
 
   object_class->finalize = gtd_task_list_eds_finalize;
   object_class->get_property = gtd_task_list_eds_get_property;
@@ -200,6 +315,7 @@ gtd_task_list_eds_class_init (GtdTaskListEdsClass *klass)
 static void
 gtd_task_list_eds_init (GtdTaskListEds *self)
 {
+  self->pending_subtasks = g_ptr_array_new_with_free_func ((GDestroyNotify) pending_subtask_data_free);
 }
 
 GtdTaskListEds*
diff --git a/plugins/eds/meson.build b/plugins/eds/meson.build
index 6f4cb0d..458e3c5 100644
--- a/plugins/eds/meson.build
+++ b/plugins/eds/meson.build
@@ -7,20 +7,21 @@ sources = files(
   'gtd-provider-' + plugin_name + '.c',
   'gtd-provider-goa.c',
   'gtd-provider-local.c',
-  'gtd-task-list-' + plugin_name + '.c'
+  'gtd-task-' + plugin_name + '.c',
+  'gtd-task-list-' + plugin_name + '.c',
 )
 
 plugins_libs += static_library(
-  plugin_name,
-  sources: sources,
+          plugin_name,
+              sources: sources,
   include_directories: plugins_incs,
-  dependencies: gnome_todo_deps
+         dependencies: gnome_todo_deps
 )
 
 plugin_data = plugin_name + '.plugin'
 
 plugins_confs += configure_file(
-  input: plugin_data + '.in',
-  output: plugin_data,
+          input: plugin_data + '.in',
+         output: plugin_data,
   configuration: plugins_conf
 )
diff --git a/plugins/todo-txt/gtd-provider-todo-txt.c b/plugins/todo-txt/gtd-provider-todo-txt.c
index 50421d9..4f69896 100644
--- a/plugins/todo-txt/gtd-provider-todo-txt.c
+++ b/plugins/todo-txt/gtd-provider-todo-txt.c
@@ -205,22 +205,6 @@ create_list (GtdProviderTodoTxt *self,
   return task_list;
 }
 
-GtdTask*
-create_task (void)
-{
-  ECalComponent *component;
-  GtdTask *task;
-
-  component = e_cal_component_new ();
-
-  e_cal_component_set_new_vtype (component, E_CAL_COMPONENT_TODO);
-  e_cal_component_set_uid (component, e_cal_component_gen_uid ());
-
-  task = gtd_task_new (component);
-
-  return task;
-}
-
 static void
 gtd_provider_todo_txt_load_tasks (GtdProviderTodoTxt *self)
 {
@@ -305,7 +289,7 @@ gtd_provider_todo_txt_load_tasks (GtdProviderTodoTxt *self)
                 }
               else
                 {
-                  parent_task = create_task ();
+                  parent_task = gtd_task_new ();
                   gtd_task_set_list (parent_task, list);
                   gtd_task_set_title (parent_task, g_object_get_data (G_OBJECT (task), "root_task_name"));
 
diff --git a/plugins/todo-txt/gtd-todo-txt-parser.c b/plugins/todo-txt/gtd-todo-txt-parser.c
index 0e03458..012a50e 100644
--- a/plugins/todo-txt/gtd-todo-txt-parser.c
+++ b/plugins/todo-txt/gtd-todo-txt-parser.c
@@ -175,7 +175,7 @@ gtd_todo_txt_parser_parse_tokens (GList *tokens)
 
   dt = NULL;
   l = NULL;
-  task = create_task ();
+  task = gtd_task_new ();
   list_name = g_string_new (NULL);
   title = g_string_new (NULL);
   root_task_name = g_string_new (NULL);
diff --git a/plugins/todoist/gtd-provider-todoist.c b/plugins/todoist/gtd-provider-todoist.c
index d55ff27..5d07549 100644
--- a/plugins/todoist/gtd-provider-todoist.c
+++ b/plugins/todoist/gtd-provider-todoist.c
@@ -331,7 +331,6 @@ parse_array_to_task (GtdProviderTodoist *self,
     {
       JsonObject *object;
       GtdTaskList *list;
-      ECalComponent *component;
       GtdTask *task;
       const gchar *title;
       const gchar *due_date;
@@ -341,10 +340,6 @@ parse_array_to_task (GtdProviderTodoist *self,
       gint priority;
       guint is_complete;
 
-      component = e_cal_component_new ();
-      e_cal_component_set_new_vtype (component, E_CAL_COMPONENT_TODO);
-      e_cal_component_set_uid (component, e_cal_component_gen_uid ());
-
       object = json_node_get_object (l->data);
 
       title = json_object_get_string_member (object, "content");
@@ -358,7 +353,7 @@ parse_array_to_task (GtdProviderTodoist *self,
       uid = g_strdup_printf ("%u", id);
 
       /* Setup the task */
-      task = gtd_task_new (component);
+      task = gtd_task_new ();
       gtd_object_set_uid (GTD_OBJECT (task), uid);
       gtd_task_set_title (task, title);
       gtd_task_set_list (task, list);
diff --git a/src/gtd-dnd-row.c b/src/gtd-dnd-row.c
index 11c9a4a..5b4520d 100644
--- a/src/gtd-dnd-row.c
+++ b/src/gtd-dnd-row.c
@@ -278,8 +278,6 @@ gtd_dnd_row_drag_drop (GtkWidget      *widget,
 
   /* Save the task */
   provider = gtd_task_list_get_provider (gtd_task_get_list (row_task));
-
-  gtd_task_save (row_task);
   gtd_provider_update_task (provider, row_task);
 
   gtk_list_box_invalidate_sort (GTK_LIST_BOX (gtk_widget_get_parent (widget)));
diff --git a/src/gtd-edit-pane.c b/src/gtd-edit-pane.c
index dacedd7..e007031 100644
--- a/src/gtd-edit-pane.c
+++ b/src/gtd-edit-pane.c
@@ -75,12 +75,7 @@ gtd_edit_pane__no_date_button_clicked (GtkButton   *button,
 static void
 save_task (GtdEditPane *self)
 {
-  GtdManager *manager;
-
-  manager = gtd_manager_get_default ();
-
-  gtd_task_save (self->task);
-  gtd_manager_update_task (manager, self->task);
+  gtd_manager_update_task (gtd_manager_get_default (), self->task);
 }
 
 static void
diff --git a/src/gtd-new-task-row.c b/src/gtd-new-task-row.c
index f004ed8..8c3b9c5 100644
--- a/src/gtd-new-task-row.c
+++ b/src/gtd-new-task-row.c
@@ -142,9 +142,8 @@ entry_activated_cb (GtdNewTaskRow *self)
   if (gtk_entry_get_text_length (self->entry) == 0)
     return;
 
-  new_task = gtd_task_new (NULL);
+  new_task = gtd_task_new ();
   gtd_task_set_title (new_task, gtk_entry_get_text (self->entry));
-  gtd_task_save (new_task);
 
   g_signal_emit (self, signals[CREATE_TASK], 0, new_task, self->selected_tasklist);
 
diff --git a/src/gtd-task-list.c b/src/gtd-task-list.c
index 1bff3b7..956cd72 100644
--- a/src/gtd-task-list.c
+++ b/src/gtd-task-list.c
@@ -36,18 +36,11 @@
 
 typedef struct
 {
-  gchar   *parent_uid;
-  GtdTask *child;
-} PendingSubtaskData;
-
-typedef struct
-{
   GList               *tasks;
   GtdProvider         *provider;
   GdkRGBA             *color;
 
   GHashTable          *uid_to_task;
-  GPtrArray           *pending_subtasks;
 
   gchar               *name;
   gboolean             removable : 1;
@@ -75,91 +68,6 @@ enum
   LAST_PROP
 };
 
-static PendingSubtaskData*
-pending_subtask_data_new (GtdTaskList *self,
-                          GtdTask     *child,
-                          const gchar *parent_uid)
-{
-  PendingSubtaskData *data;
-
-  data = g_new0 (PendingSubtaskData, 1);
-  data->child = child;
-  data->parent_uid = g_strdup (parent_uid);
-
-  return data;
-}
-
-static void
-pending_subtask_data_free (PendingSubtaskData *data)
-{
-  g_free (data->parent_uid);
-  g_free (data);
-}
-
-static void
-setup_parent_task (GtdTaskList *self,
-                   GtdTask     *task)
-{
-  GtdTaskListPrivate *priv;
-  ECalComponent *component;
-  icalcomponent *ical_comp;
-  icalproperty *property;
-  GtdTask *parent_task;
-  const gchar *parent_uid;
-
-  priv = gtd_task_list_get_instance_private (self);
-  component = gtd_task_get_component (task);
-  ical_comp = e_cal_component_get_icalcomponent (component);
-  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
-
-  if (!property)
-    return;
-
-  parent_uid = icalproperty_get_relatedto (property);
-  parent_task = g_hash_table_lookup (priv->uid_to_task, parent_uid);
-
-  if (parent_task)
-    {
-      gtd_task_add_subtask (parent_task, task);
-    }
-  else
-    {
-      PendingSubtaskData *data;
-
-      data = pending_subtask_data_new (self, task, parent_uid);
-
-      g_ptr_array_add (priv->pending_subtasks, data);
-    }
-}
-
-static void
-process_pending_subtasks (GtdTaskList *self,
-                          GtdTask     *task)
-{
-  GtdTaskListPrivate *priv;
-  ECalComponentId *id;
-  guint i;
-
-  priv = gtd_task_list_get_instance_private (self);
-  id = e_cal_component_get_id (gtd_task_get_component (task));
-
-  for (i = 0; i < priv->pending_subtasks->len; i++)
-    {
-      PendingSubtaskData *data;
-
-      data = g_ptr_array_index (priv->pending_subtasks, i);
-
-      if (g_strcmp0 (id->uid, data->parent_uid) == 0)
-        {
-          gtd_task_add_subtask (task, data->child);
-          g_ptr_array_remove (priv->pending_subtasks, data);
-          i--;
-        }
-    }
-
-  e_cal_component_free_id (id);
-}
-
 static void
 task_changed_cb (GtdTask     *task,
                  GParamSpec  *pspec,
@@ -174,7 +82,6 @@ gtd_task_list_finalize (GObject *object)
   GtdTaskList *self = (GtdTaskList*) object;
   GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self);
 
-  g_clear_object (&priv->pending_subtasks);
   g_clear_object (&priv->provider);
 
   g_clear_pointer (&priv->uid_to_task, g_hash_table_destroy);
@@ -385,8 +292,6 @@ gtd_task_list_init (GtdTaskList *self)
                                              g_str_equal,
                                              g_free,
                                              NULL);
-
-  priv->pending_subtasks = g_ptr_array_new_with_free_func ((GDestroyNotify) pending_subtask_data_free);
 }
 
 /**
@@ -595,15 +500,13 @@ gtd_task_list_save_task (GtdTaskList *list,
     }
   else
     {
-      ECalComponentId *id;
+      const gchar *uid;
 
-      id = e_cal_component_get_id (gtd_task_get_component (task));
+      uid = gtd_object_get_uid (GTD_OBJECT (task));
 
-      priv->tasks = g_list_append (priv->tasks, task);
+      priv->tasks = g_list_prepend (priv->tasks, task);
 
-      g_hash_table_insert (priv->uid_to_task, g_strdup (id->uid), task);
-      process_pending_subtasks (list, task);
-      setup_parent_task (list, task);
+      g_hash_table_insert (priv->uid_to_task, g_strdup (uid), task);
 
       g_signal_connect (task,
                         "notify",
@@ -611,8 +514,6 @@ gtd_task_list_save_task (GtdTaskList *list,
                         list);
 
       g_signal_emit (list, signals[TASK_ADDED], 0, task);
-
-      e_cal_component_free_id (id);
     }
 }
 
@@ -715,3 +616,25 @@ gtd_task_list_set_is_removable (GtdTaskList *list,
       g_object_notify (G_OBJECT (list), "is-removable");
     }
 }
+
+/**
+ * gtd_task_list_get_task_by_id:
+ * @list: a #GtdTaskList
+ * @id: the id of the task
+ *
+ * Retrieves a task from @self with the given @id.
+ *
+ * Returns: (transfer none)(nullable): a #GtdTask, or %NULL
+ */
+GtdTask*
+gtd_task_list_get_task_by_id (GtdTaskList *self,
+                              const gchar *id)
+{
+  GtdTaskListPrivate *priv;
+
+  g_return_val_if_fail (GTD_IS_TASK_LIST (self), NULL);
+
+  priv = gtd_task_list_get_instance_private (self);
+
+  return g_hash_table_lookup (priv->uid_to_task, id);
+}
diff --git a/src/gtd-task-list.h b/src/gtd-task-list.h
index 6528240..8550dd5 100644
--- a/src/gtd-task-list.h
+++ b/src/gtd-task-list.h
@@ -80,6 +80,9 @@ void                    gtd_task_list_remove_task               (GtdTaskList
 gboolean                gtd_task_list_contains                  (GtdTaskList            *list,
                                                                  GtdTask                *task);
 
+GtdTask*                gtd_task_list_get_task_by_id            (GtdTaskList            *self,
+                                                                 const gchar            *id);
+
 G_END_DECLS
 
 #endif /* GTD_TASK_LIST_H */
diff --git a/src/gtd-task.c b/src/gtd-task.c
index 1143dad..5a8a417 100644
--- a/src/gtd-task.c
+++ b/src/gtd-task.c
@@ -20,9 +20,6 @@
 #include "gtd-task-list.h"
 
 #include <glib/gi18n.h>
-#include <libecal/libecal.h>
-#include <libical/icaltime.h>
-#include <libical/icaltimezone.h>
 
 /**
  * SECTION:gtd-task
@@ -39,10 +36,17 @@ typedef struct
 {
   gchar           *description;
   GtdTaskList     *list;
-  ECalComponent   *component;
   GtdTask         *parent;
   GList           *subtasks;
   gint             depth;
+
+  GDateTime       *creation_date;
+  GDateTime       *due_date;
+
+  gchar           *title;
+
+  gint32           priority;
+  gboolean         complete : 1;
 } GtdTaskPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtdTask, gtd_task, GTD_TYPE_OBJECT)
@@ -51,7 +55,6 @@ enum
 {
   PROP_0,
   PROP_COMPLETE,
-  PROP_COMPONENT,
   PROP_DEPTH,
   PROP_DESCRIPTION,
   PROP_CREATION_DATE,
@@ -178,25 +181,6 @@ compare_by_subtasks (GtdTask **t1,
   return 0;
 }
 
-
-static GDateTime*
-gtd_task__convert_icaltime (const icaltimetype *date)
-{
-  GDateTime *dt;
-
-  if (!date)
-    return NULL;
-
-  dt = g_date_time_new_utc (date->year,
-                            date->month,
-                            date->day,
-                            date->is_date ? 0 : date->hour,
-                            date->is_date ? 0 : date->minute,
-                            date->is_date ? 0 : date->second);
-
-  return dt;
-}
-
 static void
 set_depth (GtdTask *self,
            gint     depth)
@@ -216,32 +200,18 @@ real_add_subtask (GtdTask *self,
                   GtdTask *subtask)
 {
   GtdTaskPrivate *priv, *subtask_priv;
-  ECalComponentId *id;
-  ECalComponent *comp;
-  icalcomponent *ical_comp;
-  icalproperty *property;
 
   priv = gtd_task_get_instance_private (self);
 
   if (g_list_find (priv->subtasks, subtask))
     return;
 
-  id = e_cal_component_get_id (priv->component);
   subtask_priv = gtd_task_get_instance_private (subtask);
-  comp = subtask_priv->component;
 
   /* First, remove the subtask from it's parent's subtasks list */
   if (subtask_priv->parent)
     gtd_task_remove_subtask (subtask_priv->parent, subtask);
 
-  ical_comp = e_cal_component_get_icalcomponent (comp);
-  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
-
-  if (property)
-    icalproperty_set_relatedto (property, id->uid);
-  else
-    icalcomponent_add_property (ical_comp, icalproperty_new_relatedto (id->uid));
-
   /* Add to this task's list of subtasks */
   priv->subtasks = g_list_prepend (priv->subtasks, subtask);
 
@@ -251,8 +221,6 @@ real_add_subtask (GtdTask *self,
 
   /* And also the task's depth */
   set_depth (subtask, priv->depth + 1);
-
-  e_cal_component_free_id (id);
 }
 
 static void
@@ -260,8 +228,6 @@ real_remove_subtask (GtdTask *self,
                      GtdTask *subtask)
 {
   GtdTaskPrivate *priv, *subtask_priv;
-  icalcomponent *ical_comp;
-  icalproperty *property;
 
   priv = gtd_task_get_instance_private (self);
 
@@ -270,15 +236,6 @@ real_remove_subtask (GtdTask *self,
 
   subtask_priv = gtd_task_get_instance_private (subtask);
 
-  /* Remove the parent link from the subtask's component */
-  ical_comp = e_cal_component_get_icalcomponent (subtask_priv->component);
-  property = icalcomponent_get_first_property (ical_comp, ICAL_RELATEDTO_PROPERTY);
-
-  if (!property)
-    return;
-
-  icalcomponent_remove_property (ical_comp, property);
-
   /* Add to this task's list of subtasks */
   priv->subtasks = g_list_remove (priv->subtasks, subtask);
 
@@ -299,58 +256,124 @@ task_list_weak_notified (gpointer  data,
   priv->list = NULL;
 }
 
-static void
-gtd_task_finalize (GObject *object)
+/*
+ * GtdTask default implementations
+ */
+
+static gboolean
+gtd_task_real_get_complete (GtdTask *self)
 {
-  GtdTask *self = (GtdTask*) object;
   GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  if (priv->list)
-    g_object_weak_unref (G_OBJECT (priv->list), task_list_weak_notified, self);
+  return priv->complete;
+}
 
-  priv->list = NULL;
-  g_free (priv->description);
-  g_object_unref (priv->component);
+static void
+gtd_task_real_set_complete (GtdTask  *self,
+                            gboolean  complete)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  G_OBJECT_CLASS (gtd_task_parent_class)->finalize (object);
+  priv->complete = complete;
+}
+
+static GDateTime*
+gtd_task_real_get_creation_date (GtdTask *self)
+{
+  return NULL;
 }
 
 static const gchar*
-gtd_task__get_uid (GtdObject *object)
+gtd_task_real_get_description (GtdTask *self)
 {
-  GtdTaskPrivate *priv = gtd_task_get_instance_private (GTD_TASK (object));
-  const gchar *uid;
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  g_return_val_if_fail (GTD_IS_TASK (object), NULL);
+  return priv->description ? priv->description : "";
+}
 
-  if (priv->component)
-    e_cal_component_get_uid (priv->component, &uid);
-  else
-    uid = NULL;
+static void
+gtd_task_real_set_description (GtdTask     *self,
+                               const gchar *description)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  return uid;
+  g_clear_pointer (&priv->description, g_free);
+  priv->description = g_strdup (description);
+}
+
+static GDateTime*
+gtd_task_real_get_due_date (GtdTask *self)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
+
+  return priv->due_date;
 }
 
 static void
-gtd_task__set_uid (GtdObject   *object,
-                   const gchar *uid)
+gtd_task_real_set_due_date (GtdTask   *self,
+                            GDateTime *due_date)
 {
-  GtdTaskPrivate *priv = gtd_task_get_instance_private (GTD_TASK (object));
-  const gchar *current_uid;
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  g_return_if_fail (GTD_IS_TASK (object));
+  g_clear_pointer (&priv->due_date, g_date_time_unref);
 
-  if (!priv->component)
-    return;
+  if (due_date)
+    priv->due_date = g_date_time_ref (due_date);
+}
 
-  e_cal_component_get_uid (priv->component, &current_uid);
+static gint32
+gtd_task_real_get_priority (GtdTask *self)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
-  if (g_strcmp0 (current_uid, uid) != 0)
-    {
-      e_cal_component_set_uid (priv->component, uid);
+  return priv->priority;
+}
 
-      g_object_notify (G_OBJECT (object), "uid");
-    }
+static void
+gtd_task_real_set_priority (GtdTask *self,
+                            gint32   priority)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
+
+  priv->priority = priority;
+}
+
+static const gchar*
+gtd_task_real_get_title (GtdTask *self)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
+
+  return priv->title;
+}
+
+static void
+gtd_task_real_set_title (GtdTask     *self,
+                         const gchar *title)
+{
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
+
+  g_clear_pointer (&priv->title, g_free);
+  priv->title = title ? g_strdup (title) : NULL;
+}
+
+
+/*
+ * GObject overrides
+ */
+
+static void
+gtd_task_finalize (GObject *object)
+{
+  GtdTask *self = (GtdTask*) object;
+  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
+
+  if (priv->list)
+    g_object_weak_unref (G_OBJECT (priv->list), task_list_weak_notified, self);
+
+  priv->list = NULL;
+  g_free (priv->description);
+
+  G_OBJECT_CLASS (gtd_task_parent_class)->finalize (object);
 }
 
 static void
@@ -369,10 +392,6 @@ gtd_task_get_property (GObject    *object,
       g_value_set_boolean (value, gtd_task_get_complete (self));
       break;
 
-    case PROP_COMPONENT:
-      g_value_set_object (value, priv->component);
-      break;
-
     case PROP_CREATION_DATE:
       g_value_set_boxed (value, gtd_task_get_creation_date (self));
       break;
@@ -419,7 +438,6 @@ gtd_task_set_property (GObject      *object,
                        GParamSpec   *pspec)
 {
   GtdTask *self = GTD_TASK (object);
-  GtdTaskPrivate *priv = gtd_task_get_instance_private (self);
 
   switch (prop_id)
     {
@@ -427,21 +445,6 @@ gtd_task_set_property (GObject      *object,
       gtd_task_set_complete (self, g_value_get_boolean (value));
       break;
 
-    case PROP_COMPONENT:
-      priv->component = g_value_get_object (value);
-
-      if (!priv->component)
-        {
-          priv->component = e_cal_component_new ();
-          e_cal_component_set_new_vtype (priv->component, E_CAL_COMPONENT_TODO);
-        }
-      else
-        {
-          g_object_ref (priv->component);
-        }
-
-      break;
-
     case PROP_DESCRIPTION:
       gtd_task_set_description (self, g_value_get_string (value));
       break;
@@ -471,8 +474,18 @@ static void
 gtd_task_class_init (GtdTaskClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtdObjectClass *obj_class = GTD_OBJECT_CLASS (klass);
 
+  klass->get_complete = gtd_task_real_get_complete;
+  klass->set_complete = gtd_task_real_set_complete;
+  klass->get_creation_date = gtd_task_real_get_creation_date;
+  klass->get_description = gtd_task_real_get_description;
+  klass->set_description = gtd_task_real_set_description;
+  klass->get_due_date = gtd_task_real_get_due_date;
+  klass->set_due_date = gtd_task_real_set_due_date;
+  klass->get_priority = gtd_task_real_get_priority;
+  klass->set_priority = gtd_task_real_set_priority;
+  klass->get_title = gtd_task_real_get_title;
+  klass->set_title = gtd_task_real_set_title;
   klass->subtask_added = real_add_subtask;
   klass->subtask_removed = real_remove_subtask;
 
@@ -480,9 +493,6 @@ gtd_task_class_init (GtdTaskClass *klass)
   object_class->get_property = gtd_task_get_property;
   object_class->set_property = gtd_task_set_property;
 
-  obj_class->get_uid = gtd_task__get_uid;
-  obj_class->set_uid = gtd_task__set_uid;
-
   /**
    * GtdTask::complete:
    *
@@ -499,20 +509,6 @@ gtd_task_class_init (GtdTaskClass *klass)
                               G_PARAM_READWRITE));
 
   /**
-   * GtdTask::component:
-   *
-   * The #ECalComponent of the task.
-   */
-  g_object_class_install_property (
-        object_class,
-        PROP_COMPONENT,
-        g_param_spec_object ("component",
-                              "Component of the task",
-                              "The #ECalComponent this task handles.",
-                              E_TYPE_CAL_COMPONENT,
-                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-  /**
    * GtdTask::creation-date:
    *
    * The @GDateTime that represents the time in which the task was created.
@@ -666,78 +662,35 @@ gtd_task_class_init (GtdTaskClass *klass)
 static void
 gtd_task_init (GtdTask *self)
 {
-  ;
 }
 
 /**
  * gtd_task_new:
- * @component: (nullable): a #ECalComponent
  *
  * Creates a new #GtdTask
  *
  * Returns: (transfer full): a #GtdTask
  */
 GtdTask *
-gtd_task_new (ECalComponent *component)
+gtd_task_new (void)
 {
-  const gchar *uid;
-
-  if (component)
-    e_cal_component_get_uid (component, &uid);
-  else
-    uid = NULL;
-
-  return g_object_new (GTD_TYPE_TASK,
-                       "component", component,
-                       NULL);
+  return g_object_new (GTD_TYPE_TASK, NULL);
 }
 
 /**
  * gtd_task_get_complete:
- * @task: a #GtdTask
+ * @self: a #GtdTask
  *
  * Retrieves whether the task is complete or not.
  *
  * Returns: %TRUE if the task is complete, %FALSE otherwise
  */
 gboolean
-gtd_task_get_complete (GtdTask *task)
+gtd_task_get_complete (GtdTask *self)
 {
-  GtdTaskPrivate *priv;
-  icaltimetype *dt;
-  gboolean completed;
-
-  g_return_val_if_fail (GTD_IS_TASK (task), FALSE);
-
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_get_completed (priv->component, &dt);
-  completed = (dt != NULL);
-
-  if (dt)
-    e_cal_component_free_icaltimetype (dt);
-
-  return completed;
-}
-
-/**
- * gtd_task_get_component:
- * @task: a #GtdTask
- *
- * Retrieves the internal #ECalComponent of @task.
- *
- * Returns: (transfer none): a #ECalComponent
- */
-ECalComponent*
-gtd_task_get_component (GtdTask *task)
-{
-  GtdTaskPrivate *priv;
-
-  g_return_val_if_fail (GTD_IS_TASK (task), NULL);
-
-  priv = gtd_task_get_instance_private (task);
+  g_return_val_if_fail (GTD_IS_TASK (self), FALSE);
 
-  return priv->component;
+  return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (self))->get_complete (self);
 }
 
 /**
@@ -751,61 +704,14 @@ void
 gtd_task_set_complete (GtdTask  *task,
                        gboolean  complete)
 {
-  GtdTaskPrivate *priv;
-
   g_assert (GTD_IS_TASK (task));
 
-  priv = gtd_task_get_instance_private (task);
-
-  if (gtd_task_get_complete (task) != complete)
-    {
-      icaltimetype *dt;
-      icalproperty_status status;
-      gint percent;
-
-      if (complete)
-        {
-          GDateTime *now = g_date_time_new_now_utc ();
-
-          percent = 100;
-          status = ICAL_STATUS_COMPLETED;
-
-          dt = g_new0 (icaltimetype, 1);
-          dt->year = g_date_time_get_year (now);
-          dt->month = g_date_time_get_month (now);
-          dt->day = g_date_time_get_day_of_month (now);
-          dt->hour = g_date_time_get_hour (now);
-          dt->minute = g_date_time_get_minute (now);
-          dt->second = g_date_time_get_seconds (now);
-          dt->is_date = 0;
-          dt->is_utc = 1;
-
-          /* convert timezone
-           *
-           * FIXME: This does not do anything until we have an ical
-           * timezone associated with the task
-           */
-          icaltimezone_convert_time (dt,
-                                     NULL,
-                                     icaltimezone_get_utc_timezone ());
-          g_date_time_unref (now);
-        }
-      else
-        {
-          dt = NULL;
-          percent = 0;
-          status = ICAL_STATUS_NEEDSACTION;
-        }
-
-      e_cal_component_set_percent_as_int (priv->component, percent);
-      e_cal_component_set_status (priv->component, status);
-      e_cal_component_set_completed (priv->component, dt);
+  if (gtd_task_get_complete (task) == complete)
+    return;
 
-      if (dt)
-        e_cal_component_free_icaltimetype (dt);
+  GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_complete (task, complete);
 
-      g_object_notify (G_OBJECT (task), "complete");
-    }
+  g_object_notify (G_OBJECT (task), "complete");
 }
 
 /**
@@ -822,24 +728,9 @@ gtd_task_set_complete (GtdTask  *task,
 GDateTime*
 gtd_task_get_creation_date (GtdTask *task)
 {
-  GtdTaskPrivate *priv;
-  icaltimetype *idt;
-  GDateTime *dt;
-
   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
-  priv = gtd_task_get_instance_private (task);
-
-  idt = NULL;
-  dt = NULL;
 
-  e_cal_component_get_created (priv->component, &idt);
-
-  if (idt)
-    dt = gtd_task__convert_icaltime (idt);
-
-  g_clear_pointer (&idt, e_cal_component_free_icaltimetype);
-
-  return dt;
+  return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_creation_date (task);
 }
 
 /**
@@ -853,52 +744,9 @@ gtd_task_get_creation_date (GtdTask *task)
 const gchar*
 gtd_task_get_description (GtdTask *task)
 {
-  GtdTaskPrivate *priv;
-  GSList *text_list;
-  GSList *l;
-  gchar *desc = NULL;
-
   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
 
-  priv = gtd_task_get_instance_private (task);
-
-  /* concatenates the multiple descriptions a task may have */
-  e_cal_component_get_description_list (priv->component, &text_list);
-  for (l = text_list; l != NULL; l = l->next)
-    {
-      if (l->data != NULL)
-        {
-          ECalComponentText *text;
-          gchar *carrier;
-          text = l->data;
-
-          if (desc != NULL)
-            {
-              carrier = g_strconcat (desc,
-                                     "\n",
-                                     text->value,
-                                     NULL);
-              g_free (desc);
-              desc = carrier;
-            }
-          else
-            {
-              desc = g_strdup (text->value);
-            }
-        }
-    }
-
-  if (g_strcmp0 (priv->description, desc) != 0)
-    {
-      g_clear_pointer (&priv->description, g_free);
-
-      priv->description = g_strdup (desc);
-    }
-
-  g_free (desc);
-  e_cal_component_free_text_list (text_list);
-
-  return priv->description ? priv->description : "";
+  return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_description (task);
 }
 
 /**
@@ -920,25 +768,12 @@ gtd_task_set_description (GtdTask     *task,
 
   priv = gtd_task_get_instance_private (task);
 
-  if (g_strcmp0 (priv->description, description) != 0)
-    {
-      GSList note;
-      ECalComponentText text;
-
-      g_clear_pointer (&priv->description, g_free);
-
-      priv->description = g_strdup (description);
-
-      text.value = priv->description;
-      text.altrep = NULL;
-
-      note.data = &text;
-      note.next = NULL;
+  if (g_strcmp0 (priv->description, description) == 0)
+    return;
 
-      e_cal_component_set_description_list (priv->component, &note);
+  GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_description (task, description);
 
-      g_object_notify (G_OBJECT (task), "description");
-    }
+  g_object_notify (G_OBJECT (task), "description");
 }
 
 /**
@@ -955,19 +790,9 @@ gtd_task_set_description (GtdTask     *task,
 GDateTime*
 gtd_task_get_due_date (GtdTask *task)
 {
-  ECalComponentDateTime comp_dt;
-  GtdTaskPrivate *priv;
-  GDateTime *date;
-
   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
 
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_get_due (priv->component, &comp_dt);
-
-  date = gtd_task__convert_icaltime (comp_dt.value);
-  e_cal_component_free_datetime (&comp_dt);
-  return date;
+  return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_due_date (task);
 }
 
 /**
@@ -981,72 +806,19 @@ void
 gtd_task_set_due_date (GtdTask   *task,
                        GDateTime *dt)
 {
-  GtdTaskPrivate *priv;
-  GDateTime *current_dt;
+  g_autoptr (GDateTime) current_dt = NULL;
 
   g_assert (GTD_IS_TASK (task));
 
-  priv = gtd_task_get_instance_private (task);
-
   current_dt = gtd_task_get_due_date (task);
 
-  if (dt != current_dt)
-    {
-      ECalComponentDateTime comp_dt;
-      icaltimetype *idt;
-      gboolean changed = FALSE;
-
-      comp_dt.value = NULL;
-      comp_dt.tzid = NULL;
-      idt = NULL;
-
-      if (!current_dt ||
-          (current_dt &&
-           dt &&
-           g_date_time_compare (current_dt, dt) != 0))
-        {
-          idt = g_new0 (icaltimetype, 1);
-
-          g_date_time_ref (dt);
-
-          /* Copy the given dt */
-          idt->year = g_date_time_get_year (dt);
-          idt->month = g_date_time_get_month (dt);
-          idt->day = g_date_time_get_day_of_month (dt);
-          idt->hour = g_date_time_get_hour (dt);
-          idt->minute = g_date_time_get_minute (dt);
-          idt->second = g_date_time_get_seconds (dt);
-          idt->is_date = (idt->hour == 0 &&
-                          idt->minute == 0 &&
-                          idt->second == 0);
-
-          comp_dt.tzid = g_strdup ("UTC");
-
-          comp_dt.value = idt;
-
-          e_cal_component_set_due (priv->component, &comp_dt);
-
-          e_cal_component_free_datetime (&comp_dt);
-
-          g_date_time_unref (dt);
-
-          changed = TRUE;
-        }
-      else if (!dt)
-        {
-          idt = NULL;
-          comp_dt.tzid = NULL;
-
-          e_cal_component_set_due (priv->component, NULL);
-
-          changed = TRUE;
-        }
+  /* Don't do anything if the date is equal */
+  if (current_dt == dt || (current_dt && dt && g_date_time_equal (current_dt, dt)))
+    return;
 
-      if (changed)
-        g_object_notify (G_OBJECT (task), "due-date");
-    }
+  GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_due_date (task, dt);
 
-  g_clear_pointer (&current_dt, g_date_time_unref);
+  g_object_notify (G_OBJECT (task), "due-date");
 }
 
 /**
@@ -1112,24 +884,9 @@ gtd_task_set_list (GtdTask     *task,
 gint
 gtd_task_get_priority (GtdTask *task)
 {
-  GtdTaskPrivate *priv;
-  gint *priority = NULL;
-  gint p;
-
   g_assert (GTD_IS_TASK (task));
 
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_get_priority (priv->component, &priority);
-
-  if (!priority)
-    return -1;
-
-  p = *priority;
-
-  g_free (priority);
-
-  return p;
+  return GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_priority (task);
 }
 
 /**
@@ -1145,18 +902,16 @@ void
 gtd_task_set_priority (GtdTask *task,
                        gint     priority)
 {
-  GtdTaskPrivate *priv;
   gint current;
 
   g_assert (GTD_IS_TASK (task));
   g_assert (priority >= -1);
 
-  priv = gtd_task_get_instance_private (task);
   current = gtd_task_get_priority (task);
 
   if (priority != current)
     {
-      e_cal_component_set_priority (priv->component, &priority);
+      GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_priority (task, priority);
       g_object_notify (G_OBJECT (task), "priority");
     }
 }
@@ -1172,16 +927,13 @@ gtd_task_set_priority (GtdTask *task,
 const gchar*
 gtd_task_get_title (GtdTask *task)
 {
-  GtdTaskPrivate *priv;
-  ECalComponentText summary;
+  const gchar *title;
 
   g_return_val_if_fail (GTD_IS_TASK (task), NULL);
 
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_get_summary (priv->component, &summary);
+  title = GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->get_title (task);
 
-  return summary.value ? summary.value : "";
+  return title ? title : "";
 }
 
 /**
@@ -1196,64 +948,19 @@ void
 gtd_task_set_title (GtdTask     *task,
                     const gchar *title)
 {
-  GtdTaskPrivate *priv;
-  ECalComponentText summary;
+  const gchar *current_title;
 
   g_return_if_fail (GTD_IS_TASK (task));
   g_return_if_fail (g_utf8_validate (title, -1, NULL));
 
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_get_summary (priv->component, &summary);
+  current_title = gtd_task_get_title (task);
 
-  if (g_strcmp0 (summary.value, title) != 0)
-    {
-      ECalComponentText new_summary;
-
-      new_summary.value = title;
-      new_summary.altrep = NULL;
-
-      e_cal_component_set_summary (priv->component, &new_summary);
-
-      g_object_notify (G_OBJECT (task), "title");
-    }
-}
-
-/**
- * gtd_task_abort:
- * @task: a #GtdTask
- *
- * Cancels any editing made on @task after the latest
- * call of @gtd_task_save.
- */
-void
-gtd_task_abort (GtdTask *task)
-{
-  GtdTaskPrivate *priv;
-
-  g_return_if_fail (GTD_IS_TASK (task));
-
-  priv = gtd_task_get_instance_private (task);
-
-  e_cal_component_abort_sequence (priv->component);
-}
-
-/**
- * gtd_task_save:
- * @task: a #GtdTask
- *
- * Save any changes made on @task.
- */
-void
-gtd_task_save (GtdTask *task)
-{
-  GtdTaskPrivate *priv;
-
-  g_return_if_fail (GTD_IS_TASK (task));
+  if (g_strcmp0 (current_title, title) == 0)
+    return;
 
-  priv = gtd_task_get_instance_private (task);
+  GTD_TASK_CLASS (G_OBJECT_GET_CLASS (task))->set_title (task, title);
 
-  e_cal_component_commit_sequence (priv->component);
+  g_object_notify (G_OBJECT (task), "title");
 }
 
 /**
diff --git a/src/gtd-task.h b/src/gtd-task.h
index 4dfb710..81040aa 100644
--- a/src/gtd-task.h
+++ b/src/gtd-task.h
@@ -22,7 +22,6 @@
 #include "gtd-object.h"
 
 #include <glib-object.h>
-#include <libecal/libecal.h>
 
 G_BEGIN_DECLS
 
@@ -34,25 +33,46 @@ struct _GtdTaskClass
 {
   GtdObjectClass parent;
 
+  gboolean      (*get_complete)                       (GtdTask              *self);
+  void          (*set_complete)                       (GtdTask              *self,
+                                                       gboolean              complete);
+
+  GDateTime*    (*get_creation_date)                  (GtdTask              *self);
+
+  const gchar*  (*get_description)                    (GtdTask              *self);
+  void          (*set_description)                    (GtdTask              *self,
+                                                       const gchar          *description);
+
+  GDateTime*    (*get_due_date)                       (GtdTask              *self);
+  void          (*set_due_date)                       (GtdTask              *self,
+                                                       GDateTime            *due_date);
+
+  gint32        (*get_priority)                       (GtdTask              *self);
+  void          (*set_priority)                       (GtdTask              *self,
+                                                       gint32                priority);
+
+  const gchar*  (*get_title)                          (GtdTask              *self);
+  void          (*set_title)                          (GtdTask              *self,
+                                                       const gchar          *title);
+
   /*< signals >*/
+
   void          (*subtask_added)                      (GtdTask              *self,
                                                        GtdTask              *subtask);
 
   void          (*subtask_removed)                    (GtdTask              *self,
                                                        GtdTask              *subtask);
 
-  gpointer       padding[8];
+  gpointer       padding[6];
 };
 
-GtdTask*            gtd_task_new                      (ECalComponent        *component);
+GtdTask*            gtd_task_new                      (void);
 
 gboolean            gtd_task_get_complete             (GtdTask              *task);
 
 void                gtd_task_set_complete             (GtdTask              *task,
                                                        gboolean              complete);
 
-ECalComponent*      gtd_task_get_component            (GtdTask              *task);
-
 GDateTime*          gtd_task_get_creation_date        (GtdTask              *task);
 
 const gchar*        gtd_task_get_description          (GtdTask              *task);
@@ -80,10 +100,6 @@ const gchar*        gtd_task_get_title                (GtdTask              *tas
 void                gtd_task_set_title                (GtdTask              *task,
                                                        const gchar          *title);
 
-void                gtd_task_abort                    (GtdTask              *task);
-
-void                gtd_task_save                     (GtdTask              *task);
-
 gint                gtd_task_compare                  (GtdTask              *t1,
                                                        GtdTask              *t2);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]