# HG changeset patch
# Node ID 5470d94ecfc1
# Parent  f9bb5493c0a0
diff --git a/libpurple/buddy.c b/libpurple/buddy.c
--- a/libpurple/buddy.c
+++ b/libpurple/buddy.c
@@ -24,6 +24,7 @@
 
 #include "debug.h"
 #include "purplebuddypresence.h"
+#include "purplecontactmanager.h"
 #include "purpleconversationmanager.h"
 #include "purpleprotocolclient.h"
 #include "util.h"
@@ -292,15 +293,25 @@
 PurpleBuddy *
 purple_buddy_new(PurpleAccount *account, const gchar *name, const gchar *alias)
 {
+	PurpleBuddy *buddy = NULL;
+	PurpleContactManager *manager = NULL;
+
 	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
 	g_return_val_if_fail(name != NULL, NULL);
 
-	return g_object_new(
+	buddy = g_object_new(
 		PURPLE_TYPE_BUDDY,
 		"account", account,
 		"name", name,
 		"local-alias", alias,
 		NULL);
+
+	manager = purple_contact_manager_get_default();
+	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+	purple_contact_manager_add_buddy(manager, buddy);
+	G_GNUC_END_IGNORE_DEPRECATIONS
+
+	return buddy;
 }
 
 const gchar *
diff --git a/libpurple/purplecontact.c b/libpurple/purplecontact.c
--- a/libpurple/purplecontact.c
+++ b/libpurple/purplecontact.c
@@ -150,9 +150,6 @@
 		case PROP_AVATAR:
 			purple_contact_set_avatar(contact, g_value_get_object(value));
 			break;
-		case PROP_PRESENCE:
-			purple_contact_set_presence(contact, g_value_get_object(value));
-			break;
 		case PROP_PERSON:
 			purple_contact_set_person(contact, g_value_get_object(value));
 			break;
@@ -202,6 +199,7 @@
 static void
 purple_contact_init(PurpleContact *contact) {
 	contact->tags = purple_tags_new();
+	contact->presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL);
 }
 
 static void
@@ -308,7 +306,7 @@
 		"presence", "presence",
 		"The presence of the contact",
 		PURPLE_TYPE_PRESENCE,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleContact:tags:
@@ -443,15 +441,6 @@
 	return contact->presence;
 }
 
-void
-purple_contact_set_presence(PurpleContact *contact, PurplePresence *presence) {
-	g_return_if_fail(PURPLE_IS_CONTACT(contact));
-
-	if(g_set_object(&contact->presence, presence)) {
-		g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_PRESENCE]);
-	}
-}
-
 PurpleTags *
 purple_contact_get_tags(PurpleContact *contact) {
 	g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL);
diff --git a/libpurple/purplecontactmanager.h b/libpurple/purplecontactmanager.h
--- a/libpurple/purplecontactmanager.h
+++ b/libpurple/purplecontactmanager.h
@@ -141,6 +141,23 @@
  */
 PurpleContact *purple_contact_manager_find_with_id(PurpleContactManager *manager, PurpleAccount *account, const gchar *id);
 
+/**
+ * purple_contact_manager_add_buddy:
+ * @manager: The instance.
+ * @buddy: A [class@Purple.Buddy] instance.
+ *
+ * Creates a new [class@Purple.Contact] and binds it's properties to @buddy and
+ * then adds the new [class@Purple.Contact] via
+ * [method@Purple.ContactManager.add].
+ *
+ * This method is meant to help us transition to the new API and this method
+ * shouldn't be used elsewhere.
+ *
+ * Since: 3.0.0
+ */
+G_DEPRECATED
+void purple_contact_manager_add_buddy(PurpleContactManager *manager, PurpleBuddy *buddy);
+
 G_END_DECLS
 
 #endif /* PURPLE_CONTACT_MANAGER_H */
diff --git a/libpurple/purplecontactmanager.c b/libpurple/purplecontactmanager.c
--- a/libpurple/purplecontactmanager.c
+++ b/libpurple/purplecontactmanager.c
@@ -19,6 +19,8 @@
 #include <glib/gi18n-lib.h>
 
 #include "purplecontactmanager.h"
+
+#include "purplegdkpixbuf.h"
 #include "purpleprivate.h"
 #include "util.h"
 
@@ -68,6 +70,64 @@
 	return purple_strequal(id_a, id_b);
 }
 
