diff options
Diffstat (limited to 'addressbook')
-rw-r--r-- | addressbook/ChangeLog | 36 | ||||
-rw-r--r-- | addressbook/gui/component/addressbook-view.c | 138 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-addressbook-view.c | 62 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-minicard-view.c | 50 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-minicard-view.h | 2 | ||||
-rw-r--r-- | addressbook/util/eab-book-util.c | 65 | ||||
-rw-r--r-- | addressbook/util/eab-book-util.h | 3 |
7 files changed, 257 insertions, 99 deletions
diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index abfccf0a9f..9c48b34a98 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,39 @@ +2004-06-23 Hans Petter Jansson <hpj@ximian.com> + + * gui/component/addressbook-view.c: Add an x-source-vcard target entry + that includes the source book URI. + (destroy_merge_context): Implement. + (removed_contact_cb): Implement. + (merged_contact_cb): Implement. + (selector_tree_drag_data_received): Get the source and target books, + and see if we need to remove contacts from source after they're added + to target. Copy contacts sequentially, not in parallel, with a + callback. + + * gui/widgets/e-addressbook-view.c: Add an x-source-vcard target entry + that includes the source book URI. + (table_drag_data_delete): Remove. This is handled by the drag target. + (table_drag_data_get): Handle more than one contact. Supply source. + (create_table_view): Don't connect to the delete signal. + + * gui/widgets/e-minicard-view.c: Add an x-source-vcard target entry + that includes the source book URI. + (e_minicard_view_drag_data_delete): Remove. This is handled by the + drag target. + (e_minicard_view_drag_data_get): Handle x-source-vcard target. + (e_minicard_view_drag_begin): Don't connect to the delete signal. + (e_minicard_view_dispose): Don't disconnect from the delete signal. + (e_minicard_view_init): Don't init delete_id. + + * gui/widgets/e-minicard-view.h: Remove delete_id from struct. + + * util/eab-book-util.[ch] (eab_contact_list_from_string): Skip the + source URI if present. + (eab_book_and_contact_list_from_string): Create the source book from + the provided URI, if present. + (eab_book_and_contact_list_to_string): Include the book URI in + generated string. + 2004-06-23 Rodney Dawes <dobey@novell.com> * gui/component/select-names/select-names.glade: diff --git a/addressbook/gui/component/addressbook-view.c b/addressbook/gui/component/addressbook-view.c index 149bf3521b..cfedd0b949 100644 --- a/addressbook/gui/component/addressbook-view.c +++ b/addressbook/gui/component/addressbook-view.c @@ -93,9 +93,12 @@ struct _AddressbookViewPrivate { enum DndTargetType { DND_TARGET_TYPE_VCARD_LIST, + DND_TARGET_TYPE_SOURCE_VCARD_LIST }; -#define VCARD_TYPE "text/x-vcard" +#define VCARD_TYPE "text/x-vcard" +#define SOURCE_VCARD_TYPE "text/x-source-vcard" static GtkTargetEntry drag_types[] = { + { SOURCE_VCARD_TYPE, 0, DND_TARGET_TYPE_SOURCE_VCARD_LIST }, { VCARD_TYPE, 0, DND_TARGET_TYPE_VCARD_LIST } }; static gint num_drag_types = sizeof(drag_types) / sizeof(drag_types[0]); @@ -791,6 +794,79 @@ selector_tree_drag_motion (GtkWidget *widget, return TRUE; } +typedef struct +{ + guint remove_from_source : 1; + guint copy_done : 1; + gint pending_removals; + + EContact *current_contact; + GList *remaining_contacts; + + EBook *source_book; + EBook *target_book; +} +MergeContext; + +static void +destroy_merge_context (MergeContext *merge_context) +{ + if (merge_context->source_book) + g_object_unref (merge_context->source_book); + if (merge_context->target_book) + g_object_unref (merge_context->target_book); + + g_free (merge_context); +} + +static void +removed_contact_cb (EBook *book, EBookStatus status, gpointer closure) +{ + MergeContext *merge_context = closure; + + merge_context->pending_removals--; + + if (merge_context->copy_done && merge_context->pending_removals == 0) { + /* Finished */ + + destroy_merge_context (merge_context); + } +} + +static void +merged_contact_cb (EBook *book, EBookStatus status, const char *id, gpointer closure) +{ + MergeContext *merge_context = closure; + + if (merge_context->remove_from_source && status == E_BOOK_ERROR_OK) { + /* Remove previous contact from source */ + + e_book_async_remove_contact (merge_context->source_book, merge_context->current_contact, + removed_contact_cb, merge_context); + merge_context->pending_removals++; + } + + g_object_unref (merge_context->current_contact); + + if (merge_context->remaining_contacts) { + /* Copy next contact */ + + merge_context->current_contact = merge_context->remaining_contacts->data; + merge_context->remaining_contacts = g_list_delete_link (merge_context->remaining_contacts, + merge_context->remaining_contacts); + eab_merging_book_add_contact (merge_context->target_book, merge_context->current_contact, + merged_contact_cb, merge_context); + } else if (merge_context->pending_removals == 0) { + /* Finished */ + + destroy_merge_context (merge_context); + } else { + /* Finished, but have pending removals */ + + merge_context->copy_done = TRUE; + } +} + static gboolean selector_tree_drag_data_received (GtkWidget *widget, GdkDragContext *context, @@ -803,12 +879,13 @@ selector_tree_drag_data_received (GtkWidget *widget, { GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; - gpointer source = NULL; + gpointer source, target = NULL; GtkTreeModel *model; GtkTreeIter iter; gboolean success = FALSE; - EBook *book; + EBook *source_book, *target_book; + MergeContext *merge_context; GList *contactlist; GList *l; @@ -821,37 +898,52 @@ selector_tree_drag_data_received (GtkWidget *widget, if (!gtk_tree_model_get_iter (model, &iter, path)) goto finish; - gtk_tree_model_get (model, &iter, 0, &source, -1); + gtk_tree_model_get (model, &iter, 0, &target, -1); - if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source)) + if (E_IS_SOURCE_GROUP (target) || e_source_get_readonly (target)) goto finish; - book = e_book_new (source, NULL); - if (!book) { + target_book = e_book_new (target, NULL); + if (!target_book) { g_message (G_STRLOC ":Couldn't create EBook."); return FALSE; } - e_book_open (book, TRUE, NULL); - contactlist = eab_contact_list_from_string (data->data); - - for (l = contactlist; l; l = l->next) { - EContact *contact = l->data; - - /* XXX NULL for a callback /sigh */ - if (contact) - eab_merging_book_add_contact (book, contact, NULL /* XXX */, NULL); - success = TRUE; + e_book_open (target_book, TRUE, NULL); + + eab_book_and_contact_list_from_string (data->data, &source_book, &contactlist); + + if (source_book) { + if (!e_book_open (source_book, FALSE, NULL)) { + g_warning (G_STRLOC ": Couldn't open source EBook."); + g_object_unref (source_book); + source_book = NULL; + } + } else { + g_warning (G_STRLOC ": No source EBook provided."); } - - g_list_foreach (contactlist, (GFunc)g_object_unref, NULL); - g_list_free (contactlist); - g_object_unref (book); + + /* Set up merge context */ + + merge_context = g_new0 (MergeContext, 1); + + merge_context->source_book = source_book; + merge_context->target_book = target_book; + + merge_context->current_contact = contactlist->data; + merge_context->remaining_contacts = g_list_delete_link (contactlist, contactlist); + + merge_context->remove_from_source = context->suggested_action == GDK_ACTION_MOVE ? TRUE : FALSE; + + /* Start merge */ + + eab_merging_book_add_contact (target_book, merge_context->current_contact, + merged_contact_cb, merge_context); finish: if (path) gtk_tree_path_free (path); - if (source) - g_object_unref (source); + if (target) + g_object_unref (target); gtk_drag_finish (context, success, context->action == GDK_ACTION_MOVE, time); diff --git a/addressbook/gui/widgets/e-addressbook-view.c b/addressbook/gui/widgets/e-addressbook-view.c index 44db0a1edd..907309348a 100644 --- a/addressbook/gui/widgets/e-addressbook-view.c +++ b/addressbook/gui/widgets/e-addressbook-view.c @@ -98,6 +98,7 @@ static void writable_status (GtkObject *object, gboolean writable, EABView *e static void backend_died (GtkObject *object, EABView *eav); static void contact_changed (EABModel *model, gint index, EABView *eav); static void contact_removed (EABModel *model, gint index, EABView *eav); +static GList *get_selected_contacts (EABView *view); static void command_state_change (EABView *eav); @@ -137,11 +138,14 @@ enum { }; enum DndTargetType { - DND_TARGET_TYPE_VCARD, + DND_TARGET_TYPE_SOURCE_VCARD, + DND_TARGET_TYPE_VCARD }; #define VCARD_TYPE "text/x-vcard" +#define SOURCE_VCARD_TYPE "text/x-source-vcard" static GtkTargetEntry drag_types[] = { - { VCARD_TYPE, 0, DND_TARGET_TYPE_VCARD }, + { SOURCE_VCARD_TYPE, 0, DND_TARGET_TYPE_SOURCE_VCARD }, + { VCARD_TYPE, 0, DND_TARGET_TYPE_VCARD } }; static const int num_drag_types = sizeof (drag_types) / sizeof (drag_types[0]); @@ -1218,36 +1222,6 @@ table_white_space_event(ETableScrolled *table, GdkEvent *event, EABView *view) } static void -table_drag_data_delete (ETable *table, - int row, - int col, - GdkDragContext *context, - gpointer user_data) -{ - EABView *view = user_data; - EContact *contact; - EBook *book; - - if (!E_IS_ADDRESSBOOK_TABLE_ADAPTER(view->object)) - return; - - g_object_get(view->model, - "book", &book, - NULL); - - contact = eab_model_contact_at(view->model, row); - /* Remove the card. */ - /* XXX no callback specified... ugh */ - e_book_async_remove_contact (book, - contact, - NULL, - NULL); - - g_object_unref(book); -} - - -static void table_drag_data_get (ETable *table, int row, int col, @@ -1258,15 +1232,18 @@ table_drag_data_get (ETable *table, gpointer user_data) { EABView *view = user_data; + GList *contact_list; if (!E_IS_ADDRESSBOOK_TABLE_ADAPTER(view->object)) return; + contact_list = get_selected_contacts (view); + switch (info) { case DND_TARGET_TYPE_VCARD: { char *value; - value = e_vcard_to_string (E_VCARD (view->model->data[row]), EVC_FORMAT_VCARD_30); + value = eab_contact_list_to_string (contact_list); gtk_selection_data_set (selection_data, selection_data->target, @@ -1274,7 +1251,21 @@ table_drag_data_get (ETable *table, value, strlen (value)); break; } + case DND_TARGET_TYPE_SOURCE_VCARD: { + char *value; + + value = eab_book_and_contact_list_to_string (view->book, contact_list); + + gtk_selection_data_set (selection_data, + selection_data->target, + 8, + value, strlen (value)); + break; } + } + + g_list_foreach (contact_list, (GFunc) g_object_unref, NULL); + g_list_free (contact_list); } static void @@ -1433,11 +1424,6 @@ create_table_view (EABView *view) "table_drag_data_get", G_CALLBACK (table_drag_data_get), view); - - g_signal_connect (E_TABLE_SCROLLED(table)->table, - "table_drag_data_delete", - G_CALLBACK (table_drag_data_delete), - view); gtk_paned_add1 (GTK_PANED (view->paned), table); diff --git a/addressbook/gui/widgets/e-minicard-view.c b/addressbook/gui/widgets/e-minicard-view.c index 60893d45f1..6cae674c98 100644 --- a/addressbook/gui/widgets/e-minicard-view.c +++ b/addressbook/gui/widgets/e-minicard-view.c @@ -62,9 +62,12 @@ static guint signals [LAST_SIGNAL] = {0, }; enum DndTargetType { DND_TARGET_TYPE_VCARD_LIST, + DND_TARGET_TYPE_SOURCE_VCARD_LIST }; #define VCARD_LIST_TYPE "text/x-vcard" +#define SOURCE_VCARD_LIST_TYPE "text/x-source-vcard" static GtkTargetEntry drag_types[] = { + { SOURCE_VCARD_LIST_TYPE, 0, DND_TARGET_TYPE_SOURCE_VCARD_LIST }, { VCARD_LIST_TYPE, 0, DND_TARGET_TYPE_VCARD_LIST } }; static gint num_drag_types = sizeof(drag_types) / sizeof(drag_types[0]); @@ -92,32 +95,19 @@ e_minicard_view_drag_data_get(GtkWidget *widget, value, strlen (value)); break; } - } -} - -static void -e_minicard_view_drag_data_delete (GtkWidget *widget, - GdkDragContext *context, - EMinicardView *view) -{ - EBook *book; - GList *l; - - if (!E_IS_MINICARD_VIEW (view)) - return; + case DND_TARGET_TYPE_SOURCE_VCARD_LIST: { + EBook *book; + char *value; + + g_object_get (view->adapter, "book", &book, NULL); + value = eab_book_and_contact_list_to_string (book, view->drag_list); - if (!view->drag_list) { - g_warning ("e_minicard_view_drag_data_delete called without contact list"); - return; + gtk_selection_data_set (selection_data, + selection_data->target, + 8, + value, strlen (value)); + break; } - - g_object_get (view->adapter, "book", &book, NULL); - - for (l = view->drag_list; l; l = g_list_next (l)) { - EContact *contact = l->data; - - /* XXX no callback */ - e_book_async_remove_contact (book, contact, NULL, NULL); } } @@ -152,11 +142,6 @@ e_minicard_view_drag_begin (EAddressbookReflowAdapter *adapter, GdkEvent *event, "drag_data_get", G_CALLBACK (e_minicard_view_drag_data_get), view); - if (!view->canvas_drag_data_delete_id) - view->canvas_drag_data_delete_id = g_signal_connect (GNOME_CANVAS_ITEM (view)->canvas, - "drag_data_delete", - G_CALLBACK (e_minicard_view_drag_data_delete), - view); gtk_drag_set_icon_default (context); @@ -314,12 +299,6 @@ e_minicard_view_dispose (GObject *object) view->canvas_drag_data_get_id = 0; } - if (view->canvas_drag_data_delete_id) { - g_signal_handler_disconnect (GNOME_CANVAS_ITEM (view)->canvas, - view->canvas_drag_data_delete_id); - view->canvas_drag_data_delete_id = 0; - } - if (view->adapter) { if (view->writable_status_id) { EABModel *model; @@ -545,7 +524,6 @@ e_minicard_view_init (EMinicardView *view) view->drag_list = NULL; view->adapter = NULL; view->canvas_drag_data_get_id = 0; - view->canvas_drag_data_delete_id = 0; view->writable_status_id = 0; set_empty_message (view); diff --git a/addressbook/gui/widgets/e-minicard-view.h b/addressbook/gui/widgets/e-minicard-view.h index 679e5252c0..50964568cf 100644 --- a/addressbook/gui/widgets/e-minicard-view.h +++ b/addressbook/gui/widgets/e-minicard-view.h @@ -69,8 +69,6 @@ struct _EMinicardView GList *drag_list; guint canvas_drag_data_get_id; - guint canvas_drag_data_delete_id; - guint writable_status_id; }; diff --git a/addressbook/util/eab-book-util.c b/addressbook/util/eab-book-util.c index 53a09c323b..f6429dee0a 100644 --- a/addressbook/util/eab-book-util.c +++ b/addressbook/util/eab-book-util.c @@ -188,6 +188,18 @@ eab_contact_list_from_string (const char *str) char *q; char *blank_line; + if (!p) + return NULL; + + if (!strncmp (p, "Book: ", 6)) { + p = strchr (p, '\n'); + if (!p) { + g_warning (G_STRLOC ": Got book but no newline!"); + return NULL; + } + p++; + } + while (*p) { if (*p != '\r') g_string_append_c (gstr, *p); @@ -240,6 +252,59 @@ eab_contact_list_to_string (GList *contacts) return g_string_free (str, FALSE); } +gboolean +eab_book_and_contact_list_from_string (const char *str, EBook **book, GList **contacts) +{ + const char *s0, *s1; + char *uri; + + g_return_val_if_fail (str != NULL, FALSE); + g_return_val_if_fail (book != NULL, FALSE); + g_return_val_if_fail (contacts != NULL, FALSE); + + *contacts = eab_contact_list_from_string (str); + + if (!strncmp (str, "Book: ", 6)) { + s0 = str + 6; + s1 = strchr (str, '\r'); + + if (!s1) + s1 = strchr (str, '\n'); + } else { + s0 = NULL; + s1 = NULL; + } + + if (!s0 || !s1) { + *book = NULL; + return FALSE; + } + + uri = g_strndup (s0, s1 - s0); + *book = e_book_new_from_uri (uri, NULL); + g_free (uri); + + return *book ? TRUE : FALSE; +} + +char * +eab_book_and_contact_list_to_string (EBook *book, GList *contacts) +{ + char *s0, *s1; + + s0 = eab_contact_list_to_string (contacts); + if (!s0) + s0 = g_strdup (""); + + if (book) + s1 = g_strconcat ("Book: ", e_book_get_uri (book), "\r\n", s0, NULL); + else + s1 = g_strdup (s0); + + g_free (s0); + return s1; +} + #if notyet /* * Convenience routine to check for addresses in the local address book. diff --git a/addressbook/util/eab-book-util.h b/addressbook/util/eab-book-util.h index ae3a45ef35..c1362f1571 100644 --- a/addressbook/util/eab-book-util.h +++ b/addressbook/util/eab-book-util.h @@ -54,6 +54,9 @@ guint eab_nickname_query (EBook * GList *eab_contact_list_from_string (const char *str); char *eab_contact_list_to_string (GList *contacts); +gboolean eab_book_and_contact_list_from_string (const char *str, EBook **book, GList **contacts); +char *eab_book_and_contact_list_to_string (EBook *book, GList *contacts); + /* Returns the EContact associated to email in the callback, or NULL if no match is found in the default address book. */ void eab_query_address_default (const gchar *email, |