diff options
-rw-r--r-- | sync/ChangeLog | 20 | ||||
-rw-r--r-- | sync/data/sync.glade | 121 | ||||
-rw-r--r-- | sync/src/Makefile.am | 4 | ||||
-rw-r--r-- | sync/src/sync.h | 53 | ||||
-rw-r--r-- | sync/src/sync_evo2_item.c | 93 | ||||
-rw-r--r-- | sync/src/sync_evo2_item.h | 13 | ||||
-rw-r--r-- | sync/src/sync_file_item.c | 63 | ||||
-rw-r--r-- | sync/src/sync_file_item.h | 10 | ||||
-rw-r--r-- | sync/src/sync_group.c | 828 | ||||
-rw-r--r-- | sync/src/sync_group.h | 85 | ||||
-rw-r--r-- | sync/src/sync_item.c | 324 | ||||
-rw-r--r-- | sync/src/sync_item.h | 77 | ||||
-rw-r--r-- | sync/src/sync_main.c | 462 | ||||
-rw-r--r-- | sync/src/sync_main.h | 51 |
14 files changed, 1815 insertions, 389 deletions
diff --git a/sync/ChangeLog b/sync/ChangeLog index 40aa389..79e0bed 100644 --- a/sync/ChangeLog +++ b/sync/ChangeLog @@ -1,3 +1,23 @@ +2006-07-27 Chris Lord,,, <chris@openedhand.com> + + * data/sync.glade: + - Minor tweaks to UI + + * src/Makefile.am: + * src/sync.h: + * src/sync_evo2_item.c: + * src/sync_evo2_item.h: + * src/sync_file_item.c: + * src/sync_file_item.h: + * src/sync_group.c: + * src/sync_group.h: + * src/sync_item.c: + * src/sync_item.h: + * src/sync_main.c: (sync_get_backup_group): + * src/sync_main.h: + - Refactor into an OpenSync convenience library and the application + - Fix various bugs, add progress indicator + 2006-07-24 Chris Lord,,, <chris@openedhand.com> * configure.ac: diff --git a/sync/data/sync.glade b/sync/data/sync.glade index 0db0f91..f8f06f1 100644 --- a/sync/data/sync.glade +++ b/sync/data/sync.glade @@ -300,59 +300,136 @@ <property name="spacing">6</property> <child> - <widget class="GtkHBox" id="sync_image_hbox"> + <widget class="GtkNotebook" id="sync_image_notebook"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> + <property name="show_tabs">False</property> + <property name="show_border">False</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> <child> - <widget class="GtkImage" id="sync_image_left"> + <widget class="GtkImage" id="prepare_image"> <property name="visible">True</property> <property name="icon_size">6</property> - <property name="icon_name">stock_contact</property> + <property name="icon_name">gtk-disconnect</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> </widget> <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> </packing> </child> <child> - <widget class="GtkImage" id="sync_image_mid"> + <widget class="GtkLabel" id="label14"> <property name="visible">True</property> - <property name="icon_size">6</property> - <property name="icon_name">gtk-copy</property> - <property name="xalign">0</property> + <property name="label" translatable="yes">label14</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> </widget> <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> + <property name="type">tab</property> </packing> </child> <child> - <widget class="GtkImage" id="sync_image_right"> + <widget class="GtkHBox" id="sync_image_hbox"> <property name="visible">True</property> - <property name="icon_size">6</property> - <property name="icon_name">gtk-floppy</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkImage" id="sync_image_left"> + <property name="visible">True</property> + <property name="icon_size">6</property> + <property name="icon_name">stock_contact</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkImage" id="sync_image_mid"> + <property name="visible">True</property> + <property name="icon_size">6</property> + <property name="icon_name">gtk-copy</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkImage" id="sync_image_right"> + <property name="visible">True</property> + <property name="icon_size">6</property> + <property name="icon_name">gtk-floppy</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="label" translatable="yes">label15</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> </widget> <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> + <property name="type">tab</property> </packing> </child> </widget> @@ -870,7 +947,7 @@ <widget class="GtkLabel" id="error_label"> <property name="visible">True</property> <property name="label" translatable="yes">An unrecoverable error occurred and the -synchornisation was aborted.</property> +synchronisation was aborted.</property> <property name="use_underline">False</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> diff --git a/sync/src/Makefile.am b/sync/src/Makefile.am index 646b906..1682416 100644 --- a/sync/src/Makefile.am +++ b/sync/src/Makefile.am @@ -3,7 +3,9 @@ AM_CFLAGS = -Wall -export-dynamic $(SYNC_CFLAGS) bin_PROGRAMS = sync -sync_SOURCES = sync_main.c sync.h +sync_SOURCES = sync_main.c sync.h sync_group.c sync_group.h sync_item.c \ + sync_item.h sync_evo2_item.c sync_evo2_item.h sync_file_item.c \ + sync_file_item.h sync_LDADD = $(SYNC_LIBS) diff --git a/sync/src/sync.h b/sync/src/sync.h deleted file mode 100644 index ad46340..0000000 --- a/sync/src/sync.h +++ /dev/null @@ -1,53 +0,0 @@ - -#ifndef SYNC_H -#define SYNC_H - -#include <gtk/gtk.h> -#include <glade/glade.h> -#include <opensync/opensync.h> - -#define XML_FILE PKGDATADIR G_DIR_SEPARATOR_S "sync.glade" -#define BACKUP_GROUP "sync-backup" -#define BACKUP_PATH "%s" G_DIR_SEPARATOR_S ".gnome2" G_DIR_SEPARATOR_S \ - "sync" G_DIR_SEPARATOR_S "backup" - -#define EVO2_PLUGIN "evo2-sync" -#define EVO2_CONFIG "<config>" \ - "<adress_path>default</adress_path>" \ - "<calendar_path>default</calendar_path>" \ - "<tasks_path>default</tasks_path>" \ - "</config>" - -#define FILE_PLUGIN "file-sync" -#define FILE_CONFIG "<config>" \ - "<path>%s</path>" \ - "<recursive>FALSE</recursive>" \ - "</config>" - -enum { - COL_ID, - COL_NAME, - COL_PIXBUF, -}; - -enum { - TAB_CHOOSE = 0, - TAB_SYNC, - TAB_CONFLICT, - TAB_ERROR, - TAB_SUCCESS -}; - -typedef struct { - GladeXML *xml; - GdkPixbuf *backup_icon; - GdkPixbuf *network_icon; - GtkListStore *model; - OSyncEnv *osync; - - gboolean syncing; - guint animate_id; -} SyncData; - - -#endif diff --git a/sync/src/sync_evo2_item.c b/sync/src/sync_evo2_item.c new file mode 100644 index 0000000..69a0c65 --- /dev/null +++ b/sync/src/sync_evo2_item.c @@ -0,0 +1,93 @@ + +#include <opensync/opensync.h> +#include <glib.h> +#include <string.h> +#include "sync.h" +#include "sync_item.h" +#include "sync_evo2_item.h" + +#define EVO2_PLUGIN "evo2-sync" +#define EVO2_CONFIG "<config>" \ + "<adress_path>%s</adress_path>" \ + "<calendar_path>%s</calendar_path>" \ + "<tasks_path>%s</tasks_path>" \ + "</config>" + +typedef struct { + gchar *adr_path; + gchar *cal_path; + gchar *task_path; +} SyncEvo2ItemData; + +static OSyncMember * +sync_evo2_item_get_member (SyncItem *item) +{ + OSyncMember *member; + OSyncError *error = NULL; + OSyncConfigurationTypes type = NO_CONFIGURATION; + + /* Setup Evolution2 sync member */ + member = osync_member_new (sync_group_get_osync_group ( + sync_item_get_group (item))); + if (!osync_member_instance_plugin (member, EVO2_PLUGIN, &error)) { + g_warning ("Error instancing Evolution2 plug-in: %s", + osync_error_print (&error)); + osync_error_free (&error); + return NULL; + } + if (!osync_member_need_config (member, &type, &error)) { + g_warning ("Error reading plug-in config requirements: %s", + osync_error_print (&error)); + osync_error_free (&error); + return NULL; + } + if (type == NO_CONFIGURATION) { + g_warning ( + "Evolution2 plug-in reports no need for configuration"); + } else { + SyncEvo2ItemData *data; + gchar *evo2_config; + + data = (SyncEvo2ItemData *)sync_item_get_data (item); + evo2_config = g_strdup_printf (EVO2_CONFIG, data->adr_path, + data->cal_path, data->task_path); + osync_member_set_config ( + member, evo2_config, strlen (evo2_config)); + g_free (evo2_config); + } + + return member; +} + +static void +sync_evo2_free_data (SyncEvo2ItemData *data) +{ + g_free (data->adr_path); + g_free (data->cal_path); + g_free (data->task_path); + g_free (data); +} + +SyncItem * +sync_evo2_item_new (const gchar *name, const gchar *address_path, + const gchar *calendar_path, const gchar *tasks_path) +{ + SyncItem *item; + SyncEvo2ItemData *data; + + item = sync_item_new (name, NULL, sync_evo2_item_get_member); + + data = g_new0 (SyncEvo2ItemData, 1); + data->adr_path = address_path ? + g_strdup (address_path) : g_strdup ("default"); + data->cal_path = calendar_path ? + g_strdup (calendar_path) : g_strdup ("default"); + data->task_path = tasks_path ? + g_strdup (tasks_path) : g_strdup ("default"); + + sync_item_set_data (item, data); + sync_item_set_data_free_func ( + item, (SyncItemDataFreeFunc)sync_evo2_free_data); + + return item; +} diff --git a/sync/src/sync_evo2_item.h b/sync/src/sync_evo2_item.h new file mode 100644 index 0000000..9717aae --- /dev/null +++ b/sync/src/sync_evo2_item.h @@ -0,0 +1,13 @@ + +#ifndef SYNC_EVO2_H +#define SYNC_EVO2_H + +#include <glib.h> +#include "sync_item.h" + +SyncItem * sync_evo2_item_new (const gchar *name, + const gchar *address_path, + const gchar *calendar_path, + const gchar *tasks_path); + +#endif diff --git a/sync/src/sync_file_item.c b/sync/src/sync_file_item.c new file mode 100644 index 0000000..9e4ce0b --- /dev/null +++ b/sync/src/sync_file_item.c @@ -0,0 +1,63 @@ + +#include <opensync/opensync.h> +#include <glib.h> +#include <string.h> +#include "sync.h" +#include "sync_item.h" +#include "sync_group.h" +#include "sync_file_item.h" + +#define FILE_PLUGIN "file-sync" +#define FILE_CONFIG "<config>" \ + "<path>%s</path>" \ + "<recursive>FALSE</recursive>" \ + "</config>" + +static OSyncMember * +sync_file_item_get_member (SyncItem *item) +{ + OSyncMember *member; + OSyncError *error = NULL; + OSyncConfigurationTypes type = NO_CONFIGURATION; + + member = osync_member_new (sync_group_get_osync_group ( + sync_item_get_group (item))); + if (!osync_member_instance_plugin (member, FILE_PLUGIN, &error)) { + g_warning ("Error instancing file plug-in: %s", + osync_error_print (&error)); + osync_error_free (&error); + return NULL; + } + if (!osync_member_need_config (member, &type, &error)) { + g_warning ("Error reading plug-in config requirements: %s", + osync_error_print (&error)); + osync_error_free (&error); + return NULL; + } + if (type == NO_CONFIGURATION) { + g_warning ( + "File sync plug-in reports no need for configuration"); + } else { + const gchar *file_path; + gchar *file_config; + + file_path = sync_item_get_adr (item); + if (g_mkdir_with_parents (file_path, 0755) == -1) { + g_warning ("Couldn't create directory '%s'", file_path); + return NULL; + } + + file_config = g_strdup_printf (FILE_CONFIG, file_path); + osync_member_set_config ( + member, file_config, strlen (file_config)); + g_free (file_config); + } + + return member; +} + +SyncItem * +sync_file_item_new (const gchar *name, const gchar *adr) +{ + return sync_item_new (name, adr, sync_file_item_get_member); +} diff --git a/sync/src/sync_file_item.h b/sync/src/sync_file_item.h new file mode 100644 index 0000000..b1ae516 --- /dev/null +++ b/sync/src/sync_file_item.h @@ -0,0 +1,10 @@ + +#ifndef SYNC_FILE_H +#define SYNC_FILE_H + +#include <glib.h> +#include "sync_item.h" + +SyncItem * sync_file_item_new (const gchar *name, const gchar *adr); + +#endif diff --git a/sync/src/sync_group.c b/sync/src/sync_group.c new file mode 100644 index 0000000..f9aa6f6 --- /dev/null +++ b/sync/src/sync_group.c @@ -0,0 +1,828 @@ + +#include <glib.h> +#include <opensync/opensync.h> +#include <osengine/engine.h> +#include "sync_group.h" +#include "sync_item.h" + +G_DEFINE_TYPE (SyncGroup, sync_group, G_TYPE_OBJECT); + +#define GROUP_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYNC_TYPE_GROUP, SyncGroupPrivate)) + +/*#define DEBUG*/ + +typedef struct _SyncGroupPrivate SyncGroupPrivate; + +enum { + PROP_ITEM1 = 1, + PROP_ITEM2, + PROP_NAMESPACE, +}; + +enum { + STARTED, + PROGRESS, + CONFLICT, + FINISHED, + FAILED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _SyncGroupPrivate +{ + SyncItem *item1; + SyncItem *item2; + gchar *namespace; + OSyncGroup *group; + OSyncEngine *engine; + OSyncMapping *mapping; /* Required for conflicts */ + + gint max_changes; /* Used to work out sync progress */ + gint changes; + guint progress_id; + GMutex *mutex; +}; + +static OSyncEnv *default_env = NULL; + +static void +sync_group_finalize (GObject *object) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (object); + + /* FIXME: Guard against finalisation during syncs */ + if (priv->item1) g_object_unref (priv->item1); + if (priv->item2) g_object_unref (priv->item2); + g_mutex_free (priv->mutex); + + G_OBJECT_CLASS (sync_group_parent_class)->finalize (object); +} + +static void +sync_group_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SyncGroup *group = SYNC_GROUP (object); + + switch (prop_id) { + case PROP_ITEM1 : + sync_group_set_item1 (group, + g_value_get_object (value)); + break; + case PROP_ITEM2 : + sync_group_set_item2 (group, + g_value_get_object (value)); + break; + case PROP_NAMESPACE : + sync_group_set_namespace (group, + g_value_get_string (value)); + break; + default : + G_OBJECT_WARN_INVALID_PROPERTY_ID ( + object, prop_id, pspec); + break; + } +} + +static void +sync_group_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SyncGroup *group = SYNC_GROUP (object); + + switch (prop_id) { + case PROP_ITEM1 : + g_value_set_object (value, + sync_group_get_item1 (group)); + break; + case PROP_ITEM2 : + g_value_set_object (value, + sync_group_get_item2 (group)); + break; + case PROP_NAMESPACE : + g_value_set_string (value, + sync_group_get_namespace (group)); + break; + default : + G_OBJECT_WARN_INVALID_PROPERTY_ID ( + object, prop_id, pspec); + break; + } +} + +static void +sync_group_class_init (SyncGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = sync_group_set_property; + object_class->get_property = sync_group_get_property; + + g_object_class_install_property ( + object_class, + PROP_ITEM1, + g_param_spec_object ( + "item1", + ("Item1"), + ("The first item in the synchronisation pair"), + SYNC_TYPE_ITEM, + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_ITEM2, + g_param_spec_object ( + "item2", + ("Item2"), + ("The second item in the synchronisation pair"), + SYNC_TYPE_ITEM, + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_NAMESPACE, + g_param_spec_string ( + "namespace", + ("Name-space"), + ("Name-space of the synchronisation group."), + ("libsync"), + G_PARAM_READWRITE)); + + signals[STARTED] = + g_signal_new ("started", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SyncGroupClass, 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 (SyncGroupClass, 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 (SyncGroupClass, conflict), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[FINISHED] = + g_signal_new ("finished", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SyncGroupClass, 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 (SyncGroupClass, failed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (SyncGroupPrivate)); + + object_class->finalize = sync_group_finalize; +} + +static void +sync_group_init (SyncGroup *self) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (self); + + priv->mutex = g_mutex_new (); + priv->namespace = g_strdup ("libsync"); +} + +SyncGroup* +sync_group_new (void) +{ + return g_object_new (SYNC_TYPE_GROUP, NULL); +} + +SyncGroup * +sync_group_new_with_items (SyncItem *item1, SyncItem *item2) +{ + return g_object_new (SYNC_TYPE_GROUP, "item1", item1, "item2", item2, + NULL); +} + +void +sync_group_set_item1 (SyncGroup *group, SyncItem *item1) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if (priv->item1) + sync_item_ungroup (priv->item1); + if (item1) { + priv->item1 = g_object_ref_sink (item1); + sync_item_set_group (item1, group); + } +} + +void +sync_group_set_item2 (SyncGroup *group, SyncItem *item2) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if (priv->item2) + sync_item_ungroup (priv->item2); + if (item2) { + priv->item2 = g_object_ref_sink (item2); + sync_item_set_group (item2, group); + } +} + +void sync_group_set_namespace (SyncGroup *group, const gchar *namespace) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + g_free (priv->namespace); + priv->namespace = g_strdup (namespace); +} + +SyncItem * +sync_group_get_item1 (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + return priv->item1; +} + +SyncItem * +sync_group_get_item2 (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + return priv->item2; +} + +const gchar * +sync_group_get_namespace (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + return priv->namespace; +} + +static OSyncEnv * +sync_group_get_default_env () +{ + if (!default_env) { + OSyncError *error = NULL; + default_env = osync_env_new (); + g_debug ("Creating new environment"); + if (!osync_env_initialize (default_env, &error)) { + g_error ("Unable to initialise OSyncEnv: %s", + osync_error_print (&error)); + osync_env_free (default_env); + default_env = NULL; + } + } + + return default_env; +} + +gchar *sync_group_get_name (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if ((!priv->item1) || (!priv->item2)) { + g_warning ("%s called on group without two items", G_STRFUNC); + return NULL; + } + + return g_strdup_printf ("%s-%s-%s", + sync_group_get_namespace (group), + sync_item_get_name (priv->item1), + sync_item_get_name (priv->item2)); +} + +gboolean +sync_group_delete (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + OSyncEnv *env; + OSyncGroup *os_group; + OSyncError *error = NULL; + gchar *name; + SyncItem *item1 = NULL; + SyncItem *item2 = NULL; + + if (priv->item1) { + item1 = g_object_ref (priv->item1); + sync_item_ungroup (priv->item1); + } + if (priv->item2) { + item2 = g_object_ref (priv->item2); + sync_item_ungroup (priv->item2); + } + + env = sync_group_get_default_env (); + name = sync_group_get_name (group); + os_group = osync_env_find_group (env, name); + g_free (name); + if (!os_group) return TRUE; + + if (!osync_group_delete (os_group, &error)) { + g_warning ("Failed to delete group: %s", + osync_error_print (&error)); + osync_error_free (&error); + return FALSE; + } + + if (item1) { + sync_group_set_item1 (group, item1); + g_object_unref (item1); + } + if (item2) { + sync_group_set_item2 (group, item2); + g_object_unref (item2); + } + + return TRUE; +} + +gboolean +sync_group_save (SyncGroup *group) +{ + OSyncError *error = NULL; + OSyncGroup *os_group = sync_group_get_osync_group (group); + + if (!os_group) { + g_warning ("Failed to retrieve OSyncGroup in %s", G_STRFUNC); + return FALSE; + } + + if (!osync_group_save (os_group, &error)) { + g_warning ("Unable to save OSyncGroup: %s", + osync_error_print (&error)); + osync_error_free (&error); + return FALSE; + } + + return TRUE; +} + +OSyncGroup * +sync_group_get_osync_group (SyncGroup *group) +{ + gchar *name; + OSyncEnv *env; + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if ((!priv->item1) || (!priv->item2)) { + g_warning ("%s called on group without two items", G_STRFUNC); + return NULL; + } + + if (priv->group) + return priv->group; + + env = sync_group_get_default_env (); + name = sync_group_get_name (group); + priv->group = osync_env_find_group (env, name); + + if (!priv->group) { + priv->group = osync_group_new (env); + g_debug ("Creating new group"); + osync_group_set_name (priv->group, name); + + /* Create OSyncMembers (adds them to group) */ + sync_item_get_member (priv->item1); + sync_item_get_member (priv->item2); + } + g_free (name); + + return priv->group; +} + +static gboolean +sync_group_started_idle (SyncGroup *group) +{ + g_signal_emit (group, signals[STARTED], 0); + + return FALSE; +} + +static gboolean +sync_group_progress_idle (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + g_mutex_lock (priv->mutex); + g_signal_emit (group, signals[PROGRESS], 0, priv->changes / + (gdouble)priv->max_changes); + priv->progress_id = 0; + g_mutex_unlock (priv->mutex); + + return FALSE; +} + +static gboolean +sync_group_conflict_idle (SyncGroup *group) +{ + g_signal_emit (group, signals[CONFLICT], 0); + + return FALSE; +} + +static void +sync_group_free_engine (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + osengine_finalize (priv->engine); + osengine_free (priv->engine); + priv->engine = NULL; +} + +static gboolean +sync_group_finished_idle (SyncGroup *group) +{ + sync_group_free_engine (group); + g_signal_emit (group, signals[FINISHED], 0); + + return FALSE; +} + +static gboolean +sync_group_failed_idle (SyncGroup *group) +{ + sync_group_free_engine (group); + g_signal_emit (group, signals[FAILED], 0); + + return FALSE; +} + +static const char * +OSyncChangeType2String (OSyncChangeType c) +{ + switch (c) { + case CHANGE_ADDED: return "ADDED"; + case CHANGE_UNMODIFIED: return "UNMODIFIED"; + case CHANGE_DELETED: return "DELETED"; + case CHANGE_MODIFIED: return "MODIFIED"; + default: + case CHANGE_UNKNOWN: return "?"; + } +} + +static void +sync_conflict_cb (OSyncEngine *engine, OSyncMapping *mapping, + void *user_data) +{ + SyncGroup *group = (SyncGroup *)user_data; + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + priv->mapping = mapping; + g_idle_add ((GSourceFunc)sync_group_conflict_idle, group); +} + +static void +sync_changestatus_cb (OSyncEngine *engine, OSyncChangeUpdate *status, + void *user_data) +{ + SyncGroup *group = (SyncGroup *)user_data; + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + switch (status->type) { + case CHANGE_RECEIVED: + /* Shouldn't need to lock here, but just in case */ + g_mutex_lock (priv->mutex); + priv->max_changes ++; + g_mutex_unlock (priv->mutex); +#ifdef DEBUG + g_debug ("Received a entry %s with data of size %i from " + "member %i. Changetype %s", + osync_change_get_uid (status->change), + osync_change_get_datasize (status->change), + status->member_id, + OSyncChangeType2String ( + osync_change_get_changetype ( + status->change))); +#endif + break; + case CHANGE_SENT: + /* Update progress and add idle func to send progress + * signal in main thread. + */ + g_mutex_lock (priv->mutex); + if (priv->changes == 0) + g_idle_add ((GSourceFunc) + sync_group_started_idle, group); + priv->changes ++; + if (priv->progress_id == 0) + priv->progress_id = g_idle_add ((GSourceFunc) + sync_group_progress_idle, group); + g_mutex_unlock (priv->mutex); +#ifdef DEBUG + g_debug ("Sent a entry %s of size %i to member %i. " + "Changetype %s", + osync_change_get_uid (status->change), + osync_change_get_datasize (status->change), + status->member_id, + OSyncChangeType2String ( + osync_change_get_changetype ( + status->change))); +#endif + break; +#ifdef DEBUG + case CHANGE_RECEIVED_INFO: + g_debug ("Received a entry %s without data from member " + "%i. Changetype %s", + osync_change_get_uid (status->change), + status->member_id, + OSyncChangeType2String ( + osync_change_get_changetype ( + status->change))); + break; + case CHANGE_WRITE_ERROR: + g_debug ("Error writing entry %s to member %i: %s", + osync_change_get_uid (status->change), + status->member_id, + osync_error_print (&(status->error))); + break; + case CHANGE_RECV_ERROR: + g_debug ("Error reading entry %s from member %i: %s", + osync_change_get_uid (status->change), + status->member_id, + osync_error_print (&(status->error))); + break; +#endif + } +} +#ifdef DEBUG +static void +sync_mappingstatus_cb (OSyncMappingUpdate *status, void *user_data) +{ +/* SyncGroup *group = (SyncGroup *)user_data; + SyncGroupPrivate *priv = GROUP_PRIVATE (group);*/ + + switch (status->type) { + case MAPPING_SOLVED: + g_debug ("Mapping solved"); + break; + case MAPPING_SYNCED: + g_debug ("Mapping Synced"); + break; + case MAPPING_WRITE_ERROR: + g_debug ("Mapping Write Error: %s", + osync_error_print (&(status->error))); + break; + } +} +#endif +static void +sync_enginestatus_cb (OSyncEngine *engine, OSyncEngineUpdate *status, + void *user_data) +{ + SyncGroup *group = (SyncGroup *)user_data; + + switch (status->type) { +#ifdef DEBUG + case ENG_PREV_UNCLEAN: + g_debug ("The previous synchronization was unclean. " + "Slow-syncing"); + break; + case ENG_ENDPHASE_CON: + g_debug ("All clients connected or error"); + break; + case ENG_END_CONFLICTS: + g_debug ("All conflicts have been reported"); + break; + case ENG_ENDPHASE_READ: + g_debug ("All clients sent changes or error"); + break; + case ENG_ENDPHASE_WRITE: + g_debug ("All clients have written"); + break; + case ENG_ENDPHASE_DISCON: + g_debug ("All clients have disconnected"); + break; +#endif + case ENG_SYNC_SUCCESSFULL: + g_idle_add ((GSourceFunc) + sync_group_finished_idle, group); + break; + case ENG_ERROR: + g_idle_add ((GSourceFunc) + sync_group_failed_idle, group); + break; + } +} +#ifdef DEBUG +static void +sync_memberstatus_cb (OSyncMemberUpdate *status, void *user_data) +{ + switch (status->type) { + case MEMBER_CONNECTED: + g_debug ("Member %lli of type %s just connected", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member)); + break; + case MEMBER_DISCONNECTED: + g_debug ("Member %lli of type %s just disconnected", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member)); + break; + case MEMBER_SENT_CHANGES: + g_debug ("Member %lli of type %s just sent all changes", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member)); + break; + case MEMBER_COMMITTED_ALL: + g_debug ( + "Member %lli of type %s committed all changes.", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member)); + break; + case MEMBER_CONNECT_ERROR: + g_debug ("Member %lli of type %s had an " + "error while connecting: %s", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member), + osync_error_print (&(status->error))); + break; + case MEMBER_GET_CHANGES_ERROR: + g_debug ("Member %lli of type %s had an error while " + "getting changes: %s", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member), + osync_error_print (&(status->error))); + break; + case MEMBER_SYNC_DONE_ERROR: + g_debug ("Member %lli of type %s had an error while " + "calling sync done: %s", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member), + osync_error_print (&(status->error))); + break; + case MEMBER_DISCONNECT_ERROR: + g_debug ("Member %lli of type %s had an error while " + "disconnecting: %s", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member), + osync_error_print (&(status->error))); + break; + case MEMBER_COMMITTED_ALL_ERROR: + g_debug ("Member %lli of type %s had an error while " + "commiting changes: %s", + osync_member_get_id (status->member), + osync_member_get_pluginname (status->member), + osync_error_print (&(status->error))); + break; + } +} +#endif +gboolean +sync_group_start (SyncGroup *group) +{ + OSyncError *error = NULL; + OSyncGroup *os_group; + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if (!(os_group = sync_group_get_osync_group (group))) { + g_warning ("Failed to create OSyncGroup"); + return FALSE; + } + + /* Save the group (NOTE: Won't be necessary in the future?) */ + if (!sync_group_save (group)) { + g_warning ("Save failed, not starting sync"); + return FALSE; + } + + if (!(priv->engine = osengine_new (os_group, &error))) { + g_warning ("Error while creating syncengine: %s", + osync_error_print (&error)); + osync_error_free (&error); + return FALSE; + } + + osengine_set_conflict_callback ( + priv->engine, sync_conflict_cb, group); + osengine_set_changestatus_callback ( + priv->engine, sync_changestatus_cb, group); +#ifdef DEBUG + osengine_set_mappingstatus_callback ( + priv->engine, sync_mappingstatus_cb, group); + osengine_set_memberstatus_callback ( + priv->engine, sync_memberstatus_cb, group); +#endif + osengine_set_enginestatus_callback ( + priv->engine, sync_enginestatus_cb, group); + + if (!osengine_init (priv->engine, &error)) { + g_warning ("Error while initializing syncengine: %s", + osync_error_print (&error)); + osync_error_free (&error); + sync_group_free_engine (group); + return FALSE; + } + + priv->changes = 0; + priv->max_changes = 0; + + if (!osengine_synchronize (priv->engine, &error)) { + g_warning ("Error while starting synchronization: %s", + osync_error_print (&error)); + osync_error_free (&error); + sync_group_free_engine (group); + return FALSE; + } + + return TRUE; +} + +void +sync_group_resolve_conflict (SyncGroup *group, SyncGroupConflictRes res) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + if ((!priv->mapping) || (!priv->engine)) { + g_warning ("%s called with no conflict to resolve", G_STRFUNC); + return; + } + + switch (res) { + case USE_ITEM1: + case USE_ITEM2: + case ABORT: + case IGNORE: + default: + osengine_mapping_ignore_conflict ( + priv->engine, priv->mapping); + } + + priv->mapping = NULL; +} + +void +sync_group_abort (SyncGroup *group) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + if (priv->engine) osengine_abort (priv->engine); + else g_warning ("%s called, but no sync in progress", G_STRFUNC); +} + +static void +sync_group_regroup (SyncGroup *group, gboolean item1, gboolean item2) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + + /* Regroup items so their members can be recreated */ + if (priv->group) { + SyncItem *item; + + /* Freeing groups with OpenSync removes them from the env */ + priv->group = NULL; + + if (item1) { + item = g_object_ref (priv->item1); + sync_item_ungroup (priv->item1); + sync_group_set_item1 (group, item); + g_object_unref (item); + } + if (item2) { + item = g_object_ref (priv->item2); + sync_item_ungroup (priv->item2); + sync_group_set_item2 (group, item); + g_object_unref (item); + } + } +} + +void sync_group_remove_item (SyncGroup *group, + SyncItem *item) +{ + SyncGroupPrivate *priv = GROUP_PRIVATE (group); + gboolean item1; + + if (priv->item1 == item) { + sync_group_regroup (group, FALSE, TRUE); + priv->item1 = NULL; + g_object_unref (item); + item1 = TRUE; + } else if (priv->item2 == item) { + sync_group_regroup (group, TRUE, FALSE); + priv->item2 = NULL; + g_object_unref (item); + } else { + g_warning ("%s called on group not containing item", G_STRFUNC); + return; + } +} diff --git a/sync/src/sync_group.h b/sync/src/sync_group.h new file mode 100644 index 0000000..a9e5869 --- /dev/null +++ b/sync/src/sync_group.h @@ -0,0 +1,85 @@ + +#ifndef SYNC_GROUP_H +#define SYNC_GROUP_H + +#include <opensync/opensync.h> + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define SYNC_TYPE_GROUP sync_group_get_type() + +#define SYNC_GROUP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + SYNC_TYPE_GROUP, SyncGroup)) + +#define SYNC_GROUP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + SYNC_TYPE_GROUP, SyncGroupClass)) + +#define SYNC_IS_GROUP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + SYNC_TYPE_GROUP)) + +#define SYNC_IS_GROUP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + SYNC_TYPE_GROUP)) + +#define SYNC_GROUP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + SYNC_TYPE_GROUP, SyncGroupClass)) + +typedef struct { + GObject parent; +} SyncGroup; + +typedef struct { + GObjectClass parent_class; + + void (* started) (SyncGroup *group); + void (* progress) (SyncGroup *group, gdouble progress); + void (* conflict) (SyncGroup *group); + void (* finished) (SyncGroup *group); + void (* failed) (SyncGroup *group); +} SyncGroupClass; + +GType sync_group_get_type (void); + +typedef enum { + USE_ITEM1, + USE_ITEM2, + ABORT, + IGNORE, +} SyncGroupConflictRes; + +#include "sync_item.h" + +SyncGroup * sync_group_new (void); +SyncGroup * sync_group_new_with_items (SyncItem *item1, + SyncItem *item2); + +void sync_group_set_item1 (SyncGroup *group, + SyncItem *item1); +void sync_group_set_item2 (SyncGroup *group, + SyncItem *item2); +void sync_group_set_namespace (SyncGroup *group, + const gchar *namespace); +SyncItem * sync_group_get_item1 (SyncGroup *group); +SyncItem * sync_group_get_item2 (SyncGroup *group); +OSyncGroup * sync_group_get_osync_group (SyncGroup *group); +const gchar * sync_group_get_namespace (SyncGroup *group); +gchar * sync_group_get_name (SyncGroup *group); + +gboolean sync_group_start (SyncGroup *group); +void sync_group_resolve_conflict (SyncGroup *group, + SyncGroupConflictRes res); +void sync_group_abort (SyncGroup *group); +void sync_group_remove_item (SyncGroup *group, + SyncItem *item); +gboolean sync_group_delete (SyncGroup *group); +gboolean sync_group_save (SyncGroup *group); + +G_END_DECLS + +#endif diff --git a/sync/src/sync_item.c b/sync/src/sync_item.c new file mode 100644 index 0000000..324972c --- /dev/null +++ b/sync/src/sync_item.c @@ -0,0 +1,324 @@ + +#include <glib.h> +#include <opensync/opensync.h> +#include "sync_item.h" +#include "sync_group.h" + +G_DEFINE_TYPE (SyncItem, sync_item, G_TYPE_INITIALLY_UNOWNED); + +#define ITEM_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYNC_TYPE_ITEM, SyncItemPrivate)) + +typedef struct _SyncItemPrivate SyncItemPrivate; + +enum { + PROP_NAME = 1, + PROP_ADR, + PROP_DATA, + PROP_DATA_FREE_FUNC, + PROP_MEMBER_FUNC, + PROP_GROUP, +}; + +struct _SyncItemPrivate +{ + gchar *name; + gchar *adr; + gpointer data; + SyncItemMemberFunc get_member; + SyncItemDataFreeFunc free_data; + SyncGroup *group; + OSyncMember *member; +}; + +static void +sync_item_finalize (GObject *object) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (object); + + g_free (priv->name); + g_free (priv->adr); + if (priv->free_data && priv->data) + priv->free_data (priv->data); + + G_OBJECT_CLASS (sync_item_parent_class)->finalize (object); +} + +static void +sync_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SyncItem *item = SYNC_ITEM (object); + + switch (prop_id) { + case PROP_NAME : + sync_item_set_name (item, g_value_get_string (value)); + break; + case PROP_ADR : + sync_item_set_adr (item, g_value_get_string (value)); + break; + case PROP_DATA : + sync_item_set_data (item, g_value_get_pointer (value)); + break; + case PROP_DATA_FREE_FUNC : + sync_item_set_data_free_func ( + item, g_value_get_pointer (value)); + break; + case PROP_MEMBER_FUNC : + sync_item_set_member_func ( + item, g_value_get_pointer (value)); + break; + case PROP_GROUP : + sync_item_set_group (item, + SYNC_GROUP (g_value_get_object (value))); + break; + default : + G_OBJECT_WARN_INVALID_PROPERTY_ID ( + object, prop_id, pspec); + break; + } +} + +static void +sync_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SyncItem *item = SYNC_ITEM (object); + + switch (prop_id) { + case PROP_NAME : + g_value_set_string (value, sync_item_get_name (item)); + break; + case PROP_ADR : + g_value_set_string (value, sync_item_get_adr (item)); + break; + case PROP_DATA : + g_value_set_pointer (value, sync_item_get_data (item)); + break; + case PROP_DATA_FREE_FUNC : + g_value_set_pointer ( + value, sync_item_get_data_free_func (item)); + break; + case PROP_MEMBER_FUNC : + g_value_set_pointer ( + value, sync_item_get_member_func (item)); + break; + case PROP_GROUP : + g_value_set_object ( + value, sync_item_get_group (item)); + break; + default : + G_OBJECT_WARN_INVALID_PROPERTY_ID ( + object, prop_id, pspec); + break; + } +} + +static void +sync_item_class_init (SyncItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = sync_item_set_property; + object_class->get_property = sync_item_get_property; + + g_object_class_install_property ( + object_class, + PROP_NAME, + g_param_spec_string ( + "name", + ("Name"), + ("Name of the synchronisation item."), + ("Name"), + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_ADR, + g_param_spec_string ( + "adr", + ("Address"), + ("Location of the synchronisation item."), + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_DATA, + g_param_spec_pointer ( + "data", + ("Data"), + ("Custom data"), + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_DATA_FREE_FUNC, + g_param_spec_pointer ( + "data_free_func", + ("Data Freeing Function"), + ("Function pointer to free set custom data."), + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_MEMBER_FUNC, + g_param_spec_pointer ( + "member_func", + ("Member Function"), + ("Function pointer to retrieve OSyncMember."), + G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_GROUP, + g_param_spec_object ( + "group", + ("SyncGroup"), + ("SyncGroup this item belongs to."), + SYNC_TYPE_GROUP, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (SyncItemPrivate)); + + object_class->finalize = sync_item_finalize; +} + +static void +sync_item_init (SyncItem *self) +{ + /* Make sure this object is floating? */ + if (g_object_is_floating (G_OBJECT (self))) + g_object_force_floating (G_OBJECT (self)); +} + +SyncItem* +sync_item_new (const gchar *name, + const gchar *adr, + SyncItemMemberFunc member_func) +{ + return g_object_new (SYNC_TYPE_ITEM, "name", name, "adr", adr, + "member_func", member_func, NULL); +} + +const gchar * +sync_item_get_name (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->name; +} + +const gchar * +sync_item_get_adr (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->adr; +} + +gpointer +sync_item_get_data (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->data; +} + +SyncItemDataFreeFunc +sync_item_get_data_free_func (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->free_data; +} + +SyncItemMemberFunc +sync_item_get_member_func (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->get_member; +} + +OSyncMember * +sync_item_get_member (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + + if (!priv->group) { + g_warning ("%s called on SyncItem with no group", G_STRFUNC); + return NULL; + } + + if (!priv->member) + priv->member = priv->get_member (item); + + return priv->member; +} + +SyncGroup * +sync_item_get_group (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + return priv->group; +} + +void +sync_item_set_name (SyncItem *item, const gchar *name) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + priv->name = g_strdup (name); +} + +void +sync_item_set_adr (SyncItem *item, const gchar *adr) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + priv->adr = g_strdup (adr); +} + +void +sync_item_set_data (SyncItem *item, gpointer data) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + if (priv->free_data && priv->data) + priv->free_data (priv->data); + priv->data = data; +} + +void +sync_item_set_data_free_func (SyncItem *item, + SyncItemDataFreeFunc data_free_func) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + priv->free_data = data_free_func; +} + +void +sync_item_set_member_func (SyncItem *item, + SyncItemMemberFunc member_func) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + priv->get_member = member_func; +} + +void +sync_item_set_group (SyncItem *item, SyncGroup *group) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + if (priv->group) { + g_warning ("SyncItem already contained in a group"); + return; + } + priv->group = group; +} + +void +sync_item_ungroup (SyncItem *item) +{ + SyncItemPrivate *priv = ITEM_PRIVATE (item); + if (priv->group) { + /* Removing the item from the group frees the group, which in + * turn frees the member. + */ + sync_group_remove_item (priv->group, item); + priv->group = NULL; + priv->member = NULL; + } else + g_warning ("%s called on SyncItem with no group", G_STRFUNC); +} diff --git a/sync/src/sync_item.h b/sync/src/sync_item.h new file mode 100644 index 0000000..f7a4930 --- /dev/null +++ b/sync/src/sync_item.h @@ -0,0 +1,77 @@ + +#ifndef SYNC_ITEM_H +#define SYNC_ITEM_H + +#include <glib.h> +#include <glib-object.h> +#include <opensync/opensync.h> + +G_BEGIN_DECLS + +#define SYNC_TYPE_ITEM sync_item_get_type() + +#define SYNC_ITEM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + SYNC_TYPE_ITEM, SyncItem)) + +#define SYNC_ITEM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + SYNC_TYPE_ITEM, SyncItemClass)) + +#define SYNC_IS_ITEM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + SYNC_TYPE_ITEM)) + +#define SYNC_IS_ITEM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + SYNC_TYPE_ITEM)) + +#define SYNC_ITEM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + SYNC_TYPE_ITEM, SyncItemClass)) + +typedef struct { + GObject parent; +} SyncItem; + +typedef struct { + GObjectClass parent_class; +} SyncItemClass; + +typedef OSyncMember * (*SyncItemMemberFunc) (SyncItem *item); +typedef void (*SyncItemDataFreeFunc) (gpointer data); + +GType sync_item_get_type (void); + +#include "sync_group.h" + +SyncItem * sync_item_new (const gchar * name, + const gchar * adr, + SyncItemMemberFunc member_func); +const gchar * sync_item_get_name (SyncItem * item); +const gchar * sync_item_get_adr (SyncItem * item); +gpointer sync_item_get_data (SyncItem * item); +OSyncMember * sync_item_get_member (SyncItem * item); +SyncGroup * sync_item_get_group (SyncItem * item); + +SyncItemDataFreeFunc sync_item_get_data_free_func (SyncItem * item); +SyncItemMemberFunc sync_item_get_member_func (SyncItem * item); + +void sync_item_set_name (SyncItem * item, + const gchar * name); +void sync_item_set_adr (SyncItem * item, + const gchar * adr); +void sync_item_set_data (SyncItem * item, + gpointer data); +void sync_item_set_data_free_func (SyncItem * item, + SyncItemDataFreeFunc data_free_func); +void sync_item_set_member_func (SyncItem * item, + SyncItemMemberFunc member_func); +void sync_item_set_group (SyncItem * item, + SyncGroup * group); + +void sync_item_ungroup (SyncItem * item); + +G_END_DECLS + +#endif diff --git a/sync/src/sync_main.c b/sync/src/sync_main.c index e83f21d..85ca89c 100644 --- a/sync/src/sync_main.c +++ b/sync/src/sync_main.c @@ -9,8 +9,12 @@ #include <opensync/opensync.h> #include <osengine/engine.h> -#include "sync.h" - +#include "sync_main.h" +#include "sync_group.h" +#include "sync_item.h" +#include "sync_evo2_item.h" +#include "sync_file_item.h" +#if 0 static gboolean sync_clean_backup (SyncData *data) { @@ -137,317 +141,94 @@ sync_get_backup_group (SyncData *data) return group; } +#endif static gboolean sync_animate (SyncData *data) { static gint direction = 1; - gfloat x, y; - GtkWidget *widget = glade_xml_get_widget (data->xml, "sync_image_mid"); - - gtk_misc_get_alignment (GTK_MISC (widget), &x, &y); - x += 0.05 * direction; - if ((x > 1) || (x < 0)) { - direction = -direction; - } - gtk_misc_set_alignment (GTK_MISC (widget), x, y); + static gint count = 0; - return data->syncing; -} - -static const char * -OSyncChangeType2String (OSyncChangeType c) -{ - switch (c) { - case CHANGE_ADDED: return "ADDED"; - case CHANGE_UNMODIFIED: return "UNMODIFIED"; - case CHANGE_DELETED: return "DELETED"; - case CHANGE_MODIFIED: return "MODIFIED"; - default: - case CHANGE_UNKNOWN: return "?"; + if (data->started) { + gfloat x, y; + GtkWidget *widget = glade_xml_get_widget ( + data->xml, "sync_image_mid"); + + gtk_misc_get_alignment (GTK_MISC (widget), &x, &y); + x += 0.05 * direction; + if ((x > 1) || (x < 0)) { + direction = -direction; + } + gtk_misc_set_alignment (GTK_MISC (widget), x, y); + } else { + count ++; + if (count >= ANIM_FREQ / SWITCH_FREQ) { + GtkWidget *widget = glade_xml_get_widget (data->xml, + "prepare_image"); + + count = 0; + if (direction == 1) { + gtk_image_set_from_icon_name ( + GTK_IMAGE (widget), + "gtk-connect", GTK_ICON_SIZE_DIALOG); + } else { + gtk_image_set_from_icon_name ( + GTK_IMAGE (widget), + "gtk-disconnect", GTK_ICON_SIZE_DIALOG); + } + direction = -direction; + } } -} - -static void -sync_conflict_cb (OSyncEngine *engine, OSyncMapping *mapping, - void *user_data) -{ - GtkWidget *widget; - SyncData *data = (SyncData *)user_data; - - gdk_threads_enter (); - widget = glade_xml_get_widget (data->xml, "main_notebook"); - gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_CONFLICT); - gdk_threads_leave (); - osengine_mapping_ignore_conflict (engine, mapping); + return TRUE; } static void -sync_changestatus_cb (OSyncEngine *engine, OSyncChangeUpdate *status, - void *user_data) +sync_started_cb (SyncGroup *group, SyncData *data) { - SyncData *data = (SyncData *)user_data; - - switch (status->type) { - case CHANGE_RECEIVED_INFO: - g_debug ("Received a entry %s without data from member " - "%i. Changetype %s", - osync_change_get_uid (status->change), - status->member_id, - OSyncChangeType2String ( - osync_change_get_changetype ( - status->change))); - break; - case CHANGE_RECEIVED: - g_debug ("Received a entry %s with data of size %i from " - "member %i. Changetype %s", - osync_change_get_uid (status->change), - osync_change_get_datasize (status->change), - status->member_id, - OSyncChangeType2String ( - osync_change_get_changetype ( - status->change))); - break; - case CHANGE_SENT: - g_debug ("Sent a entry %s of size %i to member %i. " - "Changetype %s", - osync_change_get_uid (status->change), - osync_change_get_datasize (status->change), - status->member_id, - OSyncChangeType2String ( - osync_change_get_changetype ( - status->change))); - break; - case CHANGE_WRITE_ERROR: - g_debug ("Error writing entry %s to member %i: %s", - osync_change_get_uid (status->change), - status->member_id, - osync_error_print (&(status->error))); - break; - case CHANGE_RECV_ERROR: - g_debug ("Error reading entry %s from member %i: %s", - osync_change_get_uid (status->change), - status->member_id, - osync_error_print (&(status->error))); - break; - } + GtkWidget *widget = glade_xml_get_widget ( + data->xml, "sync_progressbar"); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget), + "Synchronising..."); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget), 0); + + widget = glade_xml_get_widget ( + data->xml, "sync_image_notebook"); + gtk_notebook_set_current_page ( + GTK_NOTEBOOK (widget), IMAGE_TAB_SYNC); + + data->started = TRUE; } static void -sync_mappingstatus_cb (OSyncMappingUpdate *status, void *user_data) -{ - SyncData *data = (SyncData *)user_data; - - switch (status->type) { - case MAPPING_SOLVED: - g_debug ("Mapping solved"); - break; - case MAPPING_SYNCED: - g_debug ("Mapping Synced"); - break; - case MAPPING_WRITE_ERROR: - g_debug ("Mapping Write Error: %s", - osync_error_print (&(status->error))); - break; - } -} - -static gboolean -sync_finish_cb (gpointer data) +sync_progress_cb (SyncGroup *group, gdouble progress, SyncData *data) { - OSyncEngine *engine = (OSyncEngine *)data; - - g_debug ("Finalizing/freeing sync engine"); - osengine_finalize (engine); - osengine_free (engine); - - return FALSE; + GtkWidget *widget = glade_xml_get_widget ( + data->xml, "sync_progressbar"); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget), progress); } static void -sync_enginestatus_cb (OSyncEngine *engine, OSyncEngineUpdate *status, - void *user_data) +sync_conflict_cb (SyncGroup *group, SyncData *data) { - GtkWidget *widget; - SyncData *data = (SyncData *)user_data; - - switch (status->type) { - case ENG_PREV_UNCLEAN: - g_debug ("The previous synchronization was unclean. " - "Slow-syncing"); - return; - case ENG_ENDPHASE_CON: - g_debug ("All clients connected or error"); - return; - case ENG_END_CONFLICTS: - g_debug ("All conflicts have been reported"); - return; - case ENG_ENDPHASE_READ: - g_debug ("All clients sent changes or error"); - return; - case ENG_ENDPHASE_WRITE: - g_debug ("All clients have written"); - return; - case ENG_ENDPHASE_DISCON: - g_debug ("All clients have disconnected"); - return; - case ENG_SYNC_SUCCESSFULL: - g_debug ("The sync was successful"); - gdk_threads_enter (); - widget = glade_xml_get_widget ( - data->xml, "main_notebook"); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_SUCCESS); - gdk_threads_leave (); - break; - case ENG_ERROR: - g_debug ("The sync failed: %s", - osync_error_print (&(status->error))); - gdk_threads_enter (); - widget = glade_xml_get_widget ( - data->xml, "main_notebook"); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_ERROR); - gdk_threads_leave (); -/* g_debug ("Aborting synchronisation..."); - osengine_abort (engine);*/ - break; - } - - data->syncing = FALSE; - g_debug ("Setting idle function to clean up engine."); - g_idle_add (sync_finish_cb, engine); + GtkWidget *widget = glade_xml_get_widget (data->xml, "main_notebook"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_CONFLICT); } static void -sync_memberstatus_cb (OSyncMemberUpdate *status, void *user_data) +sync_finished_cb (SyncGroup *group, SyncData *data) { - SyncData *data = (SyncData *)user_data; - - switch (status->type) { - case MEMBER_CONNECTED: - g_debug ("Member %lli of type %s just connected", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member)); - break; - case MEMBER_DISCONNECTED: - g_debug ("Member %lli of type %s just disconnected", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member)); - break; - case MEMBER_SENT_CHANGES: - g_debug ("Member %lli of type %s just sent all changes", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member)); - break; - case MEMBER_COMMITTED_ALL: - g_debug ( - "Member %lli of type %s committed all changes.", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member)); - break; - case MEMBER_CONNECT_ERROR: - g_debug ("Member %lli of type %s had an " - "error while connecting: %s", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member), - osync_error_print (&(status->error))); - break; - case MEMBER_GET_CHANGES_ERROR: - g_debug ("Member %lli of type %s had an error while " - "getting changes: %s", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member), - osync_error_print (&(status->error))); - break; - case MEMBER_SYNC_DONE_ERROR: - g_debug ("Member %lli of type %s had an error while " - "calling sync done: %s", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member), - osync_error_print (&(status->error))); - break; - case MEMBER_DISCONNECT_ERROR: - g_debug ("Member %lli of type %s had an error while " - "disconnecting: %s", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member), - osync_error_print (&(status->error))); - break; - case MEMBER_COMMITTED_ALL_ERROR: - g_debug ("Member %lli of type %s had an error while " - "commiting changes: %s", - osync_member_get_id (status->member), - osync_member_get_pluginname (status->member), - osync_error_print (&(status->error))); - break; - } + GtkWidget *widget = glade_xml_get_widget (data->xml, "main_notebook"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_SUCCESS); + g_source_remove (data->animate_id); } static void -sync_backup (SyncData *data) +sync_failed_cb (SyncGroup *group, SyncData *data) { - OSyncGroup *group; - OSyncEngine *engine; - GtkWidget *widget; - OSyncError *error = NULL; - - widget = glade_xml_get_widget (data->xml, "main_notebook"); - - if (!(group = sync_get_backup_group (data))) { - g_warning ("Unable to retrieve back-up group"); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_ERROR); - return; - } - - if (!(engine = osengine_new (group, &error))) { - g_warning ("Error while creating syncengine: %s", - osync_error_print (&error)); - osync_error_free (&error); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_ERROR); - return; - } - -/* osengine_set_message_callback (engine, NULL, NULL);*/ - osengine_set_conflict_callback ( - engine, sync_conflict_cb, data); - osengine_set_changestatus_callback ( - engine, sync_changestatus_cb, data); - osengine_set_mappingstatus_callback ( - engine, sync_mappingstatus_cb, data); - osengine_set_enginestatus_callback ( - engine, sync_enginestatus_cb, data); - osengine_set_memberstatus_callback ( - engine, sync_memberstatus_cb, data); - - if (!osengine_init (engine, &error)) { - g_warning ("Error while initializing syncengine: %s", - osync_error_print (&error)); - osync_error_free (&error); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_ERROR); - return; - } - - if (!osengine_synchronize (engine, &error)) { - g_warning ("Error while starting synchronization: %s", - osync_error_print (&error)); - osync_error_free (&error); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (widget), TAB_ERROR); - return; - } - - /* Switch to sync tab */ - gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_SYNC); - /* Start sync animation */ - g_debug ("Started synchronisation..."); - data->syncing = TRUE; - data->animate_id = g_timeout_add ( - 100, (GSourceFunc)sync_animate, data); + GtkWidget *widget = glade_xml_get_widget (data->xml, "main_notebook"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_ERROR); + g_source_remove (data->animate_id); } static gboolean @@ -464,17 +245,47 @@ sync_item_activated_cb (GtkIconView *iconview, GtkTreePath *arg1, if (gtk_tree_model_get_iter ( GTK_TREE_MODEL (data->model), &iter, arg1)) { - gint id; + SyncItem *item; + const gchar *name; + GtkWidget *widget; + gfloat x, y; + gtk_tree_model_get (GTK_TREE_MODEL (data->model), - &iter, COL_ID, &id, -1); + &iter, COL_NAME, &name, -1); - /* Special case for back-up item */ - if (id == 0) { - g_debug ("Performing backup"); - sync_backup (data); - + item = (SyncItem *)g_hash_table_lookup (data->sync_items, name); + + sync_group_set_item2 (data->group, item); + data->started = FALSE; + + if (!sync_group_start (data->group)) { + /* Switch to error tab */ + widget = glade_xml_get_widget ( + data->xml, "main_notebook"); + gtk_notebook_set_current_page ( + GTK_NOTEBOOK (widget), TAB_ERROR); return; } + + /* Set progress bar text */ + widget = glade_xml_get_widget (data->xml, "sync_progressbar"); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget), + "Preparing..."); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget), 0); + + /* Switch to sync/prepare tab and start animation */ + widget = glade_xml_get_widget ( + data->xml, "sync_image_mid"); + gtk_misc_get_alignment (GTK_MISC (widget), &x, &y); + gtk_misc_set_alignment (GTK_MISC (widget), 0, y); + widget = glade_xml_get_widget ( + data->xml, "sync_image_notebook"); + gtk_notebook_set_current_page ( + GTK_NOTEBOOK (widget), IMAGE_TAB_PREPARE); + widget = glade_xml_get_widget (data->xml, "main_notebook"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), TAB_SYNC); + data->animate_id = g_timeout_add ( + ANIM_FREQ, (GSourceFunc)sync_animate, data); } } @@ -506,14 +317,15 @@ main (int argc, char **argv) GtkTreePath *path; GtkIconTheme *icon_theme; gint width = 0, height = 0; - OSyncError *error = NULL; SyncData data; + SyncItem *preset_item; + gchar *temp_path; gtk_init (&argc, &argv); glade_init (); - g_thread_init (NULL); - gdk_threads_init (); - /* Must be called due to buggy OpenSync */ +/* g_thread_init (NULL); + gdk_threads_init ();*/ + /* FIXME: Must be called due to buggy OpenSync */ gconf_client_get_default (); data.xml = glade_xml_new (XML_FILE, NULL, NULL); @@ -524,38 +336,50 @@ main (int argc, char **argv) icon_theme = gtk_icon_theme_get_default (); gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height); data.backup_icon = gtk_icon_theme_load_icon (icon_theme, - GTK_STOCK_FLOPPY, MIN (width, height), 0, NULL); + GTK_STOCK_FLOPPY, MIN (width, height), + GTK_ICON_LOOKUP_USE_BUILTIN, NULL); + data.restore_icon = gtk_icon_theme_load_icon (icon_theme, + GTK_STOCK_OPEN, MIN (width, height), + GTK_ICON_LOOKUP_USE_BUILTIN, NULL); data.network_icon = gtk_icon_theme_load_icon (icon_theme, - GTK_STOCK_NETWORK, MIN (width, height), 0, NULL); + GTK_STOCK_NETWORK, MIN (width, height), + GTK_ICON_LOOKUP_USE_BUILTIN, NULL); /* Setup icon-view */ widget = glade_xml_get_widget (data.xml, "main_iconview"); data.model = gtk_list_store_new ( - 3, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF); + 2, G_TYPE_STRING, GDK_TYPE_PIXBUF); gtk_icon_view_set_model (GTK_ICON_VIEW (widget), GTK_TREE_MODEL (data.model)); gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (widget), COL_PIXBUF); gtk_icon_view_set_text_column (GTK_ICON_VIEW (widget), COL_NAME); - /* Insert backup item in icon-view */ + /* Create local sync-item and sync group */ + data.local_item = sync_evo2_item_new ("Local", NULL, NULL, NULL); + data.group = sync_group_new_with_items (data.local_item, NULL); + + /* Create SyncItem hash-table */ + data.sync_items = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, g_object_unref); + /* Insert preset items */ + temp_path = g_strdup_printf (TEMP_PATH, g_getenv ("HOME")); + preset_item = g_object_ref (sync_file_item_new ("Test", temp_path)); + g_hash_table_insert (data.sync_items, "Test", preset_item); gtk_list_store_insert_with_values (GTK_LIST_STORE (data.model), &iter, - 0, COL_ID, 0, COL_NAME, "Backup", - COL_PIXBUF, data.backup_icon, -1); + 0, COL_NAME, "Test", COL_PIXBUF, data.backup_icon, -1); + g_free (temp_path); + + /* TODO: Insert saved items */ + /* TODO: Avahi detection of local sync servers */ + /* TODO: Bluetooth scanning? */ + /* Insert backup item */ + /* Insert restore item */ + + /* Select first item */ path = gtk_tree_path_new_first (); gtk_icon_view_select_path (GTK_ICON_VIEW (widget), path); gtk_tree_path_free (path); - /* Create OpenSync environment */ - data.osync = osync_env_new (); - osync_env_set_option (data.osync, - "GROUPS_DIRECTORY", (const char *)NULL); - osync_env_set_option (data.osync, "LOAD_GROUPS", "TRUE"); - osync_env_set_option (data.osync, "LOAD_PLUGINS", "TRUE"); - osync_env_set_option (data.osync, "LOAD_FORMATS", "TRUE"); - if (!osync_env_initialize (data.osync, &error)) { - g_error ("Unable to initialise OpenSync environment: %s", - osync_error_print (&error)); - } widget = glade_xml_get_widget (data.xml, "quit_menuitem"); g_signal_connect (G_OBJECT (widget), "activate", @@ -579,6 +403,18 @@ main (int argc, char **argv) widget = glade_xml_get_widget (data.xml, "error_close_button"); g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (sync_chooser_cb), &data); + + /* Connect SyncGroup signals */ + g_signal_connect (G_OBJECT (data.group), "started", + G_CALLBACK (sync_started_cb), &data); + g_signal_connect (G_OBJECT (data.group), "progress", + G_CALLBACK (sync_progress_cb), &data); + g_signal_connect (G_OBJECT (data.group), "conflict", + G_CALLBACK (sync_conflict_cb), &data); + g_signal_connect (G_OBJECT (data.group), "finished", + G_CALLBACK (sync_finished_cb), &data); + g_signal_connect (G_OBJECT (data.group), "failed", + G_CALLBACK (sync_failed_cb), &data); gtk_main (); diff --git a/sync/src/sync_main.h b/sync/src/sync_main.h new file mode 100644 index 0000000..528b70e --- /dev/null +++ b/sync/src/sync_main.h @@ -0,0 +1,51 @@ + +#ifndef SYNC_MAIN_H +#define SYNC_MAIN_H + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <opensync/opensync.h> +#include "sync_group.h" +#include "sync_item.h" + +#define XML_FILE PKGDATADIR G_DIR_SEPARATOR_S "sync.glade" +#define TEMP_PATH "%s" G_DIR_SEPARATOR_S ".gnome2" G_DIR_SEPARATOR_S \ + "sync" G_DIR_SEPARATOR_S "backup" + +#define ANIM_FREQ 100 +#define SWITCH_FREQ 10 + +enum { + COL_NAME, + COL_PIXBUF, +}; + +enum { + TAB_CHOOSE = 0, + TAB_SYNC, + TAB_CONFLICT, + TAB_ERROR, + TAB_SUCCESS +}; + +enum { + IMAGE_TAB_PREPARE = 0, + IMAGE_TAB_SYNC, +}; + +typedef struct { + GladeXML *xml; + GdkPixbuf *backup_icon; + GdkPixbuf *restore_icon; + GdkPixbuf *network_icon; + GtkListStore *model; + SyncItem *local_item; + SyncGroup *group; + GHashTable *sync_items; + + gboolean started; + guint animate_id; +} SyncData; + + +#endif |