# HG changeset patch
# Node ID e8dd63649c84
# Parent  2273647d24b0
diff --git a/libpurple/protocols/demo/purpledemocontacts.c b/libpurple/protocols/demo/purpledemocontacts.c
--- a/libpurple/protocols/demo/purpledemocontacts.c
+++ b/libpurple/protocols/demo/purpledemocontacts.c
@@ -26,199 +26,214 @@
  * Helpers
  *****************************************************************************/
 static void
-purple_demo_protocol_load_status(PurpleBuddy *buddy, JsonObject *buddy_object)
+purple_demo_contacts_load_contact_icon(PurpleContactInfo *info,
+                                       const char *name)
 {
-	PurplePresence *presence = NULL;
-	JsonObject *status_object = NULL;
+	GdkPixbuf *pixbuf = NULL;
+	GError *error = NULL;
+	char *path = NULL;
+
+	path = g_strdup_printf("/im/pidgin/purple/demo/buddy_icons/%s.png", name);
+	pixbuf = gdk_pixbuf_new_from_resource(path, &error);
 
-	if(!json_object_has_member(buddy_object, "status")) {
+	if(error != NULL) {
+		g_message("Failed to load find an icon for %s: %s", path,
+		          error->message);
+
+		g_free(path);
+		g_clear_error(&error);
+
 		return;
 	}
 
-	presence = purple_buddy_get_presence(buddy);
+	g_free(path);
 
-	status_object = json_object_get_object_member(buddy_object, "status");
+	if(GDK_IS_PIXBUF(pixbuf)) {
+		purple_contact_info_set_avatar(info, pixbuf);
 
-	if(json_object_has_member(status_object, "primitive")) {
-		PurplePresencePrimitive primitive = PURPLE_PRESENCE_PRIMITIVE_OFFLINE;
-		const char *name = NULL;
+		g_clear_object(&pixbuf);
+	}
+}
+
+static void
+purple_demo_contacts_load_contact_person(JsonObject *person_object,
+                                         PurpleContactInfo *info)
+{
+	PurplePerson *person = NULL;
+	const char *value = NULL;
 
-		name = json_object_get_string_member(status_object, "primitive");
-		if(!purple_strempty(name)) {
-			GEnumClass *klass = NULL;
-			GEnumValue *value = NULL;
-
-			klass = g_type_class_ref(PURPLE_TYPE_PRESENCE_PRIMITIVE);
-			value = g_enum_get_value_by_nick(klass, name);
+	/* If the person has an id, grab it so we can use it when constructing the
+	 * person object.
+	 */
+	if(json_object_has_member(person_object, "id")) {
+		value = json_object_get_string_member(person_object, "id");
+	}
 
-			if(value != NULL) {
-				primitive = value->value;
-			}
+	/* Now create the person with the optional id. */
+	person = g_object_new(PURPLE_TYPE_PERSON, "id", value, NULL);
 
-			g_type_class_unref(klass);
+	/* Alias */
+	if(json_object_has_member(person_object, "alias")) {
+		value = json_object_get_string_member(person_object, "alias");
+		if(!purple_strempty(value)) {
+			purple_person_set_alias(person, value);
 		}
-
-		purple_presence_set_primitive(presence, primitive);
 	}
 
-	if(json_object_has_member(status_object, "message")) {
-		const gchar *message = NULL;
+	/* Create the link between the person and the contact info. */
+	purple_person_add_contact_info(person, info);
+	purple_contact_info_set_person(info, person);
+
+	g_clear_object(&person);
+}
 
-		message = json_object_get_string_member(status_object, "message");
+static void
+purple_demo_contacts_load_contact_presence(JsonObject *presence_object,
+                                           PurpleContactInfo *info)
+{
+	PurplePresence *presence = NULL;
+	const gchar *value = NULL;
 
-		purple_presence_set_message(presence, message);
+	presence = purple_contact_info_get_presence(info);
+
+	/* Emoji */
+	if(json_object_has_member(presence_object, "emoji")) {
+		value = json_object_get_string_member(presence_object, "emoji");
+		if(!purple_strempty(value)) {
+			purple_presence_set_emoji(presence, value);
+		}
 	}
 
-	if(json_object_has_member(status_object, "idle")) {
+	/* Idle Time */
+	if(json_object_has_member(presence_object, "idle")) {
 		GDateTime *now = NULL;
 		GDateTime *idle_since = NULL;
-		gint idle_minutes = 0;
+		gint64 ivalue = 0;
 
-		idle_minutes = json_object_get_int_member(status_object, "idle");
+		ivalue = json_object_get_int_member(presence_object, "idle");
+
 		now = g_date_time_new_now_local();
-		idle_since = g_date_time_add_minutes(now, -1 * idle_minutes);
+		idle_since = g_date_time_add_minutes(now, -1 * ivalue);
 
 		purple_presence_set_idle(presence, TRUE, idle_since);
 
 		g_date_time_unref(idle_since);
 		g_date_time_unref(now);
 	}
-}
 
-static void
-purple_demo_protocol_load_icon(PurpleAccount *account, const gchar *name)
-{
-	gchar *path = NULL;
-	GBytes *icon = NULL;
-	gpointer icon_data = NULL;
-	gsize icon_len = 0;
+	/* Message */
+	if(json_object_has_member(presence_object, "message")) {
+		value = json_object_get_string_member(presence_object, "message");
+		if(!purple_strempty(value)) {
+			purple_presence_set_message(presence, value);
+		}
+	}
 
-	path = g_strdup_printf("/im/pidgin/purple/demo/buddy_icons/%s.png", name);
-	icon = g_resource_lookup_data(purple_demo_get_resource(), path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
-	g_free(path);
-
-	if(icon == NULL) {
-		/* No stored icon. */
-		return;
+	/* Mobile */
+	if(json_object_has_member(presence_object, "mobile")) {
+		gboolean bvalue = FALSE;
+		bvalue = json_object_get_boolean_member(presence_object, "mobile");
+		purple_presence_set_mobile(presence, bvalue);
 	}
 
-	icon_data = g_bytes_unref_to_data(icon, &icon_len);
-	purple_buddy_icons_set_for_user(account, name, icon_data, icon_len, NULL);
-}
+	/* Primitive */
+	if(json_object_has_member(presence_object, "primitive")) {
+		PurplePresencePrimitive primitive = PURPLE_PRESENCE_PRIMITIVE_OFFLINE;
+
+		value = json_object_get_string_member(presence_object, "primitive");
+		if(!purple_strempty(value)) {
+			GEnumClass *enum_class = NULL;
+			GEnumValue *enum_value = NULL;
 
-static void
-purple_demo_protocol_load_buddies(PurpleAccount *account, PurpleGroup *group,
-                                  PurpleMetaContact *contact, GList *buddies)
-{
-	while(buddies != NULL) {
-		JsonNode *buddy_node = NULL;
-		JsonObject *buddy_object = NULL;
-		const gchar *name = NULL, *alias = NULL;
+			enum_class = g_type_class_ref(PURPLE_TYPE_PRESENCE_PRIMITIVE);
+			enum_value = g_enum_get_value_by_nick(enum_class, value);
 
-		buddy_node = (JsonNode *)buddies->data;
-		buddy_object = json_node_get_object(buddy_node);
+			if(enum_value != NULL) {
+				primitive = enum_value->value;
+			}
 
-		if(json_object_has_member(buddy_object, "name")) {
-			name = json_object_get_string_member(buddy_object, "name");
+			g_type_class_unref(enum_class);
 		}
 
-		if(json_object_has_member(buddy_object, "alias")) {
-			alias = json_object_get_string_member(buddy_object, "alias");
-		}
-
-		if(name != NULL) {
-			PurpleBuddy *buddy = NULL;
-
-			buddy = purple_blist_find_buddy(account, name);
-			if(buddy == NULL) {
-				buddy = purple_buddy_new(account, name, alias);
-				purple_blist_add_buddy(buddy, contact, group, NULL);
-			}
-
-			purple_demo_protocol_load_icon(account, name);
-			purple_demo_protocol_load_status(buddy, buddy_object);
-			if (purple_strequal(name, "Echo")) {
-				purple_protocol_got_media_caps(account, name);
-			}
-		}
-
-		buddies = g_list_delete_link(buddies, buddies);
+		purple_presence_set_primitive(presence, primitive);
 	}
 }
 
 static void
-purple_demo_protocol_load_contacts(PurpleAccount *account, PurpleGroup *group,
-                                   GList *contacts)
+purple_demo_contacts_load_contact(PurpleContactManager *manager,
+                                  PurpleAccount *account,
+                                  JsonObject *contact_object)
 {
-	while(contacts != NULL) {
-		PurpleMetaContact *contact = NULL;
-		JsonNode *contact_node = NULL;
-		JsonObject *contact_object = NULL;
+	PurpleContact *contact = NULL;
+	PurpleContactInfo *info = NULL;
+	const char *id = NULL;
+	const char *value = NULL;
 
-		contact_node = (JsonNode *)contacts->data;
-		contact_object = json_node_get_object(contact_node);
-
-		contact = purple_meta_contact_new();
-
-		/* Set the contact's alias if one was specified. */
-		if(json_object_has_member(contact_object, "alias")) {
-			const gchar *alias = NULL;
+	/* If we have an id, grab so we can create the contact with it. */
+	if(json_object_has_member(contact_object, "id")) {
+		id = json_object_get_string_member(contact_object, "id");
+	}
 
-			alias = json_object_get_string_member(contact_object, "alias");
-
-			purple_meta_contact_set_alias(contact, alias);
-		}
-
-		/* Add the contact to the group. */
-		purple_blist_add_contact(contact, group, NULL);
+	/* Create the contact using the id if one was provided. */
+	contact = purple_contact_new(account, id);
+	info = PURPLE_CONTACT_INFO(contact);
 
-		/* Finally add the buddies */
-		if(json_object_has_member(contact_object, "buddies")) {
-			JsonArray *buddies_array = NULL;
-			GList *buddies = NULL;
+	/* Alias */
+	if(json_object_has_member(contact_object, "alias")) {
+		value = json_object_get_string_member(contact_object, "alias");
+		if(!purple_strempty(value)) {
+			purple_contact_info_set_alias(info, value);
+		}
+	}
 
-			buddies_array = json_object_get_array_member(contact_object,
-			                                             "buddies");
-			buddies = json_array_get_elements(buddies_array);
+	/* Color */
+	if(json_object_has_member(contact_object, "color")) {
+		value = json_object_get_string_member(contact_object, "color");
+		if(!purple_strempty(value)) {
+			purple_contact_info_set_color(info, value);
+		}
+	}
 
-			purple_demo_protocol_load_buddies(account, group, contact,
-			                                  buddies);
+	/* Display Name */
+	if(json_object_has_member(contact_object, "display_name")) {
+		value = json_object_get_string_member(contact_object, "display_name");
+		if(!purple_strempty(value)) {
+			purple_contact_info_set_display_name(info, value);
 		}
-
-		contacts = g_list_delete_link(contacts, contacts);
 	}
-}
 
-static void
-purple_demo_protocol_load_groups(PurpleAccount *account,
-                                 JsonObject *root_object)
-{
-	PurpleGroup *last = NULL;
-	GList *groups = NULL;
+	/* Username */
+	if(json_object_has_member(contact_object, "username")) {
+		value = json_object_get_string_member(contact_object, "username");
+		if(!purple_strempty(value)) {
+			purple_contact_info_set_username(info, value);
 
-	/* Get the members of the root object, this is our list of group names. */
-	groups = json_object_get_members(root_object);
+			purple_demo_contacts_load_contact_icon(info, value);
+		}
+	}
 
-	while(groups != NULL) {
-		PurpleGroup *group = NULL;
-		JsonArray *group_array = NULL;
-		GList *contacts = NULL;
-		const gchar *group_name = (const gchar *)groups->data;
+	/* Load the person. */
+	if(json_object_has_member(contact_object, "person")) {
+		JsonObject *person_object = NULL;
 
-		/* Add each group according to the json file. */
-		group = purple_group_new(group_name);
-		purple_blist_add_group(group, PURPLE_BLIST_NODE(last));
+		person_object = json_object_get_object_member(contact_object,
+		                                              "person");
+
+		purple_demo_contacts_load_contact_person(person_object, info);
+	}
 
-		/* Now get the contacts and add them. */
-		group_array = json_object_get_array_member(root_object, group_name);
-		contacts = json_array_get_elements(group_array);
+	/* Load the presence. */
+	if(json_object_has_member(contact_object, "presence")) {
+		JsonObject *presence_object = NULL;
 
-		purple_demo_protocol_load_contacts(account, group, contacts);
+		presence_object = json_object_get_object_member(contact_object,
+		                                                "presence");
 
-		groups = g_list_delete_link(groups, groups);
+		purple_demo_contacts_load_contact_presence(presence_object, info);
+	}
 
-		last = group;
-	}
+	/* Finally add the contact to the contact manager. */
+	purple_contact_manager_add(manager, contact);
 }
 
 /******************************************************************************
@@ -226,11 +241,13 @@
  *****************************************************************************/
 void
 purple_demo_contacts_load(PurpleAccount *account) {
+	PurpleContactManager *manager = NULL;
+	GError *error = NULL;
 	GInputStream *istream = NULL;
-	GError *error = NULL;
+	GList *contacts = NULL;
+	JsonArray *contacts_array = NULL;
+	JsonNode *root_node = NULL;
 	JsonParser *parser = NULL;
-	JsonNode *root_node = NULL;
-	JsonObject *root_object = NULL;
 
 	/* get a stream to the contacts.json resource */
 	istream = g_resource_open_stream(purple_demo_get_resource(),
@@ -246,12 +263,24 @@
 		return;
 	}
 
-	/* Load our data */
 	root_node = json_parser_get_root(parser);
-	root_object = json_node_get_object(root_node);
+
+	manager = purple_contact_manager_get_default();
 
-	/* Load the groups! */
-	purple_demo_protocol_load_groups(account, root_object);
+	/* Load the contacts! */
+	contacts_array = json_node_get_array(root_node);
+	contacts = json_array_get_elements(contacts_array);
+	while(contacts != NULL) {
+		JsonNode *contact_node = NULL;
+		JsonObject *contact_object = NULL;
+
+		contact_node = contacts->data;
+		contact_object = json_node_get_object(contact_node);
+
+		purple_demo_contacts_load_contact(manager, account, contact_object);
+
+		contacts = g_list_delete_link(contacts, contacts);
+	}
 
 	/* Clean up everything else... */
 	g_clear_object(&parser);
diff --git a/libpurple/protocols/demo/resources/contacts.json b/libpurple/protocols/demo/resources/contacts.json
--- a/libpurple/protocols/demo/resources/contacts.json
+++ b/libpurple/protocols/demo/resources/contacts.json
@@ -1,64 +1,93 @@
-{
-	"Family": [{
+[{
+	"id": "grim",
+	"person": {
 		"alias": "Grim",
-		"buddies": [{
-			"name": "Gary",
-			"status": {
-				"primitive": "available",
-				"message": "💤 Sleeping... 😴",
-				"idle": 1337
-			}
-		}]
-	}],
-	"Friends": [{
-		"buddies": [{
-			"name": "John",
-			"alias": "Rekkanoryo",
-			"status": {
-				"primitive": "away"
-			}
-		}]
-	}],
-	"Work": [{
-		"buddies": [{
-			"name": "Elliott",
-			"status": {
-				"primitive": "away",
-				"message": "Coding..."
-			}
-		}]
-	}, {
-		"buddies": [{
-			"name": "Richard",
-			"status": {
-				"primitive": "available"
-			}
-		}]
-	}],
-	"School": [{
-		"buddies": [{
-			"name": "Eion",
-			"status": {
-				"primitive": "available",
-				"message": "Writing crazy patches!"
-			}
-		}]
-	}, {
-		"buddies": [{
-			"name": "Markus",
-			"status": {
-				"primitive": "extended_away",
-				"message": "Running all the things in valgrind..."
-			}
-		}]
-	}],
-	"Nymphs": [{
-		"buddies": [{
-			"name": "Echo",
-			"status": {
-				"primitive": "available",
-				"message": "Cursed to speak the last words spoken to me"
-			}
-		}]
-	}]
-}
+		"id": "gary"
+	},
+	"presence": {
+		"primitive": "available",
+		"message": "💤 Sleeping... 😴",
+		"idle": 1337
+	},
+	"username": "Gary",
+	"tags": [
+		"group:Family"
+	]
+}, {
+	"id": "john",
+	"alias": "Rekkanoryo",
+	"person": {
+		"id": "john"
+	},
+	"presence": {
+		"primitive": "away"
+	},
+	"tags": [
+		"group:Friends"
+	],
+	"username": "John"
+}, {
+	"id": "elliott",
+	"person": {
+		"id": "elliott"
+	},
+	"presence": {
+		"primitive": "away",
+		"message": "Coding..."
+	},
+	"tags": [
+		"group:Work"
+	],
+	"username": "Elliott"
+}, {
+	"id": "richard",
+	"person": {
+		"id": "richard"
+	},
+	"presence": {
+		"primitive": "available"
+	},
+	"tags": [
+		"group:Work"
+	],
+	"username": "Richard"
+}, {
+	"id": "eion",
+	"person": {
+		"id": "eion"
+	},
+	"presence": {
+		"primitive": "available",
+		"message": "Writing crazy patches!"
+	},
+	"tags": [
+		"group:School"
+	],
+	"username": "Eion"
+}, {
+	"id": "markus",
+	"person": {
+		"id": "markus"
+	},
+	"presence": {
+		"primitive": "extended_away",
+		"message": "Running all the things in valgrind..."
+	},
+	"tags": [
+		"group:School"
+	],
+	"username": "Markus"
+}, {
+	"id": "echo",
+	"person": {
+		"id": "echo"
+	},
+	"presence": {
+		"primitive": "available",
+		"message": "Cursed to speak the last words spoken to me"
+	},
+	"tags": [
+		"group:Nymphs"
+	],
+	"username": "Echo"
+}]
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -212,7 +212,7 @@
 #######################################################################
 # Check for gdk-pixbuf (required)
 #######################################################################
-gdk_pixbuf = dependency('gdk-pixbuf-2.0')
+gdk_pixbuf = dependency('gdk-pixbuf-2.0', version : '>= 2.26.0')
 
 #######################################################################
 # Check for GObject Introspection
