commit dd6a5777279c1d8f88a592d34c76a58d6d31f974
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Sun Jun 26 12:05:54 2011 -0400

    Handle fields-of-interest for local addressbook backend.
    
    This patch modifies the local file addressbook backend to notify
    with vcards holding only the fields requested in the EBookClientView's
    fields-of-interest.
    
    A special case it made if only the UID is requested, in that case
    the notification vCard is created from an id and no additional
    parsing and filtering is needed.
    
    Additionally, the patch adds a filtering algorithm in
    e_book_backend_notify_update() to automatically filter vcard
    notifications in response to a client adding or modifying a contact.
    
    This patch should address both bugs:
      https://bugzilla.gnome.org/show_bug.cgi?id=652179
      https://bugzilla.gnome.org/show_bug.cgi?id=652172

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 596c829..740b251 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -633,6 +633,80 @@ get_closure (EDataBookView *book_view)
 	return g_object_get_data (G_OBJECT (book_view), "EBookBackendFile.BookView::closure");
 }
 
+static gboolean
+view_wants_uid_only (EDataBookView *book_view)
+{
+	GHashTable *fields = e_data_book_view_get_fields_of_interest (book_view);
+
+	if (fields &&
+	    g_hash_table_size (fields) == 1 &&
+	    g_hash_table_lookup (fields, e_contact_field_name (E_CONTACT_UID)))
+	    return TRUE;
+
+	return FALSE;
+}
+
+typedef struct {	
+	EContact   *contact;
+	EContact   *shallow;
+} FilterContactData;
+
+static void
+foreach_filter_contact (const gchar       *field,
+			gpointer           present,
+			FilterContactData *data)
+{
+	EContactField   field_id;
+
+	field_id = e_contact_field_id (field);
+	e_contact_copy_attributes (data->contact, data->shallow, field_id);
+}
+
+static void
+notify_update_vcard (EDataBookView *book_view,
+		     gboolean       prefiltered,
+		     const gchar   *id,
+		     const gchar   *vcard)
+{
+	GHashTable *fields = e_data_book_view_get_fields_of_interest (book_view);
+	gchar      *final_vcard;
+
+	if (view_wants_uid_only (book_view)) {
+		/* Create a shallow version of the contacts for views that are
+		 * only interested in the uid. */
+		EContact *shallow = e_contact_new ();
+
+		e_contact_set (shallow, E_CONTACT_UID, id);
+		final_vcard = e_vcard_to_string (E_VCARD (shallow), EVC_FORMAT_VCARD_30);
+		g_object_unref (shallow);
+
+	} else if (fields) {
+
+		FilterContactData data = { 0, };
+		EContact         *shallow = e_contact_new ();
+		EContact         *contact = create_contact (id, vcard);
+
+		/* Set the UID unconditionally */
+		e_contact_set (shallow, E_CONTACT_UID, id);
+
+		/* Transfer any attributes for the fields-of-interest */
+		data.contact = contact;
+		data.shallow = shallow;
+		g_hash_table_foreach (fields, (GHFunc)foreach_filter_contact, &data);
+
+		final_vcard = e_vcard_to_string (E_VCARD (shallow), EVC_FORMAT_VCARD_30);
+		g_object_unref (shallow);
+		g_object_unref (contact);
+	} else {
+		final_vcard = g_strdup (vcard);
+	}
+
+	if (prefiltered)
+		e_data_book_view_notify_update_prefiltered_vcard (book_view, id, final_vcard);
+	else
+		e_data_book_view_notify_update_vcard (book_view, final_vcard);
+}
+
 static gpointer
 book_view_thread (gpointer data)
 {
@@ -643,7 +717,7 @@ book_view_thread (gpointer data)
 	DB  *db;
 	DBT id_dbt, vcard_dbt;
 	gint db_error;
-	gboolean allcontacts;
+	gboolean allcontacts, uid_only;
 
 	g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL);
 
@@ -672,6 +746,9 @@ book_view_thread (gpointer data)
 		allcontacts = FALSE;
 	}
 
+	/* Check if the view only wants UID in the fields-of-interest */
+	uid_only = view_wants_uid_only (book_view);
+
 	d(printf ("signalling parent thread\n"));
 	e_flag_set (closure->running);
 
@@ -689,6 +766,11 @@ book_view_thread (gpointer data)
 			if (!e_flag_is_set (closure->running))
 				break;
 
+			if (uid_only) {
+				notify_update_vcard (book_view, TRUE, id, NULL);
+				continue;
+			}
+
 			string_to_dbt (id, &id_dbt);
 			memset (&vcard_dbt, 0, sizeof (vcard_dbt));
 			vcard_dbt.flags = DB_DBT_MALLOC;
@@ -696,7 +778,7 @@ book_view_thread (gpointer data)
 			db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
 
 			if (db_error == 0) {
-				e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard_dbt.data);
+				notify_update_vcard (book_view, TRUE, id, vcard_dbt.data);
 			}
 			else {
 				g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
@@ -724,10 +806,8 @@ book_view_thread (gpointer data)
 
 				/* don't include the version in the list of cards */
 				if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
-					if (allcontacts)
-						e_data_book_view_notify_update_prefiltered_vcard (book_view, id_dbt.data, vcard_dbt.data);
-					else
-						e_data_book_view_notify_update_vcard (book_view, vcard_dbt.data);
+					notify_update_vcard (book_view, allcontacts, 
+							     id_dbt.data, vcard_dbt.data);
 				} else {
 					g_free (vcard_dbt.data);
 				}