+static gboolean
+purple_contact_manager_convert_icon_to_avatar(GBinding *binding,
+                                              const GValue *from_value,
+                                              GValue *to_value,
+                                              gpointer user_data)
+{
+	PurpleBuddyIcon *icon = g_value_get_pointer(from_value);
+	GdkPixbuf *avatar = NULL;
+	gconstpointer data = NULL;
+	size_t len;
+
+	if(icon == NULL) {
+		g_value_set_object(to_value, NULL);
+		return TRUE;
+	}
+
+	data = purple_buddy_icon_get_data(icon, &len);
+
+	avatar = purple_gdk_pixbuf_from_data(data, len);
+
+	g_value_set_object(to_value, avatar);
+
+	g_clear_object(&avatar);
+
+	return TRUE;
+}
+
+static gboolean
+purple_contact_manager_convert_avatar_to_icon(GBinding *binding,
+                                              const GValue *from_value,
+                                              GValue *to_value,
+                                              gpointer user_data)
+{
+	PurpleBuddyIcon *icon = NULL;
+	GdkPixbuf *avatar = g_value_get_object(from_value);
+	gchar *buffer = NULL;
+	gsize len;
+	gboolean result = FALSE;
+
+	if(!GDK_IS_PIXBUF(avatar)) {
+		g_value_set_pointer(to_value, NULL);
+
+		return TRUE;
+	}
+
+	result = gdk_pixbuf_save_to_buffer(avatar, &buffer, &len, "png", NULL,
+	                                   "compression", "9", NULL);
+	if(!result) {
+		return FALSE;
+	}
+
+	purple_buddy_icon_set_data(icon, (guchar *)buffer, len, NULL);
+
+	g_free(buffer);
+
+	return TRUE;
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -367,3 +427,60 @@
 
 	return NULL;
 }
