#include #include "sync_group.h" #include "sync_collection.h" G_DEFINE_TYPE (SyncCollection, sync_collection, G_TYPE_OBJECT); #define COLLECTION_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYNC_TYPE_COLLECTION, SyncCollectionPrivate)) typedef struct _SyncCollectionPrivate SyncCollectionPrivate; enum { STARTED, PROGRESS, CONFLICT, FINISHED, FAILED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { SyncGroup *group; gdouble progress; gchar *error; } SyncCollectionGroup; struct _SyncCollectionPrivate { GList *groups; GList *current_group; gboolean started; gdouble progress; gint num_groups; gint num_finished; gboolean abort_failed; }; static void sync_collection_finalize (GObject *object) { SyncCollection *collection = SYNC_COLLECTION (object); /* SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection);*/ /* TODO: Guard against, or cater for this happening during a sync */ sync_collection_remove_all (collection); G_OBJECT_CLASS (sync_collection_parent_class)->finalize (object); } static void sync_collection_class_init (SyncCollectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); signals[STARTED] = g_signal_new ("started", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SyncCollectionClass, started), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[PROGRESS] = g_signal_new ("progress", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SyncCollectionClass, progress), NULL, NULL, g_cclosure_marshal_VOID__DOUBLE, G_TYPE_NONE, 1, G_TYPE_DOUBLE); signals[CONFLICT] = g_signal_new ("conflict", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SyncCollectionClass, conflict), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[FINISHED] = g_signal_new ("finished", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SyncCollectionClass, finished), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[FAILED] = g_signal_new ("failed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SyncCollectionClass, failed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_type_class_add_private (klass, sizeof (SyncCollectionPrivate)); object_class->finalize = sync_collection_finalize; } static void sync_collection_init (SyncCollection *self) { } SyncCollection* sync_collection_new (void) { return g_object_new (SYNC_TYPE_COLLECTION, NULL); } static void sync_collection_namespace_func (gpointer data, gpointer user_data) { SyncCollectionGroup *col_group = data; const gchar *namespace = user_data; sync_group_set_namespace (col_group->group, namespace); } static void sync_collection_start_func (gpointer data, gpointer user_data) { SyncCollectionGroup *col_group = data; gboolean *success = user_data; if (!sync_group_start (col_group->group)) *success = FALSE; } static void sync_collection_abort_func (gpointer data, gpointer user_data) { SyncCollectionGroup *col_group = data; SyncCollection *collection = user_data; SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); if (!sync_group_abort (col_group->group)) priv->abort_failed = TRUE; } static void sync_collection_save_func (gpointer data, gpointer user_data) { SyncCollectionGroup *col_group = data; gboolean *success = user_data; if (!sync_group_save (col_group->group)) *success = FALSE; } static void sync_collection_delete_func (gpointer data, gpointer user_data) { SyncCollectionGroup *col_group = data; gboolean *success = user_data; if (!sync_group_delete (col_group->group)) *success = FALSE; } static gint sync_collection_find_func (gconstpointer a, gconstpointer b) { const SyncCollectionGroup *col_group = a; const SyncGroup *group = b; if (col_group->group == group) return 0; else return -1; } static void sync_collection_started_cb (SyncGroup *group, SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); if (!priv->started) { priv->started = TRUE; g_signal_emit (collection, signals[STARTED], 0); } } static void sync_collection_progress_cb (SyncGroup *group, gdouble progress, SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); SyncCollectionGroup *col_group = (SyncCollectionGroup *) ((g_list_find_custom (priv->groups, group, sync_collection_find_func))->data); priv->progress -= col_group->progress; col_group->progress = progress; priv->progress += col_group->progress; g_signal_emit (collection, signals[PROGRESS], 0, priv->progress / (gdouble)priv->num_groups); } static void sync_collection_conflict_cb (SyncGroup *group, SyncCollection *collection) { g_signal_emit (collection, signals[CONFLICT], 0, group); } static void sync_collection_finished_cb (SyncGroup *group, SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); g_debug ("Group in collection finished syncing"); priv->num_finished ++; if (priv->num_finished == priv->num_groups) { GList *c; gchar *error = NULL; g_debug ("All groups in collection finished syncing"); for (c = priv->groups; c; c = c->next) { gchar *new_error; SyncCollectionGroup *col_group = c->data; col_group->progress = 0; if (!col_group->error) continue; if (!error) { error = g_strdup (col_group->error); continue; } new_error = g_strconcat (error, "\n", col_group->error, NULL); g_free (error); error = new_error; g_free (col_group->error); col_group->error = NULL; } priv->num_finished = 0; priv->progress = 0; priv->started = FALSE; if (!error) { g_signal_emit (collection, signals[FINISHED], 0); } else { g_signal_emit (collection, signals[FAILED], 0, error); g_free (error); } } else { SyncCollectionGroup *col_group; priv->current_group = priv->current_group->next; col_group = (SyncCollectionGroup *)priv->current_group->data; if (!sync_group_start (col_group->group)) { col_group->error = g_strdup ( "Error starting synchronisation"); sync_collection_finished_cb (col_group->group, collection); } } } static void sync_collection_failed_cb (SyncGroup *group, const gchar *error, SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); SyncCollectionGroup *col_group = (SyncCollectionGroup *) ((g_list_find_custom (priv->groups, group, sync_collection_find_func))->data); col_group->error = g_strdup (error); sync_collection_finished_cb (col_group->group, collection); } static SyncCollectionGroup * sync_collection_group_new (SyncCollection *collection, SyncGroup *group) { SyncCollectionGroup *col_group = g_new0 (SyncCollectionGroup, 1); col_group->group = g_object_ref (group); g_signal_connect (G_OBJECT (group), "started", G_CALLBACK (sync_collection_started_cb), collection); g_signal_connect (G_OBJECT (group), "progress", G_CALLBACK (sync_collection_progress_cb), collection); g_signal_connect (G_OBJECT (group), "conflict", G_CALLBACK (sync_collection_conflict_cb), collection); g_signal_connect (G_OBJECT (group), "finished", G_CALLBACK (sync_collection_finished_cb), collection); g_signal_connect (G_OBJECT (group), "failed", G_CALLBACK (sync_collection_failed_cb), collection); return col_group; } static void sync_collection_group_free (SyncCollection *collection, SyncCollectionGroup *col_group) { g_signal_handlers_disconnect_by_func ( col_group->group, sync_collection_started_cb, collection); g_signal_handlers_disconnect_by_func ( col_group->group, sync_collection_progress_cb, collection); g_signal_handlers_disconnect_by_func ( col_group->group, sync_collection_conflict_cb, collection); g_signal_handlers_disconnect_by_func ( col_group->group, sync_collection_finished_cb, collection); g_signal_handlers_disconnect_by_func ( col_group->group, sync_collection_failed_cb, collection); g_object_unref (col_group->group); g_free (col_group->error); g_free (col_group); } void sync_collection_add_group (SyncCollection *collection, SyncGroup *group) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); priv->groups = g_list_prepend (priv->groups, sync_collection_group_new ( collection, group)); priv->num_groups ++; } void sync_collection_set_namespace (SyncCollection *collection, const gchar *namespace) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); g_list_foreach (priv->groups, sync_collection_namespace_func, (gpointer)namespace); } GList * sync_collection_get_groups (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); return g_list_copy (priv->groups); } gboolean sync_collection_start (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); if (priv->groups) { SyncCollectionGroup *col_group = (SyncCollectionGroup *) priv->groups->data; priv->current_group = priv->groups; return sync_group_start (col_group->group); } else return FALSE; } gboolean sync_collection_abort (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); priv->abort_failed = FALSE; g_list_foreach (priv->groups, sync_collection_abort_func, collection); return !priv->abort_failed; } void sync_collection_remove_group (SyncCollection *collection, SyncGroup *group) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); GList *col_group; col_group = g_list_find_custom ( priv->groups, group, sync_collection_find_func); if (col_group) { sync_collection_group_free (collection, (SyncCollectionGroup *) col_group->data); priv->groups = g_list_delete_link (priv->groups, col_group); priv->num_groups --; } } void sync_collection_remove_all (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); while (priv->groups) { SyncCollectionGroup *col_group = priv->groups->data; sync_collection_group_free (collection, col_group); priv->groups = g_list_delete_link (priv->groups, priv->groups); } priv->num_groups = 0; } gboolean sync_collection_delete (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); gboolean success = TRUE; g_list_foreach (priv->groups, sync_collection_delete_func, &success); return success; } gboolean sync_collection_save (SyncCollection *collection) { SyncCollectionPrivate *priv = COLLECTION_PRIVATE (collection); gboolean success = TRUE; g_list_foreach (priv->groups, sync_collection_save_func, &success); return success; }