/* 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); }