+
+/******************************************************************************
+ * Migration API
+ *****************************************************************************/
+void
+purple_contact_manager_add_buddy(PurpleContactManager *manager,
+                                 PurpleBuddy *buddy)
+{
+	PurpleAccount *account = NULL;
+	PurpleContact *contact = NULL;
+	PurplePresence *buddy_presence = NULL;
+	PurplePresence *contact_presence = NULL;
+	const gchar *id = NULL;
+
+	g_return_if_fail(PURPLE_IS_CONTACT_MANAGER(manager));
+	g_return_if_fail(PURPLE_IS_BUDDY(buddy));
+
+	/* Create the new contact. */
+	account = purple_buddy_get_account(buddy);
+	id = purple_buddy_get_id(buddy);
+	contact = purple_contact_new(account, id);
+
+	/* Bind all of the properties. */
+	g_object_bind_property(buddy, "name", contact, "username",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	g_object_bind_property(buddy, "local-alias", contact, "alias",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	g_object_bind_property(buddy, "server-alias", contact, "display-name",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	buddy_presence = purple_buddy_get_presence(buddy);
+	contact_presence = purple_contact_get_presence(contact);
+
+	g_object_bind_property(buddy_presence, "idle", contact_presence, "idle",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	g_object_bind_property(buddy_presence, "idle-time", contact_presence,
+	                       "idle-time",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	g_object_bind_property(buddy_presence, "login-time", contact_presence,
+	                       "login-time",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	g_object_bind_property(buddy_presence, "active-status", contact_presence,
+	                       "active-status",
+	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	g_object_bind_property_full(buddy, "icon", contact, "avatar",
+	                            G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+	                            purple_contact_manager_convert_icon_to_avatar,
+	                            purple_contact_manager_convert_avatar_to_icon,
+	                            NULL, NULL);
+
+	/* Finally add it to the manager. */
+	purple_contact_manager_add(manager, contact);
+
+	/* purple_contact_manager_add adds it's own reference, so free our copy. */
+	g_clear_object(&contact);
+}
diff --git a/libpurple/purpleperson.c b/libpurple/purpleperson.c
--- a/libpurple/purpleperson.c
+++ b/libpurple/purpleperson.c
@@ -113,18 +113,6 @@
 	purple_person_sort_contacts(data);
 }
 
-static void
-purple_person_contact_notify_cb(GObject *obj,
-                                G_GNUC_UNUSED GParamSpec *pspec,
-                                gpointer data)
-{
-	PurpleContact *contact = PURPLE_CONTACT(obj);
-
-	g_signal_connect_object(contact, "notify",
-	                        G_CALLBACK(purple_person_presence_notify_cb),
-	                        data, 0);
-}
-
 /******************************************************************************
  * GListModel Implementation
  *****************************************************************************/
@@ -407,17 +395,11 @@
 
 	g_ptr_array_add(person->contacts, g_object_ref(contact));
 
-	g_signal_connect_object(contact, "notify::presence",
-	                        G_CALLBACK(purple_person_contact_notify_cb),
+	presence = purple_contact_get_presence(contact);
+	g_signal_connect_object(presence, "notify",
+	                        G_CALLBACK(purple_person_presence_notify_cb),
 	                        person, 0);
 
-	presence = purple_contact_get_presence(contact);
-	if(PURPLE_IS_PRESENCE(presence)) {
-		g_signal_connect_object(presence, "notify",
-		                        G_CALLBACK(purple_person_presence_notify_cb),
-		                        person, 0);
-	}
-
 	purple_contact_set_person(contact, person);
 
 	purple_person_sort_contacts(person);
@@ -441,17 +423,10 @@
 	if(removed) {
 		PurplePresence *presence = purple_contact_get_presence(contact);
 
-		/* Disconnect our signal handlers. */
-		g_signal_handlers_disconnect_by_func(contact,
-		                                     purple_person_contact_notify_cb,
+		g_signal_handlers_disconnect_by_func(presence,
+		                                     purple_person_presence_notify_cb,
 		                                     person);
 
-		if(PURPLE_IS_PRESENCE(presence)) {
-			g_signal_handlers_disconnect_by_func(presence,
-			                                     purple_person_presence_notify_cb,
-			                                     person);
-		}
-
 		purple_contact_set_person(contact, NULL);
 
 		purple_person_sort_contacts(person);
diff --git a/libpurple/tests/test_contact.c b/libpurple/tests/test_contact.c
--- a/libpurple/tests/test_contact.c
+++ b/libpurple/tests/test_contact.c
@@ -47,7 +47,6 @@
 	PurpleContact *contact = NULL;
 	PurplePerson *person = NULL;
 	PurplePerson *person1 = NULL;
-	PurplePresence *presence = NULL;
 	PurplePresence *presence1 = NULL;
 	PurpleTags *tags = NULL;
 	GdkPixbuf *avatar = NULL;
@@ -59,7 +58,6 @@
 
 	account = purple_account_new("test", "test");
 	avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
-	presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL);
 	person = purple_person_new();
 
 	/* Use g_object_new so we can test setting properties by name. All of them
@@ -74,7 +72,6 @@
 		"display-name", "display-name",
 		"alias", "alias",
 		"avatar", avatar,
-		"presence", presence,
 		"person", person,
 		NULL);
 
@@ -98,7 +95,7 @@
 	g_assert_cmpstr(display_name, ==, "display-name");
 	g_assert_cmpstr(alias, ==, "alias");
 	g_assert_true(avatar1 == avatar);
-	g_assert_true(presence1 == presence);
+	g_assert_nonnull(presence1);
 	g_assert_nonnull(tags);
 	g_assert_true(person1 == person);
 
@@ -114,7 +111,6 @@
 	g_clear_object(&person);
 	g_clear_object(&person1);
 
-	g_clear_object(&presence);
 	g_clear_object(&avatar);
 	g_clear_object(&contact);
 	g_clear_object(&account);
diff --git a/libpurple/tests/test_contact_manager.c b/libpurple/tests/test_contact_manager.c
--- a/libpurple/tests/test_contact_manager.c
+++ b/libpurple/tests/test_contact_manager.c
@@ -260,6 +260,92 @@
 	g_clear_object(&manager);
 }
 
+static void
+test_purple_contact_manager_add_buddy(void) {
+	PurpleAccount *account = NULL;
+	PurpleBuddy *buddy = NULL;
+	PurpleContact *contact = NULL;
+	PurpleContactManager *manager = NULL;
+	PurpleStatusType *type = NULL;
+	GList *statuses = NULL;
+	const gchar *id = NULL;
+	const gchar *source = NULL;
+	const gchar *destination = NULL;
+
+	manager = purple_contact_manager_get_default();
+
+	/* Create our account and add the statuses for testing. */
+	account = purple_account_new("test", "test");
+
+	type = purple_status_type_new(PURPLE_STATUS_OFFLINE, "offline",
+	                              "offline", TRUE);
+	statuses = g_list_append(statuses, type);
+	type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, "online",
+	                              "online", TRUE);
+
+	purple_account_set_status_types(account, statuses);
+
+	/* purple_buddy_new will call purple_contact_manager_add_buddy. */
+	buddy = purple_buddy_new(account, "buddy-name", "buddy-alias");
+
+	/* Verify that we can find the created contact via id. */
+	id = purple_buddy_get_id(buddy);
+	contact = purple_contact_manager_find_with_id(manager, account, id);
+	g_assert_nonnull(contact);
+	g_assert_true(PURPLE_IS_CONTACT(contact));
+
+	/* Verify that we can find the created contact via username. */
+	contact = purple_contact_manager_find_with_username(manager, account,
+	                                                    "buddy-name");
+	g_assert_nonnull(contact);
+	g_assert_true(PURPLE_IS_CONTACT(contact));
+
+	/* Now check the alias and display name to make sure they were synced as
+	 * well.
+	 */
+	source = purple_buddy_get_local_alias(buddy);
+	destination = purple_contact_get_alias(contact);
+	g_assert_cmpstr(destination, ==, source);
+
+	source = purple_buddy_get_server_alias(buddy);
+	destination = purple_contact_get_display_name(contact);
+	g_assert_cmpstr(destination, ==, source);
+
+	/* Now lets change the settings in the buddy and verify they made it to the
+	 * contact.
+	 */
+	/* We have to skip testing the name because we have to stand up a LOT more
+	 * of libpurple to be able to change the name.
+	purple_buddy_set_name(buddy, "guy-name");
+	g_assert_cmpstr(purple_contact_get_username(contact), ==, "guy-name");
+	*/
+
+	purple_buddy_set_local_alias(buddy, "guy-alias");
+	g_assert_cmpstr(purple_contact_get_alias(contact), ==, "guy-alias");
+
+	purple_buddy_set_server_alias(buddy, "server-guy");
+	g_assert_cmpstr(purple_contact_get_display_name(contact), ==,
+	                "server-guy");
+
+	purple_contact_set_alias(contact, "friend-alias");
+	g_assert_cmpstr(purple_buddy_get_local_alias(buddy), ==, "friend-alias");
+
+	purple_contact_set_display_name(contact, "server-friend");
+	g_assert_cmpstr(purple_buddy_get_server_alias(buddy), ==, "server-friend");
+
+	/* We can't verify the presences changes because PurpleBuddy has to be in
+	 * a PurpleMetaContact for that to not crash.
+	 */
+
+	/* Since we're working on the default contact manager, make sure we remove
+	 * any contacts for our test account.
+	 */
+	purple_contact_manager_remove_all(manager, account);
+
+	g_clear_object(&account);
+	g_clear_object(&buddy);
+	g_clear_object(&contact);
+}
 /******************************************************************************
  * Main
  *****************************************************************************/
@@ -286,5 +372,8 @@
 	g_test_add_func("/contact-manager/find/with-id",
 	                test_purple_contact_manager_find_with_id);
 
+	g_test_add_func("/contact-manager/add-buddy",
+	                test_purple_contact_manager_add_buddy);
+
 	return g_test_run();
 }
diff --git a/libpurple/tests/test_person.c b/libpurple/tests/test_person.c
--- a/libpurple/tests/test_person.c
+++ b/libpurple/tests/test_person.c
@@ -236,19 +236,18 @@
 	priority = purple_person_get_priority_contact(person);
 	g_assert_null(priority);
 
-	/* Build the presence and status for the contact. */
-	presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL);
+	/* Now create a real contact. */
+	contact = purple_contact_new(account, "username");
+	purple_person_add_contact(person, contact);
+
+	/* Set the status of the contact. */
+	presence = purple_contact_get_presence(contact);
 	status_type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, "available",
 	                                     "Available", FALSE);
 	status = purple_status_new(status_type, presence);
 	g_object_set(G_OBJECT(presence), "active-status", status, NULL);
 	g_clear_object(&status);
 
-	/* Now create a real contact. */
-	contact = purple_contact_new(account, "username");
-	purple_contact_set_presence(contact, presence);
-	purple_person_add_contact(person, contact);
-
 	g_assert_true(called);
 
 	priority = purple_person_get_priority_contact(person);
@@ -309,17 +308,16 @@
 		/* Set changed to false as it shouldn't be changed. */
 		changed = FALSE;
 
-		/* Build the presence and status for the contact. */
-		presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL);
-		status = purple_status_new(offline, presence);
-		g_object_set(G_OBJECT(presence), "active-status", status, NULL);
-		g_clear_object(&status);
-
 		/* Now create a real contact. */
 		username = g_strdup_printf("username%d", i + 1);
 		contact = purple_contact_new(account, username);
 		g_free(username);
-		purple_contact_set_presence(contact, presence);
+
+		/* Set the status for the contact. */
+		presence = purple_contact_get_presence(contact);
+		status = purple_status_new(offline, presence);
+		g_object_set(G_OBJECT(presence), "active-status", status, NULL);
+		g_clear_object(&status);
 
 		purple_person_add_contact(person, contact);
 
@@ -336,7 +334,6 @@
 		}
 
 		g_clear_object(&contact);
-		g_clear_object(&presence);
 	}
 
 	n_items = g_list_model_get_n_items(G_LIST_MODEL(person));
