From 5d8e439bc597159e3c9f0a8b65c0ae869dead3a8 Mon Sep 17 00:00:00 2001 From: Matthew Fennell Date: Sat, 27 Dec 2025 12:40:20 +0000 Subject: Import Upstream version 43.0 --- src/core/gtd-task-list.c | 1161 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1161 insertions(+) create mode 100644 src/core/gtd-task-list.c (limited to 'src/core/gtd-task-list.c') diff --git a/src/core/gtd-task-list.c b/src/core/gtd-task-list.c new file mode 100644 index 0000000..2e71a9a --- /dev/null +++ b/src/core/gtd-task-list.c @@ -0,0 +1,1161 @@ +/* gtd-task-list.c + * + * Copyright (C) 2015-2020 Georges Basile Stavracas Neto + * + * 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 . + */ + +#define G_LOG_DOMAIN "GtdTaskList" + +#include "gtd-debug.h" +#include "gtd-provider.h" +#include "gtd-task.h" +#include "gtd-task-list.h" + +#include + +/** + * SECTION:gtd-task-list + * @short_description:a list of tasks + * @title:GtdTaskList + * @stability:Unstable + * @see_also:#GtdTask + * + * A #GtdTaskList represents a task list, and contains a list of tasks, a + * color, a name and the provider who generated it. + * + * Only a #GtdProvider can create a #GtdTaskList. Equally, a #GtdTaskList + * is only valid when associated with a #GtdProvider. + * + * It implements #GListModel, and can be used as the model for #GtkListBox. + */ + +typedef struct +{ + GtdTask *task; + GTask *gtask; +} ImportingTaskData; + +typedef struct +{ + GtdProvider *provider; + GdkRGBA *color; + + GHashTable *task_to_uid; + GHashTable *tasks; + GSequence *sorted_tasks; + guint n_tasks; + + guint freeze_counter; + + gchar *name; + gboolean removable; + gboolean archived; +} GtdTaskListPrivate; + + +static gint compare_tasks_cb (gconstpointer a, + gconstpointer b, + gpointer user_data); + +static void task_changed_cb (GtdTask *task, + GParamSpec *pspec, + GtdTaskList *self); + +static void g_list_model_iface_init (GListModelInterface *iface); + + +G_DEFINE_TYPE_WITH_CODE (GtdTaskList, gtd_task_list, GTD_TYPE_OBJECT, + G_ADD_PRIVATE (GtdTaskList) + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init)) + +enum +{ + TASK_ADDED, + TASK_REMOVED, + TASK_UPDATED, + NUM_SIGNALS +}; + +enum +{ + PROP_0, + PROP_ARCHIVED, + PROP_COLOR, + PROP_IS_REMOVABLE, + PROP_NAME, + PROP_PROVIDER, + N_PROPS +}; + +static guint signals[NUM_SIGNALS] = { 0, }; +static GParamSpec *properties[N_PROPS] = { NULL, }; + + +/* + * Auxiliary functions + */ + +static void +update_task_uid (GtdTaskList *self, + GtdTask *task) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + const gchar *old_uid; + gchar *new_uid; + + priv = gtd_task_list_get_instance_private (self); + + g_debug ("Updating uid of task '%s'", gtd_task_get_title (task)); + + new_uid = g_strdup (gtd_object_get_uid (GTD_OBJECT (task))); + + old_uid = g_hash_table_lookup (priv->task_to_uid, task); + iter = g_hash_table_lookup (priv->tasks, old_uid); + + g_assert (g_sequence_get (iter) == task); + + g_hash_table_remove (priv->tasks, old_uid); + + g_hash_table_insert (priv->task_to_uid, task, new_uid); + g_hash_table_insert (priv->tasks, new_uid, iter); +} + +static guint +add_task (GtdTaskList *self, + GtdTask *task) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + gchar *uid; + + priv = gtd_task_list_get_instance_private (self); + + uid = g_strdup (gtd_object_get_uid (GTD_OBJECT (task))); + iter = g_sequence_insert_sorted (priv->sorted_tasks, + g_object_ref (task), + compare_tasks_cb, + NULL); + + g_hash_table_insert (priv->task_to_uid, task, uid); + g_hash_table_insert (priv->tasks, uid, iter); + + g_signal_connect (task, "notify", G_CALLBACK (task_changed_cb), self); + + priv->n_tasks++; + + g_signal_emit (self, signals[TASK_ADDED], 0, task); + + return g_sequence_iter_get_position (iter); +} + +static guint +remove_task (GtdTaskList *self, + GtdTask *task) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + const gchar *uid; + guint position; + + priv = gtd_task_list_get_instance_private (self); + + g_signal_handlers_disconnect_by_func (task, task_changed_cb, self); + + uid = gtd_object_get_uid (GTD_OBJECT (task)); + iter = g_hash_table_lookup (priv->tasks, uid); + position = g_sequence_iter_get_position (iter); + + g_hash_table_remove (priv->task_to_uid, task); + g_hash_table_remove (priv->tasks, uid); + + g_sequence_remove (iter); + + priv->n_tasks--; + + g_signal_emit (self, signals[TASK_REMOVED], 0, task); + + return position; +} + + +/* + * Callbacks + */ + +static void +on_import_old_task_removed_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GtdTask *new_task; + GTask *gtask; + g_autoptr (GError) error = NULL; + + gtask = user_data; + + gtd_provider_remove_task_finish (GTD_PROVIDER (object), result, &error); + + if (error) + { + g_warning ("Error removing task: %s", error->message); + return; + } + + new_task = g_task_get_task_data (gtask); + g_task_return_pointer (gtask, g_object_ref (new_task), g_object_unref); +} + +static void +on_task_updated_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr (GError) error = NULL; + + gtd_provider_update_task_finish (GTD_PROVIDER (object), result, &error); + + if (error) + { + g_warning ("Error updating task: %s", error->message); + return; + } +} + +static void +on_import_new_task_added_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr (GtdTask) new_task = NULL; + g_autoptr (GError) error = NULL; + GTask *gtask; + GtdProvider *provider; + GtdTask *importing_task; + GtdTaskList *list; + ImportingTaskData *data; + + GTD_ENTRY; + + data = user_data; + gtask = data->gtask; + importing_task = data->task; + + new_task = gtd_provider_create_task_finish (GTD_PROVIDER (object), result, &error); + g_task_set_task_data (gtask, g_object_ref (new_task), g_object_unref); + list = gtd_task_get_list (new_task); + + if (error) + { + g_warning ("Error creating task: %s", error->message); + GTD_RETURN (); + } + + gtd_task_set_complete (new_task, gtd_task_get_complete (importing_task)); + gtd_task_set_description (new_task, gtd_task_get_description (importing_task)); + gtd_task_set_due_date (new_task, gtd_task_get_due_date (importing_task)); + gtd_task_set_important (new_task, gtd_task_get_important (importing_task)); + gtd_task_set_title (new_task, gtd_task_get_title (importing_task)); + + gtd_task_list_update_task (list, new_task); + + provider = gtd_task_get_provider (importing_task); + + gtd_provider_remove_task (provider, + importing_task, + NULL, + on_import_old_task_removed_cb, + gtask); + + g_free (data); + + GTD_EXIT; +} + +static gint +compare_tasks_cb (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + return gtd_task_compare ((GtdTask*) a, (GtdTask*) b); +} + +static void +task_changed_cb (GtdTask *task, + GParamSpec *pspec, + GtdTaskList *self) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + guint old_position; + guint new_position; + + GTD_ENTRY; + + priv = gtd_task_list_get_instance_private (self); + + if (g_strcmp0 (g_param_spec_get_name (pspec), "loading") == 0) + GTD_RETURN (); + + if (g_strcmp0 (g_param_spec_get_name (pspec), "uid") == 0) + { + update_task_uid (self, task); + GTD_RETURN (); + } + + /* Don't update when the list is frozen */ + if (priv->freeze_counter > 0) + GTD_RETURN (); + + iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task))); + + old_position = g_sequence_iter_get_position (iter); + g_sequence_sort_changed (iter, compare_tasks_cb, NULL); + new_position = g_sequence_iter_get_position (iter); + + if (old_position != new_position) + { + GTD_TRACE_MSG ("Old position: %u, New position: %u", old_position, new_position); + + g_list_model_items_changed (G_LIST_MODEL (self), old_position, 1, 0); + g_list_model_items_changed (G_LIST_MODEL (self), new_position, 0, 1); + } + + GTD_EXIT; +} + + +/* + * GListModel iface + */ + +static GType +gtd_list_model_get_type (GListModel *model) +{ + return GTD_TYPE_TASK; +} + +static guint +gtd_list_model_get_n_items (GListModel *model) +{ + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (GTD_TASK_LIST (model)); + return priv->n_tasks; +} + +static gpointer +gtd_list_model_get_item (GListModel *model, + guint i) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + GtdTask *task; + + priv = gtd_task_list_get_instance_private (GTD_TASK_LIST (model)); + iter = g_sequence_get_iter_at_pos (priv->sorted_tasks, i); + task = g_sequence_get (iter); + + return g_object_ref (task); +} + +static void +g_list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = gtd_list_model_get_type; + iface->get_n_items = gtd_list_model_get_n_items; + iface->get_item = gtd_list_model_get_item; +} + + +/* + * GtdTaskList overrides + */ + +static gboolean +gtd_task_list_real_get_archived (GtdTaskList *self) +{ + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + + return priv->archived; +} + +static void +gtd_task_list_real_set_archived (GtdTaskList *self, + gboolean archived) +{ + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + + priv->archived = archived; +} + + +/* + * GObject overrides + */ + +static void +gtd_task_list_finalize (GObject *object) +{ + GtdTaskList *self = (GtdTaskList*) object; + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + + g_clear_object (&priv->provider); + + g_clear_pointer (&priv->color, gdk_rgba_free); + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->sorted_tasks, g_sequence_free); + g_clear_pointer (&priv->tasks, g_hash_table_destroy); + g_clear_pointer (&priv->task_to_uid, g_hash_table_destroy); + + G_OBJECT_CLASS (gtd_task_list_parent_class)->finalize (object); +} + +static void +gtd_task_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtdTaskList *self = GTD_TASK_LIST (object); + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + + switch (prop_id) + { + case PROP_ARCHIVED: + g_value_set_boolean (value, gtd_task_list_get_archived (self)); + break; + + case PROP_COLOR: + { + GdkRGBA *color = gtd_task_list_get_color (self); + g_value_set_boxed (value, color); + gdk_rgba_free (color); + break; + } + + case PROP_IS_REMOVABLE: + g_value_set_boolean (value, gtd_task_list_is_removable (self)); + break; + + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_PROVIDER: + g_value_set_object (value, priv->provider); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtd_task_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtdTaskList *self = GTD_TASK_LIST (object); + + switch (prop_id) + { + case PROP_ARCHIVED: + gtd_task_list_set_archived (self, g_value_get_boolean (value)); + break; + + case PROP_COLOR: + gtd_task_list_set_color (self, g_value_get_boxed (value)); + break; + + case PROP_IS_REMOVABLE: + gtd_task_list_set_is_removable (self, g_value_get_boolean (value)); + break; + + case PROP_NAME: + gtd_task_list_set_name (self, g_value_get_string (value)); + break; + + case PROP_PROVIDER: + gtd_task_list_set_provider (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtd_task_list_class_init (GtdTaskListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtd_task_list_finalize; + object_class->get_property = gtd_task_list_get_property; + object_class->set_property = gtd_task_list_set_property; + + klass->get_archived = gtd_task_list_real_get_archived; + klass->set_archived = gtd_task_list_real_set_archived; + + /** + * GtdTaskList::archived: + * + * Whether the task list is archived or not. + */ + properties[PROP_ARCHIVED] = g_param_spec_boolean ("archived", + "Whether the list is archived", + "Whether the list is archived or not", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtdTaskList::color: + * + * The color of the list. + */ + properties[PROP_COLOR] = g_param_spec_boxed ("color", + "Color of the list", + "The color of the list", + GDK_TYPE_RGBA, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtdTaskList::is-removable: + * + * Whether the task list can be removed from the system. + */ + properties[PROP_IS_REMOVABLE] = g_param_spec_boolean ("is-removable", + "Whether the task list is removable", + "Whether the task list can be removed from the system", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtdTaskList::name: + * + * The display name of the list. + */ + properties[PROP_NAME] = g_param_spec_string ("name", + "Name of the list", + "The name of the list", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtdTaskList::provider: + * + * The data provider of the list. + */ + properties[PROP_PROVIDER] = g_param_spec_object ("provider", + "Provider of the list", + "The provider that handles the list", + GTD_TYPE_PROVIDER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + /** + * GtdTaskList::task-added: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * The ::task-added signal is emitted after a #GtdTask + * is added to the list. + */ + signals[TASK_ADDED] = g_signal_new ("task-added", + GTD_TYPE_TASK_LIST, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtdTaskListClass, task_added), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + GTD_TYPE_TASK); + + /** + * GtdTaskList::task-removed: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * The ::task-removed signal is emitted after a #GtdTask + * is removed from the list. + */ + signals[TASK_REMOVED] = g_signal_new ("task-removed", + GTD_TYPE_TASK_LIST, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtdTaskListClass, task_removed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + GTD_TYPE_TASK); + + /** + * GtdTaskList::task-updated: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * The ::task-updated signal is emitted after a #GtdTask + * in the list is updated. + */ + signals[TASK_UPDATED] = g_signal_new ("task-updated", + GTD_TYPE_TASK_LIST, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtdTaskListClass, task_updated), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + GTD_TYPE_TASK); +} + +static void +gtd_task_list_init (GtdTaskList *self) +{ + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + + priv->task_to_uid = g_hash_table_new (g_str_hash, g_str_equal); + priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->sorted_tasks = g_sequence_new (g_object_unref); +} + +/** + * gtd_task_list_new: + * @provider: (nullable): a #GtdProvider + * + * Creates a new list. + * + * Returns: (transfer full): the new #GtdTaskList + */ +GtdTaskList * +gtd_task_list_new (GtdProvider *provider) +{ + return g_object_new (GTD_TYPE_TASK_LIST, + "provider", provider, + NULL); +} + +/** + * gtd_task_list_get_color: + * @list: a #GtdTaskList + * + * Retrieves the color of %list. It is guarantee that it always returns a + * color, given a valid #GtdTaskList. + * + * Returns: (transfer full): the color of %list. Free with %gdk_rgba_free after use. + */ +GdkRGBA* +gtd_task_list_get_color (GtdTaskList *list) +{ + GtdTaskListPrivate *priv; + GdkRGBA rgba; + + g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL); + + priv = gtd_task_list_get_instance_private (list); + + if (!priv->color) + { + gdk_rgba_parse (&rgba, "#ffffff"); + priv->color = gdk_rgba_copy (&rgba); + } + + return gdk_rgba_copy (priv->color); +} + +/** + * gtd_task_list_set_color: + * @list: a #GtdTaskList + * #color: a #GdkRGBA + * + * sets the color of @list. + */ +void +gtd_task_list_set_color (GtdTaskList *list, + const GdkRGBA *color) +{ + GtdTaskListPrivate *priv; + GdkRGBA *current_color; + + g_return_if_fail (GTD_IS_TASK_LIST (list)); + + priv = gtd_task_list_get_instance_private (list); + current_color = gtd_task_list_get_color (list); + + if (!gdk_rgba_equal (current_color, color)) + { + g_clear_pointer (&priv->color, gdk_rgba_free); + priv->color = gdk_rgba_copy (color); + + g_object_notify (G_OBJECT (list), "color"); + } + + gdk_rgba_free (current_color); +} + +/** + * gtd_task_list_get_name: + * @list: a #GtdTaskList + * + * Retrieves the user-visible name of @list, or %NULL. + * + * Returns: (transfer none): the internal name of @list. Do not free + * after use. + */ +const gchar* +gtd_task_list_get_name (GtdTaskList *list) +{ + GtdTaskListPrivate *priv; + + g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL); + + priv = gtd_task_list_get_instance_private (list); + + return priv->name; +} + +/** + * gtd_task_list_set_name: + * @list: a #GtdTaskList + * @name: (nullable): the name of @list + * + * Sets the @list name to @name. + */ +void +gtd_task_list_set_name (GtdTaskList *list, + const gchar *name) +{ + GtdTaskListPrivate *priv; + + g_assert (GTD_IS_TASK_LIST (list)); + + priv = gtd_task_list_get_instance_private (list); + + if (g_strcmp0 (priv->name, name) != 0) + { + g_free (priv->name); + priv->name = g_strdup (name); + + g_object_notify (G_OBJECT (list), "name"); + } +} + +/** + * gtd_task_list_get_provider: + * @list: a #GtdTaskList + * + * Retrieves the #GtdProvider who owns this list. + * + * Returns: (transfer none): a #GtdProvider + */ +GtdProvider* +gtd_task_list_get_provider (GtdTaskList *list) +{ + GtdTaskListPrivate *priv; + + g_return_val_if_fail (GTD_IS_TASK_LIST (list), NULL); + + priv = gtd_task_list_get_instance_private (list); + + return priv->provider; +} + +/** + * gtd_task_list_set_provider: + * @self: a #GtdTaskList + * @provider: (nullable): a #GtdProvider, or %NULL + * + * Sets the provider of this tasklist. + */ +void +gtd_task_list_set_provider (GtdTaskList *self, + GtdProvider *provider) +{ + GtdTaskListPrivate *priv; + + g_assert (GTD_IS_TASK_LIST (self)); + + priv = gtd_task_list_get_instance_private (self); + + if (g_set_object (&priv->provider, provider)) + g_object_notify (G_OBJECT (self), "provider"); +} + +/** + * gtd_task_list_add_task: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * Adds @task to @list. + */ +void +gtd_task_list_add_task (GtdTaskList *self, + GtdTask *task) +{ + guint position; + + g_assert (GTD_IS_TASK_LIST (self)); + g_assert (GTD_IS_TASK (task)); + g_assert (!gtd_task_list_contains (self, task)); + + position = add_task (self, task); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); +} + +/** + * gtd_task_list_update_task: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * Updates @task at @list. + */ +void +gtd_task_list_update_task (GtdTaskList *self, + GtdTask *task) +{ + GtdTaskListPrivate *priv; + GSequenceIter *iter; + + g_return_if_fail (GTD_IS_TASK_LIST (self)); + g_return_if_fail (GTD_IS_TASK (task)); + + priv = gtd_task_list_get_instance_private (self); + + g_return_if_fail (gtd_task_list_contains (self, task)); + + iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task))); + + g_list_model_items_changed (G_LIST_MODEL (self), + g_sequence_iter_get_position (iter), + 1, + 1); + + g_signal_emit (self, signals[TASK_UPDATED], 0, task); +} + +/** + * gtd_task_list_remove_task: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * Removes @task from @list if it's inside the list. + */ +void +gtd_task_list_remove_task (GtdTaskList *list, + GtdTask *task) +{ + guint position; + + g_assert (GTD_IS_TASK_LIST (list)); + g_assert (GTD_IS_TASK (task)); + g_assert (gtd_task_list_contains (list, task)); + + position = remove_task (list, task); + g_list_model_items_changed (G_LIST_MODEL (list), position, 1, 0); +} + +/** + * gtd_task_list_contains: + * @list: a #GtdTaskList + * @task: a #GtdTask + * + * Checks if @task is inside @list. + * + * Returns: %TRUE if @list contains @task, %FALSE otherwise + */ +gboolean +gtd_task_list_contains (GtdTaskList *list, + GtdTask *task) +{ + GtdTaskListPrivate *priv; + + g_assert (GTD_IS_TASK_LIST (list)); + g_assert (GTD_IS_TASK (task)); + + priv = gtd_task_list_get_instance_private (list); + + return g_hash_table_contains (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task))); +} + +/** + * gtd_task_list_get_is_removable: + * @list: a #GtdTaskList + * + * Retrieves whether @list can be removed or not. + * + * Returns: %TRUE if the @list can be removed, %FALSE otherwise + */ +gboolean +gtd_task_list_is_removable (GtdTaskList *list) +{ + GtdTaskListPrivate *priv; + + g_return_val_if_fail (GTD_IS_TASK_LIST (list), FALSE); + + priv = gtd_task_list_get_instance_private (list); + + return priv->removable; +} + +/** + * gtd_task_list_set_is_removable: + * @list: a #GtdTaskList + * @is_removable: %TRUE if @list can be deleted, %FALSE otherwise + * + * Sets whether @list can be deleted or not. + */ +void +gtd_task_list_set_is_removable (GtdTaskList *list, + gboolean is_removable) +{ + GtdTaskListPrivate *priv; + + g_return_if_fail (GTD_IS_TASK_LIST (list)); + + priv = gtd_task_list_get_instance_private (list); + + if (priv->removable != is_removable) + { + priv->removable = is_removable; + + g_object_notify (G_OBJECT (list), "is-removable"); + } +} + +/** + * gtd_task_list_get_task_by_id: + * @self: 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; + GSequenceIter *iter; + + g_return_val_if_fail (GTD_IS_TASK_LIST (self), NULL); + + priv = gtd_task_list_get_instance_private (self); + iter = g_hash_table_lookup (priv->tasks, id); + + if (!iter) + return NULL; + + return g_sequence_get (iter); +} + +/** + * gtd_task_list_move_task_to_position: + * @self: a #GtdTaskList + * @task: a #GtdTask + * @new_position: the new position of @task inside @self + * + * Moves @task to @new_position, and repositions the elements + * in between as well. + * + * @task must belong to @self. + */ +void +gtd_task_list_move_task_to_position (GtdTaskList *self, + GtdTask *task, + guint new_position) +{ + + GtdTaskListPrivate *priv = gtd_task_list_get_instance_private (self); + GSequenceIter *new_position_iter; + GSequenceIter *iter; + guint old_position; + guint length; + guint start; + guint i; + + + g_return_if_fail (GTD_IS_TASK_LIST (self)); + g_return_if_fail (GTD_IS_TASK (task)); + g_return_if_fail (gtd_task_list_contains (self, task)); + g_return_if_fail (g_list_model_get_n_items (G_LIST_MODEL (self)) >= new_position); + + iter = g_hash_table_lookup (priv->tasks, gtd_object_get_uid (GTD_OBJECT (task))); + old_position = g_sequence_iter_get_position (iter); + + if (old_position == new_position) + return; + + GTD_TRACE_MSG ("Moving task '%s' (%s) from %u to %u", + gtd_task_get_title (task), + gtd_object_get_uid (GTD_OBJECT (task)), + old_position, + new_position); + + /* Update the GSequence */ + new_position_iter = new_position < old_position ? + g_sequence_get_iter_at_pos (priv->sorted_tasks, new_position) : + g_sequence_get_iter_at_pos (priv->sorted_tasks, new_position + 1); + g_sequence_move (iter, new_position_iter); + + /* Update the 'position' property of all tasks in between */ + priv->freeze_counter++; + + length = ABS ((gint) new_position - (gint64) old_position) + 1; + start = MIN (old_position, new_position); + iter = g_sequence_get_iter_at_pos (priv->sorted_tasks, start); + + for (i = 0; i < length; i++) + { + GtdTask *aux = g_sequence_get (iter); + + g_signal_handlers_block_by_func (aux, task_changed_cb, self); + gtd_task_set_position (aux, start + i); + g_signal_handlers_unblock_by_func (aux, task_changed_cb, self); + + gtd_provider_update_task (priv->provider, aux, NULL, on_task_updated_cb, self); + + iter = g_sequence_iter_next (iter); + } + + g_list_model_items_changed (G_LIST_MODEL (self), old_position, 1, 0); + g_list_model_items_changed (G_LIST_MODEL (self), new_position, 0, 1); + + priv->freeze_counter--; +} + +/** + * gtd_task_list_get_archived: + * @self: a #GtdTaskList + * + * Retrieves whether @self is archived or not. Archived task lists + * are hidden by default, and new tasks cannot be added. + * + * Returns: %TRUE if @self is archived, %FALSE otherwise. + */ +gboolean +gtd_task_list_get_archived (GtdTaskList *self) +{ + g_return_val_if_fail (GTD_IS_TASK_LIST (self), FALSE); + + return GTD_TASK_LIST_GET_CLASS (self)->get_archived (self); +} + +/** + * gtd_task_list_set_archived: + * @self: a #GtdTaskList + * @archived: whether @self is archived or not + * + * Sets the "archive" property of @self to @archived. + */ +void +gtd_task_list_set_archived (GtdTaskList *self, + gboolean archived) +{ + gboolean was_archived; + + g_return_if_fail (GTD_IS_TASK_LIST (self)); + + was_archived = gtd_task_list_get_archived (self); + + if (archived == was_archived) + return; + + GTD_TASK_LIST_GET_CLASS (self)->set_archived (self, archived); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ARCHIVED]); +} + +/** + * gtd_task_list_import_task: + * @self: a #GtdTaskList + * @task: a #GtdTask + * + * Imports task into @self + */ +void +gtd_task_list_import_task (GtdTaskList *self, + GtdTask *task, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr (GTask) gtask = NULL; + GtdProvider *provider; + ImportingTaskData *data; + + g_return_if_fail (GTD_IS_TASK_LIST (self)); + + gtask = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (gtask, gtd_task_list_import_task); + + provider = gtd_task_get_provider (task); + + data = g_new0 (ImportingTaskData, 1); + data->gtask = g_object_ref (gtask); + data->task = g_object_ref (task); + + gtd_provider_create_task (provider, + self, + gtd_task_get_title (task), + gtd_task_get_due_date (task), + NULL, + on_import_new_task_added_cb, + data); +} + +/** + * gtd_task_list_import_task_finish: + * @self: a #GtdTaskList + * @result: a #GAsyncResult + * @error: a #GError + * + * Imports task into @self + * + * Returns: (transfer full): a #GTask + */ +GTask * +gtd_task_list_import_task_finish (GtdTaskList *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtd_task_list_import_task, NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + + +/** + * gtd_task_list_is_inbox: + * @self: a #GtdTaskList + * + * Retrieves whether @self is the inbox task list of its provider. + * + * Returns: %TRUE if @self is the inbox of it's provider, %FALSE otherwise. + */ +gboolean +gtd_task_list_is_inbox (GtdTaskList *self) +{ + GtdTaskListPrivate *priv; + + g_return_val_if_fail (GTD_IS_TASK_LIST (self), FALSE); + + priv = gtd_task_list_get_instance_private (self); + + return self == gtd_provider_get_inbox (priv->provider); +} -- cgit v1.2.3