[evolution] Bug #657808 - Copy/move of a single instance should grab whole serie



commit ed2bc85f4fe13a67aec032c8dddef0614df6419f
Author: Milan Crha <mcrha redhat com>
Date:   Mon Nov 4 21:04:07 2013 +0100

    Bug #657808 - Copy/move of a single instance should grab whole serie

 calendar/gui/comp-util.c                    |  363 +++++++++++++++++++++++++++
 calendar/gui/comp-util.h                    |   17 ++
 calendar/gui/e-calendar-selector.c          |  320 ++++++++++++++++--------
 modules/calendar/e-cal-shell-view-private.c |  222 +++++++----------
 modules/calendar/e-cal-shell-view-private.h |    3 +
 5 files changed, 696 insertions(+), 229 deletions(-)
---
diff --git a/calendar/gui/comp-util.c b/calendar/gui/comp-util.c
index be67211..a5c231e 100644
--- a/calendar/gui/comp-util.c
+++ b/calendar/gui/comp-util.c
@@ -822,3 +822,366 @@ icalcomp_suggest_filename (icalcomponent *icalcomp,
 
        return g_strconcat (summary, ".ics", NULL);
 }
+
+typedef struct _AsyncContext {
+       ECalClient *src_client;
+       icalcomponent *icalcomp_clone;
+       gboolean do_copy;
+} AsyncContext;
+
+struct ForeachTzidData
+{
+       ECalClient *source_client;
+       ECalClient *destination_client;
+       GCancellable *cancellable;
+       GError **error;
+       gboolean success;
+};
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+       if (async_context->src_client)
+               g_object_unref (async_context->src_client);
+
+       if (async_context->icalcomp_clone)
+               icalcomponent_free (async_context->icalcomp_clone);
+
+       g_slice_free (AsyncContext, async_context);
+}
+
+static void
+add_timezone_to_cal_cb (icalparameter *param,
+                       gpointer data)
+{
+       struct ForeachTzidData *ftd = data;
+       icaltimezone *tz = NULL;
+       const gchar *tzid;
+
+       g_return_if_fail (ftd != NULL);
+       g_return_if_fail (ftd->source_client != NULL);
+       g_return_if_fail (ftd->destination_client != NULL);
+
+       if (!ftd->success)
+               return;
+
+       if (ftd->cancellable && g_cancellable_is_cancelled (ftd->cancellable)) {
+               ftd->success = FALSE;
+               return;
+       }
+
+       tzid = icalparameter_get_tzid (param);
+       if (!tzid || !*tzid)
+               return;
+
+       if (e_cal_client_get_timezone_sync (ftd->source_client, tzid, &tz, ftd->cancellable, NULL) && tz)
+               ftd->success = e_cal_client_add_timezone_sync (
+                               ftd->destination_client, tz, ftd->cancellable, ftd->error);
+}
+
+/* Helper for cal_comp_transfer_item_to() */
+static void
+cal_comp_transfer_item_to_thread (GSimpleAsyncResult *simple,
+                                 GObject *source_object,
+                                 GCancellable *cancellable)
+{
+       AsyncContext *async_context;
+       GError *local_error = NULL;
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       cal_comp_transfer_item_to_sync (
+               async_context->src_client,
+               E_CAL_CLIENT (source_object),
+               async_context->icalcomp_clone,
+               async_context->do_copy,
+               cancellable, &local_error);
+
+       if (local_error != NULL)
+               g_simple_async_result_take_error (simple, local_error);
+}
+
+void
+cal_comp_transfer_item_to (ECalClient *src_client,
+                          ECalClient *dest_client,
+                          icalcomponent *icalcomp_vcal,
+                          gboolean do_copy,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (E_IS_CAL_CLIENT (src_client), FALSE);
+       g_return_val_if_fail (E_IS_CAL_CLIENT (dest_client), FALSE);
+       g_return_val_if_fail (icalcomp_vcal != NULL, FALSE);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->src_client = g_object_ref (src_client);
+       async_context->icalcomp_clone = icalcomponent_new_clone (icalcomp_vcal);
+       async_context->do_copy = do_copy;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (dest_client), callback, user_data,
+               cal_comp_transfer_item_to);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       g_simple_async_result_run_in_thread (
+               simple, cal_comp_transfer_item_to_thread,
+               G_PRIORITY_DEFAULT, cancellable);
+
+       g_object_unref (simple);
+}
+
+gboolean
+cal_comp_transfer_item_to_finish (ECalClient *client,
+                                 GAsyncResult *result,
+                                 GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (result, G_OBJECT (client), cal_comp_transfer_item_to),
+               FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean
+cal_comp_transfer_item_to_sync (ECalClient *src_client,
+                               ECalClient *dest_client,
+                               icalcomponent *icalcomp_vcal,
+                               gboolean do_copy,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       icalcomponent *icalcomp;
+       icalcomponent *icalcomp_event, *subcomp;
+       icalcomponent_kind icalcomp_kind;
+       const gchar *uid;
+       gchar *new_uid = NULL;
+       struct ForeachTzidData ftd;
+       ECalClientSourceType source_type;
+       GHashTable *processed_uids;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_CLIENT (src_client), FALSE);
+       g_return_val_if_fail (E_IS_CAL_CLIENT (dest_client), FALSE);
+       g_return_val_if_fail (icalcomp_vcal != NULL, FALSE);
+
+       icalcomp_event = icalcomponent_get_inner (icalcomp_vcal);
+       g_return_val_if_fail (icalcomp_event != NULL, FALSE);
+
+       source_type = e_cal_client_get_source_type (src_client);
+       switch (source_type) {
+               case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+                       icalcomp_kind = ICAL_VEVENT_COMPONENT;
+                       break;
+               case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+                       icalcomp_kind = ICAL_VTODO_COMPONENT;
+                       break;
+               case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+                       icalcomp_kind = ICAL_VJOURNAL_COMPONENT;
+                       break;
+               default:
+                       g_return_val_if_reached (FALSE);
+       }
+
+       processed_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       icalcomp_event = icalcomponent_get_first_component (icalcomp_vcal, icalcomp_kind);
+       /*
+        * This check should be removed in the near future.
+        * We should be able to work properly with multiselection, which means that we always
+        * will receive a component with subcomponents.
+        */
+       if (icalcomp_event == NULL)
+               icalcomp_event = icalcomp_vcal;
+       for (;
+            icalcomp_event;
+            icalcomp_event = icalcomponent_get_next_component (icalcomp_vcal, icalcomp_kind)) {
+               GError *local_error = NULL;
+
+               uid = icalcomponent_get_uid (icalcomp_event);
+
+               if (g_hash_table_lookup (processed_uids, uid))
+                       continue;
+
+               success = e_cal_client_get_object_sync (dest_client, uid, NULL, &icalcomp, cancellable, 
&local_error);
+               if (success) {
+                       success = e_cal_client_modify_object_sync (
+                               dest_client, icalcomp_event, CALOBJ_MOD_ALL, cancellable, error);
+
+                       icalcomponent_free (icalcomp);
+                       if (!success)
+                               goto exit;
+
+                       if (!do_copy) {
+                               ECalObjModType mod_type = CALOBJ_MOD_THIS;
+
+                               /* Remove the item from the source calendar. */
+                               if (e_cal_util_component_is_instance (icalcomp_event) ||
+                                   e_cal_util_component_has_recurrences (icalcomp_event))
+                                       mod_type = CALOBJ_MOD_ALL;
+
+                               success = e_cal_client_remove_object_sync (
+                                               src_client, uid, NULL, mod_type, cancellable, error);
+                               if (!success)
+                                       goto exit;
+                       }
+
+                       continue;
+               } else if (local_error != NULL && !g_error_matches (
+                                       local_error, E_CAL_CLIENT_ERROR, 
E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
+                       g_propagate_error (error, local_error);
+                       goto exit;
+               } else {
+                       g_clear_error (&local_error);
+               }
+
+               if (e_cal_util_component_is_instance (icalcomp_event)) {
+                       GSList *ecalcomps = NULL, *eiter;
+                       ECalComponent *comp ;
+
+                       success = e_cal_client_get_objects_for_uid_sync (src_client, uid, &ecalcomps, 
cancellable, error);
+                       if (!success)
+                               goto exit;
+
+                       if (ecalcomps && !ecalcomps->next) {
+                               /* only one component, no need for a vCalendar list */
+                               comp = ecalcomps->data;
+                               icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
+                       } else {
+                               icalcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT);
+                               for (eiter = ecalcomps; eiter; eiter = g_slist_next (eiter)) {
+                                       comp = eiter->data;
+
+                                       icalcomponent_add_component (icalcomp,
+                                               icalcomponent_new_clone (e_cal_component_get_icalcomponent 
(comp)));
+                               }
+                       }
+
+                       e_cal_client_free_ecalcomp_slist (ecalcomps);
+               } else {
+                       icalcomp = icalcomponent_new_clone (icalcomp_event);
+               }
+
+               if (do_copy) {
+                       /* Change the UID to avoid problems with duplicated UID */
+                       new_uid = e_cal_component_gen_uid ();
+                       if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
+                               /* in case of a vCalendar, the component might have detached instances,
+                                  thus change the UID on all of the subcomponents of it */
+                               for (subcomp = icalcomponent_get_first_component (icalcomp, icalcomp_kind);
+                                    subcomp;
+                                    subcomp = icalcomponent_get_next_component (icalcomp, icalcomp_kind)) {
+                                       icalcomponent_set_uid (subcomp, new_uid);
+                               }
+                       } else {
+                               icalcomponent_set_uid (icalcomp, new_uid);
+                       }
+                       g_free (new_uid);
+                       new_uid = NULL;
+               }
+
+               ftd.source_client = src_client;
+               ftd.destination_client = dest_client;
+               ftd.cancellable = cancellable;
+               ftd.error = error;
+               ftd.success = TRUE;
+
+               if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
+                       /* in case of a vCalendar, the component might have detached instances,
+                          thus check timezones on all of the subcomponents of it */
+                       for (subcomp = icalcomponent_get_first_component (icalcomp, icalcomp_kind);
+                            subcomp && ftd.success;
+                            subcomp = icalcomponent_get_next_component (icalcomp, icalcomp_kind)) {
+                               icalcomponent_foreach_tzid (subcomp, add_timezone_to_cal_cb, &ftd);
+                       }
+               } else {
+                       icalcomponent_foreach_tzid (icalcomp, add_timezone_to_cal_cb, &ftd);
+               }
+
+               if (!ftd.success) {
+                       success = FALSE;
+                       goto exit;
+               }
+
+               if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
+                       gboolean did_add = FALSE;
+
+                       /* in case of a vCalendar, the component might have detached instances,
+                          thus add the master object first, and then all of the subcomponents of it */
+                       for (subcomp = icalcomponent_get_first_component (icalcomp, icalcomp_kind);
+                            subcomp && !did_add;
+                            subcomp = icalcomponent_get_next_component (icalcomp, icalcomp_kind)) {
+                               if (icaltime_is_null_time (icalcomponent_get_recurrenceid (subcomp))) {
+                                       did_add = TRUE;
+                                       success = e_cal_client_create_object_sync (dest_client, subcomp,
+                                               &new_uid, cancellable, error);
+                                       g_free (new_uid);
+                               }
+                       }
+
+                       if (!success) {
+                               icalcomponent_free (icalcomp);
+                               goto exit;
+                       }
+
+                       /* deal with detached instances */
+                       for (subcomp = icalcomponent_get_first_component (icalcomp, icalcomp_kind);
+                            subcomp && success;
+                            subcomp = icalcomponent_get_next_component (icalcomp, icalcomp_kind)) {
+                               if (!icaltime_is_null_time (icalcomponent_get_recurrenceid (subcomp))) {
+                                       if (did_add) {
+                                               success = e_cal_client_modify_object_sync (dest_client, 
subcomp,
+                                                       CALOBJ_MOD_THIS, cancellable, error);
+                                       } else {
+                                               /* just in case there are only detached instances and no 
master object */
+                                               did_add = TRUE;
+                                               success = e_cal_client_create_object_sync (dest_client, 
subcomp,
+                                                       &new_uid, cancellable, error);
+                                               g_free (new_uid);
+                                       }
+                               }
+                       }
+               } else {
+                       success = e_cal_client_create_object_sync (dest_client, icalcomp, &new_uid, 
cancellable, error);
+                       g_free (new_uid);
+               }
+
+               icalcomponent_free (icalcomp);
+               if (!success)
+                       goto exit;
+
+               if (!do_copy) {
+                       ECalObjModType mod_type = CALOBJ_MOD_THIS;
+
+                       /* Remove the item from the source calendar. */
+                       if (e_cal_util_component_is_instance (icalcomp_event) ||
+                           e_cal_util_component_has_recurrences (icalcomp_event))
+                               mod_type = CALOBJ_MOD_ALL;
+
+                       success = e_cal_client_remove_object_sync (src_client, uid, NULL, mod_type, 
cancellable, error);
+                       if (!success)
+                               goto exit;
+               }
+
+               g_hash_table_insert (processed_uids, g_strdup (uid), GINT_TO_POINTER (1));
+       }
+
+ exit:
+       g_hash_table_destroy (processed_uids);
+
+       return success;
+}
diff --git a/calendar/gui/comp-util.h b/calendar/gui/comp-util.h
index dcf5844..56ddd54 100644
--- a/calendar/gui/comp-util.h
+++ b/calendar/gui/comp-util.h
@@ -75,4 +75,21 @@ void comp_util_sanitize_recurrence_master (ECalComponent *comp, ECalClient *clie
 
 gchar *icalcomp_suggest_filename (icalcomponent *icalcomp, const gchar *default_name);
 
+void cal_comp_transfer_item_to                 (ECalClient *src_client,
+                                                ECalClient *dest_client,
+                                                icalcomponent *icalcomp_vcal,
+                                                gboolean do_copy,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean cal_comp_transfer_item_to_finish      (ECalClient *client,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean cal_comp_transfer_item_to_sync                (ECalClient *src_client,
+                                                ECalClient *dest_client,
+                                                icalcomponent *icalcomp_event,
+                                                gboolean do_copy,
+                                                GCancellable *cancellable,
+                                                GError **error);
+
 #endif
diff --git a/calendar/gui/e-calendar-selector.c b/calendar/gui/e-calendar-selector.c
index 9557c44..b588c6a 100644
--- a/calendar/gui/e-calendar-selector.c
+++ b/calendar/gui/e-calendar-selector.c
@@ -20,7 +20,10 @@
 
 #include <config.h>
 
+#include <glib/gi18n.h>
+
 #include "e-calendar-selector.h"
+#include "comp-util.h"
 
 #include <libecal/libecal.h>
 
@@ -30,6 +33,8 @@
 
 struct _ECalendarSelectorPrivate {
        EShellView *shell_view;
+
+       gpointer transfer_alert; /* weak pointer to EAlert */
 };
 
 G_DEFINE_TYPE (
@@ -42,118 +47,188 @@ enum {
        PROP_SHELL_VIEW,
 };
 
-static gboolean
-calendar_selector_update_single_object (ECalClient *client,
-                                        icalcomponent *icalcomp)
+static void
+cal_transferring_update_alert (ECalendarSelector *calendar_selector,
+                              EShellView *shell_view,
+                              const gchar *domain,
+                              const gchar *calendar,
+                              const gchar *message)
 {
-       gchar *uid;
-       icalcomponent *tmp_icalcomp = NULL;
-       gboolean success;
-
-       uid = (gchar *) icalcomponent_get_uid (icalcomp);
+       ECalendarSelectorPrivate *priv;
+       EShellContent *shell_content;
+       EAlert *alert;
 
-       e_cal_client_get_object_sync (
-               client, uid, NULL, &tmp_icalcomp, NULL, NULL);
+       g_return_if_fail (calendar_selector != NULL);
+       g_return_if_fail (calendar_selector->priv != NULL);
 
-       if (tmp_icalcomp != NULL) {
-               icalcomponent_free (tmp_icalcomp);
+       priv = calendar_selector->priv;
 
-               return e_cal_client_modify_object_sync (
-                       client, icalcomp, CALOBJ_MOD_ALL, NULL, NULL);
+       if (priv->transfer_alert) {
+               e_alert_response (
+                       priv->transfer_alert,
+                       e_alert_get_default_response (priv->transfer_alert));
+               priv->transfer_alert = NULL;
        }
 
-       uid = NULL;
-       success = e_cal_client_create_object_sync (
-               client, icalcomp, &uid, NULL, NULL);
+       if (!message)
+               return;
 
-       if (uid != NULL) {
-               icalcomponent_set_uid (icalcomp, uid);
-               g_free (uid);
-       }
+       alert = e_alert_new (domain, calendar, message, NULL);
+       g_return_if_fail (alert != NULL);
 
-       return success;
+       priv->transfer_alert = alert;
+       g_object_add_weak_pointer (G_OBJECT (alert), &priv->transfer_alert);
+       e_alert_start_timer (priv->transfer_alert, 300);
+
+       shell_content = e_shell_view_get_shell_content (shell_view);
+       e_alert_sink_submit_alert (E_ALERT_SINK (shell_content), priv->transfer_alert);
+       g_object_unref (priv->transfer_alert);
 }
 
-static gboolean
-calendar_selector_update_objects (ECalClient *client,
-                                  icalcomponent *icalcomp)
+typedef struct _TransferItemToData {
+       ESource *destination;
+       ESourceSelector *selector;
+       EClient *src_client;
+       EShellView *shell_view;
+       EActivity *activity;
+       icalcomponent *icalcomp;
+       const gchar *display_name;
+       gboolean do_copy;
+} TransferItemToData;
+
+static void
+transfer_item_to_cb (GObject *source_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
 {
-       icalcomponent *subcomp;
-       icalcomponent_kind kind;
-
-       kind = icalcomponent_isa (icalcomp);
-       if (kind == ICAL_VTODO_COMPONENT || kind == ICAL_VEVENT_COMPONENT)
-               return calendar_selector_update_single_object (
-                       client, icalcomp);
-       else if (kind != ICAL_VCALENDAR_COMPONENT)
-               return FALSE;
-
-       subcomp = icalcomponent_get_first_component (
-               icalcomp, ICAL_ANY_COMPONENT);
-       while (subcomp != NULL) {
-               gboolean success;
-
-               kind = icalcomponent_isa (subcomp);
-               if (kind == ICAL_VTIMEZONE_COMPONENT) {
-                       icaltimezone *zone;
-                       GError *error = NULL;
-
-                       zone = icaltimezone_new ();
-                       icaltimezone_set_component (zone, subcomp);
-
-                       e_cal_client_add_timezone_sync (client, zone, NULL, &error);
-                       icaltimezone_free (zone, 1);
-
-                       if (error != NULL) {
-                               g_warning (
-                                       "%s: Failed to add timezone: %s",
-                                       G_STRFUNC, error->message);
-                               g_error_free (error);
-                               return FALSE;
-                       }
-               } else if (kind == ICAL_VTODO_COMPONENT ||
-                       kind == ICAL_VEVENT_COMPONENT) {
-                       success = calendar_selector_update_single_object (
-                               client, subcomp);
-                       if (!success)
-                               return FALSE;
-               }
-
-               subcomp = icalcomponent_get_next_component (
-                       icalcomp, ICAL_ANY_COMPONENT);
+       TransferItemToData *titd = user_data;
+       GError *error = NULL;
+       GCancellable *cancellable;
+       gboolean success;
+
+       success = cal_comp_transfer_item_to_finish (E_CAL_CLIENT (source_object), result, &error);
+
+       if (!success) {
+               cal_transferring_update_alert (
+                       E_CALENDAR_SELECTOR (titd->selector),
+                       titd->shell_view,
+                       titd->do_copy ? "calendar:failed-copy-event" : "calendar:failed-move-event",
+                       titd->display_name,
+                       error->message);
+               g_clear_error (&error);
        }
 
-       return TRUE;
+       cancellable = e_activity_get_cancellable (titd->activity);
+       e_activity_set_state (
+               titd->activity,
+               g_cancellable_is_cancelled (cancellable) ? E_ACTIVITY_CANCELLED : E_ACTIVITY_COMPLETED);
+
+       g_object_unref (titd->activity);
+       icalcomponent_free (titd->icalcomp);
+       g_free (titd);
 }
 
 static void
-client_connect_cb (GObject *source_object,
-                   GAsyncResult *result,
-                   gpointer user_data)
+destination_client_connect_cb (GObject *source_object,
+                              GAsyncResult *result,
+                              gpointer user_data)
 {
        EClient *client;
-       icalcomponent *icalcomp = user_data;
+       TransferItemToData *titd = user_data;
+       GCancellable *cancellable;
        GError *error = NULL;
 
-       g_return_if_fail (icalcomp != NULL);
+       client = e_client_selector_get_client_finish (E_CLIENT_SELECTOR (source_object), result, &error);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((client != NULL) && (error == NULL)) ||
+               ((client == NULL) && (error != NULL)));
+
+       cancellable = e_activity_get_cancellable (titd->activity);
+
+       if (error != NULL) {
+               cal_transferring_update_alert (
+                       E_CALENDAR_SELECTOR (titd->selector),
+                       titd->shell_view,
+                       titd->do_copy ? "calendar:failed-copy-event" : "calendar:failed-move-event",
+                       titd->display_name,
+                       error->message);
+               g_clear_error (&error);
+
+               goto exit;
+       }
+
+       if (g_cancellable_is_cancelled (cancellable))
+               goto exit;
+
+       cal_comp_transfer_item_to (
+               E_CAL_CLIENT (titd->src_client), E_CAL_CLIENT (client),
+               titd->icalcomp, titd->do_copy, cancellable, transfer_item_to_cb, titd);
+
+       return;
+
+exit:
+       e_activity_set_state (
+               titd->activity,
+               g_cancellable_is_cancelled (cancellable) ? E_ACTIVITY_CANCELLED : E_ACTIVITY_COMPLETED);
+
+       g_object_unref (titd->activity);
+       icalcomponent_free (titd->icalcomp);
+       g_free (titd);
+
+}
+
+static void
+source_client_connect_cb (GObject *source_object,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+       EClient *client;
+       TransferItemToData *titd = user_data;
+       GCancellable *cancellable;
+       GError *error = NULL;
 
-       client = e_client_selector_get_client_finish (
-               E_CLIENT_SELECTOR (source_object), result, &error);
+       client = e_client_selector_get_client_finish (E_CLIENT_SELECTOR (source_object), result, &error);
 
        /* Sanity check. */
        g_return_if_fail (
                ((client != NULL) && (error == NULL)) ||
                ((client == NULL) && (error != NULL)));
 
+       cancellable = e_activity_get_cancellable (titd->activity);
+
        if (error != NULL) {
-               g_warning ("%s: %s", G_STRFUNC, error->message);
-               g_error_free (error);
+               cal_transferring_update_alert (
+                       E_CALENDAR_SELECTOR (titd->selector),
+                       titd->shell_view,
+                       titd->do_copy ? "calendar:failed-copy-event" : "calendar:failed-move-event",
+                       titd->display_name,
+                       error->message);
+               g_clear_error (&error);
+
+               goto exit;
        }
 
-       calendar_selector_update_objects (E_CAL_CLIENT (client), icalcomp);
-       g_object_unref (client);
+       if (g_cancellable_is_cancelled (cancellable))
+               goto exit;
+
+       titd->src_client = client;
+
+       e_client_selector_get_client (
+               E_CLIENT_SELECTOR (titd->selector), titd->destination, cancellable,
+               destination_client_connect_cb, titd);
+
+       return;
+
+exit:
+       e_activity_set_state (
+               titd->activity,
+               g_cancellable_is_cancelled (cancellable) ? E_ACTIVITY_CANCELLED : E_ACTIVITY_COMPLETED);
 
-       icalcomponent_free (icalcomp);
+       g_object_unref (titd->activity);
+       icalcomponent_free (titd->icalcomp);
+       g_free (titd);
 }
 
 static void
@@ -239,40 +314,83 @@ calendar_selector_data_dropped (ESourceSelector *selector,
                                 GdkDragAction action,
                                 guint info)
 {
-       GtkTreePath *path = NULL;
        icalcomponent *icalcomp;
+       EActivity *activity;
+       EShellBackend *shell_backend;
+       EShellView *shell_view;
+       ESource *source;
+       ESourceRegistry *registry;
+       GCancellable *cancellable;
+       gchar **segments;
+       gchar *source_uid = NULL;
+       gchar *message;
+       const gchar *display_name;
        const guchar *data;
-       gboolean success = FALSE;
-       gpointer object = NULL;
+       gboolean do_copy;
+       TransferItemToData *titd;
 
        data = gtk_selection_data_get_data (selection_data);
-       icalcomp = icalparser_parse_string ((const gchar *) data);
+       g_return_val_if_fail (data != NULL, FALSE);
 
-       if (icalcomp == NULL)
+       segments = g_strsplit ((const gchar *) data, "\n", 2);
+       if (g_strv_length (segments) != 2)
                goto exit;
 
-       /* FIXME Deal with GDK_ACTION_ASK. */
-       if (action == GDK_ACTION_COPY) {
-               gchar *uid;
+       source_uid = g_strdup (segments[0]);
+       icalcomp = icalparser_parse_string (segments[1]);
 
-               uid = e_cal_component_gen_uid ();
-               icalcomponent_set_uid (icalcomp, uid);
-       }
+       if (!icalcomp)
+               goto exit;
 
-       e_client_selector_get_client (
-               E_CLIENT_SELECTOR (selector), destination, NULL,
-               client_connect_cb, icalcomp);
+       registry = e_source_selector_get_registry (selector);
+       source = e_source_registry_ref_source (registry, source_uid);
+       if (!source)
+               goto exit;
+
+       shell_view = e_calendar_selector_get_shell_view (E_CALENDAR_SELECTOR (selector));
+       shell_backend = e_shell_view_get_shell_backend (shell_view);
 
-       success = TRUE;
+       display_name = e_source_get_display_name (destination);
+
+       do_copy = action == GDK_ACTION_COPY ? TRUE : FALSE;
+
+       message = do_copy ?
+               g_strdup_printf (_("Copying an event into the calendar %s"), display_name) :
+               g_strdup_printf (_("Moving an event into the calendar %s"), display_name);
+
+       cancellable = g_cancellable_new ();
+       activity = e_activity_new ();
+       e_activity_set_cancellable (activity, cancellable);
+       e_activity_set_state (activity, E_ACTIVITY_RUNNING);
+       e_activity_set_text (activity, message);
+       g_free (message);
+
+       e_shell_backend_add_activity (shell_backend, activity);
+
+       titd = g_new0 (TransferItemToData, 1);
+
+       titd->destination = destination;
+       titd->icalcomp = icalcomponent_new_clone (icalcomp);
+       titd->selector = selector;
+       titd->shell_view = shell_view;
+       titd->activity = activity;
+       titd->display_name = display_name;
+       titd->do_copy = do_copy;
+
+       e_client_selector_get_client (
+               E_CLIENT_SELECTOR (selector), source, cancellable,
+               source_client_connect_cb, titd);
 
 exit:
-       if (path != NULL)
-               gtk_tree_path_free (path);
+       if (source)
+               g_object_unref (source);
 
-       if (object != NULL)
-               g_object_unref (object);
+       if (icalcomp)
+               icalcomponent_free (icalcomp);
 
-       return success;
+       g_free (source_uid);
+       g_strfreev (segments);
+       return TRUE;
 }
 
 static void
diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c
index 46f89e8..98c6da4 100644
--- a/modules/calendar/e-cal-shell-view-private.c
+++ b/modules/calendar/e-cal-shell-view-private.c
@@ -993,33 +993,78 @@ e_cal_shell_view_set_status_message (ECalShellView *cal_shell_view,
        cal_shell_view->priv->calendar_activity = activity;
 }
 
-struct ForeachTzidData
+static void
+cal_transferring_update_alert (ECalShellView *cal_shell_view,
+                              const gchar *domain,
+                              const gchar *calendar,
+                              const gchar *message)
 {
-       ECalClient *source_client;
-       ECalClient *dest_client;
-};
+       ECalShellViewPrivate *priv;
+       EShellContent *shell_content;
+       EAlert *alert;
+
+       g_return_if_fail (cal_shell_view != NULL);
+       g_return_if_fail (cal_shell_view->priv != NULL);
+
+       priv = cal_shell_view->priv;
+
+       if (priv->transfer_alert) {
+               e_alert_response (
+                       priv->transfer_alert,
+                       e_alert_get_default_response (priv->transfer_alert));
+               priv->transfer_alert = NULL;
+       }
+
+       if (!message)
+               return;
+
+       alert = e_alert_new (domain, calendar, message, NULL);
+       g_return_if_fail (alert != NULL);
+
+       priv->transfer_alert = alert;
+       g_object_add_weak_pointer (G_OBJECT (alert), &priv->transfer_alert);
+       e_alert_start_timer (priv->transfer_alert, 300);
+
+       shell_content = e_shell_view_get_shell_content (E_SHELL_VIEW (cal_shell_view));
+       e_alert_sink_submit_alert (E_ALERT_SINK (shell_content), priv->transfer_alert);
+       g_object_unref (priv->transfer_alert);
+}
+
+typedef struct _TransferItemToData {
+       ECalShellView *cal_shell_view;
+       EActivity *activity;
+       const gchar *display_name;
+       gboolean remove;
+} TransferItemToData;
 
 static void
-add_timezone_to_cal_cb (icalparameter *param,
-                        gpointer data)
+transfer_item_to_cb (GObject *src_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
 {
-       struct ForeachTzidData *ftd = data;
-       icaltimezone *tz = NULL;
-       const gchar *tzid;
+       TransferItemToData *titd = user_data;
+       GError *error = NULL;
+       GCancellable *cancellable;
+       gboolean success;
 
-       g_return_if_fail (ftd != NULL);
-       g_return_if_fail (ftd->source_client != NULL);
-       g_return_if_fail (ftd->dest_client != NULL);
+       success = cal_comp_transfer_item_to_finish (E_CAL_CLIENT (src_object), result, &error);
 
-       tzid = icalparameter_get_tzid (param);
-       if (!tzid || !*tzid)
-               return;
+       cancellable = e_activity_get_cancellable (titd->activity);
+       e_activity_set_state (
+               titd->activity,
+               g_cancellable_is_cancelled (cancellable) ? E_ACTIVITY_CANCELLED : E_ACTIVITY_COMPLETED);
 
-       e_cal_client_get_timezone_sync (
-               ftd->source_client, tzid, &tz, NULL, NULL);
-       if (tz != NULL)
-               e_cal_client_add_timezone_sync (
-                       ftd->dest_client, tz, NULL, NULL);
+       if (!success) {
+               cal_transferring_update_alert (
+                       titd->cal_shell_view,
+                       titd->remove ? "calendar:failed-move-event" : "calendar:failed-copy-event",
+                       titd->display_name,
+                       error->message);
+               g_clear_error (&error);
+       }
+
+       g_object_unref (titd->activity);
+       g_free (titd);
 }
 
 void
@@ -1028,129 +1073,50 @@ e_cal_shell_view_transfer_item_to (ECalShellView *cal_shell_view,
                                    ECalClient *destination_client,
                                    gboolean remove)
 {
-       icalcomponent *icalcomp;
-       icalcomponent *icalcomp_clone;
-       icalcomponent *icalcomp_event;
-       gboolean success;
-       const gchar *uid;
-
-       /* XXX This function should be split up into
-        *     smaller, more understandable pieces. */
+       EActivity *activity;
+       EShellBackend *shell_backend;
+       ESource *source;
+       GCancellable *cancellable = NULL;
+       gchar *message;
+       const gchar *display_name;
+       TransferItemToData *titd;
 
        g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
        g_return_if_fail (event != NULL);
+       g_return_if_fail (is_comp_data_valid (event) != FALSE);
        g_return_if_fail (E_IS_CAL_CLIENT (destination_client));
 
        if (!is_comp_data_valid (event))
                return;
 
-       icalcomp_event = event->comp_data->icalcomp;
-       uid = icalcomponent_get_uid (icalcomp_event);
+       source = e_client_get_source (E_CLIENT (destination_client));
+       display_name = e_source_get_display_name (source);
 
-       /* Put the new object into the destination calendar. */
+       message = remove ?
+               g_strdup_printf (_("Moving an event into the calendar %s"), display_name) :
+               g_strdup_printf (_("Copying an event into the calendar %s"), display_name);
 
-       success = e_cal_client_get_object_sync (
-               destination_client, uid, NULL, &icalcomp, NULL, NULL);
+       shell_backend = e_shell_view_get_shell_backend (E_SHELL_VIEW (cal_shell_view));
 
-       if (success) {
-               icalcomponent_free (icalcomp);
-               success = e_cal_client_modify_object_sync (
-                       destination_client, icalcomp_event,
-                       CALOBJ_MOD_ALL, NULL, NULL);
+       cancellable = g_cancellable_new ();
+       activity = e_activity_new ();
+       e_activity_set_cancellable (activity, cancellable);
+       e_activity_set_state (activity, E_ACTIVITY_RUNNING);
+       e_activity_set_text (activity, message);
+       g_free (message);
 
-               /* do not delete the event when it was found in the calendar */
-               return;
-       } else {
-               icalproperty *icalprop;
-               gchar *new_uid;
-               GError *error = NULL;
-               struct ForeachTzidData ftd;
-
-               ftd.source_client = event->comp_data->client;
-               ftd.dest_client = destination_client;
-
-               if (e_cal_util_component_is_instance (icalcomp_event)) {
-                       success = e_cal_client_get_object_sync (
-                               event->comp_data->client,
-                               uid, NULL, &icalcomp, NULL, NULL);
-                       if (success) {
-                               /* Use master object when working
-                                * with a recurring event ... */
-                               icalcomp_clone = icalcomponent_new_clone (icalcomp);
-                               icalcomponent_free (icalcomp);
-                       } else {
-                               /* ... or remove the recurrence ID ... */
-                               icalcomp_clone =
-                                       icalcomponent_new_clone (icalcomp_event);
-                               if (e_cal_util_component_has_recurrences (icalcomp_clone)) {
-                                       /* ... for non-detached instances,
-                                        * to make it a master object. */
-                                       icalprop = icalcomponent_get_first_property (
-                                               icalcomp_clone, ICAL_RECURRENCEID_PROPERTY);
-                                       if (icalprop != NULL)
-                                               icalcomponent_remove_property (
-                                                       icalcomp_clone, icalprop);
-                               }
-                       }
-               } else
-                       icalcomp_clone =
-                               icalcomponent_new_clone (icalcomp_event);
-
-               icalprop = icalproperty_new_x ("1");
-               icalproperty_set_x_name (icalprop, "X-EVOLUTION-MOVE-CALENDAR");
-               icalcomponent_add_property (icalcomp_clone, icalprop);
-
-               if (!remove) {
-                       /* Change the UID to avoid problems with
-                        * duplicated UIDs. */
-                       new_uid = e_cal_component_gen_uid ();
-                       icalcomponent_set_uid (icalcomp_clone, new_uid);
-                       g_free (new_uid);
-               }
+       e_shell_backend_add_activity (shell_backend, activity);
 
-               new_uid = NULL;
-               icalcomponent_foreach_tzid (
-                       icalcomp_clone, add_timezone_to_cal_cb, &ftd);
-               success = e_cal_client_create_object_sync (
-                       destination_client, icalcomp_clone,
-                       &new_uid, NULL, &error);
-               if (!success) {
-                       icalcomponent_free (icalcomp_clone);
-                       g_warning (
-                               "%s: Failed to create object: %s",
-                               G_STRFUNC, error->message);
-                       g_error_free (error);
-                       return;
-               }
+       titd = g_new0 (TransferItemToData, 1);
 
-               icalcomponent_free (icalcomp_clone);
-               g_free (new_uid);
-       }
+       titd->cal_shell_view = cal_shell_view;
+       titd->activity = activity;
+       titd->display_name = display_name;
+       titd->remove = remove;
 
-       if (remove) {
-               ECalClient *source_client = event->comp_data->client;
-
-               /* Remove the item from the source calendar. */
-               if (e_cal_util_component_is_instance (icalcomp_event) ||
-                       e_cal_util_component_has_recurrences (icalcomp_event)) {
-                       icaltimetype icaltime;
-                       gchar *rid;
-
-                       icaltime =
-                               icalcomponent_get_recurrenceid (icalcomp_event);
-                       if (!icaltime_is_null_time (icaltime))
-                               rid = icaltime_as_ical_string_r (icaltime);
-                       else
-                               rid = NULL;
-                       e_cal_client_remove_object_sync (
-                               source_client, uid, rid,
-                               CALOBJ_MOD_ALL, NULL, NULL);
-                       g_free (rid);
-               } else
-                       e_cal_client_remove_object_sync (
-                               source_client, uid, NULL,
-                               CALOBJ_MOD_THIS, NULL, NULL);
-       }
+       cal_comp_transfer_item_to (
+               event->comp_data->client, destination_client,
+               event->comp_data->icalcomp, !remove, cancellable, transfer_item_to_cb, titd);
 }
 
 void
diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h
index 000602b..40b2ec6 100644
--- a/modules/calendar/e-cal-shell-view-private.h
+++ b/modules/calendar/e-cal-shell-view-private.h
@@ -150,6 +150,9 @@ struct _ECalShellViewPrivate {
        gint search_direction; /* negative value is backward, positive is forward, zero is error; in days */
        GSList *search_hit_cache; /* pointers on time_t for matched events */
 
+       /* Event/Task/Memo transferring */
+       gpointer transfer_alert; /* weak pointer to EAlert * */
+
         GFileMonitor *monitors[CHECK_NB];
 };
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]