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

This commit is contained in:
Milan Crha
2013-11-04 21:04:07 +01:00
parent acdf842238
commit ed2bc85f4f
5 changed files with 696 additions and 229 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;
ECalendarSelectorPrivate *priv;
EShellContent *shell_content;
EAlert *alert;
uid = (gchar *) icalcomponent_get_uid (icalcomp);
g_return_if_fail (calendar_selector != NULL);
g_return_if_fail (calendar_selector->priv != NULL);
e_cal_client_get_object_sync (
client, uid, NULL, &tmp_icalcomp, NULL, NULL);
priv = calendar_selector->priv;
if (tmp_icalcomp != NULL) {
icalcomponent_free (tmp_icalcomp);
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;
TransferItemToData *titd = user_data;
GError *error = NULL;
GCancellable *cancellable;
gboolean success;
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;
success = cal_comp_transfer_item_to_finish (E_CAL_CLIENT (source_object), result, &error);
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);
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);
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;
icalcomponent_free (icalcomp);
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);
/* 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;
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);
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;
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);
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), destination, NULL,
client_connect_cb, icalcomp);
success = TRUE;
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

View File

@ -993,33 +993,78 @@ e_cal_shell_view_set_status_message (ECalShellView *cal_shell_view,
cal_shell_view->priv->calendar_activity = activity;
}
struct ForeachTzidData
{
ECalClient *source_client;
ECalClient *dest_client;
};
static void
add_timezone_to_cal_cb (icalparameter *param,
gpointer data)
cal_transferring_update_alert (ECalShellView *cal_shell_view,
const gchar *domain,
const gchar *calendar,
const gchar *message)
{
struct ForeachTzidData *ftd = data;
icaltimezone *tz = NULL;
const gchar *tzid;
ECalShellViewPrivate *priv;
EShellContent *shell_content;
EAlert *alert;
g_return_if_fail (ftd != NULL);
g_return_if_fail (ftd->source_client != NULL);
g_return_if_fail (ftd->dest_client != NULL);
g_return_if_fail (cal_shell_view != NULL);
g_return_if_fail (cal_shell_view->priv != NULL);
tzid = icalparameter_get_tzid (param);
if (!tzid || !*tzid)
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;
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);
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
transfer_item_to_cb (GObject *src_object,
GAsyncResult *result,
gpointer user_data)
{
TransferItemToData *titd = user_data;
GError *error = NULL;
GCancellable *cancellable;
gboolean success;
success = cal_comp_transfer_item_to_finish (E_CAL_CLIENT (src_object), result, &error);
cancellable = e_activity_get_cancellable (titd->activity);
e_activity_set_state (
titd->activity,
g_cancellable_is_cancelled (cancellable) ? E_ACTIVITY_CANCELLED : E_ACTIVITY_COMPLETED);
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;
e_shell_backend_add_activity (shell_backend, activity);
ftd.source_client = event->comp_data->client;
ftd.dest_client = destination_client;
titd = g_new0 (TransferItemToData, 1);
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);
titd->cal_shell_view = cal_shell_view;
titd->activity = activity;
titd->display_name = display_name;
titd->remove = remove;
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);
}
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;
}
icalcomponent_free (icalcomp_clone);
g_free (new_uid);
}
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

View File

@ -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];
};