diff --git a/addressbook/libebook/e-contact.c b/addressbook/libebook/e-contact.c
index 8ea4904..048d159 100644
--- a/addressbook/libebook/e-contact.c
+++ b/addressbook/libebook/e-contact.c
@@ -1719,6 +1719,44 @@ e_contact_set_attributes (EContact *contact, EContactField field_id, GList *attr
 }
 
 /**
+ * e_contact_copy_attributes:
+ * @source: an #EContact
+ * @dest: another #EContact
+ * @field_id: an #EContactField
+ * @attributes: a #GList of pointers to #EVCardAttribute
+ *
+ * Copies the attributes for @field_id from @source into @dest
+ **/
+void
+e_contact_copy_attributes (EContact *source, EContact *dest, EContactField field_id)
+{
+	GList *attrs, *a;
+	const EContactFieldInfo *info = NULL;
+
+	g_return_if_fail (source && E_IS_CONTACT (source));
+	g_return_if_fail (dest && E_IS_CONTACT (dest));
+	g_return_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST);
+
+	info = &field_info[field_id];
+
+	attrs = e_vcard_get_attributes (E_VCARD (source));
+
+	e_vcard_remove_attributes (E_VCARD (dest), NULL, info->vcard_field_name);
+
+	for (a = attrs; a; a = a->next) {
+		EVCardAttribute *attr = a->data;
+		const gchar *name;
+
+		name = e_vcard_attribute_get_name (attr);
+
+		if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
+			e_vcard_append_attribute (E_VCARD (dest),
+						  e_vcard_attribute_copy (attr));
+		}
+	}
+}
+
+/**
  * e_contact_name_new:
  *
  * Creates a new #EContactName struct.
diff --git a/addressbook/libebook/e-contact.h b/addressbook/libebook/e-contact.h
index 729eeeb..64ed5b8 100644
--- a/addressbook/libebook/e-contact.h
+++ b/addressbook/libebook/e-contact.h
@@ -314,6 +314,7 @@ void                    e_contact_set              (EContact *contact, EContactF
    EVCardAttribute*'s. */
 GList*                  e_contact_get_attributes   (EContact *contact, EContactField field_id);
 void                    e_contact_set_attributes   (EContact *contact, EContactField field_id, GList *attributes);
+void                    e_contact_copy_attributes  (EContact *source, EContact *dest, EContactField field_id);
 
 /* misc functions for structured values */
 GType                   e_contact_date_get_type    (void);
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 358f489..3ae958e 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -804,7 +804,9 @@ e_book_backend_remove_client (EBookBackend *backend,
  * @callback returns %FALSE to stop further processing.
  **/
 void
-e_book_backend_foreach_view (EBookBackend *backend, gboolean (* callback) (EDataBookView *view, gpointer user_data), gpointer user_data)
+e_book_backend_foreach_view (EBookBackend *backend, 
+			     gboolean (* callback) (EDataBookView *view, gpointer user_data), 
+			     gpointer user_data)
 {
 	const GSList *views;
 	EDataBookView *view;
@@ -1026,10 +1028,51 @@ e_book_backend_sync (EBookBackend *backend)
 
 
 
+
+typedef struct {	
+	EContact   *contact;
+	EContact   *shallow;
+} FilterContactData;
+
+static void
+foreach_filter_contact (const gchar       *field,
+			gpointer           present,
+			FilterContactData *data)
+{
+	EContactField   field_id;
+
+	field_id = e_contact_field_id (field);
+	e_contact_copy_attributes (data->contact, data->shallow, field_id);
+}
+
 static gboolean
-view_notify_update (EDataBookView *view, gpointer contact)
+view_notify_update (EDataBookView *view, gpointer data)
 {
-	e_data_book_view_notify_update (view, contact);
+	EContact   *contact = data;
+	GHashTable *fields;
+
+	fields = e_data_book_view_get_fields_of_interest (view);
+	if (fields && g_hash_table_size (fields) > 0) {
+		FilterContactData data = { 0, };
+		EContact         *shallow = e_contact_new ();
+		gchar            *vcard;
+
+		/* Set the UID unconditionally */
+		e_contact_set (shallow, E_CONTACT_UID, e_contact_get_const (contact, E_CONTACT_UID));
+
+		/* Transfer any attributes for the fields-of-interest */
+		data.contact = contact;
+		data.shallow = shallow;
+		g_hash_table_foreach (fields, (GHFunc)foreach_filter_contact, &data);
+
+		vcard = e_vcard_to_string (E_VCARD (shallow), EVC_FORMAT_VCARD_30);
+		g_object_unref (shallow);
+
+		/* Notify with a stripped vcard holding only the fields-of-interest */
+		e_data_book_view_notify_update_vcard (view, vcard);
+	} else {
+		e_data_book_view_notify_update (view, contact);
+	}
 
 	return TRUE;
 }
