/* gtd-provider-eds.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 "GtdProviderEds" #include "gtd-debug.h" #include "gtd-eds-autoptr.h" #include "gtd-provider-eds.h" #include "gtd-task-eds.h" #include "gtd-task-list-eds.h" #include /** * #GtdProviderEds is the base class of #GtdProviderLocal * and #GtdProviderGoa. It provides the common functionality * shared between these two providers. * * The subclasses basically have to implement GtdProviderEds->should_load_source * which decides whether a given #ESource should be loaded (and added to the * sources list) or not. #GtdProviderLocal for example would filter out * sources whose backend is not "local". */ typedef struct { GtdTaskList *list; GDateTime *due_date; gchar *title; ESource *source; /* Update Task */ ECalComponent *component; GtdTask *task; } AsyncData; typedef struct { GHashTable *task_lists; ESourceRegistry *source_registry; GCancellable *cancellable; gint lazy_load_id; } GtdProviderEdsPrivate; static void gtd_provider_iface_init (GtdProviderInterface *iface); G_DEFINE_TYPE_WITH_CODE (GtdProviderEds, gtd_provider_eds, GTD_TYPE_OBJECT, G_ADD_PRIVATE (GtdProviderEds) G_IMPLEMENT_INTERFACE (GTD_TYPE_PROVIDER, gtd_provider_iface_init)) enum { PROP_0, PROP_ENABLED, PROP_DESCRIPTION, PROP_ICON, PROP_ID, PROP_NAME, PROP_PROVIDER_TYPE, PROP_REGISTRY, N_PROPS }; /* * Auxiliary methods */ static void async_data_free (gpointer data) { AsyncData *async_data = data; g_clear_pointer (&async_data->due_date, g_date_time_unref); g_clear_pointer (&async_data->title, g_free); g_clear_object (&async_data->source); g_clear_object (&async_data->list); g_clear_object (&async_data->task); g_clear_object (&async_data->component); g_free (async_data); } static void set_default_list (GtdProviderEds *self, GtdTaskList *list) { GtdProviderEdsPrivate *priv; GtdManager *manager; ESource *source; priv = gtd_provider_eds_get_instance_private (self); source = gtd_task_list_eds_get_source (GTD_TASK_LIST_EDS (list)); manager = gtd_manager_get_default (); e_source_registry_set_default_task_list (priv->source_registry, source); if (gtd_manager_get_default_provider (manager) != (GtdProvider*) self) gtd_manager_set_default_provider (manager, GTD_PROVIDER (self)); } static void ensure_offline_sync (GtdProviderEds *self, ESource *source) { GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self); ESourceOffline *extension; extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OFFLINE); e_source_offline_set_stay_synchronized (extension, TRUE); e_source_registry_commit_source (priv->source_registry, source, NULL, NULL, NULL); } /* * Callbacks */ static void on_task_list_eds_loaded_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { g_autoptr (GError) error = NULL; GtdProviderEdsPrivate *priv; GtdProviderEds *self; GtdTaskListEds *list; ESource *source; self = GTD_PROVIDER_EDS (user_data); priv = gtd_provider_eds_get_instance_private (self); list = gtd_task_list_eds_new_finish (result, &error); if (error) { g_warning ("Error creating task list: %s", error->message); return; } source = gtd_task_list_eds_get_source (list); g_hash_table_insert (priv->task_lists, e_source_dup_uid (source), g_object_ref (list)); g_object_set_data (G_OBJECT (source), "task-list", list); g_debug ("Task list '%s' successfully connected", e_source_get_display_name (source)); } static void on_client_connected_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { g_autoptr (GError) error = NULL; GtdProviderEdsPrivate *priv; GtdProviderEds *self; ECalClient *client; ESource *source; self = GTD_PROVIDER_EDS (user_data); priv = gtd_provider_eds_get_instance_private (self); source = e_client_get_source (E_CLIENT (source_object)); client = E_CAL_CLIENT (e_cal_client_connect_finish (result, &error)); if (error) { g_warning ("Failed to connect to task list '%s': %s", e_source_get_uid (source), error->message); gtd_manager_emit_error_message (gtd_manager_get_default (), _("Failed to connect to task list"), error->message, NULL, NULL); gtd_object_pop_loading (GTD_OBJECT (self)); return; } ensure_offline_sync (self, source); /* creates a new task list */ gtd_task_list_eds_new (GTD_PROVIDER (self), source, client, on_task_list_eds_loaded_cb, priv->cancellable, self); } static void on_source_added_cb (GtdProviderEds *provider, ESource *source) { /* Don't load the source if it's not a tasklist */ if (!e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST) || !GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->should_load_source (provider, source)) { GTD_TRACE_MSG ("Ignoring source %s (%s)", e_source_get_display_name (source), e_source_get_uid (source)); return; } /* * The pop_loading() is actually emited by GtdTaskListEds, after the * ECalClientView sends the :complete signal. */ gtd_object_push_loading (GTD_OBJECT (provider)); gtd_object_push_loading (GTD_OBJECT (gtd_manager_get_default ())); e_cal_client_connect (source, E_CAL_CLIENT_SOURCE_TYPE_TASKS, 15, /* seconds to wait */ NULL, on_client_connected_cb, provider); } static void on_source_removed_cb (GtdProviderEds *provider, ESource *source) { GtdProviderEdsPrivate *priv; GtdTaskList *list; GTD_ENTRY; priv = gtd_provider_eds_get_instance_private (provider); list = g_object_get_data (G_OBJECT (source), "task-list"); if (!g_hash_table_remove (priv->task_lists, gtd_object_get_uid (GTD_OBJECT (list)))) GTD_RETURN (); /* * Since all subclasses will have this signal given that they * are all GtdProvider implementations, it's not that bad * to let it stay here. */ g_signal_emit_by_name (provider, "list-removed", list); GTD_EXIT; } static void on_source_refreshed_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (user_data); g_autoptr (GError) error = NULL; GTD_ENTRY; e_source_registry_refresh_backend_finish (priv->source_registry, result, &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Error refreshing source: %s", error->message); GTD_RETURN (); } GTD_EXIT; } static void create_task_in_thread_cb (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { g_autoptr (ECalComponent) component = NULL; g_autoptr (GError) error = NULL; g_autofree gchar *new_uid = NULL; ECalComponentText *new_summary; GtdTaskListEds *tasklist; ECalClient *client; AsyncData *data; GtdTask *new_task; GTD_ENTRY; data = task_data; tasklist = GTD_TASK_LIST_EDS (data->list); client = gtd_task_list_eds_get_client (tasklist); /* Create the new task */ component = e_cal_component_new (); e_cal_component_set_new_vtype (component, E_CAL_COMPONENT_TODO); new_summary = e_cal_component_text_new (data->title, NULL); e_cal_component_set_summary (component, new_summary); if (data->due_date) { ECalComponentDateTime *comp_dt; ICalTime *idt; idt = i_cal_time_new_null_time (); i_cal_time_set_date (idt, g_date_time_get_year (data->due_date), g_date_time_get_month (data->due_date), g_date_time_get_day_of_month (data->due_date)); i_cal_time_set_time (idt, g_date_time_get_hour (data->due_date), g_date_time_get_minute (data->due_date), g_date_time_get_seconds (data->due_date)); i_cal_time_set_is_date (idt, i_cal_time_get_hour (idt) == 0 && i_cal_time_get_minute (idt) == 0 && i_cal_time_get_second (idt) == 0); comp_dt = e_cal_component_datetime_new_take (idt, g_strdup ("UTC")); e_cal_component_set_due (component, comp_dt); e_cal_component_commit_sequence (component); e_cal_component_datetime_free (comp_dt); } e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (component), E_CAL_OPERATION_FLAG_NONE, &new_uid, cancellable, &error); e_cal_component_text_free (new_summary); if (error) { g_task_return_error (task, g_steal_pointer (&error)); return; } new_task = gtd_task_eds_new (component); gtd_task_set_position (new_task, g_list_model_get_n_items (G_LIST_MODEL (tasklist))); /* * In the case the task UID changes because of creation proccess, * reapply it to the task. */ if (new_uid) gtd_object_set_uid (GTD_OBJECT (new_task), new_uid); /* Effectively apply the updated component */ gtd_task_eds_apply (GTD_TASK_EDS (new_task)); g_task_return_pointer (task, g_object_ref (new_task), g_object_unref); GTD_EXIT; } static void update_task_in_thread_cb (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { g_autoptr (GError) error = NULL; GtdTaskListEds *tasklist; ECalClient *client; AsyncData *data; GTD_ENTRY; data = task_data; tasklist = GTD_TASK_LIST_EDS (gtd_task_get_list (data->task)); client = gtd_task_list_eds_get_client (tasklist); e_cal_client_modify_object_sync (client, e_cal_component_get_icalcomponent (data->component), E_CAL_OBJ_MOD_THIS, E_CAL_OPERATION_FLAG_NONE, cancellable, &error); if (error) { g_task_return_error (task, g_steal_pointer (&error)); GTD_RETURN (); } g_task_return_boolean (task, TRUE); GTD_EXIT; } static void remove_task_in_thread_cb (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { g_autoptr (ECalComponentId) id = NULL; g_autoptr (GError) error = NULL; GtdTaskListEds *tasklist; ECalClient *client; AsyncData *data; GTD_ENTRY; data = task_data; tasklist = GTD_TASK_LIST_EDS (gtd_task_get_list (data->task)); client = gtd_task_list_eds_get_client (tasklist); id = e_cal_component_get_id (data->component); e_cal_client_remove_object_sync (client, e_cal_component_id_get_uid (id), e_cal_component_id_get_rid (id), E_CAL_OBJ_MOD_THIS, E_CAL_OPERATION_FLAG_NONE, cancellable, &error); if (error) { g_task_return_error (task, g_steal_pointer (&error)); GTD_RETURN (); } g_task_return_boolean (task, TRUE); GTD_EXIT; } static void create_or_update_task_list_in_thread_cb (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GtdProviderEdsPrivate *priv; g_autoptr (GError) error = NULL; GtdProviderEds *self; AsyncData *data; GTD_ENTRY; data = task_data; self = GTD_PROVIDER_EDS (source_object); priv = gtd_provider_eds_get_instance_private (self); e_source_registry_commit_source_sync (priv->source_registry, data->source, cancellable, &error); if (error) { g_task_return_error (task, g_steal_pointer (&error)); GTD_RETURN (); } g_task_return_boolean (task, TRUE); GTD_EXIT; } static void remove_task_list_in_thread_cb (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { g_autoptr (GError) error = NULL; AsyncData *data; GTD_ENTRY; data = task_data; e_source_remove_sync (data->source, cancellable, &error); if (error) { g_task_return_error (task, g_steal_pointer (&error)); GTD_RETURN (); } g_task_return_boolean (task, TRUE); GTD_EXIT; } /* * GtdProvider iface */ static const gchar* gtd_provider_eds_get_id (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_id (GTD_PROVIDER_EDS (provider)); } static const gchar* gtd_provider_eds_get_name (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_name (GTD_PROVIDER_EDS (provider)); } static const gchar* gtd_provider_eds_get_provider_type (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_provider_type (GTD_PROVIDER_EDS (provider)); } static const gchar* gtd_provider_eds_get_description (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_description (GTD_PROVIDER_EDS (provider)); } static gboolean gtd_provider_eds_get_enabled (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), FALSE); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_enabled (GTD_PROVIDER_EDS (provider)); } static void gtd_provider_eds_refresh (GtdProvider *provider) { g_autoptr (GHashTable) collections = NULL; GtdProviderEdsPrivate *priv; GtdProviderEds *self; GHashTableIter iter; GtdTaskListEds *list; GTD_ENTRY; g_return_if_fail (GTD_IS_PROVIDER_EDS (provider)); self = GTD_PROVIDER_EDS (provider); priv = gtd_provider_eds_get_instance_private (self); collections = g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_iter_init (&iter, priv->task_lists); while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &list)) { g_autoptr (ESource) collection = NULL; ESource *source; source = gtd_task_list_eds_get_source (list); collection = e_source_registry_find_extension (priv->source_registry, source, E_SOURCE_EXTENSION_COLLECTION); if (!collection || g_hash_table_contains (collections, collection)) continue; GTD_TRACE_MSG ("Refreshing collection %s", e_source_get_uid (collection)); e_source_registry_refresh_backend (priv->source_registry, e_source_get_uid (collection), priv->cancellable, on_source_refreshed_cb, g_object_ref (self)); g_hash_table_add (collections, collection); } GTD_EXIT; } static GIcon* gtd_provider_eds_get_icon (GtdProvider *provider) { g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); return GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->get_icon (GTD_PROVIDER_EDS (provider)); } static void gtd_provider_eds_create_task (GtdProvider *provider, GtdTaskList *list, const gchar *title, GDateTime *due_date, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) task = NULL; GtdProviderEds *self; AsyncData *data; g_return_if_fail (GTD_IS_TASK_LIST_EDS (list)); GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); data = g_new0 (AsyncData, 1); data->list = g_object_ref (list); data->title = g_strdup (title); data->due_date = due_date ? g_date_time_ref (due_date) : NULL; gtd_object_push_loading (GTD_OBJECT (self)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, gtd_provider_eds_create_task); g_task_set_task_data (task, data, async_data_free); g_task_run_in_thread (task, create_task_in_thread_cb); GTD_EXIT; } static GtdTask* gtd_provider_eds_create_task_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { g_autoptr (GtdTask) new_task = NULL; GtdProviderEds *self; GtdTaskList *list; AsyncData *data; GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); data = g_task_get_task_data (G_TASK (result)); list = data->list; gtd_object_pop_loading (GTD_OBJECT (self)); new_task = g_task_propagate_pointer (G_TASK (result), error); if (new_task) { gtd_task_set_list (new_task, list); gtd_task_list_add_task (list, new_task); set_default_list (self, list); } GTD_RETURN (new_task); } static void gtd_provider_eds_update_task (GtdProvider *provider, GtdTask *task, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) gtask = NULL; ECalComponent *component; AsyncData *data; GTD_ENTRY; g_return_if_fail (GTD_IS_TASK (task)); g_return_if_fail (GTD_IS_TASK_LIST_EDS (gtd_task_get_list (task))); component = gtd_task_eds_get_component (GTD_TASK_EDS (task)); e_cal_component_commit_sequence (component); /* The task is not ready until we finish the operation */ gtd_object_push_loading (GTD_OBJECT (task)); gtd_object_push_loading (GTD_OBJECT (provider)); data = g_new0 (AsyncData, 1); data->task = g_object_ref (task); data->component = e_cal_component_clone (component); gtask = g_task_new (provider, cancellable, callback, user_data); g_task_set_source_tag (gtask, gtd_provider_eds_update_task); g_task_set_task_data (gtask, data, async_data_free); g_task_run_in_thread (gtask, update_task_in_thread_cb); GTD_EXIT; } static gboolean gtd_provider_eds_update_task_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { GtdProviderEds *self; AsyncData *data; GtdTask *task; GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); data = g_task_get_task_data (G_TASK (result)); task = data->task; gtd_object_pop_loading (GTD_OBJECT (self)); gtd_object_pop_loading (GTD_OBJECT (task)); if (!g_task_propagate_boolean (G_TASK (result), error)) { gtd_task_eds_revert (GTD_TASK_EDS (task)); GTD_RETURN (FALSE); } gtd_task_eds_apply (GTD_TASK_EDS (task)); gtd_task_list_update_task (gtd_task_get_list (task), task); GTD_RETURN (TRUE); } static void gtd_provider_eds_remove_task (GtdProvider *provider, GtdTask *task, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) gtask = NULL; ECalComponent *component; AsyncData *data; GTD_ENTRY; g_return_if_fail (GTD_IS_TASK (task)); g_return_if_fail (GTD_IS_TASK_LIST_EDS (gtd_task_get_list (task))); component = gtd_task_eds_get_component (GTD_TASK_EDS (task)); gtd_object_push_loading (GTD_OBJECT (provider)); data = g_new0 (AsyncData, 1); data->task = g_object_ref (task); data->component = e_cal_component_clone (component); gtask = g_task_new (provider, cancellable, callback, user_data); g_task_set_source_tag (gtask, gtd_provider_eds_remove_task); g_task_set_task_data (gtask, data, async_data_free); g_task_run_in_thread (gtask, remove_task_in_thread_cb); GTD_EXIT; } static gboolean gtd_provider_eds_remove_task_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { GTD_ENTRY; gtd_object_pop_loading (GTD_OBJECT (provider)); GTD_RETURN (g_task_propagate_boolean (G_TASK (result), error)); } static void gtd_provider_eds_create_task_list (GtdProvider *provider, const gchar *name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) task = NULL; GtdProviderEds *self; AsyncData *data; ESource *source; GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); source = NULL; /* Create an ESource */ if (!GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->create_source) { g_debug ("Can't create task list: not supported by %s", G_OBJECT_TYPE_NAME (provider)); return; } source = GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->create_source (self); if (!source) { g_debug ("Can't create task list: create_source() returned NULL"); return; } /* EDS properties */ e_source_set_display_name (source, name); data = g_new0 (AsyncData, 1); data->title = g_strdup (name); data->source = g_object_ref (source); gtd_object_push_loading (GTD_OBJECT (provider)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, gtd_provider_eds_create_task_list); g_task_set_task_data (task, data, async_data_free); g_task_run_in_thread (task, create_or_update_task_list_in_thread_cb); GTD_EXIT; } static gboolean gtd_provider_eds_create_task_list_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { GtdProviderEds *self; GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); gtd_object_pop_loading (GTD_OBJECT (self)); GTD_RETURN (g_task_propagate_boolean (G_TASK (result), error)); } static void gtd_provider_eds_update_task_list (GtdProvider *provider, GtdTaskList *list, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) task = NULL; AsyncData *data; ESource *source; GTD_ENTRY; g_assert (GTD_IS_TASK_LIST_EDS (list)); g_assert (gtd_task_list_eds_get_source (GTD_TASK_LIST_EDS (list)) != NULL); source = gtd_task_list_eds_get_source (GTD_TASK_LIST_EDS (list)); gtd_object_push_loading (GTD_OBJECT (provider)); gtd_object_push_loading (GTD_OBJECT (list)); data = g_new0 (AsyncData, 1); data->list = g_object_ref (list); data->source = g_object_ref (source); task = g_task_new (provider, cancellable, callback, user_data); g_task_set_source_tag (task, gtd_provider_eds_update_task_list); g_task_set_task_data (task, data, async_data_free); g_task_run_in_thread (task, create_or_update_task_list_in_thread_cb); GTD_EXIT; } static gboolean gtd_provider_eds_update_task_list_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { GtdProviderEds *self; AsyncData *data; GTD_ENTRY; self = GTD_PROVIDER_EDS (provider); data = g_task_get_task_data (G_TASK (result)); gtd_object_pop_loading (GTD_OBJECT (data->list)); gtd_object_pop_loading (GTD_OBJECT (self)); g_signal_emit_by_name (self, "list-changed", data->list); GTD_RETURN (g_task_propagate_boolean (G_TASK (result), error)); } static void gtd_provider_eds_remove_task_list (GtdProvider *provider, GtdTaskList *list, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) gtask = NULL; AsyncData *data; ESource *source; GTD_ENTRY; g_assert (GTD_IS_TASK_LIST_EDS (list)); g_assert (gtd_task_list_eds_get_source (GTD_TASK_LIST_EDS (list)) != NULL); source = gtd_task_list_eds_get_source (GTD_TASK_LIST_EDS (list)); gtd_object_push_loading (GTD_OBJECT (provider)); data = g_new0 (AsyncData, 1); data->source = g_object_ref (source); gtask = g_task_new (provider, cancellable, callback, user_data); g_task_set_source_tag (gtask, gtd_provider_eds_remove_task_list); g_task_set_task_data (gtask, data, async_data_free); g_task_run_in_thread (gtask, remove_task_list_in_thread_cb); GTD_EXIT; } static gboolean gtd_provider_eds_remove_task_list_finish (GtdProvider *provider, GAsyncResult *result, GError **error) { GTD_ENTRY; gtd_object_pop_loading (GTD_OBJECT (provider)); GTD_RETURN (g_task_propagate_boolean (G_TASK (result), error)); } static GList* gtd_provider_eds_get_task_lists (GtdProvider *provider) { GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (GTD_PROVIDER_EDS (provider)); return g_hash_table_get_values (priv->task_lists); } static GtdTaskList* gtd_provider_eds_get_inbox (GtdProvider *provider) { GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (GTD_PROVIDER_EDS (provider)); return g_hash_table_lookup (priv->task_lists, GTD_PROVIDER_EDS_INBOX_ID); } static void gtd_provider_iface_init (GtdProviderInterface *iface) { iface->get_id = gtd_provider_eds_get_id; iface->get_name = gtd_provider_eds_get_name; iface->get_provider_type = gtd_provider_eds_get_provider_type; iface->get_description = gtd_provider_eds_get_description; iface->get_enabled = gtd_provider_eds_get_enabled; iface->refresh = gtd_provider_eds_refresh; iface->get_icon = gtd_provider_eds_get_icon; iface->create_task = gtd_provider_eds_create_task; iface->create_task_finish = gtd_provider_eds_create_task_finish; iface->update_task = gtd_provider_eds_update_task; iface->update_task_finish = gtd_provider_eds_update_task_finish; iface->remove_task = gtd_provider_eds_remove_task; iface->remove_task_finish = gtd_provider_eds_remove_task_finish; iface->create_task_list = gtd_provider_eds_create_task_list; iface->create_task_list_finish = gtd_provider_eds_create_task_list_finish; iface->update_task_list = gtd_provider_eds_update_task_list; iface->update_task_list_finish = gtd_provider_eds_update_task_list_finish; iface->remove_task_list = gtd_provider_eds_remove_task_list; iface->remove_task_list_finish = gtd_provider_eds_remove_task_list_finish; iface->get_task_lists = gtd_provider_eds_get_task_lists; iface->get_inbox = gtd_provider_eds_get_inbox; } /* * GObject overrides */ static void gtd_provider_eds_finalize (GObject *object) { GtdProviderEds *self = (GtdProviderEds *)object; GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self); g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_object (&priv->source_registry); g_clear_pointer (&priv->task_lists, g_hash_table_destroy); G_OBJECT_CLASS (gtd_provider_eds_parent_class)->finalize (object); } static void gtd_provider_eds_constructed (GObject *object) { GtdProviderEdsPrivate *priv; GtdProviderEds *self; g_autoptr (GError) error = NULL; GList *sources; GList *l; self = GTD_PROVIDER_EDS (object); priv = gtd_provider_eds_get_instance_private (self); if (error) { g_warning ("%s: %s", "Error loading task manager", error->message); return; } /* Load task list sources */ sources = e_source_registry_list_sources (priv->source_registry, E_SOURCE_EXTENSION_TASK_LIST); for (l = sources; l != NULL; l = l->next) on_source_added_cb (self, l->data); g_list_free_full (sources, g_object_unref); /* listen to the signals, so new sources don't slip by */ g_signal_connect_swapped (priv->source_registry, "source-added", G_CALLBACK (on_source_added_cb), self); g_signal_connect_swapped (priv->source_registry, "source-removed", G_CALLBACK (on_source_removed_cb), self); } static void gtd_provider_eds_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtdProvider *provider = GTD_PROVIDER (object); GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (GTD_PROVIDER_EDS (object)); switch (prop_id) { case PROP_DESCRIPTION: g_value_set_string (value, gtd_provider_eds_get_description (provider)); break; case PROP_ENABLED: g_value_set_boolean (value, gtd_provider_eds_get_enabled (provider)); break; case PROP_ICON: g_value_set_object (value, gtd_provider_eds_get_icon (provider)); break; case PROP_ID: g_value_set_string (value, gtd_provider_eds_get_id (provider)); break; case PROP_NAME: g_value_set_string (value, gtd_provider_eds_get_name (provider)); break; case PROP_PROVIDER_TYPE: g_value_set_string (value, gtd_provider_eds_get_provider_type (provider)); break; case PROP_REGISTRY: g_value_set_object (value, priv->source_registry); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gtd_provider_eds_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GtdProviderEds *self = GTD_PROVIDER_EDS (object); GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self); switch (prop_id) { case PROP_REGISTRY: if (g_set_object (&priv->source_registry, g_value_get_object (value))) g_object_notify (object, "registry"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gtd_provider_eds_class_init (GtdProviderEdsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtd_provider_eds_finalize; object_class->constructed = gtd_provider_eds_constructed; object_class->get_property = gtd_provider_eds_get_property; object_class->set_property = gtd_provider_eds_set_property; g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); g_object_class_override_property (object_class, PROP_ENABLED, "enabled"); g_object_class_override_property (object_class, PROP_ICON, "icon"); g_object_class_override_property (object_class, PROP_ID, "id"); g_object_class_override_property (object_class, PROP_NAME, "name"); g_object_class_override_property (object_class, PROP_PROVIDER_TYPE, "provider-type"); g_object_class_install_property (object_class, PROP_REGISTRY, g_param_spec_object ("registry", "Source registry", "The EDS source registry object", E_TYPE_SOURCE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void gtd_provider_eds_init (GtdProviderEds *self) { GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self); priv->cancellable = g_cancellable_new (); priv->task_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } GtdProviderEds* gtd_provider_eds_new (ESourceRegistry *registry) { return g_object_new (GTD_TYPE_PROVIDER_EDS, "registry", registry, NULL); } ESourceRegistry* gtd_provider_eds_get_registry (GtdProviderEds *provider) { GtdProviderEdsPrivate *priv; g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL); priv = gtd_provider_eds_get_instance_private (provider); return priv->source_registry; }