From a1a1f31ef8aa921e84f82d0e88b00ead5e92c738 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 5 Nov 2007 10:07:23 +0000 Subject: ** Fix for bug #315101 2007-11-05 Milan Crha ** Fix for bug #315101 * drag and drop to other source for multiselect * gui/comp-util.h: * gui/comp-util.c: (cal_comp_selection_set_string_list), (cal_comp_selection_get_string_list): Two new helper functions to set and get list of strings into GtkSelectionData. * gui/e-tasks.c: (get_selected_components_cb), (do_for_selected_components), (obtain_list_of_components), (table_drag_data_get): * gui/e-memos.c: (get_selected_components_cb), (do_for_selected_components), (obtain_list_of_components), (table_drag_data_get): Pass list of selected components as data for drag and drop instead of focused component from the list. * gui/e-tasks.c: (table_drag_data_delete): * gui/e-memos.c: (table_drag_data_delete): Obsolete now. * gui/tasks-component.c: (selector_tree_drag_data_received): * gui/memos-component.c: (selector_tree_drag_data_received): Receiving list of components, so made changes here to reflect it. svn path=/trunk/; revision=34496 --- calendar/gui/comp-util.c | 69 ++++++++++++++++++++ calendar/gui/comp-util.h | 5 ++ calendar/gui/e-memos.c | 140 +++++++++++++++++++++++++++++------------ calendar/gui/e-tasks.c | 139 +++++++++++++++++++++++++++++----------- calendar/gui/memos-component.c | 94 ++++++++++++++++++++------- calendar/gui/tasks-component.c | 94 ++++++++++++++++++++------- 6 files changed, 420 insertions(+), 121 deletions(-) (limited to 'calendar/gui') diff --git a/calendar/gui/comp-util.c b/calendar/gui/comp-util.c index 6919302b73..0be96dbefa 100644 --- a/calendar/gui/comp-util.c +++ b/calendar/gui/comp-util.c @@ -432,3 +432,72 @@ cal_comp_util_get_n_icons (ECalComponent *comp) return num_icons; } + +/** + * cal_comp_selection_set_string_list + * Stores list of strings into selection target data. + * Use @ref cal_comp_selection_get_string_list to get this list from target data. + * + * @param data Selection data, where to put list of strings. + * @param str_list List of strings. (Each element is of type const gchar *.) + **/ +void +cal_comp_selection_set_string_list (GtkSelectionData *data, GSList *str_list) +{ + /* format is "str1\0str2\0...strN\0" */ + GSList *p; + GByteArray *array; + + g_return_if_fail (data != NULL); + + if (!str_list) + return; + + array = g_byte_array_new (); + for (p = str_list; p; p = p->next) { + const guint8 *c = p->data; + + if (c) + g_byte_array_append (array, c, strlen ((const char *) c) + 1); + } + + gtk_selection_data_set (data, data->target, 8, array->data, array->len); + g_byte_array_free (array, TRUE); +} + +/** + * cal_comp_selection_get_string_list + * Converts data from selection to list of strings. Data should be assigned + * to selection data with @ref cal_comp_selection_set_string_list. + * Each string in newly created list should be freed by g_free. + * List itself should be freed by g_slist_free. + * + * @param data Selection data, where to put list of strings. + * @return Newly allocated GSList of strings. + **/ +GSList * +cal_comp_selection_get_string_list (GtkSelectionData *data) +{ + /* format is "str1\0str2\0...strN\0" */ + char *inptr, *inend; + GSList *list; + + g_return_val_if_fail (data != NULL, NULL); + + list = NULL; + inptr = (char *)data->data; + inend = (char *)(data->data + data->length); + + while (inptr < inend) { + char *start = inptr; + + while (inptr < inend && *inptr) + inptr++; + + list = g_slist_prepend (list, g_strndup (start, inptr - start)); + + inptr++; + } + + return list; +} diff --git a/calendar/gui/comp-util.h b/calendar/gui/comp-util.h index 651fde686a..dfc5165b66 100644 --- a/calendar/gui/comp-util.h +++ b/calendar/gui/comp-util.h @@ -22,7 +22,9 @@ #ifndef COMP_UTIL_H #define COMP_UTIL_H +#include #include +#include #include #include @@ -47,4 +49,7 @@ ECalComponent *cal_comp_event_new_with_current_time (ECal *client, gboolean all_ ECalComponent *cal_comp_task_new_with_defaults (ECal *client); ECalComponent *cal_comp_memo_new_with_defaults (ECal *client); +void cal_comp_selection_set_string_list (GtkSelectionData *data, GSList *str_list); +GSList *cal_comp_selection_get_string_list (GtkSelectionData *data); + #endif diff --git a/calendar/gui/e-memos.c b/calendar/gui/e-memos.c index d20d909808..a028f5e47b 100644 --- a/calendar/gui/e-memos.c +++ b/calendar/gui/e-memos.c @@ -341,6 +341,94 @@ setup_config (EMemos *memos) priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); } +struct AffectedComponents { + EMemoTable *memo_table; + GSList *components; /* contains pointers to ECalModelComponent */ +}; + +/** + * get_selected_components_cb + * Helper function to fill list of selected components in EMemoTable. + * This function is called from e_table_selected_row_foreach. + **/ +static void +get_selected_components_cb (int model_row, gpointer data) +{ + struct AffectedComponents *ac = (struct AffectedComponents *) data; + + if (!ac || !ac->memo_table) + return; + + ac->components = g_slist_prepend (ac->components, e_cal_model_get_component_at (E_CAL_MODEL (e_memo_table_get_model (ac->memo_table)), model_row)); +} + +/** + * do_for_selected_components + * Calls function func for all selected components in memo_table. + * + * @param memo_table Table with selected components of our interest. + * @param func Function to be called on each selected component from cal_table. + * The first parameter of this function is a pointer to ECalModelComponent and + * the second parameter of this function is pointer to cal_table + * @param user_data User data, will be passed to func. + **/ +static void +do_for_selected_components (EMemoTable *memo_table, GFunc func, gpointer user_data) +{ + ETable *etable; + struct AffectedComponents ac; + + g_return_if_fail (memo_table != NULL); + + ac.memo_table = memo_table; + ac.components = NULL; + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); + e_table_selected_row_foreach (etable, get_selected_components_cb, &ac); + + g_slist_foreach (ac.components, func, user_data); + g_slist_free (ac.components); +} + +/** + * obtain_list_of_components + * As a callback function to convert each ECalModelComponent to string + * of format "source_uid\ncomponent_str" and add this newly allocated + * string to the list of components. Strings should be freed with g_free. + * + * @param data ECalModelComponent object. + * @param user_data Pointer to GSList list, where to put new strings. + **/ +static void +obtain_list_of_components (gpointer data, gpointer user_data) +{ + GSList **list; + ECalModelComponent *comp_data; + + list = (GSList **) user_data; + comp_data = (ECalModelComponent *) data; + + if (list && comp_data) { + char *comp_str; + icalcomponent *vcal; + + vcal = e_cal_util_new_top_level (); + e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); + icalcomponent_add_component (vcal, icalcomponent_new_clone (comp_data->icalcomp)); + + comp_str = icalcomponent_as_ical_string (vcal); + if (comp_str) { + ESource *source = e_cal_get_source (comp_data->client); + const char *source_uid = e_source_peek_uid (source); + + *list = g_slist_prepend (*list, g_strdup_printf ("%s\n%s", source_uid, comp_str)); + g_free (comp_str); + } + + icalcomponent_free (vcal); + } +} + static void table_drag_data_get (ETable *table, int row, @@ -352,35 +440,20 @@ table_drag_data_get (ETable *table, EMemos *memos) { EMemosPrivate *priv; - ECalModelComponent *comp_data; priv = memos->priv; - if (priv->current_uid) { - ECalModel *model; + if (info == TARGET_VCALENDAR) { + /* we will pass an icalcalendar component for both types */ + GSList *components = NULL; - model = e_memo_table_get_model ( - E_MEMO_TABLE (priv->memos_view)); + do_for_selected_components (E_MEMO_TABLE (priv->memos_view), obtain_list_of_components, &components); - comp_data = e_cal_model_get_component_at (model, row); + if (components) { + cal_comp_selection_set_string_list (selection_data, components); - if (info == TARGET_VCALENDAR) { - /* we will pass an icalcalendar component for both types */ - char *comp_str; - icalcomponent *vcal; - - vcal = e_cal_util_new_top_level (); - e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); - icalcomponent_add_component ( - vcal, - icalcomponent_new_clone (comp_data->icalcomp)); - - comp_str = icalcomponent_as_ical_string (vcal); - if (comp_str) { - gtk_selection_data_set (selection_data, selection_data->target, - 8, (unsigned char *)comp_str, strlen (comp_str)); - } - icalcomponent_free (vcal); + g_slist_foreach (components, (GFunc)g_free, NULL); + g_slist_free (components); } } } @@ -392,22 +465,11 @@ table_drag_data_delete (ETable *table, GdkDragContext *context, EMemos *memos) { - EMemosPrivate *priv; - ECalModelComponent *comp_data; - ECalModel *model; - gboolean read_only = TRUE; - - priv = memos->priv; - - model = e_memo_table_get_model ( - E_MEMO_TABLE (priv->memos_view)); - comp_data = e_cal_model_get_component_at (model, row); - - e_cal_is_read_only (comp_data->client, &read_only, NULL); - if (read_only) - return; - - e_cal_remove_object (comp_data->client, icalcomponent_get_uid (comp_data->icalcomp), NULL); + /* Moved components are deleted from source immediately when moved, + because some of them can be part of destination source, and we + don't want to delete not-moved tasks. There is no such information + which event has been moved and which not, so skip this method. + */ } #define E_MEMOS_TABLE_DEFAULT_STATE \ diff --git a/calendar/gui/e-tasks.c b/calendar/gui/e-tasks.c index 13c1be8a4c..44e417b5bb 100644 --- a/calendar/gui/e-tasks.c +++ b/calendar/gui/e-tasks.c @@ -429,6 +429,94 @@ setup_config (ETasks *tasks) priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); } +struct AffectedComponents { + ECalendarTable *cal_table; + GSList *components; /* contains pointers to ECalModelComponent */ +}; + +/** + * get_selected_components_cb + * Helper function to fill list of selected components in ECalendarTable. + * This function is called from e_table_selected_row_foreach. + **/ +static void +get_selected_components_cb (int model_row, gpointer data) +{ + struct AffectedComponents *ac = (struct AffectedComponents *) data; + + if (!ac || !ac->cal_table) + return; + + ac->components = g_slist_prepend (ac->components, e_cal_model_get_component_at (E_CAL_MODEL (ac->cal_table->model), model_row)); +} + +/** + * do_for_selected_components + * Calls function func for all selected components in cal_table. + * + * @param cal_table Table with selected components of our interest. + * @param func Function to be called on each selected component from cal_table. + * The first parameter of this function is a pointer to ECalModelComponent and + * the second parameter of this function is pointer to cal_table + * @param user_data User data, will be passed to func. + **/ +static void +do_for_selected_components (ECalendarTable *cal_table, GFunc func, gpointer user_data) +{ + ETable *etable; + struct AffectedComponents ac; + + g_return_if_fail (cal_table != NULL); + + ac.cal_table = cal_table; + ac.components = NULL; + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (cal_table->etable)); + e_table_selected_row_foreach (etable, get_selected_components_cb, &ac); + + g_slist_foreach (ac.components, func, user_data); + g_slist_free (ac.components); +} + +/** + * obtain_list_of_components + * As a callback function to convert each ECalModelComponent to string + * of format "source_uid\ncomponent_str" and add this newly allocated + * string to the list of components. Strings should be freed with g_free. + * + * @param data ECalModelComponent object. + * @param user_data Pointer to GSList list, where to put new strings. + **/ +static void +obtain_list_of_components (gpointer data, gpointer user_data) +{ + GSList **list; + ECalModelComponent *comp_data; + + list = (GSList **) user_data; + comp_data = (ECalModelComponent *) data; + + if (list && comp_data) { + char *comp_str; + icalcomponent *vcal; + + vcal = e_cal_util_new_top_level (); + e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); + icalcomponent_add_component (vcal, icalcomponent_new_clone (comp_data->icalcomp)); + + comp_str = icalcomponent_as_ical_string (vcal); + if (comp_str) { + ESource *source = e_cal_get_source (comp_data->client); + const char *source_uid = e_source_peek_uid (source); + + *list = g_slist_prepend (*list, g_strdup_printf ("%s\n%s", source_uid, comp_str)); + g_free (comp_str); + } + + icalcomponent_free (vcal); + } +} + static void table_drag_data_get (ETable *table, int row, @@ -440,33 +528,20 @@ table_drag_data_get (ETable *table, ETasks *tasks) { ETasksPrivate *priv; - ECalModelComponent *comp_data; priv = tasks->priv; - if (priv->current_uid) { - ECalModel *model; + if (info == TARGET_VCALENDAR) { + /* we will pass an icalcalendar component for both types */ + GSList *components = NULL; - model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); - comp_data = e_cal_model_get_component_at (model, row); + do_for_selected_components (E_CALENDAR_TABLE (priv->tasks_view), obtain_list_of_components, &components); - if (info == TARGET_VCALENDAR) { - /* we will pass an icalcalendar component for both types */ - char *comp_str; - icalcomponent *vcal; - - vcal = e_cal_util_new_top_level (); - e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); - icalcomponent_add_component ( - vcal, - icalcomponent_new_clone (comp_data->icalcomp)); - - comp_str = icalcomponent_as_ical_string (vcal); - if (comp_str) { - gtk_selection_data_set (selection_data, selection_data->target, - 8, (unsigned char *)comp_str, strlen (comp_str)); - } - icalcomponent_free (vcal); + if (components) { + cal_comp_selection_set_string_list (selection_data, components); + + g_slist_foreach (components, (GFunc)g_free, NULL); + g_slist_free (components); } } } @@ -501,21 +576,11 @@ table_drag_data_delete (ETable *table, GdkDragContext *context, ETasks *tasks) { - ETasksPrivate *priv; - ECalModelComponent *comp_data; - ECalModel *model; - gboolean read_only = TRUE; - priv = tasks->priv; - - model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); - comp_data = e_cal_model_get_component_at (model, row); - - e_cal_is_read_only (comp_data->client, &read_only, NULL); - if (read_only) - return; - - if(gdk_drag_drop_succeeded(context)) - e_cal_remove_object (comp_data->client, icalcomponent_get_uid (comp_data->icalcomp), NULL); + /* Moved components are deleted from source immediately when moved, + because some of them can be part of destination source, and we + don't want to delete not-moved tasks. There is no such information + which event has been moved and which not, so skip this method. + */ } #define E_TASKS_TABLE_DEFAULT_STATE \ diff --git a/calendar/gui/memos-component.c b/calendar/gui/memos-component.c index 5024a5f2e7..40b71cf5f5 100644 --- a/calendar/gui/memos-component.c +++ b/calendar/gui/memos-component.c @@ -758,6 +758,8 @@ selector_tree_drag_data_received (GtkWidget *widget, gboolean success = FALSE; icalcomponent *icalcomp = NULL; ECal *client = NULL; + GSList *components, *p; + MemosComponent *component = MEMOS_COMPONENT (user_data); if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y, &path, &pos)) @@ -771,47 +773,97 @@ selector_tree_drag_data_received (GtkWidget *widget, gtk_tree_model_get (model, &iter, 0, &source, -1); - if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source)) + if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source) || !data->data) goto finish; - icalcomp = icalparser_parse_string ((char *)data->data); - - if (icalcomp) { - char * uid; + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + + if (!client || !e_cal_open (client, TRUE, NULL)) + goto finish; + + components = cal_comp_selection_get_string_list (data); + for (p = components; p; p = p->next) { + const char * uid; + char *old_uid = NULL; + icalcomponent *tmp_icalcomp = NULL; + GError *error = NULL; + char *comp_str; /* do not free this! */ + + /* p->data is "source_uid\ncomponent_string" */ + comp_str = strchr (p->data, '\n'); + if (!comp_str) + continue; + + comp_str [0] = 0; + comp_str++; + icalcomp = icalparser_parse_string (comp_str); + + if (!icalcomp) + continue; /* FIXME deal with GDK_ACTION_ASK */ if (context->action == GDK_ACTION_COPY) { + old_uid = g_strdup (icalcomponent_get_uid (icalcomp)); uid = e_cal_component_gen_uid (); icalcomponent_set_uid (icalcomp, uid); } - client = auth_new_cal_from_source (source, - E_CAL_SOURCE_TYPE_JOURNAL); - - if (client) { - if (e_cal_open (client, TRUE, NULL)) { + uid = icalcomponent_get_uid (icalcomp); + if (!old_uid) + old_uid = g_strdup (uid); + + if (!e_cal_get_object (client, uid, NULL, &tmp_icalcomp, &error)) { + if ((error != NULL) && (error->code != E_CALENDAR_STATUS_OBJECT_NOT_FOUND)) + g_message ("Failed to search the object in destination task list: %s",error->message); + else { + /* this will report success by last item, but we don't care */ success = update_objects (client, icalcomp); + + if (success && context->action == GDK_ACTION_MOVE) { + /* remove components rather here, because we know which has been moved */ + ESource *source_source; + ECal *source_client; + + source_source = e_source_list_peek_source_by_uid (component->priv->source_list, p->data); + + if (source_source && !E_IS_SOURCE_GROUP (source_source) && !e_source_get_readonly (source_source)) { + source_client = auth_new_cal_from_source (source_source, E_CAL_SOURCE_TYPE_JOURNAL); + + if (source_client) { + gboolean read_only = TRUE; + + e_cal_is_read_only (source_client, &read_only, NULL); + + if (!read_only && e_cal_open (source_client, TRUE, NULL)) + e_cal_remove_object (source_client, old_uid, NULL); + else if (!read_only) + g_message ("Cannot open source client to remove old memo"); + + g_object_unref (source_client); + } else + g_message ("Cannot create source client to remove old memo"); + } + } } - - g_object_unref (client); - } - + + g_clear_error (&error); + } else + icalcomponent_free (tmp_icalcomp); + + g_free (old_uid); icalcomponent_free (icalcomp); } + g_slist_foreach (components, (GFunc)g_free, NULL); + g_slist_free (components); finish: + if (client) + g_object_unref (client); if (source) g_object_unref (source); if (path) gtk_tree_path_free (path); - if (!success && context->action == GDK_ACTION_MOVE) { - /* because the delete parameter of 'gtk_drag_finish' doesn't work - as expected, then we change context->action to GDK_ACTION_COPY - to prevent from deleting data when the drag was unsuccessful. - */ - context->action = GDK_ACTION_COPY; - } gtk_drag_finish (context, success, success && context->action == GDK_ACTION_MOVE, time); } diff --git a/calendar/gui/tasks-component.c b/calendar/gui/tasks-component.c index cc87d9d287..2f8c563aa7 100644 --- a/calendar/gui/tasks-component.c +++ b/calendar/gui/tasks-component.c @@ -742,6 +742,8 @@ selector_tree_drag_data_received (GtkWidget *widget, gboolean success = FALSE; icalcomponent *icalcomp = NULL; ECal *client = NULL; + GSList *components, *p; + TasksComponent *component = TASKS_COMPONENT (user_data); if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y, &path, &pos)) @@ -755,48 +757,92 @@ selector_tree_drag_data_received (GtkWidget *widget, gtk_tree_model_get (model, &iter, 0, &source, -1); - if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source)) + if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source) || !data->data) goto finish; - icalcomp = icalparser_parse_string ((char *)data->data); + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_TODO); - if (icalcomp) { + if (!client || !e_cal_open (client, TRUE, NULL)) + goto finish; + + components = cal_comp_selection_get_string_list (data); + for (p = components; p; p = p->next) { const char * uid; - + char *old_uid = NULL; + icalcomponent *tmp_icalcomp = NULL; + GError *error = NULL; + char *comp_str; /* do not free this! */ + + /* p->data is "source_uid\ncomponent_string" */ + comp_str = strchr (p->data, '\n'); + if (!comp_str) + continue; + + comp_str [0] = 0; + comp_str++; + icalcomp = icalparser_parse_string (comp_str); + + if (!icalcomp) + continue; + /* FIXME deal with GDK_ACTION_ASK */ if (context->action == GDK_ACTION_COPY) { + old_uid = g_strdup (icalcomponent_get_uid (icalcomp)); uid = e_cal_component_gen_uid (); icalcomponent_set_uid (icalcomp, uid); } - client = auth_new_cal_from_source (source, - E_CAL_SOURCE_TYPE_TODO); - - if (client) { - if (e_cal_open (client, TRUE, NULL)) { - icalcomponent *tmp_icalcomp = NULL; - GError *error = NULL; - uid = icalcomponent_get_uid (icalcomp); - if (!e_cal_get_object (client, uid, NULL, &tmp_icalcomp, &error)) { - if ((error != NULL) && (error->code != E_CALENDAR_STATUS_OBJECT_NOT_FOUND)) - g_message ("Failed to search the object in destination task list: %s",error->message); - else { - success = TRUE; - update_objects (client, icalcomp); - } + uid = icalcomponent_get_uid (icalcomp); + if (!old_uid) + old_uid = g_strdup (uid); + + if (!e_cal_get_object (client, uid, NULL, &tmp_icalcomp, &error)) { + if ((error != NULL) && (error->code != E_CALENDAR_STATUS_OBJECT_NOT_FOUND)) + g_message ("Failed to search the object in destination task list: %s",error->message); + else { + /* this will report success by last item, but we don't care */ + success = update_objects (client, icalcomp); - g_clear_error (&error); - } else - icalcomponent_free (tmp_icalcomp); + if (success && context->action == GDK_ACTION_MOVE) { + /* remove components rather here, because we know which has been moved */ + ESource *source_source; + ECal *source_client; + + source_source = e_source_list_peek_source_by_uid (component->priv->source_list, p->data); + + if (source_source && !E_IS_SOURCE_GROUP (source_source) && !e_source_get_readonly (source_source)) { + source_client = auth_new_cal_from_source (source_source, E_CAL_SOURCE_TYPE_TODO); + + if (source_client) { + gboolean read_only = TRUE; + + e_cal_is_read_only (source_client, &read_only, NULL); + + if (!read_only && e_cal_open (source_client, TRUE, NULL)) + e_cal_remove_object (source_client, old_uid, NULL); + else if (!read_only) + g_message ("Cannot open source client to remove old task"); + + g_object_unref (source_client); + } else + g_message ("Cannot create source client to remove old task"); + } + } } - g_object_unref (client); - } + g_clear_error (&error); + } else + icalcomponent_free (tmp_icalcomp); + g_free (old_uid); icalcomponent_free (icalcomp); } + g_slist_foreach (components, (GFunc)g_free, NULL); + g_slist_free (components); finish: + if (client) + g_object_unref (client); if (source) g_object_unref (source); if (path) -- cgit