Remove PidginBlist

Fri, 17 Feb 2023 19:37:13 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 17 Feb 2023 19:37:13 -0600
changeset 42061
93ab30300b06
parent 42060
d55b605fdafb
child 42062
ab77968cc8e2

Remove PidginBlist

PidginContactList is functional enough for us to start adding features and
since GtkTreeView is being deprecated, now seemed like a great time to purge
PidginBlist.

There's a few remaining pieces in gtkblist.[ch] relating to the join chat and
im buddy dialogs, as well as the PurpleBlist subclass we need to work out yet.

I also removed the gtkbuddynote plugin as all it did was add the buddy note to
the tooltip. My future plans here will have this built in to the ui because a
few protocols support this already and we can mock it for those that don't.

Testing Done:
Launched Pidgin 3 and verified stuff worked!

Bugs closed: PIDGIN-17751

Reviewed at https://reviews.imfreedom.org/r/2228/

ChangeLog.API file | annotate | diff | comparison | revisions
pidgin/gtkblist.c file | annotate | diff | comparison | revisions
pidgin/gtkblist.h file | annotate | diff | comparison | revisions
pidgin/gtkconv.c file | annotate | diff | comparison | revisions
pidgin/pidginapplication.c file | annotate | diff | comparison | revisions
pidgin/pidginui.c file | annotate | diff | comparison | revisions
pidgin/plugins/gtkbuddynote/gtkbuddynote.c file | annotate | diff | comparison | revisions
pidgin/plugins/gtkbuddynote/meson.build file | annotate | diff | comparison | revisions
pidgin/plugins/meson.build file | annotate | diff | comparison | revisions
pidgin/plugins/transparency/transparency.c file | annotate | diff | comparison | revisions
pidgin/win32/gtkwin32dep.c file | annotate | diff | comparison | revisions
--- a/ChangeLog.API	Fri Feb 17 19:34:51 2023 -0600
+++ b/ChangeLog.API	Fri Feb 17 19:37:13 2023 -0600
@@ -964,14 +964,29 @@
 		* pidgin_append_blist_node_privacy_menu
 		* pidgin_append_blist_node_proto_menu
 		* pidgin_append_menu_action
+		* PidginBlistSortMethod
 		* pidgin_blist_add_alert
 		* pidgin_blist_draw_tooltip
+		* pidgin_blist_get_default_gtk_blist
+		* pidgin_blist_get_emblem
+		* pidgin_blist_get_handle
+		* pidgin_blist_get_name_markup
+		* pidgin_blist_get_sort_methods
 		* pidgin_blist_get_status_icon
 		* pidgin_blist_get_theme
+		* pidgin_blist_init
 		* pidgin_blist_layout_get_type
 		* pidgin_blist_make_buddy_menu
+		* pidgin_blist_node_is_contact_expanded
+		* pidgin_blist_query_tooltip_for_node
+		* pidgin_blist_refresh
 		* pidgin_blist_set_headline
 		* pidgin_blist_set_theme
+		* pidgin_blist_setup_sort_methods
+		* pidgin_blist_sort_function
+		* pidgin_blist_sort_method_reg
+		* pidgin_blist_sort_method_set
+		* pidgin_blist_sort_method_unreg
 		* pidgin_blist_theme_get_background_color
 		* pidgin_blist_theme_get_collapsed_background_color
 		* pidgin_blist_theme_get_collapsed_text_info
@@ -1008,11 +1023,13 @@
 		* pidgin_blist_theme_set_unread_message_text_info
 		* pidgin_blist_toggle_visibility
 		* pidgin_blist_tooltip_destroy
+		* pidgin_blist_uninit
 		* pidgin_blist_update_account_error_state
 		* pidgin_blist_update_accounts_menu
 		* pidgin_blist_update_columns
 		* pidgin_blist_update_plugin_actions
 		* pidgin_blist_update_refresh_timeout
+		* pidgin_blist_update_sort_methods
 		* pidgin_blist_visibility_manager_add
 		* pidgin_blist_visibility_manager_remove
 		* PidginBlistLayout
--- a/pidgin/gtkblist.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/gtkblist.c	Fri Feb 17 19:37:13 2023 -0600
@@ -27,30 +27,14 @@
 
 #include <purple.h>
 
-#include "gtkaccount.h"
 #include "gtkblist.h"
 #include "gtkconv.h"
-#include "gtkdialogs.h"
-#include "gtkxfer.h"
 #include "gtkroomlist.h"
 #include "gtkutils.h"
 #include "pidgin/pidginaccountchooser.h"
 #include "pidgin/pidginaccountfilterconnected.h"
-#include "pidgin/pidginactiongroup.h"
 #include "pidgin/pidginaddbuddydialog.h"
 #include "pidgin/pidginaddchatdialog.h"
-#include "pidgin/pidgincontactlistwindow.h"
-#include "pidgin/pidgincore.h"
-#include "pidgin/pidgindebug.h"
-#include "pidgin/pidginiconname.h"
-#include "pidgin/pidginmooddialog.h"
-#include "pidgin/pidginplugininfo.h"
-
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-
-#define UI_DATA "ui-pidgin"
 
 typedef struct
 {
@@ -68,126 +52,11 @@
 	GList *entries;
 } PidginChatData;
 
-typedef struct
-{
-	PidginChatData chat_data;
-
-	GtkWidget *alias_entry;
-	GtkWidget *group_combo;
-	GtkWidget *autojoin;
-	GtkWidget *persistent;
-} PidginAddChatData;
-
 G_DEFINE_TYPE(PidginBuddyList, pidgin_buddy_list, PURPLE_TYPE_BUDDY_LIST)
 
-enum {
-	STATUS_ICON_COLUMN,
-	STATUS_ICON_VISIBLE_COLUMN,
-	NAME_COLUMN,
-	BUDDY_ICON_COLUMN,
-	NODE_COLUMN,
-	EMBLEM_COLUMN,
-	EMBLEM_VISIBLE_COLUMN,
-	PROTOCOL_ICON_NAME_COLUMN,
-	BLIST_COLUMNS
-};
-
-static gboolean editing_blist = FALSE;
-
-static GList *pidgin_blist_sort_methods = NULL;
-static struct _PidginBlistSortMethod *current_sort_method = NULL;
-static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-
-static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-
-static PidginBuddyList *gtkblist = NULL;
-
-static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change);
-static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data);
-static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node);
-static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node);
-static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node);
-static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full);
-static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter);
-static gboolean buddy_is_displayable(PurpleBuddy *buddy);
-static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender);
-static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
-static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
-static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
-
-typedef struct {
-	GtkTreeRowReference *row;
-	gboolean contact_expanded;
-	gboolean recent_signonoff;
-	gint recent_signonoff_timer;
-	PurpleConversation *conv;
-} PidginBlistNode;
-
-/***************************************************
- *              Callbacks                          *
- ***************************************************/
-static void
-set_node_custom_icon_cb(GtkWidget *widget, gint response, gpointer data)
-{
-	if (response == GTK_RESPONSE_ACCEPT) {
-		PurpleBlistNode *node = (PurpleBlistNode*)data;
-		GFile *file = NULL;
-		gchar *filename = NULL;
-
-		file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(widget));
-		filename = g_file_get_path(file);
-		purple_buddy_icons_node_set_custom_icon_from_file(node, filename);
-		g_free(filename);
-		g_object_unref(file);
-	}
-
-	g_object_set_data(G_OBJECT(data), "buddy-icon-chooser", NULL);
-}
-
-static void
-chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields)
-{
-	GList *groups, *fields;
-
-	for (groups = purple_request_fields_get_groups(allfields); groups; groups = groups->next) {
-		fields = purple_request_field_group_get_fields(groups->data);
-		for (; fields; fields = fields->next) {
-			PurpleRequestField *field = fields->data;
-			const char *id;
-			char *val;
-
-			id = purple_request_field_get_id(field);
-			if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
-				val = g_strdup_printf("%d", purple_request_field_int_get_value(field));
-			else
-				val = g_strdup(purple_request_field_string_get_value(field));
-
-			if (!val) {
-				g_hash_table_remove(purple_chat_get_components(chat), id);
-			} else {
-				g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val);  /* val should not be free'd */
-			}
-		}
-	}
-}
-
 /******************************************************************************
  * Helpers
  *****************************************************************************/
-static PurpleBuddy *
-pidgin_blist_get_selected_buddy(PidginBuddyList *blist) {
-	PurpleBuddy *buddy = NULL;
-
-	if(PURPLE_IS_META_CONTACT(blist->selected_node)) {
-		buddy = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(blist->selected_node));
-	} else if(PURPLE_IS_BUDDY(blist->selected_node)) {
-		buddy = PURPLE_BUDDY(blist->selected_node);
-	}
-
-	return buddy;
-}
-
 static void gtk_blist_join_chat(PurpleChat *chat)
 {
 	PurpleAccount *account;
@@ -226,589 +95,10 @@
 	g_free(chat_name);
 }
 
-static void
-pidgin_blist_toggle_action(GSimpleAction *action,
-                           G_GNUC_UNUSED GVariant *parameter,
-                           G_GNUC_UNUSED gpointer data)
-{
-	GVariant *state = NULL;
-
-
-	state = g_action_get_state(G_ACTION(action));
-	g_action_change_state(
-		G_ACTION(action),
-		g_variant_new_boolean(!g_variant_get_boolean(state))
-	);
-	g_variant_unref(state);
-}
-
 /******************************************************************************
  * Actions
  *****************************************************************************/
 static void
-pidgin_blist_add_buddy_cb(G_GNUC_UNUSED GSimpleAction *action,
-                          G_GNUC_UNUSED GVariant *parameter,
-                          gpointer data)
-{
-	PidginBuddyList *blist = data;
-	GtkTreeSelection *sel = NULL;
-	GtkTreeIter iter;
-	PurpleBlistNode *node;
-
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(blist->treeview));
-
-	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (PURPLE_IS_BUDDY(node)) {
-			PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
-			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
-		} else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) {
-			PurpleGroup *group = purple_meta_contact_get_group(PURPLE_META_CONTACT(node));
-			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
-		} else if (PURPLE_IS_GROUP(node)) {
-			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(PURPLE_GROUP(node)), NULL);
-		}
-	} else {
-		purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
-	}
-}
-
-static void
-pidgin_blist_add_chat_cb(G_GNUC_UNUSED GSimpleAction *action,
-                         G_GNUC_UNUSED GVariant *parameter,
-                         gpointer data)
-{
-	PidginBuddyList *blist = data;
-	GtkTreeSelection *sel = NULL;
-	GtkTreeIter iter;
-	PurpleBlistNode *node;
-
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(blist->treeview));
-
-	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (PURPLE_IS_BUDDY(node))
-			purple_blist_request_add_chat(NULL, purple_buddy_get_group(PURPLE_BUDDY(node)), NULL, NULL);
-		if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node))
-			purple_blist_request_add_chat(NULL, purple_meta_contact_get_group(PURPLE_META_CONTACT(node)), NULL, NULL);
-		else if (PURPLE_IS_GROUP(node))
-			purple_blist_request_add_chat(NULL, (PurpleGroup*)node, NULL, NULL);
-	} else {
-		purple_blist_request_add_chat(NULL, NULL, NULL, NULL);
-	}
-}
-
-static void
-pidgin_blist_menu_alias_cb(G_GNUC_UNUSED GSimpleAction *action,
-                           G_GNUC_UNUSED GVariant *parameter,
-                           gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBlistNode *node = blist->selected_node;
-	GtkTreeIter iter;
-	GtkTreePath *path;
-
-	if (!(get_iter_from_node(node, &iter))) {
-		/* This is either a bug, or the buddy is in a collapsed contact */
-		node = purple_blist_node_get_parent(node);
-		if (!get_iter_from_node(node, &iter))
-			/* Now it's definitely a bug */
-			return;
-	}
-
-	gtk_widget_trigger_tooltip_query(blist->treeview);
-
-	path = gtk_tree_model_get_path(GTK_TREE_MODEL(blist->treemodel), &iter);
-	g_object_set(G_OBJECT(blist->text_rend), "editable", TRUE, NULL);
-	gtk_tree_view_set_enable_search (GTK_TREE_VIEW(blist->treeview), FALSE);
-	gtk_widget_grab_focus(blist->treeview);
-	gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(blist->treeview), path,
-	                                 blist->text_column, blist->text_rend,
-	                                 TRUE);
-	gtk_tree_path_free(path);
-}
-
-static void
-pidgin_blist_menu_autojoin_cb(GSimpleAction *action, GVariant *state,
-                              gpointer data)
-{
-	PidginBuddyList *blist = data;
-
-	purple_blist_node_set_bool(blist->selected_node, "gtk-autojoin",
-	                           g_variant_get_boolean(state));
-
-	g_simple_action_set_state(action, state);
-}
-
-static void
-pidgin_blist_menu_audio_call_cb(G_GNUC_UNUSED GSimpleAction *action,
-                                G_GNUC_UNUSED GVariant *parameter,
-                                gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist);
-
-	purple_protocol_initiate_media(purple_buddy_get_account(buddy),
-	                               purple_buddy_get_name(buddy),
-	                               PURPLE_MEDIA_AUDIO);
-}
-
-static void
-pidgin_blist_menu_chat_settings_cb(G_GNUC_UNUSED GSimpleAction *action,
-                                   G_GNUC_UNUSED GVariant *parameter,
-                                   gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleRequestFields *fields = purple_request_fields_new();
-	PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
-	PurpleRequestField *field;
-	GList *parts, *iter;
-	PurpleProtocolChatEntry *pce;
-	PurpleProtocol *protocol;
-	PurpleConnection *gc;
-	PurpleChat *chat = PURPLE_CHAT(blist->selected_node);
-
-	purple_request_fields_add_group(fields, group);
-
-	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	protocol = purple_connection_get_protocol(gc);
-	parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
-
-	for (iter = parts; iter; iter = iter->next) {
-		pce = iter->data;
-		if (pce->is_int) {
-			int val;
-			const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
-			if (!str || sscanf(str, "%d", &val) != 1)
-				val = pce->min;
-			field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX);
-		} else {
-			field = purple_request_field_string_new(pce->identifier, pce->label,
-					g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
-			if (pce->secret)
-				purple_request_field_string_set_masked(field, TRUE);
-		}
-
-		if (pce->required)
-			purple_request_field_set_required(field, TRUE);
-
-		purple_request_field_group_add_field(group, field);
-		g_free(pce);
-	}
-
-	g_list_free(parts);
-
-	purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please update the necessary fields."),
-			fields, _("Save"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
-			NULL, chat);
-}
-
-static void
-pidgin_blist_menu_im_cb(G_GNUC_UNUSED GSimpleAction *action,
-                        G_GNUC_UNUSED GVariant *parameter,
-                        gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist);
-
-	pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy),
-	                            purple_buddy_get_name(buddy));
-}
-
-static void
-pidgin_blist_menu_info_cb(G_GNUC_UNUSED GSimpleAction *action,
-                          G_GNUC_UNUSED GVariant *parameter,
-                          gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist);
-	PurpleAccount *account = purple_buddy_get_account(buddy);
-
-	pidgin_retrieve_user_info(purple_account_get_connection(account),
-	                          purple_buddy_get_name(buddy));
-}
-
-static void
-pidgin_blist_menu_join_cb(G_GNUC_UNUSED GSimpleAction *action,
-                          G_GNUC_UNUSED GVariant *parameter,
-                          gpointer data)
-{
-	PidginBuddyList *blist = data;
-
-	gtk_blist_join_chat(PURPLE_CHAT(blist->selected_node));
-}
-
-static void
-pidgin_blist_menu_persistent_cb(GSimpleAction *action, GVariant *state,
-                                gpointer data)
-{
-	PidginBuddyList *blist = data;
-
-	purple_blist_node_set_bool(blist->selected_node, "gtk-persistent",
-	                           g_variant_get_boolean(state));
-
-	g_simple_action_set_state(action, state);
-}
-
-static void
-pidgin_blist_menu_send_file_cb(G_GNUC_UNUSED GSimpleAction *action,
-                               G_GNUC_UNUSED GVariant *parameter,
-                               gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist);
-	PurpleAccount *account = purple_buddy_get_account(buddy);
-
-	purple_serv_send_file(purple_account_get_connection(account),
-	                      purple_buddy_get_name(buddy), NULL);
-}
-
-static void
-pidgin_blist_remove_cb(G_GNUC_UNUSED GSimpleAction *action,
-                       G_GNUC_UNUSED GVariant *parameter,
-                       gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBlistNode *node = blist->selected_node;
-
-	if(PURPLE_IS_BUDDY(node)) {
-		pidgin_dialogs_remove_buddy(PURPLE_BUDDY(node));
-	} else if(PURPLE_IS_CHAT(node)) {
-		pidgin_dialogs_remove_chat(PURPLE_CHAT(node));
-	} else if(PURPLE_IS_GROUP(node)) {
-		pidgin_dialogs_remove_group(PURPLE_GROUP(node));
-	} else if(PURPLE_IS_META_CONTACT(node)) {
-		pidgin_dialogs_remove_contact(PURPLE_META_CONTACT(node));
-	}
-}
-
-static void
-pidgin_blist_remove_node_custom_icon(G_GNUC_UNUSED GSimpleAction *action,
-                                     G_GNUC_UNUSED GVariant *parameter,
-                                     gpointer data)
-{
-	PidginBuddyList *blist = data;
-
-	purple_buddy_icons_node_set_custom_icon(blist->selected_node, NULL, 0);
-}
-
-static void
-pidgin_blist_set_node_custom_icon(G_GNUC_UNUSED GSimpleAction *action,
-                                  G_GNUC_UNUSED GVariant *parameter,
-                                  gpointer data)
-{
-	PidginBuddyList *blist = data;
-	PurpleBlistNode *node = blist->selected_node;
-	GtkFileChooserNative *win = NULL;
-
-	win = g_object_get_data(G_OBJECT(node), "buddy-icon-chooser");
-	if(win == NULL) {
-		win = gtk_file_chooser_native_new(_("Buddy Icon"), NULL,
-		                                  GTK_FILE_CHOOSER_ACTION_OPEN,
-		                                  _("_Open"), _("_Cancel"));
-
-		g_signal_connect(win, "response", G_CALLBACK(set_node_custom_icon_cb),
-		                 node);
-
-		g_object_set_data_full(G_OBJECT(node), "buddy-icon-chooser", win,
-		                       g_object_unref);
-	}
-
-	gtk_native_dialog_show(GTK_NATIVE_DIALOG(win));
-}
-
-static void
-pidgin_blist_menu_video_call_cb(G_GNUC_UNUSED GSimpleAction *action,
-                                G_GNUC_UNUSED GVariant *parameter,
-                                gpointer data)
-{
-	PurpleAccount *account = NULL;
-	PidginBuddyList *blist = data;
-	PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist);
-	PurpleMediaCaps caps = 0;
-	const gchar *buddy_name = NULL;
-
-	account = purple_buddy_get_account(buddy);
-	buddy_name = purple_buddy_get_name(buddy);
-
-	/* if the buddy supports both audio and video, start a combined call,
-	 otherwise start a pure video session */
-	caps = purple_protocol_get_media_caps(account, buddy_name);
-
-	if(caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
-		purple_protocol_initiate_media(account, buddy_name,
-		                               PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO);
-	} else {
-		purple_protocol_initiate_media(account, buddy_name, PURPLE_MEDIA_VIDEO);
-	}
-}
-
-static GActionEntry menu_actions[] = {
-	{
-		.name = "add-buddy",
-		.activate = pidgin_blist_add_buddy_cb,
-	}, {
-		.name = "add-chat",
-		.activate = pidgin_blist_add_chat_cb,
-	}, {
-		.name = "alias",
-		.activate = pidgin_blist_menu_alias_cb,
-	}, {
-		.name = "auto-join-chat",
-		.activate = pidgin_blist_toggle_action,
-		.state = "false",
-		.change_state = pidgin_blist_menu_autojoin_cb,
-	}, {
-		.name = "buddy-audio-call",
-		.activate = pidgin_blist_menu_audio_call_cb,
-	}, {
-		.name = "buddy-get-info",
-		.activate = pidgin_blist_menu_info_cb,
-	}, {
-		.name = "buddy-im",
-		.activate = pidgin_blist_menu_im_cb,
-	}, {
-		.name = "buddy-send-file",
-		.activate = pidgin_blist_menu_send_file_cb,
-	}, {
-		.name = "buddy-video-call",
-		.activate = pidgin_blist_menu_video_call_cb,
-	}, {
-		.name = "chat-settings",
-		.activate = pidgin_blist_menu_chat_settings_cb,
-	}, {
-		.name = "join-chat",
-		.activate = pidgin_blist_menu_join_cb,
-	}, {
-		.name = "persistent-chat",
-		.activate = pidgin_blist_toggle_action,
-		.state = "false",
-		.change_state = pidgin_blist_menu_persistent_cb,
-	}, {
-		.name = "remove",
-		.activate = pidgin_blist_remove_cb,
-	}, {
-		.name = "remove-custom-icon",
-		.activate = pidgin_blist_remove_node_custom_icon,
-	}, {
-		.name = "set-custom-icon",
-		.activate = pidgin_blist_set_node_custom_icon,
-	}
-};
-
-static void gtk_blist_renderer_editing_cancelled_cb(GtkCellRenderer *renderer, PurpleBuddyList *list)
-{
-	editing_blist = FALSE;
-	g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
-	pidgin_blist_refresh(list);
-}
-
-static void
-gtk_blist_renderer_editing_started_cb(G_GNUC_UNUSED GtkCellRenderer *renderer,
-                                      GtkCellEditable *editable,
-                                      gchar *path_str, gpointer user_data)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
-	GtkTreeIter iter;
-	GtkTreePath *path = NULL;
-	PurpleBlistNode *node;
-	const char *text = NULL;
-
-	path = gtk_tree_path_new_from_string (path_str);
-	gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_path_free (path);
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-
-	if (PURPLE_IS_META_CONTACT(node))
-		text = purple_meta_contact_get_alias(PURPLE_META_CONTACT(node));
-	else if (PURPLE_IS_BUDDY(node))
-		text = purple_buddy_get_alias(PURPLE_BUDDY(node));
-	else if (PURPLE_IS_GROUP(node))
-		text = purple_group_get_name(PURPLE_GROUP(node));
-	else if (PURPLE_IS_CHAT(node))
-		text = purple_chat_get_name(PURPLE_CHAT(node));
-	else
-		g_return_if_reached();
-
-	if(GTK_IS_EDITABLE(editable)) {
-		gtk_editable_set_text(GTK_EDITABLE(editable), text);
-	}
-
-	editing_blist = TRUE;
-}
-
-static void
-gtk_blist_do_personize(GList *merges)
-{
-	PurpleBlistNode *contact = NULL;
-	int max = 0;
-	GList *tmp;
-
-	/* First, we find the contact to merge the rest of the buddies into.
-	 * This will be the contact with the most buddies in it; ties are broken
-	 * by which contact is higher in the list
-	 */
-	for (tmp = merges; tmp; tmp = tmp->next) {
-		PurpleBlistNode *node = tmp->data;
-		PurpleBlistNode *b;
-		int i = 0;
-
-		if (PURPLE_IS_BUDDY(node))
-			node = purple_blist_node_get_parent(node);
-
-		if (!PURPLE_IS_META_CONTACT(node))
-			continue;
-
-		for (b = purple_blist_node_get_first_child(node);
-		     b;
-		     b = purple_blist_node_get_sibling_next(b))
-		{
-			i++;
-		}
-
-		if (i > max) {
-			contact = node;
-			max = i;
-		}
-	}
-
-	if (contact == NULL)
-		return;
-
-	/* Merge all those buddies into this contact */
-	for (tmp = merges; tmp; tmp = tmp->next) {
-		PurpleBlistNode *node = tmp->data;
-		if (PURPLE_IS_BUDDY(node))
-			node = purple_blist_node_get_parent(node);
-
-		if (node == contact)
-			continue;
-
-		purple_meta_contact_merge((PurpleMetaContact *)node, contact);
-	}
-
-	/* And show the expanded contact, so the people know what's going on */
-	pidgin_blist_expand_contact_cb(NULL, contact);
-	g_list_free(merges);
-}
-
-static void
-gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
-{
-	PurpleBlistNode *contact;
-	PurpleBlistNode *buddy;
-	GList *merges = NULL;
-	int i = 0;
-	char *a = g_utf8_casefold(alias, -1);
-
-	for (contact = purple_blist_node_get_first_child(group);
-	     contact != NULL;
-	     contact = purple_blist_node_get_sibling_next(contact)) {
-		char *node_alias;
-		if (!PURPLE_IS_META_CONTACT(contact))
-			continue;
-
-		node_alias = g_utf8_casefold(purple_meta_contact_get_alias((PurpleMetaContact *)contact), -1);
-		if (node_alias && !g_utf8_collate(node_alias, a)) {
-			merges = g_list_append(merges, contact);
-			i++;
-			g_free(node_alias);
-			continue;
-		}
-		g_free(node_alias);
-
-		for (buddy = purple_blist_node_get_first_child(contact);
-		     buddy;
-		     buddy = purple_blist_node_get_sibling_next(buddy))
-		{
-			if (!PURPLE_IS_BUDDY(buddy))
-				continue;
-
-			node_alias = g_utf8_casefold(purple_buddy_get_alias(PURPLE_BUDDY(buddy)), -1);
-			if (node_alias && !g_utf8_collate(node_alias, a)) {
-				merges = g_list_append(merges, buddy);
-				i++;
-				g_free(node_alias);
-				break;
-			}
-			g_free(node_alias);
-		}
-	}
-	g_free(a);
-
-	if (i > 1)
-	{
-		char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias);
-		purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
-							 "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL,
-				      merges, 2, _("_Yes"), G_CALLBACK(gtk_blist_do_personize), _("_No"), G_CALLBACK(g_list_free));
-		g_free(msg);
-	} else
-		g_list_free(merges);
-}
-
-static void
-gtk_blist_renderer_edited_cb(G_GNUC_UNUSED GtkCellRendererText *text_rend,
-                             char *arg1, char *arg2, PurpleBuddyList *list)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(list);
-	GtkTreeIter iter;
-	GtkTreePath *path;
-	PurpleBlistNode *node;
-	PurpleGroup *dest;
-	gchar *alias = NULL;
-
-	editing_blist = FALSE;
-	path = gtk_tree_path_new_from_string (arg1);
-	gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_path_free (path);
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-	gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), TRUE);
-	g_object_set(G_OBJECT(gtkblist->text_rend), "editable", FALSE, NULL);
-
-	if (PURPLE_IS_META_CONTACT(node)) {
-		PurpleMetaContact *contact = PURPLE_META_CONTACT(node);
-		PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-		/*
-		 * Using purple_meta_contact_get_alias here breaks because we
-		 * specifically want to check the contact alias only (i.e. not
-		 * the priority buddy, which purple_meta_contact_get_alias does).
-		 * The "alias" GObject property gives us just the alias.
-		 */
-		g_object_get(contact, "alias", &alias, NULL);
-
-		if (alias || gtknode->contact_expanded) {
-			purple_meta_contact_set_alias(contact, arg2);
-			gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
-		} else {
-			PurpleBuddy *buddy = purple_meta_contact_get_priority_buddy(contact);
-			purple_buddy_set_local_alias(buddy, arg2);
-			purple_serv_alias_buddy(buddy);
-			gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
-		}
-	} else if (PURPLE_IS_BUDDY(node)) {
-		PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
-
-		purple_buddy_set_local_alias(PURPLE_BUDDY(node), arg2);
-		purple_serv_alias_buddy(PURPLE_BUDDY(node));
-		gtk_blist_auto_personize(PURPLE_BLIST_NODE(group), arg2);
-	} else if (PURPLE_IS_GROUP(node)) {
-		dest = purple_blist_find_group(arg2);
-		if (dest != NULL && purple_utf8_strcasecmp(arg2, purple_group_get_name(PURPLE_GROUP(node)))) {
-			pidgin_dialogs_merge_groups(PURPLE_GROUP(node), arg2);
-		} else {
-			purple_group_set_name(PURPLE_GROUP(node), arg2);
-		}
-	} else if (PURPLE_IS_CHAT(node)) {
-		purple_chat_set_alias(PURPLE_CHAT(node), arg2);
-	}
-
-	g_free(alias);
-	pidgin_blist_refresh(list);
-}
-
-static void
 do_join_chat(PidginChatData *data)
 {
 	if (data)
@@ -900,16 +190,6 @@
 	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive);
 }
 
-static void
-pidgin_blist_update_privacy_cb(PurpleBuddy *buddy)
-{
-	PidginBlistNode *ui_data = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-	if (ui_data == NULL || ui_data->row == NULL)
-		return;
-	pidgin_blist_update_buddy(purple_blist_get_default(),
-	                          PURPLE_BLIST_NODE(buddy), TRUE);
-}
-
 static gboolean
 chat_account_filter_func(gpointer item, G_GNUC_UNUSED gpointer data) {
 	PurpleConnection *gc = NULL;
@@ -958,19 +238,13 @@
 	GtkWidget *content_area;
 	GtkWidget *hbox;
 	GtkWidget *vbox;
-	GtkWindow *blist_window;
 	GtkEveryFilter *every = NULL;
 	GtkFilter *filter = NULL;
-	PidginBuddyList *gtkblist;
 
 	data->account = account;
 
-	gtkblist = PIDGIN_BUDDY_LIST(purple_blist_get_default());
-	blist_window = gtkblist ? GTK_WINDOW(gtkblist->window) : NULL;
-
 	data->window = gtk_dialog_new();
 	gtk_window_set_title(GTK_WINDOW(data->window), title);
-	gtk_window_set_transient_for(GTK_WINDOW(data->window), blist_window);
 	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
 	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
 	content_area = gtk_dialog_get_content_area(GTK_DIALOG(data->window));
@@ -1170,2534 +444,6 @@
 }
 
 static void
-gtk_blist_row_expanded_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreeIter *iter,
-                          G_GNUC_UNUSED GtkTreePath *path, gpointer user_data)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
-	PurpleBlistNode *node;
-
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
-
-	if (PURPLE_IS_GROUP(node)) {
-		char *title;
-
-		title = pidgin_get_group_title(node, TRUE);
-
-		gtk_tree_store_set(gtkblist->treemodel, iter,
-		   NAME_COLUMN, title,
-		   -1);
-
-		g_free(title);
-
-		purple_blist_node_set_bool(node, "collapsed", FALSE);
-		gtk_widget_trigger_tooltip_query(gtkblist->treeview);
-	}
-}
-
-static void
-gtk_blist_row_collapsed_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreeIter *iter,
-                           G_GNUC_UNUSED GtkTreePath *path, gpointer user_data)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
-	PurpleBlistNode *node;
-
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
-
-	if (PURPLE_IS_GROUP(node)) {
-		char *title;
-		PidginBlistNode *gtknode;
-		PurpleBlistNode *cnode;
-
-		title = pidgin_get_group_title(node, FALSE);
-
-		gtk_tree_store_set(gtkblist->treemodel, iter,
-		   NAME_COLUMN, title,
-		   -1);
-
-		g_free(title);
-
-		purple_blist_node_set_bool(node, "collapsed", TRUE);
-
-		for(cnode = purple_blist_node_get_first_child(node); cnode; cnode = purple_blist_node_get_sibling_next(cnode)) {
-			if (PURPLE_IS_META_CONTACT(cnode)) {
-				gtknode = g_object_get_data(G_OBJECT(cnode), UI_DATA);
-				if (!gtknode->contact_expanded)
-					continue;
-				gtknode->contact_expanded = FALSE;
-				pidgin_blist_update_contact(NULL, cnode);
-			}
-		}
-		gtk_widget_trigger_tooltip_query(gtkblist->treeview);
-	} else if(PURPLE_IS_META_CONTACT(node)) {
-		pidgin_blist_collapse_contact_cb(NULL, node);
-	}
-}
-
-static void
-gtk_blist_row_activated_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreePath *path,
-                           G_GNUC_UNUSED GtkTreeViewColumn *col, gpointer data)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(data);
-	PurpleBlistNode *node;
-	GtkTreeIter iter;
-
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-
-	if(PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node)) {
-		PurpleBuddy *buddy;
-
-		if(PURPLE_IS_META_CONTACT(node))
-			buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-		else
-			buddy = (PurpleBuddy*)node;
-
-		pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
-	} else if (PURPLE_IS_CHAT(node)) {
-		gtk_blist_join_chat((PurpleChat *)node);
-	} else if (PURPLE_IS_GROUP(node)) {
-/*		if (gtk_tree_view_row_expanded(tv, path))
-			gtk_tree_view_collapse_row(tv, path);
-		else
-			gtk_tree_view_expand_row(tv,path,FALSE);*/
-	}
-}
-
-struct _expand {
-	GtkTreeView *treeview;
-	GtkTreePath *path;
-	PurpleBlistNode *node;
-};
-
-static gboolean
-scroll_to_expanded_cell(gpointer data)
-{
-	struct _expand *ex = data;
-	gtk_tree_view_scroll_to_cell(ex->treeview, ex->path, NULL, FALSE, 0, 0);
-	pidgin_blist_update_contact(NULL, ex->node);
-
-	gtk_tree_path_free(ex->path);
-	g_free(ex);
-
-	return FALSE;
-}
-
-static void
-pidgin_blist_expand_contact_cb(G_GNUC_UNUSED GtkWidget *w,
-                               PurpleBlistNode *node)
-{
-	PidginBlistNode *gtknode;
-	GtkTreeIter iter, parent;
-	PurpleBlistNode *bnode;
-	GtkTreePath *path;
-
-	if(!PURPLE_IS_META_CONTACT(node)) {
-		return;
-	}
-
-	gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-	gtknode->contact_expanded = TRUE;
-
-	for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) {
-		pidgin_blist_update(NULL, bnode);
-	}
-
-	/* This ensures that the bottom buddy is visible, i.e. not scrolled off the alignment */
-	if (get_iter_from_node(node, &parent)) {
-		struct _expand *ex = g_new0(struct _expand, 1);
-
-		gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(gtkblist->treemodel), &iter, &parent,
-				  gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &parent) -1);
-		path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
-
-		/* Let the treeview draw so it knows where to scroll */
-		ex->treeview = GTK_TREE_VIEW(gtkblist->treeview);
-		ex->path = path;
-		ex->node = purple_blist_node_get_first_child(node);
-		g_idle_add(scroll_to_expanded_cell, ex);
-	}
-}
-
-static void
-pidgin_blist_collapse_contact_cb(G_GNUC_UNUSED GtkWidget *w,
-                                 PurpleBlistNode *node)
-{
-	PurpleBlistNode *bnode;
-	PidginBlistNode *gtknode;
-
-	if(!PURPLE_IS_META_CONTACT(node))
-		return;
-
-	gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-	gtknode->contact_expanded = FALSE;
-
-	for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) {
-		pidgin_blist_update(NULL, bnode);
-	}
-}
-
-static gboolean
-pidgin_blist_key_press_cb(G_GNUC_UNUSED GtkEventControllerKey *controller,
-                          guint keyval, G_GNUC_UNUSED guint keycode,
-                          GdkModifierType state, gpointer data)
-{
-	PidginBuddyList *blist = PIDGIN_BUDDY_LIST(data);
-	GtkTreeView *tv = NULL;
-	GtkTreeModel *model = NULL;
-	PurpleBlistNode *node;
-	GtkTreeIter iter;
-	GtkTreeSelection *sel;
-
-	tv = GTK_TREE_VIEW(blist->treeview);
-	sel = gtk_tree_view_get_selection(tv);
-
-	if(!gtk_tree_selection_get_selected(sel, NULL, &iter))
-		return FALSE;
-
-	model = GTK_TREE_MODEL(blist->treemodel);
-	gtk_tree_model_get(model, &iter, NODE_COLUMN, &node, -1);
-
-	if(state & GDK_CONTROL_MASK && (keyval == 'o' || keyval == 'O')) {
-		PurpleBuddy *buddy;
-
-		if(PURPLE_IS_META_CONTACT(node)) {
-			buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-		} else if(PURPLE_IS_BUDDY(node)) {
-			buddy = (PurpleBuddy*)node;
-		} else {
-			return FALSE;
-		}
-		if(buddy)
-			pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy));
-	} else {
-		GtkTreeIter parent;
-		GtkTreePath *path = NULL;
-
-		switch (keyval) {
-			case GDK_KEY_F2:
-				/* FIXME: gk 2022-05-27 */
-				/*
-				gtk_blist_menu_alias_cb(tv, node);
-				 */
-				break;
-
-			case GDK_KEY_Left:
-				path = gtk_tree_model_get_path(model, &iter);
-				if (gtk_tree_view_row_expanded(tv, path)) {
-					/* Collapse the Group */
-					gtk_tree_view_collapse_row(tv, path);
-					gtk_tree_path_free(path);
-					return TRUE;
-				} else {
-					/* Select the Parent */
-					if (gtk_tree_model_get_iter(model, &iter, path)) {
-						if (gtk_tree_model_iter_parent(model, &parent, &iter)) {
-							gtk_tree_path_free(path);
-							path = gtk_tree_model_get_path(model, &parent);
-							gtk_tree_view_set_cursor(tv, path, NULL, FALSE);
-							gtk_tree_path_free(path);
-							return TRUE;
-						}
-					}
-				}
-				gtk_tree_path_free(path);
-				break;
-
-			case GDK_KEY_Right:
-				path = gtk_tree_model_get_path(model, &iter);
-				if (!gtk_tree_view_row_expanded(tv, path)) {
-					/* Expand the Group */
-					if (PURPLE_IS_META_CONTACT(node)) {
-						pidgin_blist_expand_contact_cb(NULL, node);
-						gtk_tree_path_free(path);
-						return TRUE;
-					} else if (!PURPLE_IS_BUDDY(node)) {
-						gtk_tree_view_expand_row(tv, path, FALSE);
-						gtk_tree_path_free(path);
-						return TRUE;
-					}
-				} else {
-					/* Select the First Child */
-					if (gtk_tree_model_get_iter(model, &parent, path)) {
-						if (gtk_tree_model_iter_nth_child(model, &iter, &parent, 0)) {
-							gtk_tree_path_free(path);
-							path = gtk_tree_model_get_path(model, &iter);
-							gtk_tree_view_set_cursor(tv, path, NULL, FALSE);
-							gtk_tree_path_free(path);
-							return TRUE;
-						}
-					}
-				}
-				gtk_tree_path_free(path);
-				break;
-		}
-	}
-
-	return FALSE;
-}
-
-static gboolean
-pidgin_blist_popover_closed_idle_cb(gpointer data) {
-	gtk_widget_unparent(GTK_WIDGET(data));
-	return G_SOURCE_REMOVE;
-}
-
-static void
-pidgin_blist_popover_closed_cb(GtkPopover *self, G_GNUC_UNUSED gpointer data) {
-	/* We cannot unparent this immediately on close, as it will be garbage
-	 * collected, and the selected action will not run.
-	 */
-	g_idle_add(G_SOURCE_FUNC(pidgin_blist_popover_closed_idle_cb), self);
-}
-
-static gboolean
-pidgin_blist_show_context_menu(GtkWidget *tv, PurpleBlistNode *node,
-                               gdouble x, gdouble y)
-{
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	GAction *action = NULL;
-	GActionMap *action_map = NULL;
-	GtkApplication *gtk_application = NULL;
-	GMenu *menu = NULL;
-	gboolean enabled = FALSE;
-	gboolean handled = FALSE;
-
-	gtk_application = GTK_APPLICATION(g_application_get_default());
-
-	action_map = G_ACTION_MAP(gtkblist->action_group);
-
-	/* Create a menu based on the thing we right-clicked on */
-	if (PURPLE_IS_GROUP(node)) {
-		menu = gtk_application_get_menu_by_id(gtk_application, "group");
-
-		/* Add buddy is only enabled if there is an online account. */
-		action = g_action_map_lookup_action(action_map, "add-buddy");
-		enabled = purple_connections_get_all() != NULL;
-		g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-
-		/* Add chat is only enabled if at least one protocol supports chats. */
-		action = g_action_map_lookup_action(action_map, "add-chat");
-		enabled = pidgin_blist_joinchat_is_showable();
-		g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-	} else if (PURPLE_IS_CHAT(node)) {
-		GVariant *variant = NULL;
-
-		menu = gtk_application_get_menu_by_id(gtk_application, "chat");
-
-		/* Set the auto-join state to the correct value. */
-		action = g_action_map_lookup_action(action_map, "auto-join-chat");
-		enabled = purple_blist_node_get_bool(node, "gtk-autojoin");
-		variant = g_variant_new_boolean(enabled);
-		g_simple_action_set_state(G_SIMPLE_ACTION(action), variant);
-
-		/* Set the persistent state to the correct value. */
-		action = g_action_map_lookup_action(action_map, "persistent-chat");
-		enabled = purple_blist_node_get_bool(node, "gtk-persistent");
-		variant = g_variant_new_boolean(enabled);
-		g_simple_action_set_state(G_SIMPLE_ACTION(action), variant);
-	} else if ((PURPLE_IS_META_CONTACT(node)) && (gtknode->contact_expanded)) {
-		menu = gtk_application_get_menu_by_id(gtk_application, "contact");
-	} else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node)) {
-		PurpleAccount *account = NULL;
-		PurpleBuddy *buddy = NULL;
-		PurpleConnection *connection = NULL;
-		PurpleProtocol *protocol = NULL;
-		const gchar *buddy_name = NULL;
-
-		menu = gtk_application_get_menu_by_id(gtk_application, "buddy");
-
-		if(PURPLE_IS_META_CONTACT(node)) {
-			buddy = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node));
-		} else {
-			buddy = PURPLE_BUDDY(node);
-		}
-
-		account = purple_buddy_get_account(buddy);
-		buddy_name = purple_buddy_get_name(buddy);
-		connection = purple_account_get_connection(account);
-		protocol = purple_connection_get_protocol(connection);
-
-		/* Get info is optional for protocols to support. */
-		action = g_action_map_lookup_action(action_map, "buddy-get-info");
-		enabled = protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER,
-		                                                 get_info);
-		g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-
-		/* File transfers are protocol dependent. */
-		action = g_action_map_lookup_action(action_map,
-		                                    "buddy-send-file");
-		if(protocol && PURPLE_IS_PROTOCOL_XFER(protocol)) {
-			enabled = purple_protocol_xfer_can_receive(PURPLE_PROTOCOL_XFER(protocol),
-			                                           connection, buddy_name);
-		} else {
-			enabled = FALSE;
-		}
-
-		g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-
-		if(protocol != NULL &&
-		   PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA, get_caps))
-		{
-			PurpleMediaCaps caps = 0;
-
-			caps = purple_protocol_get_media_caps(account, buddy_name);
-
-			/* Voice calls are dependent on protocol. */
-			action = g_action_map_lookup_action(action_map, "buddy-audio-call");
-			enabled = (caps & PURPLE_MEDIA_CAPS_AUDIO);
-			g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-
-			/* Video calls are dependent on protocol. */
-			action = g_action_map_lookup_action(action_map, "buddy-video-call");
-			enabled = (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) ||
-			          (caps & PURPLE_MEDIA_CAPS_VIDEO);
-			g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-		}
-	}
-
-	action = g_action_map_lookup_action(action_map,
-	                                    "remove-custom-icon");
-	enabled = purple_buddy_icons_node_has_custom_icon(node);
-	g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-
-	/* Now display the menu */
-	if (menu != NULL && x >= 0 && y >= 0) {
-		GtkWidget *popover_menu = NULL;
-
-		popover_menu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu));
-		gtk_widget_set_parent(popover_menu, tv);
-		gtk_popover_set_position(GTK_POPOVER(popover_menu), GTK_POS_BOTTOM);
-		gtk_popover_set_pointing_to(GTK_POPOVER(popover_menu),
-		                            &(const GdkRectangle){(gint)x, (gint)y, 1, 1});
-
-		gtk_popover_popup(GTK_POPOVER(popover_menu));
-		g_signal_connect(popover_menu, "closed",
-		                 G_CALLBACK(pidgin_blist_popover_closed_cb), NULL);
-
-		handled = TRUE;
-	}
-
-	return handled;
-}
-
-static gboolean
-gtk_blist_button_press_cb(GtkGestureClick *click, gint n_press, gdouble x,
-                          gdouble y, gpointer data)
-{
-	PidginBuddyList *gtkblist = data;
-	GtkWidget *tv = NULL;
-	GdkEvent *event = NULL;
-	GdkModifierType state;
-	guint button = 0;
-	GtkTreePath *path;
-	PurpleBlistNode *node;
-	GtkTreeIter iter;
-	GtkTreeSelection *sel;
-	PurpleProtocol *protocol = NULL;
-	PidginBlistNode *gtknode;
-	gboolean handled = FALSE;
-
-	tv = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(click));
-	event = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(click));
-	button = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(click));
-	state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(click));
-
-	/* Here we figure out which node was clicked */
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), x, y, &path, NULL,
-	                                   NULL, NULL)) {
-		return FALSE;
-	}
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-	gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-	/* Right click draws a context menu */
-	if (gdk_event_triggers_context_menu(event)) {
-		handled = pidgin_blist_show_context_menu(tv, node, x, y);
-
-	/* CTRL+middle click expands or collapse a contact */
-	} else if ((button == GDK_BUTTON_MIDDLE) && (n_press == 1) &&
-			   (state & GDK_CONTROL_MASK) && PURPLE_IS_META_CONTACT(node)) {
-		if (gtknode->contact_expanded)
-			pidgin_blist_collapse_contact_cb(NULL, node);
-		else
-			pidgin_blist_expand_contact_cb(NULL, node);
-		handled = TRUE;
-
-	/* Double middle click gets info */
-	} else if ((button == GDK_BUTTON_MIDDLE) && (n_press == 2) &&
-			   (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node))) {
-		PurpleAccount *account;
-		PurpleBuddy *b;
-		if(PURPLE_IS_META_CONTACT(node))
-			b = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-		else
-			b = (PurpleBuddy *)node;
-
-		account = purple_buddy_get_account(b);
-		protocol = purple_account_get_protocol(account);
-
-		if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info))
-			pidgin_retrieve_user_info(purple_account_get_connection(account), purple_buddy_get_name(b));
-		handled = TRUE;
-	}
-
-#if (1)
-	/*
-	 * This code only exists because GTK doesn't work.  If we return
-	 * FALSE here, as would be normal the event propoagates down and
-	 * somehow gets interpreted as the start of a drag event.
-	 *
-	 * Um, isn't it _normal_ to return TRUE here?  Since the event
-	 * was handled?  --Mark
-	 */
-	if(handled) {
-		sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
-		gtk_tree_selection_select_path(sel, path);
-		gtk_tree_path_free(path);
-		return TRUE;
-	}
-#endif
-	gtk_tree_path_free(path);
-
-	return handled;
-}
-
-/* Altered from do_colorshift in gnome-panel */
-static void
-do_alphashift(GdkPixbuf *pixbuf, int shift)
-{
-	gint i, j;
-	gint width, height, padding;
-	guchar *pixels;
-	int val;
-
-	if (!gdk_pixbuf_get_has_alpha(pixbuf))
-	  return;
-
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
-	padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4;
-	pixels = gdk_pixbuf_get_pixels(pixbuf);
-
-	for (i = 0; i < height; i++) {
-		for (j = 0; j < width; j++) {
-			pixels++;
-			pixels++;
-			pixels++;
-			val = *pixels - shift;
-			*(pixels++) = CLAMP(val, 0, 255);
-		}
-		pixels += padding;
-	}
-}
-
-
-static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
-                                              gboolean scaled, gboolean greyed)
-{
-	gsize len;
-	PurpleBuddy *buddy = NULL;
-	PurpleGroup *group = NULL;
-	const guchar *data = NULL;
-	GdkPixbuf *buf, *ret = NULL;
-	PurpleBuddyIcon *icon = NULL;
-	PurpleAccount *account = NULL;
-	PurpleMetaContact *contact = NULL;
-	PurpleImage *custom_img;
-	PurpleProtocol *protocol = NULL;
-	PurpleBuddyIconSpec *icon_spec = NULL;
-	gint orig_width, orig_height, scale_width, scale_height;
-
-	if (PURPLE_IS_META_CONTACT(node)) {
-		buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-		contact = (PurpleMetaContact*)node;
-	} else if (PURPLE_IS_BUDDY(node)) {
-		buddy = (PurpleBuddy*)node;
-		contact = purple_buddy_get_contact(buddy);
-	} else if (PURPLE_IS_GROUP(node)) {
-		group = (PurpleGroup*)node;
-	} else if (PURPLE_IS_CHAT(node)) {
-		/* We don't need to do anything here. We just need to not fall
-		 * into the else block and return. */
-	} else {
-		return NULL;
-	}
-
-	if (buddy) {
-		account = purple_buddy_get_account(buddy);
-	}
-
-	if(account && purple_account_get_connection(account)) {
-		protocol = purple_connection_get_protocol(purple_account_get_connection(account));
-	}
-
-	/* If we have a contact then this is either a contact or a buddy and
-	 * we want to fetch the custom icon for the contact. If we don't have
-	 * a contact then this is a group or some other type of node and we
-	 * want to use that directly. */
-	if (contact) {
-		custom_img = purple_buddy_icons_node_find_custom_icon(PURPLE_BLIST_NODE(contact));
-	} else {
-		custom_img = purple_buddy_icons_node_find_custom_icon(node);
-	}
-
-	if (custom_img) {
-		data = purple_image_get_data(custom_img);
-		len = purple_image_get_data_size(custom_img);
-	}
-
-	if (data == NULL) {
-		if (buddy) {
-			/* Not sure I like this...*/
-			if (!(icon = purple_buddy_icons_find(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy))))
-				return NULL;
-			data = purple_buddy_icon_get_data(icon, &len);
-		}
-
-		if(data == NULL)
-			return NULL;
-	}
-
-	buf = purple_gdk_pixbuf_from_data(data, len);
-	purple_buddy_icon_unref(icon);
-	if (!buf) {
-		const char *account_name = "(no account)";
-
-		if(PURPLE_IS_ACCOUNT(account)) {
-			PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
-
-			account_name = purple_contact_info_get_username(info);
-		}
-
-		purple_debug_warning("gtkblist", "Couldn't load buddy icon on "
-			"account %s (%s); buddyname=%s; custom_img_size=%" G_GSIZE_FORMAT,
-			account_name,
-			account ? purple_account_get_protocol_id(account) : "(no account)",
-			buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
-			custom_img ? purple_image_get_data_size(custom_img) : 0);
-		if (custom_img)
-			g_object_unref(custom_img);
-		return NULL;
-	}
-	if (custom_img)
-		g_object_unref(custom_img);
-
-	if (greyed) {
-		gboolean offline = FALSE, idle = FALSE;
-
-		if (buddy) {
-			PurplePresence *presence = purple_buddy_get_presence(buddy);
-			if (!PURPLE_BUDDY_IS_ONLINE(buddy))
-				offline = TRUE;
-			if (purple_presence_is_idle(presence))
-				idle = TRUE;
-		} else if (group) {
-			if (purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)) == 0)
-				offline = TRUE;
-		}
-
-		if (offline)
-			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE);
-
-		if (idle)
-			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE);
-	}
-
-	scale_width = orig_width = gdk_pixbuf_get_width(buf);
-	scale_height = orig_height = gdk_pixbuf_get_height(buf);
-
-	if (protocol)
-		icon_spec = purple_protocol_get_icon_spec(protocol);
-
-	if (icon_spec && (icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY)) {
-		purple_buddy_icon_spec_get_scaled_size(icon_spec, &scale_width,
-		                                       &scale_height);
-	}
-
-	purple_buddy_icon_spec_free(icon_spec);
-
-	if (scaled || scale_height > 200 || scale_width > 200) {
-		GdkPixbuf *tmpbuf;
-		float scale_size = scaled ? 32.0 : 200.0;
-		if(scale_height > scale_width) {
-			scale_width = scale_size * (double)scale_width / (double)scale_height;
-			scale_height = scale_size;
-		} else {
-			scale_height = scale_size * (double)scale_height / (double)scale_width;
-			scale_width = scale_size;
-		}
-		/* Scale & round before making square, so rectangular (but
-		 * non-square) images get rounded corners too. */
-		tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
-		gdk_pixbuf_fill(tmpbuf, 0x00000000);
-		gdk_pixbuf_scale(buf, tmpbuf, 0, 0, scale_width, scale_height, 0, 0, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR);
-		if (purple_gdk_pixbuf_is_opaque(tmpbuf))
-			purple_gdk_pixbuf_make_round(tmpbuf);
-		ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_size, scale_size);
-		gdk_pixbuf_fill(ret, 0x00000000);
-		gdk_pixbuf_copy_area(tmpbuf, 0, 0, scale_width, scale_height, ret, (scale_size-scale_width)/2, (scale_size-scale_height)/2);
-		g_object_unref(G_OBJECT(tmpbuf));
-	} else {
-		ret = gdk_pixbuf_scale_simple(buf,scale_width,scale_height, GDK_INTERP_BILINEAR);
-	}
-	g_object_unref(G_OBJECT(buf));
-
-	return ret;
-}
-
-static gboolean buddy_is_displayable(PurpleBuddy *buddy)
-{
-	if(!buddy)
-		return FALSE;
-
-	return purple_account_is_connected(purple_buddy_get_account(buddy));
-}
-
-/* # - Status Icon
- * P - Protocol Icon
- * A - Buddy Icon
- * [ - SMALL_SPACE
- * = - LARGE_SPACE
- *                   +--- STATUS_SIZE                +--- td->avatar_width
- *                   |         +-- td->name_width    |
- *                +----+   +-------+            +---------+
- *                |    |   |       |            |         |
- *                +-------------------------------------------+
- *                |       [          =        [               |--- TOOLTIP_BORDER
- *name_height --+-| ######[BuddyName = PP     [   AAAAAAAAAAA |--+
- *              | | ######[          = PP     [   AAAAAAAAAAA |  |
- * STATUS SIZE -| | ######[[[[[[[[[[[[[[[[[[[[[   AAAAAAAAAAA |  |
- *           +--+-| ######[Account: So-and-so [   AAAAAAAAAAA |  |-- td->avatar_height
- *           |    |       [Idle: 4h 15m       [   AAAAAAAAAAA |  |
- *  height --+    |       [Foo: Bar, Baz      [   AAAAAAAAAAA |  |
- *           |    |       [Status: Awesome    [   AAAAAAAAAAA |--+
- *           +----|       [Stop: Hammer Time  [               |
- *                |       [                   [               |--- TOOLTIP_BORDER
- *                +-------------------------------------------+
- *                 |       |                |                |
- *                 |       +----------------+                |
- *                 |               |                         |
- *                 |               +-- td->width             |
- *                 |                                         |
- *                 +---- TOOLTIP_BORDER                      +---- TOOLTIP_BORDER
- *
- *
- */
-#define STATUS_SIZE 16
-#define TOOLTIP_BORDER 12
-#define SMALL_SPACE 6
-#define LARGE_SPACE 12
-
-static void
-add_tip_for_account(GtkWidget *grid, gint row, PurpleAccount *account)
-{
-	PurpleContactInfo *info = NULL;
-	PurpleProtocol *protocol = NULL;
-	GtkWidget *image = NULL;
-	GtkWidget *name = NULL;
-	const gchar *icon_name = NULL;
-
-	protocol = purple_account_get_protocol(account);
-	icon_name = purple_protocol_get_icon_name(protocol);
-
-	image = gtk_image_new_from_icon_name(icon_name);
-	gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE);
-	gtk_grid_attach(GTK_GRID(grid), image, 0, row, 1, 1);
-
-	info = PURPLE_CONTACT_INFO(account);
-	name = gtk_label_new(purple_contact_info_get_username(info));
-	gtk_label_set_xalign(GTK_LABEL(name), 0);
-	gtk_label_set_yalign(GTK_LABEL(name), 0);
-	gtk_label_set_wrap(GTK_LABEL(name), TRUE);
-	gtk_label_set_max_width_chars(GTK_LABEL(name), 36);
-	gtk_grid_attach(GTK_GRID(grid), name, 1, row, 1, 1);
-}
-
-static const gchar *
-pidgin_blist_get_status_icon_name(PurpleBlistNode *node) {
-	const char *icon = NULL;
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	PidginBlistNode *gtkbuddynode = NULL;
-	PurpleBuddy *buddy = NULL;
-	PurpleChat *chat = NULL;
-
-	if(PURPLE_IS_META_CONTACT(node)) {
-		if(!gtknode->contact_expanded) {
-			buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-			if(buddy != NULL) {
-				gtkbuddynode = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-			}
-		}
-	} else if(PURPLE_IS_BUDDY(node)) {
-		buddy = (PurpleBuddy*)node;
-		gtkbuddynode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	} else if(PURPLE_IS_CHAT(node)) {
-		chat = (PurpleChat*)node;
-	} else {
-		return NULL;
-	}
-
-	if(buddy || chat) {
-		PurpleAccount *account;
-		PurpleProtocol *protocol;
-
-		if(buddy)
-			account = purple_buddy_get_account(buddy);
-		else
-			account = purple_chat_get_account(chat);
-
-		protocol = purple_account_get_protocol(account);
-		if(!protocol)
-			return NULL;
-	}
-
-	if(buddy) {
-		PurplePresence *p = purple_buddy_get_presence(buddy);
-
-		if (PURPLE_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) {
-			icon = "log-in";
-		} else if (gtkbuddynode && gtkbuddynode->recent_signonoff) {
-			icon = "log-out";
-		} else {
-			icon = pidgin_icon_name_from_presence(p, "pidgin-user-available");
-		}
-	} else if (chat) {
-		icon = "chat";
-	} else {
-		icon = "person";
-	}
-
-	return icon;
-}
-
-static void
-add_tip_for_node(GtkWidget *grid, gint row, PurpleBlistNode *node, gboolean full)
-{
-	GtkWidget *image = NULL;
-	GtkWidget *name = NULL;
-	GdkPixbuf *avatar = NULL;
-	GtkWidget *avatar_image = NULL;
-	PurpleAccount *account = NULL;
-	char *tmp = NULL, *node_name = NULL, *tooltip_text = NULL;
-	const gchar *status_icon = NULL;
-
-	if (PURPLE_IS_BUDDY(node)) {
-		account = purple_buddy_get_account(PURPLE_BUDDY(node));
-	} else if (PURPLE_IS_CHAT(node)) {
-		account = purple_chat_get_account(PURPLE_CHAT(node));
-	}
-
-	status_icon = pidgin_blist_get_status_icon_name(node);
-	image = gtk_image_new_from_icon_name(status_icon);
-	gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE);
-	gtk_grid_attach(GTK_GRID(grid), image, 0, row, 1, 1);
-
-	if (PURPLE_IS_BUDDY(node)) {
-		tmp = g_markup_escape_text(purple_buddy_get_name(PURPLE_BUDDY(node)), -1);
-	} else if (PURPLE_IS_CHAT(node)) {
-		tmp = g_markup_escape_text(purple_chat_get_name(PURPLE_CHAT(node)), -1);
-	} else if (PURPLE_IS_GROUP(node)) {
-		tmp = g_markup_escape_text(purple_group_get_name(PURPLE_GROUP(node)), -1);
-	} else {
-		/* I don't believe this can happen currently, I think
-		 * everything that calls this function checks for one of the
-		 * above node types first. */
-		tmp = g_strdup(_("Unknown node type"));
-	}
-	node_name = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>",
-								tmp ? tmp : "");
-	g_free(tmp);
-
-	name = gtk_label_new(NULL);
-	gtk_label_set_xalign(GTK_LABEL(name), 0);
-	gtk_label_set_yalign(GTK_LABEL(name), 0);
-	gtk_label_set_markup(GTK_LABEL(name), node_name);
-	gtk_label_set_max_width_chars(GTK_LABEL(name), 36);
-	gtk_grid_attach(GTK_GRID(grid), name, 1, row, 1, 1);
-
-	if (account != NULL) {
-		PurpleProtocol *protocol = purple_account_get_protocol(account);
-		const gchar *icon_name = purple_protocol_get_icon_name(protocol);
-
-		image = gtk_image_new_from_icon_name(icon_name);
-		gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE);
-		gtk_widget_set_halign(image, GTK_ALIGN_END);
-		gtk_grid_attach(GTK_GRID(grid), image, 2, row, 1, 1);
-	}
-
-	tooltip_text = pidgin_get_tooltip_text(node, full);
-	if (tooltip_text && *tooltip_text) {
-		GtkWidget *contents = gtk_label_new(NULL);
-		gtk_label_set_xalign(GTK_LABEL(contents), 0);
-		gtk_label_set_yalign(GTK_LABEL(contents), 0);
-		gtk_label_set_markup(GTK_LABEL(contents), tooltip_text);
-		gtk_label_set_wrap(GTK_LABEL(contents), TRUE);
-		gtk_label_set_max_width_chars(GTK_LABEL(contents), 36);
-		gtk_grid_attach(GTK_GRID(grid), contents, 1, row+1, 2, 1);
-	}
-
-	avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE);
-	if (avatar != NULL) {
-		avatar_image = gtk_image_new_from_pixbuf(avatar);
-		gtk_widget_set_halign(avatar_image, GTK_ALIGN_END);
-		gtk_widget_set_valign(avatar_image, GTK_ALIGN_START);
-		gtk_grid_attach(GTK_GRID(grid), avatar_image, 3, row, 1, 2);
-		g_object_unref(avatar);
-	}
-
-	g_free(node_name);
-	g_free(tooltip_text);
-}
-
-gboolean
-pidgin_blist_query_tooltip_for_node(PurpleBlistNode *node, GtkTooltip *tooltip)
-{
-	GtkWidget *grid;
-
-	grid = gtk_grid_new();
-
-	if (PURPLE_IS_CHAT(node) || PURPLE_IS_BUDDY(node)) {
-		add_tip_for_node(grid, 0, node, TRUE);
-
-	} else if (PURPLE_IS_GROUP(node)) {
-		PurpleGroup *group = PURPLE_GROUP(node);
-		GSList *accounts;
-		add_tip_for_node(grid, 0, node, TRUE);
-
-		/* Accounts with buddies in group */
-		accounts = purple_group_get_accounts(group);
-		for (gint row = 2; accounts != NULL;
-		     row++, accounts = g_slist_delete_link(accounts, accounts))
-		{
-			PurpleAccount *account = PURPLE_ACCOUNT(accounts->data);
-			add_tip_for_account(grid, row, account);
-		}
-
-	} else if (PURPLE_IS_META_CONTACT(node)) {
-		PurpleBlistNode *child;
-		PurpleBuddy *b = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node));
-		gint row = 0;
-
-		for(child = node->child; child; child = child->next) {
-			if(PURPLE_IS_BUDDY(child) && buddy_is_displayable(PURPLE_BUDDY(child))) {
-				if (b == (PurpleBuddy *)child) {
-					/* Priority buddy goes first (-2) and is more detailed. */
-					add_tip_for_node(grid, -2, child, TRUE);
-				} else {
-					add_tip_for_node(grid, row, child, FALSE);
-					row += 2;
-				}
-			}
-		}
-
-	} else {
-		return FALSE;
-	}
-
-	gtk_tooltip_set_custom(tooltip, grid);
-
-	return TRUE;
-}
-
-static gboolean
-pidgin_blist_query_tooltip(GtkWidget *widget, int x, int y,
-                           gboolean keyboard_mode, GtkTooltip *tooltip,
-                           gpointer userdata)
-{
-	PidginBuddyList *blist = userdata;
-	GtkTreeIter iter;
-	PurpleBlistNode *node;
-	gboolean editable = FALSE;
-	GtkTreePath *path = NULL;
-
-	/* If we're editing a cell (e.g. alias editing), don't show the tooltip */
-	g_object_get(G_OBJECT(blist->text_rend), "editable", &editable, NULL);
-	if (editable) {
-		return FALSE;
-	}
-
-	if (keyboard_mode) {
-		GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
-		if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
-			return FALSE;
-		}
-		path = gtk_tree_model_get_path(GTK_TREE_MODEL(blist->treemodel), &iter);
-	} else {
-		gint bx, by;
-
-		gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(widget),
-		                                                  x, y, &bx, &by);
-		gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bx, by, &path,
-		                              NULL, NULL, NULL);
-		if (path == NULL) {
-			return FALSE;
-		}
-		if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(blist->treemodel),
-		                             &iter, path))
-		{
-			gtk_tree_path_free(path);
-			return FALSE;
-		}
-	}
-
-	gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter,
-	                   NODE_COLUMN, &node, -1);
-	if (pidgin_blist_query_tooltip_for_node(node, tooltip)) {
-		gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path);
-		gtk_tree_path_free(path);
-		return TRUE;
-	} else {
-		gtk_tree_path_free(path);
-		return FALSE;
-	}
-}
-
-/*********************************************************
- * Private Utility functions                             *
- *********************************************************/
-
-static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
-{
-	GString *str = g_string_new("");
-	PurpleProtocol *protocol = NULL;
-	char *tmp;
-
-	if (PURPLE_IS_CHAT(node))
-	{
-		PurpleAccount *account;
-		PurpleChat *chat;
-		GList *connections;
-		GList *cur = NULL;
-		PurpleProtocolChatEntry *pce;
-		char *name, *value;
-		PurpleChatConversation *conv = NULL;
-		PidginBlistNode *bnode = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-		chat = (PurpleChat *)node;
-		account = purple_chat_get_account(chat);
-		protocol = purple_account_get_protocol(account);
-
-		connections = purple_connections_get_all();
-		if (connections && connections->next)
-		{
-			PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
-			tmp = g_markup_escape_text(purple_contact_info_get_username(info),
-			                           -1);
-			g_string_append_printf(str, _("<b>Account:</b> %s"), tmp);
-			g_free(tmp);
-		}
-
-		if (bnode && bnode->conv) {
-			conv = PURPLE_CHAT_CONVERSATION(bnode->conv);
-		} else {
-			PurpleConversation *chat_conv;
-			PurpleConversationManager *manager;
-			char *chat_name;
-
-			if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, get_name)) {
-				chat_name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol),
-				                                          purple_chat_get_components(chat));
-			} else {
-				chat_name = g_strdup(purple_chat_get_name(chat));
-			}
-
-			manager = purple_conversation_manager_get_default();
-			chat_conv = purple_conversation_manager_find_chat(manager, account,
-			                                                  chat_name);
-			g_free(chat_name);
-
-			if(PURPLE_IS_CHAT_CONVERSATION(chat_conv)) {
-				conv = PURPLE_CHAT_CONVERSATION(chat_conv);
-			}
-		}
-
-		if (conv && !purple_chat_conversation_has_left(conv)) {
-			g_string_append_printf(str, _("\n<b>Occupants:</b> %d"),
-				purple_chat_conversation_get_users_count(conv));
-
-			if (protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_CHAT_TOPIC)) {
-				const char *chattopic = purple_chat_conversation_get_topic(conv);
-				char *topic = chattopic ? g_markup_escape_text(chattopic, -1) : NULL;
-				g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
-				g_free(topic);
-			}
-		}
-
-		if(protocol) {
-			cur = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol),
-			                                purple_account_get_connection(account));
-		}
-
-		while (cur != NULL)
-		{
-			pce = cur->data;
-
-			if (!pce->secret)
-			{
-				tmp = purple_text_strip_mnemonic(pce->label);
-				name = g_markup_escape_text(tmp, -1);
-				g_free(tmp);
-				value = g_markup_escape_text(g_hash_table_lookup(
-										purple_chat_get_components(chat), pce->identifier), -1);
-				g_string_append_printf(str, "\n<b>%s</b> %s",
-							name ? name : "",
-							value ? value : "");
-				g_free(name);
-				g_free(value);
-			}
-
-			g_free(pce);
-			cur = g_list_delete_link(cur, cur);
-		}
-	}
-	else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node))
-	{
-		/* NOTE: THIS FUNCTION IS NO LONGER CALLED FOR CONTACTS.
-		 * It is only called by create_tip_for_node(), and create_tip_for_node() is never called for a contact.
-		 */
-		PurpleAccount *account;
-		PurpleMetaContact *c;
-		PurpleBuddy *b;
-		PurplePresence *presence;
-		PurpleNotifyUserInfo *user_info;
-		GDateTime *signon = NULL;
-		GList *connections;
-		char *tmp;
-		gchar *alias;
-		time_t idle_secs;
-
-		if (PURPLE_IS_META_CONTACT(node))
-		{
-			c = (PurpleMetaContact *)node;
-			b = purple_meta_contact_get_priority_buddy(c);
-		}
-		else
-		{
-			b = (PurpleBuddy *)node;
-			c = purple_buddy_get_contact(b);
-		}
-
-		account = purple_buddy_get_account(b);
-		protocol = purple_account_get_protocol(account);
-
-		presence = purple_buddy_get_presence(b);
-		user_info = purple_notify_user_info_new();
-
-		/* Account */
-		connections = purple_connections_get_all();
-		if (full && connections && connections->next)
-		{
-			PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
-			const char *username = purple_contact_info_get_username(info);
-
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Account"),
-			                                           username);
-		}
-
-		/* Alias */
-		/* If there's not a contact alias, the node is being displayed with
-		 * this alias, so there's no point in showing it in the tooltip. */
-		g_object_get(c, "alias", &alias, NULL);
-		if (full && c && purple_buddy_get_local_alias(b) != NULL && purple_buddy_get_local_alias(b)[0] != '\0' &&
-		    (alias != NULL && alias[0] != '\0') &&
-		    !purple_strequal(alias, purple_buddy_get_local_alias(b)))
-		{
-			purple_notify_user_info_add_pair_plaintext(user_info,
-					_("Buddy Alias"), purple_buddy_get_local_alias(b));
-		}
-
-		/* Nickname/Server Alias */
-		/* I'd like to only show this if there's a contact or buddy
-		 * alias, but people often set long nicknames, which
-		 * get ellipsized, so the only way to see the whole thing is
-		 * to look at the tooltip. */
-		if (full && purple_buddy_get_server_alias(b))
-		{
-			purple_notify_user_info_add_pair_plaintext(user_info,
-					_("Nickname"), purple_buddy_get_server_alias(b));
-		}
-
-		/* Logged In */
-		signon = purple_presence_get_login_time(presence);
-		if (full && PURPLE_BUDDY_IS_ONLINE(b) && signon != NULL)
-		{
-			GDateTime *now = g_date_time_new_now_utc();
-			GTimeSpan duration = g_date_time_compare(now, signon);
-
-			if(duration < 0) {
-				/*
-				 * They signed on in the future?!  Our local clock
-				 * must be wrong, show the actual date instead of
-				 * "4 days", etc.
-				 */
-				GDateTime *local = NULL;
-
-				local = g_date_time_to_local(signon);
-
-				tmp = g_date_time_format(local, _("%x %X"));
-				g_date_time_unref(local);
-			} else {
-				tmp = purple_str_seconds_to_string(duration / G_TIME_SPAN_SECOND);
-			}
-
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Logged In"), tmp);
-			g_free(tmp);
-
-			g_date_time_unref(now);
-		}
-
-		/* Idle */
-		if (purple_presence_is_idle(presence))
-		{
-			idle_secs = purple_presence_get_idle_time(presence);
-			if (idle_secs > 0)
-			{
-				tmp = purple_str_seconds_to_string(time(NULL) - idle_secs);
-				purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp);
-				g_free(tmp);
-			}
-		}
-
-		/* Last Seen */
-		if (full && c && !PURPLE_BUDDY_IS_ONLINE(b))
-		{
-			PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(c), UI_DATA);
-			PurpleBlistNode *bnode;
-			int lastseen = 0;
-
-			if (gtknode && (!gtknode->contact_expanded || PURPLE_IS_META_CONTACT(node)))
-			{
-				/* We're either looking at a buddy for a collapsed contact or
-				 * an expanded contact itself so we show the most recent
-				 * (largest) last_seen time for any of the buddies under
-				 * the contact. */
-				for (bnode = ((PurpleBlistNode *)c)->child ; bnode != NULL ; bnode = bnode->next)
-				{
-					int value = purple_blist_node_get_int(bnode, "last_seen");
-					if (value > lastseen)
-						lastseen = value;
-				}
-			}
-			else
-			{
-				/* We're dealing with a buddy under an expanded contact,
-				 * so we show the last_seen time for the buddy. */
-				lastseen = purple_blist_node_get_int(&b->node, "last_seen");
-			}
-
-			if (lastseen > 0)
-			{
-				tmp = purple_str_seconds_to_string(time(NULL) - lastseen);
-				purple_notify_user_info_add_pair_plaintext(user_info, _("Last Seen"), tmp);
-				g_free(tmp);
-			}
-		}
-
-
-		/* Offline? */
-		/* FIXME: Why is this status special-cased by the core? --rlaager
-		 * FIXME: Alternatively, why not have the core do all of them? --rlaager */
-		if (!PURPLE_BUDDY_IS_ONLINE(b)) {
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Offline"));
-		}
-
-		if (purple_account_is_connected(account) && protocol) {
-			/* Additional text from the protocol */
-			purple_protocol_client_tooltip_text(PURPLE_PROTOCOL_CLIENT(protocol), b, user_info, full);
-		}
-
-		/* These are Easter Eggs.  Patches to remove them will be rejected. */
-		if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "robflynn"))
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Description"), _("Spooky"));
-		if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "seanegn"))
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Awesome"));
-		if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "chipx86"))
-			purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Rockin'"));
-
-		tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
-		g_string_append(str, tmp);
-		g_free(tmp);
-		g_free(alias);
-
-		purple_notify_user_info_destroy(user_info);
-	} else if (PURPLE_IS_GROUP(node)) {
-		gint count;
-		PurpleGroup *group = (PurpleGroup*)node;
-		PurpleNotifyUserInfo *user_info;
-
-		user_info = purple_notify_user_info_new();
-
-		count = purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group));
-		if (count != 0) {
-			/* Online buddies in group */
-			char tmp2[12];
-			sprintf(tmp2, "%d", count);
-			purple_notify_user_info_add_pair_plaintext(user_info,
-					_("Online Buddies"), tmp2);
-		}
-
-		count = purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group));
-		if (count != 0) {
-			/* Total buddies (from online accounts) in group */
-			char tmp2[12];
-			sprintf(tmp2, "%d", count);
-			purple_notify_user_info_add_pair_html(user_info,
-					_("Total Buddies"), tmp2);
-		}
-
-		tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
-		g_string_append(str, tmp);
-		g_free(tmp);
-
-		purple_notify_user_info_destroy(user_info);
-	}
-
-	purple_signal_emit(pidgin_blist_get_handle(), "drawing-tooltip",
-	                   node, str, full);
-
-	return g_string_free(str, FALSE);
-}
-
-static GHashTable *cached_emblems;
-
-static void
-_cleanup_cached_emblem(gpointer data, G_GNUC_UNUSED GObject *obj) {
-	g_hash_table_remove(cached_emblems, data);
-}
-
-static GdkPixbuf * _pidgin_blist_get_cached_emblem(gchar *path) {
-	GdkPixbuf *pb = g_hash_table_lookup(cached_emblems, path);
-
-	if (pb != NULL) {
-		/* The caller gets a reference */
-		g_object_ref(pb);
-		g_free(path);
-	} else {
-		pb = purple_gdk_pixbuf_new_from_file(path);
-		if (pb != NULL) {
-			/* We don't want to own a ref to the pixbuf, but we need to keep clean up. */
-			/* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */
-			g_object_weak_ref(G_OBJECT(pb), _cleanup_cached_emblem, path);
-			g_hash_table_insert(cached_emblems, path, pb);
-		} else
-			g_free(path);
-	}
-
-	return pb;
-}
-
-GdkPixbuf *
-pidgin_blist_get_emblem(PurpleBlistNode *node)
-{
-	PurpleAccount *account;
-	PurpleBuddy *buddy = NULL;
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	PurpleProtocol *protocol;
-	const char *name = NULL;
-	char *filename, *path;
-	PurplePresence *p = NULL;
-	PurpleStatus *tune;
-
-	if(PURPLE_IS_META_CONTACT(node)) {
-		if(!gtknode->contact_expanded) {
-			buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-		}
-	} else if(PURPLE_IS_BUDDY(node)) {
-		PidginBlistNode *pidgin_node = NULL;
-		buddy = (PurpleBuddy*)node;
-
-		p = purple_buddy_get_presence(buddy);
-		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-			/* This emblem comes from the small emoticon set now,
-			 * to reduce duplication. */
-			path = g_build_filename(PURPLE_DATADIR, "pixmaps",
-				"pidgin", "emotes", "small", "mobile.png", NULL);
-			return _pidgin_blist_get_cached_emblem(path);
-		}
-
-		pidgin_node = g_object_get_data(G_OBJECT(node->parent), UI_DATA);
-		if(pidgin_node->contact_expanded) {
-			return NULL;
-		}
-	} else {
-		return NULL;
-	}
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	account = purple_buddy_get_account(buddy);
-
-	/* If we came through the contact code flow above, we didn't need
-	 * to get the presence until now. */
-	if (p == NULL)
-		p = purple_buddy_get_presence(buddy);
-
-	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-		/* This emblem comes from the small emoticon set now, to reduce duplication. */
-		path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
-			"emotes", "small", "mobile.png", NULL);
-		return _pidgin_blist_get_cached_emblem(path);
-	}
-
-	tune = purple_presence_get_status(p, "tune");
-	if (tune && purple_status_is_active(tune)) {
-		/* TODO: Replace "Tune" with generalized "Media" in 3.0. */
-		if (purple_status_get_attr_string(tune, "game") != NULL) {
-			path = g_build_filename(PURPLE_DATADIR, "pidgin",
-				"icons", "hicolor", "16x16", "emblems",
-				"emblem-game.png", NULL);
-			return _pidgin_blist_get_cached_emblem(path);
-		}
-		/* TODO: Replace "Tune" with generalized "Media" in 3.0. */
-		if (purple_status_get_attr_string(tune, "office") != NULL) {
-			path = g_build_filename(PURPLE_DATADIR, "pidgin",
-				"icons", "hicolor", "16x16", "emblems",
-				"emblem-office.png", NULL);
-			return _pidgin_blist_get_cached_emblem(path);
-		}
-		/* Regular old "tune" is the only one in all protocols. */
-		/* This emblem comes from the small emoticon set now, to reduce duplication. */
-		path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
-			"emotes", "small", "music.png", NULL);
-		return _pidgin_blist_get_cached_emblem(path);
-	}
-
-	protocol = purple_account_get_protocol(account);
-	if (!protocol)
-		return NULL;
-
-	name = purple_protocol_client_list_emblem(PURPLE_PROTOCOL_CLIENT(protocol), buddy);
-
-	if (name == NULL) {
-		PurpleStatus *status;
-
-		if (!purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOOD))
-			return NULL;
-
-		status = purple_presence_get_status(p, "mood");
-		name = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
-
-		if (!(name && *name))
-			return NULL;
-
-		path = pidgin_mood_get_icon_path(name);
-	} else {
-		filename = g_strdup_printf("emblem-%s.png", name);
-		path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
-			"hicolor", "16x16", "emblems", filename, NULL);
-		g_free(filename);
-	}
-
-	/* _pidgin_blist_get_cached_emblem() assumes ownership of path */
-	return _pidgin_blist_get_cached_emblem(path);
-}
-
-
-gchar *
-pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased)
-{
-	const char *name, *name_color, *status_color, *dim_grey;
-	char *text = NULL;
-	PurpleProtocol *protocol = NULL;
-	PurpleMetaContact *contact;
-	PurplePresence *presence;
-	PidginBlistNode *gtkcontactnode = NULL;
-	char *idletime = NULL, *statustext = NULL, *nametext = NULL;
-	gchar *contact_alias;
-
-	/* XXX Good luck cleaning up this crap */
-	contact = PURPLE_META_CONTACT(PURPLE_BLIST_NODE(b)->parent);
-	if(contact) {
-		gtkcontactnode = g_object_get_data(G_OBJECT(contact), UI_DATA);
-	}
-
-	g_object_get(contact, "alias", &contact_alias, NULL);
-
-	/* Name */
-	if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact_alias)
-		name = contact_alias;
-	else
-		name = purple_buddy_get_alias(b);
-
-	nametext = g_markup_escape_text(name, strlen(name));
-
-	presence = purple_buddy_get_presence(b);
-
-	/* Name is all that is needed */
-	{
-		PurpleAccount *account = purple_buddy_get_account(b);
-
-		/* Status Info */
-		protocol = purple_account_get_protocol(account);
-
-		if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, status_text) &&
-				purple_account_get_connection(account)) {
-			char *tmp = purple_protocol_client_status_text(PURPLE_PROTOCOL_CLIENT(protocol), b);
-			const char *end;
-
-			if(tmp && !g_utf8_validate(tmp, -1, &end)) {
-				char *new = g_strndup(tmp,
-						g_utf8_pointer_to_offset(tmp, end));
-				g_free(tmp);
-				tmp = new;
-			}
-			if(tmp) {
-				g_strdelimit(tmp, "\n", ' ');
-				purple_str_strip_char(tmp, '\r');
-			}
-			statustext = tmp;
-		}
-
-		if(!purple_presence_is_online(presence) && !statustext)
-				statustext = g_strdup(_("Offline"));
-
-		/* Idle Text */
-		if (purple_presence_is_idle(presence)) {
-			time_t idle_secs = purple_presence_get_idle_time(presence);
-
-			if (idle_secs > 0) {
-				int iday, ihrs, imin;
-				time_t t;
-
-				time(&t);
-				iday = (t - idle_secs) / (24 * 60 * 60);
-				ihrs = ((t - idle_secs) / 60 / 60) % 24;
-				imin = ((t - idle_secs) / 60) % 60;
-
-				if (iday)
-					idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin);
-				else if (ihrs)
-					idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin);
-				else
-					idletime = g_strdup_printf(_("Idle %dm"), imin);
-
-			} else
-				idletime = g_strdup(_("Idle"));
-		}
-	}
-
-	dim_grey = "dim grey";
-
-	/* choose the colors of the text */
-	name_color = NULL;
-	status_color = dim_grey;
-
-	if(!selected) {
-		if(purple_presence_is_idle(presence) ||
-		   !purple_presence_is_online(presence))
-		{
-			name_color = dim_grey;
-		}
-	}
-
-	if(aliased && selected) {
-		name_color = NULL;
-		status_color = NULL;
-	}
-
-	/* Put it all together */
-	if(statustext || idletime) {
-		/* using <span size='smaller'> breaks the status, so it must be separated into <small><span>*/
-		if (name_color) {
-			text = g_strdup_printf("<span foreground='%s'>%s</span>\n"
-			                       "<small><span foreground='%s'>%s%s%s</span></small>",
-			                       name_color, nametext, status_color,
-			                       idletime != NULL ? idletime : "",
-			                       (idletime != NULL && statustext != NULL) ? " - " : "",
-			                       statustext != NULL ? statustext : "");
-		} else if (status_color) {
-			text = g_strdup_printf("%s\n<small><span foreground='%s'>%s%s%s</span></small>",
-			                       nametext, status_color,
-			                       idletime != NULL ? idletime : "",
-			                       (idletime != NULL && statustext != NULL) ? " - " : "",
-			                       statustext != NULL ? statustext : "");
-		} else {
-			text = g_strdup_printf("%s\n<small>%s%s%s</small>",
-			                       nametext,
-			                       idletime != NULL ? idletime : "",
-			                       (idletime != NULL && statustext != NULL) ? " - " : "",
-			                       statustext != NULL ? statustext : "");
-		}
-	} else {
-		if (name_color) {
-			text = g_strdup_printf("<span color='%s'>%s</span>",
-			                       name_color, nametext);
-		} else {
-			text = g_strdup_printf("%s", nametext);
-		}
-	}
-	g_free(nametext);
-	g_free(statustext);
-	g_free(idletime);
-	g_free(contact_alias);
-
-	return text;
-}
-
-static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node, gboolean update)
-{
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	GtkTreeIter iter;
-
-	if (!gtknode || !gtknode->row || !gtkblist)
-		return;
-
-	if(gtkblist->selected_node == node)
-		gtkblist->selected_node = NULL;
-	if (get_iter_from_node(node, &iter)) {
-		gtk_tree_store_remove(gtkblist->treemodel, &iter);
-		if(update && (PURPLE_IS_META_CONTACT(node) ||
-			PURPLE_IS_BUDDY(node) || PURPLE_IS_CHAT(node))) {
-			pidgin_blist_update(list, node->parent);
-		}
-	}
-	gtk_tree_row_reference_free(gtknode->row);
-	gtknode->row = NULL;
-}
-
-static void
-conversation_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type,
-                        G_GNUC_UNUSED PidginBuddyList *gtkblist)
-{
-	PurpleAccount *account = purple_conversation_get_account(conv);
-
-	if (type != PURPLE_CONVERSATION_UPDATE_UNSEEN)
-		return;
-
-	if(account != NULL && purple_conversation_get_name(conv) != NULL) {
-		PurpleBuddy *buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
-		if(buddy != NULL)
-			pidgin_blist_update_buddy(NULL, PURPLE_BLIST_NODE(buddy), TRUE);
-	}
-}
-
-static void
-conversation_deleting_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
-{
-	conversation_updated_cb(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN, gtkblist);
-}
-
-static void
-conversation_deleted_update_ui_cb(PurpleConversation *conv, PidginBlistNode *ui)
-{
-	if (ui->conv != conv)
-		return;
-	ui->conv = NULL;
-}
-
-static void
-conversation_created_cb(PurpleConversation *conv,
-                        G_GNUC_UNUSED PidginBuddyList *gtkblist)
-{
-	PurpleAccount *account = purple_conversation_get_account(conv);
-
-	if (PURPLE_IS_IM_CONVERSATION(conv)) {
-		GSList *buddies = purple_blist_find_buddies(account, purple_conversation_get_name(conv));
-		while (buddies) {
-			PurpleBlistNode *buddy = buddies->data;
-			PidginBlistNode *ui = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-			buddies = g_slist_delete_link(buddies, buddies);
-			if (!ui)
-				continue;
-			ui->conv = conv;
-
-			purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
-					ui, G_CALLBACK(conversation_deleted_update_ui_cb), ui);
-		}
-	} else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
-		PurpleChat *chat = purple_blist_find_chat(account, purple_conversation_get_name(conv));
-		PidginBlistNode *ui;
-		if (!chat)
-			return;
-		ui = g_object_get_data(G_OBJECT(chat), UI_DATA);
-		if (!ui)
-			return;
-		ui->conv = conv;
-
-		purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
-				ui, G_CALLBACK(conversation_deleted_update_ui_cb), ui);
-	}
-}
-
-/**********************************************************************************
- * Public API Functions                                                           *
- **********************************************************************************/
-static void
-pidgin_blist_node_free(PidginBlistNode *node) {
-	if(node->recent_signonoff_timer > 0) {
-		g_source_remove(node->recent_signonoff_timer);
-	}
-
-	purple_signals_disconnect_by_handle(node);
-
-	g_free(node);
-}
-
-static void
-pidgin_blist_new_node(G_GNUC_UNUSED PurpleBuddyList *list,
-                      PurpleBlistNode *node)
-{
-	PidginBlistNode *pidgin_node = NULL;
-
-	pidgin_node = g_new0(PidginBlistNode, 1);
-
-	g_object_set_data_full(G_OBJECT(node), UI_DATA, pidgin_node,
-	                       (GDestroyNotify)pidgin_blist_node_free);
-}
-
-gboolean
-pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node) {
-	PidginBlistNode *pidgin_node = NULL;
-
-	if (PURPLE_IS_BUDDY(node)) {
-		node = node->parent;
-		if (node == NULL)
-			return FALSE;
-	}
-
-	g_return_val_if_fail(PURPLE_IS_META_CONTACT(node), FALSE);
-
-	pidgin_node = g_object_get_data(G_OBJECT(node), UI_DATA);
-
-	return pidgin_node->contact_expanded;
-}
-
-void
-pidgin_blist_setup_sort_methods(void)
-{
-	const char *id;
-
-	pidgin_blist_sort_method_reg("none", _("Manually"), sort_method_none);
-	pidgin_blist_sort_method_reg("alphabetical", _("Alphabetically"), sort_method_alphabetical);
-	pidgin_blist_sort_method_reg("status", _("By status"), sort_method_status);
-
-	id = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type");
-	if (id == NULL) {
-		purple_debug_warning("gtkblist", "Sort method was NULL, resetting to alphabetical\n");
-		id = "alphabetical";
-	}
-	pidgin_blist_sort_method_set(id);
-}
-
-static void
-_prefs_change_sort_method(const char *pref_name,
-                          G_GNUC_UNUSED PurplePrefType type, gconstpointer val,
-                          G_GNUC_UNUSED gpointer data)
-{
-	if(purple_strequal(pref_name, PIDGIN_PREFS_ROOT "/blist/sort_type"))
-		pidgin_blist_sort_method_set(val);
-}
-
-/* This assumes there are not things like groupless buddies or multi-leveled groups.
- * I'm sure other things in this code assumes that also.
- */
-static void
-icon_theme_changed_cb(G_GNUC_UNUSED GtkIconTheme *self, gpointer data) {
-	PurpleBuddyList *list = data;
-	PurpleBlistNode *node = purple_blist_get_root(list);
-
-	while (node) {
-		pidgin_blist_update_group(list, node);
-		node = node->next;
-	}
-}
-
-/******************************************/
-/* End of connection error handling stuff */
-/******************************************/
-
-/* builds the blist layout according to to the current theme */
-static void
-pidgin_blist_build_layout(PurpleBuddyList *list)
-{
-	GtkTreeViewColumn *column;
-	GtkCellRenderer *rend;
-
-	column = gtkblist->text_column;
-
-	gtk_tree_view_column_clear(column);
-
-	/* status icons */
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "icon-name", STATUS_ICON_COLUMN,
-					    "visible", STATUS_ICON_VISIBLE_COLUMN,
-					    NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
-
-	/* name */
-	gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
-	gtk_tree_view_column_pack_start(column, rend, TRUE);
-	gtk_tree_view_column_set_attributes(column, rend,
-	                                    "markup", NAME_COLUMN,
-	                                    NULL);
-	g_signal_connect(G_OBJECT(rend), "editing-started",
-	                 G_CALLBACK(gtk_blist_renderer_editing_started_cb), list);
-	g_signal_connect(G_OBJECT(rend), "editing-canceled",
-	                 G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
-	g_signal_connect(G_OBJECT(rend), "edited",
-	                 G_CALLBACK(gtk_blist_renderer_edited_cb), list);
-	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
-	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-
-	/* emblem */
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3,
-	             NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-	                                    "pixbuf", EMBLEM_COLUMN,
-	                                    "visible", EMBLEM_VISIBLE_COLUMN, NULL);
-
-	/* protocol icon */
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-	                                    "icon-name", PROTOCOL_ICON_NAME_COLUMN,
-	                                    NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
-
-	/* buddy icon */
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-	                                    "pixbuf", BUDDY_ICON_COLUMN,
-	                                    NULL);
-}
-
-static gboolean
-pidgin_blist_search_equal_func(GtkTreeModel *model, gint column,
-			const gchar *key, GtkTreeIter *iter, gpointer data)
-{
-	PurpleBlistNode *node = NULL;
-	gboolean res = TRUE;
-	const char *compare = NULL;
-
-	if (!pidgin_tree_view_search_equal_func(model, column, key, iter, data))
-		return FALSE;
-
-	/* If the search string does not match the displayed label, then look
-	 * at the alternate labels for the nodes and search in them. Currently,
-	 * alternate labels that make sense are usernames/email addresses for
-	 * buddies (but only for the ones who don't have a local alias).
-	 */
-
-	gtk_tree_model_get(model, iter, NODE_COLUMN, &node, -1);
-	if (!node)
-		return TRUE;
-
-	compare = NULL;
-	if (PURPLE_IS_META_CONTACT(node)) {
-		PurpleBuddy *b = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node));
-		if (!purple_buddy_get_local_alias(b))
-			compare = purple_buddy_get_name(b);
-	} else if (PURPLE_IS_BUDDY(node)) {
-		if (!purple_buddy_get_local_alias(PURPLE_BUDDY(node)))
-			compare = purple_buddy_get_name(PURPLE_BUDDY(node));
-	}
-
-	if (compare) {
-		char *tmp, *enteredstring;
-		tmp = g_utf8_normalize(key, -1, G_NORMALIZE_DEFAULT);
-		enteredstring = g_utf8_casefold(tmp, -1);
-		g_free(tmp);
-
-		if (g_str_has_prefix(compare, enteredstring)) {
-			res = FALSE;
-		}
-		g_free(enteredstring);
-	}
-
-	return res;
-}
-
-static void
-pidgin_blist_populate_menus(void) {
-	GtkApplication *application = NULL;
-	GMenu *source = NULL, *target = NULL;
-
-	application = GTK_APPLICATION(g_application_get_default());
-
-	/* Add the icon menu to all the menus that need it. */
-	source = gtk_application_get_menu_by_id(application, "custom-icon");
-
-	/* The group context menu. */
-	target = gtk_application_get_menu_by_id(application, "group-custom-icon");
-	g_menu_append_section(target, NULL, G_MENU_MODEL(source));
-
-	/* The chat context menu. */
-	target = gtk_application_get_menu_by_id(application, "chat-custom-icon");
-	g_menu_append_section(target, NULL, G_MENU_MODEL(source));
-
-	/* The contact context menu. */
-	target = gtk_application_get_menu_by_id(application, "contact-custom-icon");
-	g_menu_append_section(target, NULL, G_MENU_MODEL(source));
-
-	/* The buddy context menu. */
-	target = gtk_application_get_menu_by_id(application, "buddy-custom-icon");
-	g_menu_append_section(target, NULL, G_MENU_MODEL(source));
-
-	/* Add the voice and video menu to the buddy menu. */
-	source = gtk_application_get_menu_by_id(application, "voice-video");
-	target = gtk_application_get_menu_by_id(application, "buddy-voice-video");
-	g_menu_append_section(target, NULL, G_MENU_MODEL(source));
-}
-
-static void pidgin_blist_show(PurpleBuddyList *list)
-{
-	void *handle;
-	GtkWidget *sep, *sw;
-	GtkEventController *key_controller = NULL;
-	GtkGesture *click = NULL;
-	GtkIconTheme *icon_theme;
-	GtkTreeSelection *selection;
-
-	gtkblist = PIDGIN_BUDDY_LIST(list);
-
-	gtkblist->window = pidgin_contact_list_window_new();
-
-	/* the main vbox is already packed and shown via glade, we just need a
-	 * reference to it to pack the rest of our widgets here.
-	 */
-	gtkblist->vbox = pidgin_contact_list_window_get_vbox(PIDGIN_CONTACT_LIST_WINDOW(gtkblist->window));
-
-	/****************************** GtkTreeView **********************************/
-	gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS,
-						 G_TYPE_STRING, /* Status icon */
-						 G_TYPE_BOOLEAN,  /* Status icon visible */
-						 G_TYPE_STRING,   /* Name */
-						 GDK_TYPE_PIXBUF, /* Buddy icon */
-						 G_TYPE_POINTER,  /* Node */
-						 GDK_TYPE_PIXBUF, /* Emblem */
-						 G_TYPE_BOOLEAN,  /* Emblem visible */
-						 G_TYPE_STRING /* Protocol icon */
-						);
-
-	gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
-	gtk_widget_set_name(gtkblist->treeview, "pidgin_blist_treeview");
-
-	icon_theme = gtk_icon_theme_get_for_display(gdk_display_get_default());
-	g_signal_connect(icon_theme, "changed",
-	                 G_CALLBACK(icon_theme_changed_cb), list);
-
-	/* Set up selection stuff */
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
-	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pidgin_blist_selection_changed), NULL);
-
-	/* Tooltips */
-	gtk_widget_set_has_tooltip(gtkblist->treeview, TRUE);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "query-tooltip",
-	                 G_CALLBACK(pidgin_blist_query_tooltip), gtkblist);
-
-	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
-
-	/* everything else column */
-	gtkblist->text_column = gtk_tree_view_column_new ();
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column);
-	pidgin_blist_build_layout(list);
-
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated",
-	                 G_CALLBACK(gtk_blist_row_activated_cb), gtkblist);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded",
-	                 G_CALLBACK(gtk_blist_row_expanded_cb), gtkblist);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed",
-	                 G_CALLBACK(gtk_blist_row_collapsed_cb), gtkblist);
-
-	click = gtk_gesture_click_new();
-	gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click), 0);
-	g_signal_connect(click, "pressed", G_CALLBACK(gtk_blist_button_press_cb),
-	                 gtkblist);
-	gtk_widget_add_controller(gtkblist->treeview, GTK_EVENT_CONTROLLER(click));
-
-	key_controller = gtk_event_controller_key_new();
-	g_signal_connect(G_OBJECT(key_controller), "key-pressed",
-	                 G_CALLBACK(pidgin_blist_key_press_cb), gtkblist);
-	gtk_widget_add_controller(gtkblist->treeview, key_controller);
-
-	/* Enable CTRL+F searching */
-	gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN);
-	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview),
-			pidgin_blist_search_equal_func, NULL, NULL);
-
-	sw = gtk_scrolled_window_new();
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-	                               GTK_POLICY_AUTOMATIC,
-	                               GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), gtkblist->treeview);
-
-	gtk_widget_set_vexpand(sw, TRUE);
-	gtk_box_append(GTK_BOX(gtkblist->vbox), sw);
-
-	sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
-	gtk_box_append(GTK_BOX(gtkblist->vbox), sep);
-
-	/* Update some dynamic things */
-	pidgin_blist_update_sort_methods();
-
-	/* OK... let's show this bad boy. */
-	pidgin_blist_refresh(list);
-	purple_blist_set_visible(TRUE);
-
-	handle = pidgin_blist_get_handle();
-
-	/* sorting */
-	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/sort_type",
-			_prefs_change_sort_method, NULL);
-
-	/* Setup some purple signal handlers. */
-
-	handle = purple_conversations_get_handle();
-	purple_signal_connect(handle, "conversation-updated", gtkblist,
-	                      G_CALLBACK(conversation_updated_cb),
-	                      gtkblist);
-	purple_signal_connect(handle, "deleting-conversation", gtkblist,
-	                      G_CALLBACK(conversation_deleting_cb),
-	                      gtkblist);
-	purple_signal_connect(handle, "conversation-created", gtkblist,
-	                      G_CALLBACK(conversation_created_cb),
-	                      gtkblist);
-	purple_signal_connect(handle, "chat-joined", gtkblist,
-	                      G_CALLBACK(conversation_created_cb),
-	                      gtkblist);
-
-	/* emit our created signal */
-	handle = pidgin_blist_get_handle();
-	purple_signal_emit(handle, "gtkblist-created", list);
-
-	gtkblist->action_group = G_ACTION_GROUP(g_simple_action_group_new());
-	g_action_map_add_action_entries(G_ACTION_MAP(gtkblist->action_group),
-	                                menu_actions, G_N_ELEMENTS(menu_actions),
-	                                gtkblist);
-	gtk_widget_insert_action_group(gtkblist->treeview, "menu",
-	                               G_ACTION_GROUP(gtkblist->action_group));
-
-	pidgin_blist_populate_menus();
-}
-
-static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender)
-{
-	PurpleBlistNode *node;
-
-	gtkblist = PIDGIN_BUDDY_LIST(list);
-	if(!gtkblist || !gtkblist->treeview)
-		return;
-
-	node = purple_blist_get_root(list);
-
-	while (node)
-	{
-		/* This is only needed when we're reverting to a non-GTK sorted
-		 * status.  We shouldn't need to remove otherwise.
-		 */
-		if (remove && !PURPLE_IS_GROUP(node))
-			pidgin_blist_hide_node(list, node, FALSE);
-
-		if (PURPLE_IS_BUDDY(node))
-			pidgin_blist_update_buddy(list, node, rerender);
-		else if (PURPLE_IS_CHAT(node))
-			pidgin_blist_update(list, node);
-		else if (PURPLE_IS_GROUP(node))
-			pidgin_blist_update(list, node);
-		node = purple_blist_node_next(node, FALSE);
-	}
-
-}
-
-void pidgin_blist_refresh(PurpleBuddyList *list)
-{
-	redo_buddy_list(list, FALSE, TRUE);
-}
-
-static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) {
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	GtkTreePath *path;
-
-	if (!gtknode) {
-		return FALSE;
-	}
-
-	if (!gtkblist) {
-		purple_debug_error("gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n");
-		return FALSE;
-	}
-
-	if (!gtknode->row)
-		return FALSE;
-
-	if (!GTK_IS_TREE_MODEL(gtkblist->treemodel)) {
-		return FALSE;
-	}
-
-	if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL)
-		return FALSE;
-
-	if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) {
-		gtk_tree_path_free(path);
-		return FALSE;
-	}
-	gtk_tree_path_free(path);
-	return TRUE;
-}
-
-static void
-pidgin_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node) {
-	purple_request_close_with_handle(node);
-
-	pidgin_blist_hide_node(list, node, TRUE);
-
-	if(node->parent) {
-		pidgin_blist_update(list, node->parent);
-	}
-}
-
-static gboolean do_selection_changed(PurpleBlistNode *new_selection)
-{
-	PurpleBlistNode *old_selection = NULL;
-
-	/* test for gtkblist because crazy timeout means we can be called after the blist is gone */
-	if (gtkblist && new_selection != gtkblist->selected_node) {
-		old_selection = gtkblist->selected_node;
-		gtkblist->selected_node = new_selection;
-		if(new_selection)
-			pidgin_blist_update(NULL, new_selection);
-		if(old_selection)
-			pidgin_blist_update(NULL, old_selection);
-	}
-
-	return FALSE;
-}
-
-static void
-pidgin_blist_selection_changed(GtkTreeSelection *selection,
-                               G_GNUC_UNUSED gpointer data)
-{
-	PurpleBlistNode *new_selection = NULL;
-	GtkTreeIter iter;
-
-	if(gtk_tree_selection_get_selected(selection, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
-				NODE_COLUMN, &new_selection, -1);
-	}
-
-	/* we set this up as a timeout, otherwise the blist flickers ...
-	 * but we don't do it for groups, because it causes total bizarness -
-	 * the previously selected buddy node might rendered at half height.
-	 */
-	if ((new_selection != NULL) && PURPLE_IS_GROUP(new_selection)) {
-		do_selection_changed(new_selection);
-	} else {
-		g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
-	}
-}
-
-static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter)
-{
-	GtkTreeIter parent_iter = {0, NULL, NULL, NULL}, cur, *curptr = NULL;
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	GtkTreePath *newpath;
-
-	if(!iter)
-		return FALSE;
-
-	/* XXX: it's not necessary, but let's silence a warning*/
-	memset(&parent_iter, 0, sizeof(parent_iter));
-
-	if(node->parent && !get_iter_from_node(node->parent, &parent_iter))
-		return FALSE;
-
-	if(get_iter_from_node(node, &cur))
-		curptr = &cur;
-
-	if(PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) {
-		current_sort_method->func(node, list, parent_iter, curptr, iter);
-	} else {
-		sort_method_none(node, list, parent_iter, curptr, iter);
-	}
-
-	if(gtknode != NULL) {
-		gtk_tree_row_reference_free(gtknode->row);
-	} else {
-		pidgin_blist_new_node(list, node);
-		gtknode = g_object_get_data(G_OBJECT(node), UI_DATA);
-	}
-
-	newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel),
-			iter);
-	gtknode->row =
-		gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel),
-				newpath);
-
-	gtk_tree_path_free(newpath);
-
-	if (!editing_blist)
-		gtk_tree_store_set(gtkblist->treemodel, iter,
-				NODE_COLUMN, node,
-				-1);
-
-	if(node->parent) {
-		GtkTreePath *expand = NULL;
-		PidginBlistNode *gtkparentnode = g_object_get_data(G_OBJECT(node->parent), UI_DATA);
-
-		if(PURPLE_IS_GROUP(node->parent)) {
-			if(!purple_blist_node_get_bool(node->parent, "collapsed"))
-				expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter);
-		} else if(PURPLE_IS_META_CONTACT(node->parent) &&
-				gtkparentnode->contact_expanded) {
-			expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter);
-		}
-		if(expand) {
-			gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, FALSE);
-			gtk_tree_path_free(expand);
-		}
-	}
-
-	return TRUE;
-}
-
-/* This version of pidgin_blist_update_group can take the original buddy or a
- * group, but has much better algorithmic performance with a pre-known buddy.
- */
-static void pidgin_blist_update_group(PurpleBuddyList *list,
-                                      PurpleBlistNode *node)
-{
-	PurpleBlistNode* gnode;
-	gboolean show = FALSE;
-
-	g_return_if_fail(node != NULL);
-
-	if (editing_blist)
-		return;
-
-	if (PURPLE_IS_GROUP(node))
-		gnode = node;
-	else if (PURPLE_IS_BUDDY(node))
-		gnode = node->parent->parent;
-	else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node))
-		gnode = node->parent;
-	else
-		return;
-
-	show = TRUE;
-
-	if (show) {
-		gchar *title;
-		GtkTreeIter iter;
-		GtkTreePath *path;
-		gboolean expanded;
-		GdkPixbuf *avatar = NULL;
-
-		if(!insert_node(list, gnode, &iter))
-			return;
-
-		path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
-		expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path);
-		gtk_tree_path_free(path);
-
-		title = pidgin_get_group_title(gnode, expanded);
-		avatar = pidgin_blist_get_buddy_icon(gnode, TRUE, TRUE);
-
-		gtk_tree_store_set(gtkblist->treemodel, &iter,
-				   STATUS_ICON_VISIBLE_COLUMN, FALSE,
-				   STATUS_ICON_COLUMN, NULL,
-				   NAME_COLUMN, title,
-				   NODE_COLUMN, gnode,
-				   BUDDY_ICON_COLUMN, avatar,
-				   EMBLEM_VISIBLE_COLUMN, FALSE,
-				   -1);
-		g_free(title);
-	} else {
-		pidgin_blist_hide_node(list, gnode, TRUE);
-	}
-}
-
-static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded)
-{
-	PurpleGroup *group;
-	char group_count[12] = "";
-	char *mark, *esc;
-	PurpleBlistNode *selected_node = NULL;
-	GtkTreeIter iter;
-
-	group = (PurpleGroup*)gnode;
-
-	if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) {
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
-				NODE_COLUMN, &selected_node, -1);
-	}
-
-	if (!expanded) {
-		g_snprintf(group_count, sizeof(group_count), "%d/%d",
-		           purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)),
-		           purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)));
-	}
-
-	esc = g_markup_escape_text(purple_group_get_name(group), -1);
-
-	mark = g_strdup_printf("<b>%s</b>%s%s%s",
-	                       esc ? esc : "",
-	                       !expanded ? " <span weight='light'>(</span>" : "",
-	                       group_count,
-	                       !expanded ? "<span weight='light'>)</span>" : "");
-
-	g_free(esc);
-	return mark;
-}
-
-static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node)
-{
-	PurplePresence *presence = purple_buddy_get_presence(buddy);
-	PurpleProtocol *protocol = NULL;
-	GdkPixbuf *avatar, *emblem;
-	char *mark;
-	char *idle = NULL;
-	gboolean selected = (gtkblist->selected_node == node);
-	const gchar *protocol_icon_name = NULL, *status_icon_name = NULL;
-
-	if(editing_blist) {
-		return;
-	}
-
-	status_icon_name = pidgin_blist_get_status_icon_name(PURPLE_BLIST_NODE(buddy));
-	avatar = pidgin_blist_get_buddy_icon(PURPLE_BLIST_NODE(buddy), TRUE, TRUE);
-
-	if(avatar != NULL) {
-		if(!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence)) {
-			do_alphashift(avatar, 77);
-		}
-	}
-
-	emblem = pidgin_blist_get_emblem(PURPLE_BLIST_NODE(buddy));
-	mark = pidgin_blist_get_name_markup(buddy, selected, TRUE);
-
-	protocol = purple_account_get_protocol(purple_buddy_get_account(buddy));
-	protocol_icon_name = purple_protocol_get_icon_name(protocol);
-
-	gtk_tree_store_set(gtkblist->treemodel, iter,
-			   STATUS_ICON_COLUMN, status_icon_name,
-			   STATUS_ICON_VISIBLE_COLUMN, TRUE,
-			   NAME_COLUMN, mark,
-			   BUDDY_ICON_COLUMN, avatar,
-			   EMBLEM_COLUMN, emblem,
-			   EMBLEM_VISIBLE_COLUMN, (emblem != NULL),
-			   PROTOCOL_ICON_NAME_COLUMN, protocol_icon_name,
-			-1);
-
-	g_free(mark);
-	g_free(idle);
-	if(emblem)
-		g_object_unref(emblem);
-	if(avatar)
-		g_object_unref(avatar);
-}
-
-/* This is a variation on the original gtk_blist_update_contact. Here we
-	can know in advance which buddy has changed so we can just update that */
-static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-	PurpleBlistNode *cnode;
-	PurpleMetaContact *contact;
-	PurpleBuddy *buddy;
-	PidginBlistNode *gtknode;
-
-	if (editing_blist)
-		return;
-
-	if (PURPLE_IS_BUDDY(node))
-		cnode = node->parent;
-	else
-		cnode = node;
-
-	g_return_if_fail(PURPLE_IS_META_CONTACT(cnode));
-
-	/* First things first, update the group */
-	if (PURPLE_IS_BUDDY(node))
-		pidgin_blist_update_group(list, node);
-	else
-		pidgin_blist_update_group(list, cnode->parent);
-
-	contact = (PurpleMetaContact*)cnode;
-	buddy = purple_meta_contact_get_priority_buddy(contact);
-
-	if (buddy_is_displayable(buddy))
-	{
-		GtkTreeIter iter;
-
-		if(!insert_node(list, cnode, &iter))
-			return;
-
-		gtknode = g_object_get_data(G_OBJECT(cnode), UI_DATA);
-
-		if(gtknode->contact_expanded) {
-			gchar *mark;
-			const gchar *icon_name = NULL;
-
-			mark = g_markup_escape_text(purple_meta_contact_get_alias(contact), -1);
-
-			icon_name = pidgin_blist_get_status_icon_name(cnode);
-
-			gtk_tree_store_set(gtkblist->treemodel, &iter,
-					   STATUS_ICON_COLUMN, icon_name,
-					   STATUS_ICON_VISIBLE_COLUMN, TRUE,
-					   NAME_COLUMN, mark,
-					   BUDDY_ICON_COLUMN, NULL,
-					-1);
-			g_free(mark);
-		} else {
-			buddy_node(buddy, &iter, cnode);
-		}
-	} else {
-		pidgin_blist_hide_node(list, cnode, TRUE);
-	}
-}
-
-
-
-static void
-pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node,
-                          G_GNUC_UNUSED gboolean status_change)
-{
-	PurpleBuddy *buddy;
-	PidginBlistNode *gtkparentnode;
-
-	g_return_if_fail(PURPLE_IS_BUDDY(node));
-
-	if (node->parent == NULL)
-		return;
-
-	buddy = (PurpleBuddy*)node;
-
-	/* First things first, update the contact */
-	pidgin_blist_update_contact(list, node);
-
-	gtkparentnode = g_object_get_data(G_OBJECT(node->parent), UI_DATA);
-
-	if (gtkparentnode->contact_expanded && buddy_is_displayable(buddy))
-	{
-		GtkTreeIter iter;
-
-		if (!insert_node(list, node, &iter))
-			return;
-
-		buddy_node(buddy, &iter, node);
-
-	} else {
-		pidgin_blist_hide_node(list, node, TRUE);
-	}
-
-}
-
-static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-	PurpleChat *chat;
-
-	g_return_if_fail(PURPLE_IS_CHAT(node));
-
-	if (editing_blist)
-		return;
-
-	/* First things first, update the group */
-	pidgin_blist_update_group(list, node->parent);
-
-	chat = (PurpleChat*)node;
-
-	if(purple_account_is_connected(purple_chat_get_account(chat))) {
-		PurpleProtocol *protocol = NULL;
-		GtkTreeIter iter;
-		GdkPixbuf *avatar, *emblem;
-		const gchar *color = NULL;
-		gchar *mark, *tmp;
-		gboolean selected = (gtkblist->selected_node == node);
-		gboolean nick_said = FALSE;
-		const gchar *protocol_icon_name = NULL, *status_icon_name = NULL;
-
-		if (!insert_node(list, node, &iter))
-			return;
-
-		status_icon_name = pidgin_blist_get_status_icon_name(node);
-		emblem = pidgin_blist_get_emblem(node);
-		avatar = pidgin_blist_get_buddy_icon(node, TRUE, FALSE);
-
-		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);
-
-		if(selected ) {
-			/* nick_said color is the same as gtkconv:tab-label-attention */
-			color = (nick_said ? "#006aff" : NULL);
-		}
-
-		if(color) {
-			tmp = g_strdup_printf("<span color='%s'>%s</span>", color, mark);
-			g_free(mark);
-			mark = tmp;
-		}
-
-		protocol = purple_account_get_protocol(purple_chat_get_account(chat));
-		protocol_icon_name = purple_protocol_get_icon_name(protocol);
-
-		gtk_tree_store_set(gtkblist->treemodel, &iter,
-				STATUS_ICON_COLUMN, status_icon_name,
-				STATUS_ICON_VISIBLE_COLUMN, TRUE,
-				BUDDY_ICON_COLUMN, avatar,
-				EMBLEM_COLUMN, emblem,
-				EMBLEM_VISIBLE_COLUMN, emblem != NULL,
-				PROTOCOL_ICON_NAME_COLUMN, protocol_icon_name,
-				NAME_COLUMN, mark,
-				-1);
-
-		g_free(mark);
-		if(emblem)
-			g_object_unref(emblem);
-		if(avatar)
-			g_object_unref(avatar);
-	} else {
-		pidgin_blist_hide_node(list, node, TRUE);
-	}
-}
-
-static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-	if(list) {
-		gtkblist = PIDGIN_BUDDY_LIST(list);
-	}
-	if(!gtkblist || !gtkblist->treeview || !node) {
-		return;
-	}
-
-	if(g_object_get_data(G_OBJECT(node), UI_DATA) == NULL) {
-		pidgin_blist_new_node(list, node);
-	}
-
-	if(PURPLE_IS_GROUP(node)) {
-		pidgin_blist_update_group(list, node);
-	} else if(PURPLE_IS_META_CONTACT(node)) {
-		pidgin_blist_update_contact(list, node);
-	} else if(PURPLE_IS_BUDDY(node)) {
-		pidgin_blist_update_buddy(list, node, TRUE);
-	} else if(PURPLE_IS_CHAT(node)) {
-		pidgin_blist_update_chat(list, node);
-	}
-}
-
-static void
-pidgin_blist_set_visible(G_GNUC_UNUSED PurpleBuddyList *list, gboolean show)
-{
-	if (!(gtkblist && gtkblist->window))
-		return;
-
-	if (show) {
-		gtk_widget_show(gtkblist->window);
-	} else {
-		if (!gtk_widget_get_visible(gtkblist->window))
-			gtk_widget_show(gtkblist->window);
-		/* gtk_window_iconify(GTK_WINDOW(gtkblist->window)); */
-	}
-}
-
-static void
 pidgin_blist_request_add_buddy(G_GNUC_UNUSED PurpleBuddyList *list,
                                PurpleAccount *account, const char *username,
                                const char *group, const char *alias)
@@ -3745,139 +491,6 @@
 					   NULL, NULL);
 }
 
-PidginBuddyList *
-pidgin_blist_get_default_gtk_blist(void)
-{
-	return gtkblist;
-}
-
-static gboolean
-autojoin_cb(PurpleConnection *gc, G_GNUC_UNUSED gpointer data)
-{
-	PurpleAccount *account = purple_connection_get_account(gc);
-	PurpleBlistNode *gnode, *cnode;
-	for (gnode = purple_blist_get_default_root(); gnode;
-	     gnode = gnode->next) {
-		if(!PURPLE_IS_GROUP(gnode))
-			continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next)
-		{
-			PurpleChat *chat;
-
-			if(!PURPLE_IS_CHAT(cnode))
-				continue;
-
-			chat = (PurpleChat *)cnode;
-
-			if(purple_chat_get_account(chat) != account)
-				continue;
-
-			if (purple_blist_node_get_bool(PURPLE_BLIST_NODE(chat), "gtk-autojoin"))
-				purple_serv_join_chat(gc, purple_chat_get_components(chat));
-		}
-	}
-
-	/* Stop processing; we handled the autojoins. */
-	return TRUE;
-}
-
-void *
-pidgin_blist_get_handle(void) {
-	static int handle;
-
-	return &handle;
-}
-
-static gboolean buddy_signonoff_timeout_cb(PurpleBuddy *buddy)
-{
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-	PurpleAccount *account = NULL;
-
-	gtknode->recent_signonoff = FALSE;
-	gtknode->recent_signonoff_timer = 0;
-
-	account = purple_buddy_get_account(buddy);
-	if(purple_account_is_connected(account)) {
-		pidgin_blist_update(NULL, PURPLE_BLIST_NODE(buddy));
-	}
-
-	return FALSE;
-}
-
-static void
-buddy_signonoff_cb(PurpleBuddy *buddy) {
-	PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-
-	if(gtknode == NULL) {
-		pidgin_blist_new_node(purple_blist_get_default(),
-		                      PURPLE_BLIST_NODE(buddy));
-
-		gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA);
-	}
-
-	gtknode->recent_signonoff = TRUE;
-
-	if(gtknode->recent_signonoff_timer > 0)
-		g_source_remove(gtknode->recent_signonoff_timer);
-
-	g_object_ref(buddy);
-	gtknode->recent_signonoff_timer = g_timeout_add_seconds_full(
-			G_PRIORITY_DEFAULT, 10,
-			(GSourceFunc)buddy_signonoff_timeout_cb,
-			buddy, g_object_unref);
-}
-
-void pidgin_blist_init(void)
-{
-	void *gtk_blist_handle = pidgin_blist_get_handle();
-
-	cached_emblems = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
-	/* Remove old prefs */
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_empty_groups");
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_idle_time");
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies");
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons");
-
-	/* Initialize prefs */
-	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/blist");
-	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/sort_type", "alphabetical");
-	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
-
-	/* Register our signals */
-	purple_signal_register(gtk_blist_handle, "gtkblist-created",
-	                     purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
-	                     PURPLE_TYPE_BUDDY_LIST);
-
-	purple_signal_register(gtk_blist_handle, "drawing-tooltip",
-	                     purple_marshal_VOID__POINTER_POINTER_UINT, G_TYPE_NONE,
-	                     3, PURPLE_TYPE_BLIST_NODE,
-	                     G_TYPE_POINTER, /* pointer to a (GString *) */
-	                     G_TYPE_BOOLEAN);
-
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on",
-			gtk_blist_handle, G_CALLBACK(buddy_signonoff_cb), NULL);
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off",
-			gtk_blist_handle, G_CALLBACK(buddy_signonoff_cb), NULL);
-	purple_signal_connect(purple_blist_get_handle(), "buddy-privacy-changed",
-			gtk_blist_handle, G_CALLBACK(pidgin_blist_update_privacy_cb), NULL);
-
-	purple_signal_connect_priority(purple_connections_get_handle(), "autojoin",
-	                               gtk_blist_handle, G_CALLBACK(autojoin_cb),
-	                               NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);
-}
-
-void
-pidgin_blist_uninit(void) {
-	g_hash_table_destroy(cached_emblems);
-
-	purple_signals_unregister_by_instance(pidgin_blist_get_handle());
-	purple_signals_disconnect_by_handle(pidgin_blist_get_handle());
-
-	gtkblist = NULL;
-}
-
 /**************************************************************************
  * GTK Buddy list GObject code
  **************************************************************************/
@@ -3887,310 +500,10 @@
 }
 
 static void
-pidgin_buddy_list_finalize(GObject *obj)
-{
-	PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(obj);
-
-	purple_signals_disconnect_by_handle(gtkblist);
-
-	gtk_window_destroy(GTK_WINDOW(gtkblist->window));
-
-	gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
-	g_clear_object(&gtkblist->treemodel);
-
-	purple_prefs_disconnect_by_handle(pidgin_blist_get_handle());
+pidgin_buddy_list_class_init(PidginBuddyListClass *klass) {
+	PurpleBuddyListClass *purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
 
-	G_OBJECT_CLASS(pidgin_buddy_list_parent_class)->finalize(obj);
-}
-
-static void
-pidgin_buddy_list_class_init(PidginBuddyListClass *klass)
-{
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-	PurpleBuddyListClass *purple_blist_class;
-
-	obj_class->finalize = pidgin_buddy_list_finalize;
-
-	purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
-	purple_blist_class->new_node = pidgin_blist_new_node;
-	purple_blist_class->show = pidgin_blist_show;
-	purple_blist_class->update = pidgin_blist_update;
-	purple_blist_class->remove = pidgin_blist_remove;
-	purple_blist_class->set_visible = pidgin_blist_set_visible;
 	purple_blist_class->request_add_buddy = pidgin_blist_request_add_buddy;
 	purple_blist_class->request_add_chat = pidgin_blist_request_add_chat;
 	purple_blist_class->request_add_group = pidgin_blist_request_add_group;
 }
-
-/*********************************************************************
- * Buddy List sorting functions                                      *
- *********************************************************************/
-
-GList *
-pidgin_blist_get_sort_methods(void)
-{
-	return pidgin_blist_sort_methods;
-}
-
-void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func)
-{
-	struct _PidginBlistSortMethod *method;
-
-	g_return_if_fail(id != NULL);
-	g_return_if_fail(name != NULL);
-	g_return_if_fail(func != NULL);
-
-	method = g_new0(struct _PidginBlistSortMethod, 1);
-	method->id = g_strdup(id);
-	method->name = g_strdup(name);
-	method->func = func;
-	pidgin_blist_sort_methods = g_list_append(pidgin_blist_sort_methods, method);
-	pidgin_blist_update_sort_methods();
-}
-
-void pidgin_blist_sort_method_unreg(const char *id)
-{
-	GList *l = pidgin_blist_sort_methods;
-
-	g_return_if_fail(id != NULL);
-
-	while(l) {
-		struct _PidginBlistSortMethod *method = l->data;
-		if(purple_strequal(method->id, id)) {
-			pidgin_blist_sort_methods = g_list_delete_link(pidgin_blist_sort_methods, l);
-			g_free(method->id);
-			g_free(method->name);
-			g_free(method);
-			break;
-		}
-		l = l->next;
-	}
-	pidgin_blist_update_sort_methods();
-}
-
-void pidgin_blist_sort_method_set(const char *id){
-	GList *l = pidgin_blist_sort_methods;
-
-	if(!id)
-		id = "none";
-
-	while (l && !purple_strequal(((struct _PidginBlistSortMethod*)l->data)->id, id))
-		l = l->next;
-
-	if (l) {
-		current_sort_method = l->data;
-	} else if (!current_sort_method) {
-		pidgin_blist_sort_method_set("none");
-		return;
-	}
-	if (purple_strequal(id, "none")) {
-		redo_buddy_list(purple_blist_get_default(), TRUE, FALSE);
-	} else {
-		redo_buddy_list(purple_blist_get_default(), FALSE, FALSE);
-	}
-}
-
-/******************************************
- ** Sort Methods
- ******************************************/
-
-static void
-sort_method_none(PurpleBlistNode *node, G_GNUC_UNUSED PurpleBuddyList *blist,
-                 GtkTreeIter parent_iter, GtkTreeIter *cur, GtkTreeIter *iter)
-{
-	PurpleBlistNode *sibling = node->prev;
-	GtkTreeIter sibling_iter;
-
-	if (cur != NULL) {
-		*iter = *cur;
-		return;
-	}
-
-	while (sibling && !get_iter_from_node(sibling, &sibling_iter)) {
-		sibling = sibling->prev;
-	}
-
-	gtk_tree_store_insert_after(gtkblist->treemodel, iter,
-			node->parent ? &parent_iter : NULL,
-			sibling ? &sibling_iter : NULL);
-}
-
-static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter)
-{
-	GtkTreeIter more_z;
-
-	const char *my_name;
-
-	if(PURPLE_IS_META_CONTACT(node)) {
-		my_name = purple_meta_contact_get_alias((PurpleMetaContact*)node);
-	} else if(PURPLE_IS_CHAT(node)) {
-		my_name = purple_chat_get_name((PurpleChat*)node);
-	} else {
-		sort_method_none(node, blist, groupiter, cur, iter);
-		return;
-	}
-
-	if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) {
-		gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0);
-		return;
-	}
-
-	do {
-		PurpleBlistNode *n;
-		const char *this_name;
-		int cmp;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1);
-
-		if(PURPLE_IS_META_CONTACT(n)) {
-			this_name = purple_meta_contact_get_alias((PurpleMetaContact*)n);
-		} else if(PURPLE_IS_CHAT(n)) {
-			this_name = purple_chat_get_name((PurpleChat*)n);
-		} else {
-			this_name = NULL;
-		}
-
-		cmp = purple_utf8_strcasecmp(my_name, this_name);
-
-		if(this_name && (cmp < 0 || (cmp == 0 && node < n))) {
-			if(cur) {
-				gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z);
-				*iter = *cur;
-				return;
-			} else {
-				gtk_tree_store_insert_before(gtkblist->treemodel, iter,
-						&groupiter, &more_z);
-				return;
-			}
-		}
-	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z));
-
-	if(cur) {
-		gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL);
-		*iter = *cur;
-		return;
-	} else {
-		gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter);
-		return;
-	}
-}
-
-static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter)
-{
-	GtkTreeIter more_z;
-
-	PurpleBuddy *my_buddy, *this_buddy;
-
-	if(PURPLE_IS_META_CONTACT(node)) {
-		my_buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
-	} else if(PURPLE_IS_CHAT(node)) {
-		if (cur != NULL) {
-			*iter = *cur;
-			return;
-		}
-
-		gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter);
-		return;
-	} else {
-		sort_method_alphabetical(node, blist, groupiter, cur, iter);
-		return;
-	}
-
-
-	if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) {
-		gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0);
-		return;
-	}
-
-	do {
-		PurpleBlistNode *n;
-		gint name_cmp;
-		gint presence_cmp;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1);
-
-		if(PURPLE_IS_META_CONTACT(n)) {
-			this_buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)n);
-		} else {
-			this_buddy = NULL;
-		}
-
-		name_cmp = purple_utf8_strcasecmp(
-			purple_meta_contact_get_alias(purple_buddy_get_contact(my_buddy)),
-			(this_buddy
-			 ? purple_meta_contact_get_alias(purple_buddy_get_contact(this_buddy))
-			 : NULL));
-
-		presence_cmp = purple_buddy_presence_compare(
-			PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(my_buddy)),
-			this_buddy ? PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(this_buddy)) : NULL);
-
-		if (this_buddy == NULL ||
-			(presence_cmp < 0 ||
-			 (presence_cmp == 0 &&
-			  (name_cmp < 0 || (name_cmp == 0 && node < n)))))
-		{
-			if (cur != NULL)
-			{
-				gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z);
-				*iter = *cur;
-				return;
-			}
-			else
-			{
-				gtk_tree_store_insert_before(gtkblist->treemodel, iter,
-											 &groupiter, &more_z);
-				return;
-			}
-		}
-	}
-	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel),
-									&more_z));
-
-	if (cur) {
-		gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL);
-		*iter = *cur;
-		return;
-	} else {
-		gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter);
-		return;
-	}
-}
-
-void
-pidgin_blist_update_sort_methods(void)
-{
-	GApplication *application = NULL;
-	GMenu *menu = NULL;
-	GList *l;
-
-	if(gtkblist == NULL || !PIDGIN_IS_CONTACT_LIST_WINDOW(gtkblist->window)) {
-		return;
-	}
-
-	/* get the menu and clear any existing entries. */
-	application = g_application_get_default();
-	menu = gtk_application_get_menu_by_id(GTK_APPLICATION(application),
-	                                      "sort-buddies");
-	g_menu_remove_all(menu);
-
-	/* Walk through the sort methods and add them to the menu. */
-	for (l = pidgin_blist_sort_methods; l; l = l->next) {
-		PidginBlistSortMethod *method = NULL;
-		GMenuItem *item = NULL;
-		GVariant *value = NULL;
-		gchar *action = NULL;
-
-		method = (PidginBlistSortMethod *)l->data;
-		value = g_variant_new_string(method->id);
-
-		action = g_action_print_detailed_name("blist.sort-method", value);
-		item = g_menu_item_new(method->name, action);
-
-		g_free(action);
-		g_variant_unref(value);
-
-		g_menu_append_item(menu, item);
-		g_object_unref(item);
-	}
-}
--- a/pidgin/gtkblist.h	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/gtkblist.h	Fri Feb 17 19:37:13 2023 -0600
@@ -26,8 +26,6 @@
 #ifndef _PIDGINBLIST_H_
 #define _PIDGINBLIST_H_
 
-#include <gtk/gtk.h>
-
 #include <purple.h>
 
 #define PIDGIN_TYPE_BUDDY_LIST (pidgin_buddy_list_get_type())
@@ -39,33 +37,11 @@
  **************************************************************************/
 /**
  * PidginBuddyList:
- * @vbox:              This is the vbox that everything important gets packed
- *                     into.  Your plugin might want to pack something in it
- *                     itself.  Go, plugins!
- * @treeview:          It's a treeview... d'uh.
- * @treemodel:         This is the treemodel.
- * @text_column:       Column
- * @refresh_timer:     The timer for refreshing every 30 seconds
- * @selected_node:     The currently selected node
- * @scrollbook:        Scrollbook for alerts
  *
- * Like, everything you need to know about the gtk buddy list
+ * The remnants of the buddy list, soon to be lost to the wind.
  */
 struct _PidginBuddyList {
 	PurpleBuddyList parent;
-
-	GtkWidget *window;
-	GtkWidget *vbox;
-
-	GtkWidget *treeview;
-	GtkTreeStore *treemodel;
-	GtkTreeViewColumn *text_column;
-
-	GtkCellRenderer *text_rend;
-
-	GActionGroup *action_group;
-
-	PurpleBlistNode *selected_node;
 };
 
 G_BEGIN_DECLS
@@ -78,141 +54,6 @@
                      PurpleBuddyList)
 
 /**
- * pidgin_blist_get_handle:
- *
- * Get the handle for the GTK blist system.
- *
- * Returns: the handle to the blist system
- */
-void *pidgin_blist_get_handle(void);
-
-/**
- * pidgin_blist_init:
- *
- * Initializes the GTK blist system.
- */
-void pidgin_blist_init(void);
-
-/**
- * pidgin_blist_uninit:
- *
- * Uninitializes the GTK blist system.
- */
-void pidgin_blist_uninit(void);
-
-/**
- * pidgin_blist_get_default_gtk_blist:
- *
- * Returns the default gtk buddy list
- *
- * There's normally only one buddy list window, but that isn't a necessity. This function
- * returns the PidginBuddyList we're most likely wanting to work with. This is slightly
- * cleaner than an externed global.
- *
- * Returns: (transfer none): The default GTK buddy list.
- */
-PidginBuddyList *pidgin_blist_get_default_gtk_blist(void);
-
-/**
- * pidgin_blist_refresh:
- * @list:   This is the core list that gets updated from
- *
- * Refreshes all the nodes of the buddy list.
- * This should only be called when something changes to affect most of the nodes (such as a ui preference changing)
- */
-void pidgin_blist_refresh(PurpleBuddyList *list);
-
-/**
- * pidgin_blist_get_emblem:
- * @node:   The node to return an emblem for
- *
- * Returns the blist emblem.
- *
- * This may be an existing pixbuf that has been given an additional ref,
- * so it shouldn't be modified.
- *
- * Returns: (transfer full): A GdkPixbuf for the emblem to show, or NULL
- */
-GdkPixbuf *
-pidgin_blist_get_emblem(PurpleBlistNode *node);
-
-/**
- * pidgin_blist_node_is_contact_expanded:
- * @node: The node in question.
- *
- * Returns a boolean indicating if @node is part of an expanded contact.
- *
- * This only makes sense for contact and buddy nodes. %FALSE is returned
- * for other types of nodes.
- *
- * Returns: A boolean indicating if @node is part of an expanded contact.
- */
-gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node);
-
-/**************************************************************************
- * GTK Buddy List sorting functions
- **************************************************************************/
-
-typedef void (*pidgin_blist_sort_function)(PurpleBlistNode *new, PurpleBuddyList *blist, GtkTreeIter group, GtkTreeIter *cur, GtkTreeIter *iter);
-
-/**
- * pidgin_blist_get_sort_methods:
- *
- * Gets the current list of sort methods.
- *
- * Returns: (transfer none) (element-type PidginBlistSortMethod): A GSlist of sort methods
- */
-GList *pidgin_blist_get_sort_methods(void);
-
-struct _PidginBlistSortMethod {
-	char *id;
-	char *name;
-	pidgin_blist_sort_function func;
-};
-
-typedef struct _PidginBlistSortMethod PidginBlistSortMethod;
-
-/**
- * pidgin_blist_sort_method_reg:
- * @id:   The unique ID of the sorting method
- * @name: The method's name.
- * @func: (scope call): A pointer to the function.
- *
- * Registers a buddy list sorting method.
- */
-void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func);
-
-/**
- * pidgin_blist_sort_method_unreg:
- * @id: The method's id
- *
- * Unregisters a buddy list sorting method.
- */
-void pidgin_blist_sort_method_unreg(const char *id);
-
-/**
- * pidgin_blist_sort_method_set:
- * @id: The method's id.
- *
- * Sets a buddy list sorting method.
- */
-void pidgin_blist_sort_method_set(const char *id);
-
-/**
- * pidgin_blist_setup_sort_methods:
- *
- * Sets up the programs default sort methods
- */
-void pidgin_blist_setup_sort_methods(void);
-
-/**
- * pidgin_blist_update_sort_methods:
- *
- * Updates the Sorting menu on the GTK buddy list window.
- */
-void pidgin_blist_update_sort_methods(void);
-
-/**
  * pidgin_blist_joinchat_is_showable:
  *
  * Determines if showing the join chat dialog is a valid action.
@@ -229,31 +70,6 @@
  */
 void pidgin_blist_joinchat_show(void);
 
-/**
- * pidgin_blist_get_name_markup:
- * @buddy: The buddy to return markup from
- * @selected:  Whether this buddy is selected. If TRUE, the markup will not change the color.
- * @aliased:  TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information
- *
- * Returns a buddy's Pango markup appropriate for setting in a GtkCellRenderer.
- *
- * Returns: The markup for this buddy
- */
-gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased);
-
-/**
- * pidgin_blist_query_tooltip_for_node:
- * @node: The buddy list to query a tooltip for.
- * @tooltip: The tooltip object to add a tooltip to.
- *
- * Queries and creates a custom tooltip for a buddy list node.
- *
- * Returns: %TRUE if a custom tooltip was added, %FALSE otherwise.
- *
- * Since: 3.0.0
- */
-gboolean pidgin_blist_query_tooltip_for_node(PurpleBlistNode *node, GtkTooltip *tooltip);
-
 G_END_DECLS
 
 #endif /* _PIDGINBLIST_H_ */
--- a/pidgin/gtkconv.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/gtkconv.c	Fri Feb 17 19:37:13 2023 -0600
@@ -1379,7 +1379,8 @@
 
 static gboolean
 pidgin_conv_userlist_query_tooltip(GtkWidget *widget, int x, int y,
-                                   gboolean keyboard_mode, GtkTooltip *tooltip,
+                                   gboolean keyboard_mode,
+                                   G_GNUC_UNUSED GtkTooltip *tooltip,
                                    gpointer userdata)
 {
 	PidginConversation *gtkconv = userdata;
@@ -1389,9 +1390,6 @@
 	GtkTreePath *path = NULL;
 	GtkTreeModel *model = NULL;
 	GtkTreeIter iter;
-	PurpleBlistNode *node = NULL;
-	PurpleProtocol *protocol = NULL;
-	char *who = NULL;
 
 	conv = gtkconv->active_conv;
 	account = purple_conversation_get_account(conv);
@@ -1425,20 +1423,6 @@
 		}
 	}
 
-	gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
-
-	protocol = purple_connection_get_protocol(connection);
-	node = (PurpleBlistNode*)purple_blist_find_buddy(account, who);
-	g_free(who);
-
-	if (node && protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
-		if (pidgin_blist_query_tooltip_for_node(node, tooltip)) {
-			gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path);
-			gtk_tree_path_free(path);
-			return TRUE;
-		}
-	}
-
 	gtk_tree_path_free(path);
 	return FALSE;
 }
@@ -2084,8 +2068,6 @@
 	{
 		char *title;
 		PurpleAccount *account = purple_conversation_get_account(conv);
-		PurpleBuddy *buddy = NULL;
-		char *markup = NULL;
 
 		if ((account == NULL) ||
 			!purple_account_is_connected(account) ||
@@ -2095,43 +2077,6 @@
 		else
 			title = g_strdup(purple_conversation_get_title(conv));
 
-		if (PURPLE_IS_IM_CONVERSATION(conv)) {
-			buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
-			if (buddy) {
-				markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE);
-			} else {
-				markup = title;
-			}
-		} else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
-			const char *topic = gtkconv->topic_text
-				? gtk_editable_get_text(GTK_EDITABLE(gtkconv->topic_text))
-				: NULL;
-			const char *title = purple_conversation_get_title(conv);
-			const char *name = purple_conversation_get_name(conv);
-
-			char *topic_esc, *unaliased, *unaliased_esc, *title_esc;
-
-			topic_esc = topic ? g_markup_escape_text(topic, -1) : NULL;
-			unaliased = g_utf8_collate(title, name) ? g_strdup_printf("(%s)", name) : NULL;
-			unaliased_esc = unaliased ? g_markup_escape_text(unaliased, -1) : NULL;
-			title_esc = g_markup_escape_text(title, -1);
-
-			markup = g_strdup_printf("%s%s<span size='smaller'>%s</span>%s<span size='smaller'>%s</span>",
-						title_esc,
-						unaliased_esc ? " " : "",
-						unaliased_esc ? unaliased_esc : "",
-						topic_esc  && *topic_esc ? "\n" : "",
-						topic_esc ? topic_esc : "");
-
-			g_free(title_esc);
-			g_free(topic_esc);
-			g_free(unaliased);
-			g_free(unaliased_esc);
-		}
-
-		if (title != markup)
-			g_free(markup);
-
 		if (pidgin_display_window_conversation_is_selected(displaywin, conv)) {
 			const char* current_title = gtk_window_get_title(GTK_WINDOW(win));
 			if (current_title == NULL || !purple_strequal(current_title, title)) {
--- a/pidgin/pidginapplication.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/pidginapplication.c	Fri Feb 17 19:37:13 2023 -0600
@@ -839,9 +839,6 @@
 	/* load plugins we had when we quit */
 	purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
 
-	/* gk 20201008: this needs to be moved to the buddy list initialization. */
-	pidgin_blist_setup_sort_methods();
-
 	gtk_window_set_default_icon_name("pidgin");
 
 	g_free(opt_config_dir_arg);
--- a/pidgin/pidginui.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/pidginui.c	Fri Feb 17 19:37:13 2023 -0600
@@ -187,7 +187,6 @@
 
 	pidgin_accounts_init();
 	pidgin_request_init();
-	pidgin_blist_init();
 	pidgin_conversations_init();
 	pidgin_commands_init();
 	pidgin_xfers_init();
@@ -206,7 +205,6 @@
 	pidgin_notify_uninit();
 	pidgin_commands_uninit();
 	pidgin_conversations_uninit();
-	pidgin_blist_uninit();
 	pidgin_request_uninit();
 	pidgin_accounts_uninit();
 	pidgin_xfers_uninit();
--- a/pidgin/plugins/gtkbuddynote/gtkbuddynote.c	Fri Feb 17 19:34:51 2023 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * GtkBuddyNote - Store notes on particular buddies
- * Copyright (C) 2007 Etan Reisner <deryni@pidgin.im>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
- */
-
-#include <glib/gi18n.h>
-
-#include <purple.h>
-
-#include <pidgin.h>
-
-static void
-append_to_tooltip(PurpleBlistNode *node, GString *text, gboolean full)
-{
-	if (full) {
-		const gchar *note = purple_blist_node_get_string(node, "notes");
-
-		if ((note != NULL) && (*note != '\0')) {
-			char *tmp, *esc;
-			purple_markup_html_to_xhtml(note, NULL, &tmp);
-			esc = g_markup_escape_text(tmp, -1);
-			g_free(tmp);
-			g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"),
-			                       esc);
-			g_free(esc);
-		}
-	}
-}
-
-static GPluginPluginInfo *
-gtk_buddy_note_query(GError **error)
-{
-	const gchar * const authors[] = {
-		"Etan Reisner <deryni@pidgin.im>",
-		NULL
-	};
-
-	const gchar * const dependencies[] = {
-		"core-plugin_pack-buddynote",
-		NULL
-	};
-
-	return pidgin_plugin_info_new(
-		"id",            "gtkbuddynote",
-		"name",          N_("Buddy Note Tooltips"),
-		"version",       DISPLAY_VERSION,
-		"category",      N_("User interface"),
-		"summary",       N_("Shows stored buddy notes on the buddy's tooltip."),
-		"description",   N_("Shows stored buddy notes on the buddy's tooltip."),
-		"authors",       authors,
-		"website",       PURPLE_WEBSITE,
-		"abi-version",   PURPLE_ABI_VERSION,
-		"dependencies",  dependencies,
-		NULL
-	);
-}
-
-static gboolean
-gtk_buddy_note_load(GPluginPlugin *plugin, GError **error)
-{
-	purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip",
-	                      plugin, G_CALLBACK(append_to_tooltip), NULL);
-	return TRUE;
-}
-
-static gboolean
-gtk_buddy_note_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
-{
-	return TRUE;
-}
-
-GPLUGIN_NATIVE_PLUGIN_DECLARE(gtk_buddy_note)
--- a/pidgin/plugins/gtkbuddynote/meson.build	Fri Feb 17 19:34:51 2023 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-gtkbuddynote = library('gtkbuddynote', 'gtkbuddynote.c',
-    c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-BuddyNote"'],
-    dependencies : [libpurple_dep, libpidgin_dep, glib],
-    name_prefix : '',
-    install : true, install_dir : PIDGIN_PLUGINDIR)
-
-devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir())
--- a/pidgin/plugins/meson.build	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/plugins/meson.build	Fri Feb 17 19:37:13 2023 -0600
@@ -1,6 +1,5 @@
 subdir('disco')
 subdir('gestures')
-subdir('gtkbuddynote')
 subdir('iconaway')
 subdir('notify')
 subdir('spellchk')
--- a/pidgin/plugins/transparency/transparency.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/plugins/transparency/transparency.c	Fri Feb 17 19:37:13 2023 -0600
@@ -61,20 +61,6 @@
  *  CODE
  */
 
-static GtkWidget *
-get_buddy_list_window(void) {
-	PurpleBuddyList *purple_blist = NULL;
-
-	purple_blist = purple_blist_get_default();
-	if(PIDGIN_IS_BUDDY_LIST(purple_blist)) {
-		PidginBuddyList *pidgin_blist = PIDGIN_BUDDY_LIST(purple_blist);
-
-		return pidgin_blist->window;
-	}
-
-	return NULL;
-}
-
 /* Set window transparency level */
 static void
 set_wintrans(GtkWidget *window, int alpha, gboolean enabled)
@@ -143,35 +129,6 @@
 	g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, NULL);
 }
 
-/* When buddy list window is focused,
- * if we're only transparent when unfocused, deal with transparency */
-static void
-focus_blist_win_cb(GtkEventControllerFocus *self, gpointer data)
-{
-	GtkWidget *window = NULL;
-	gboolean enter = GPOINTER_TO_INT(data);
-	GSettings *settings = NULL;
-
-	settings = g_settings_new_with_backend(OPT_SCHEMA,
-	                                       purple_core_get_settings_backend());
-
-	if(!g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) {
-		g_object_unref(settings);
-		return;
-	}
-	if(!g_settings_get_boolean(settings, OPT_WINTRANS_BL_ONFOCUS)) {
-		g_object_unref(settings);
-		return;
-	}
-
-	window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
-	set_wintrans(window,
-	             g_settings_get_int(settings, OPT_WINTRANS_BL_ALPHA),
-	             !enter);
-
-	g_object_unref(settings);
-}
-
 static void change_alpha(GtkWidget *w, gpointer data) {
 	int alpha = gtk_range_get_value(GTK_RANGE(w));
 	GSettings *settings = NULL;
@@ -208,16 +165,6 @@
 	remove_focus_controller_from_conv_win(GTK_WIDGET(window));
 }
 
-static void set_blist_trans(GtkWidget *w, const char *pref) {
-	GtkWidget *window = get_buddy_list_window();
-	gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
-	purple_prefs_set_bool(pref, enabled);
-	if (window != NULL) {
-		set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
-			purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED));
-	}
-}
-
 static void
 remove_slider(GtkWidget *slider_frame) {
 	gtk_widget_unparent(slider_frame);
@@ -392,37 +339,6 @@
 	add_focus_controller_to_conv_win(GTK_WIDGET(window));
 }
 
-static void
-blist_created_cb(PurpleBuddyList *purple_blist, gpointer data) {
-	GtkWidget *window = get_buddy_list_window();
-	GSettings *settings = NULL;
-
-	settings = g_settings_new_with_backend(OPT_SCHEMA,
-	                                       purple_core_get_settings_backend());
-
-	if (window != NULL) {
-		GtkEventController *focus = NULL;
-
-		if (g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) {
-			set_wintrans(window,
-			             g_settings_get_int(settings, OPT_WINTRANS_BL_ALPHA),
-			             TRUE);
-		}
-
-		focus = gtk_event_controller_focus_new();
-
-		g_signal_connect(focus, "enter", G_CALLBACK(focus_blist_win_cb),
-		                 GINT_TO_POINTER(TRUE));
-		g_signal_connect(focus, "leave", G_CALLBACK(focus_blist_win_cb),
-		                 GINT_TO_POINTER(FALSE));
-
-		gtk_widget_add_controller(window, focus);
-		g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus);
-	}
-
-	g_object_unref(settings);
-}
-
 static void alpha_change(GtkWidget *w, gpointer data) {
 	GApplication *application = NULL;
 	GList *wins;
@@ -454,13 +370,6 @@
 	purple_prefs_set_int(pref, alpha);
 }
 
-static void bl_alpha_change(GtkWidget *w, gpointer data) {
-	GtkWidget *window = get_buddy_list_window();
-	if (window != NULL) {
-		change_alpha(w, window);
-	}
-}
-
 static void
 update_existing_convs(void) {
 	GApplication *application = NULL;
@@ -485,7 +394,7 @@
 static GtkWidget *
 get_config_frame(PurplePlugin *plugin) {
 	GtkWidget *ret;
-	GtkWidget *imtransbox, *bltransbox;
+	GtkWidget *imtransbox;
 	GtkWidget *hbox;
 	GtkWidget *label, *slider;
 	GtkWidget *button;
@@ -543,22 +452,6 @@
 
 	gtk_box_append(GTK_BOX(trans_box), hbox);
 
-	/* Buddy List trans options */
-	bltransbox = pidgin_make_frame (ret, _("Buddy List Window"));
-	button = pidgin_prefs_checkbox(_("_Buddy List window transparency"),
-		OPT_WINTRANS_BL_ENABLED, bltransbox);
-	g_signal_connect(G_OBJECT(button), "clicked",
-		G_CALLBACK(set_blist_trans),
-		(gpointer) OPT_WINTRANS_BL_ENABLED);
-
-	trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
-	g_object_bind_property(button, "active", trans_box, "sensitive",
-			G_BINDING_SYNC_CREATE);
-	button = pidgin_prefs_checkbox(
-		_("Remove Buddy List window transparency on focus"),
-		OPT_WINTRANS_BL_ONFOCUS, trans_box);
-	gtk_box_append(GTK_BOX(bltransbox), trans_box);
-
 	/* IM transparency slider */
 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
 
@@ -569,13 +462,6 @@
 	gtk_range_set_value(GTK_RANGE(slider),
 		purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA));
 
-	g_signal_connect(G_OBJECT(slider), "value-changed",
-		G_CALLBACK(bl_alpha_change), NULL);
-	focus = gtk_event_controller_focus_new();
-	g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int),
-	                 (gpointer)OPT_WINTRANS_BL_ALPHA);
-	gtk_widget_add_controller(slider, focus);
-
 	gtk_box_append(GTK_BOX(hbox), slider);
 
 	gtk_box_append(GTK_BOX(trans_box), hbox);
@@ -608,7 +494,6 @@
 static gboolean
 transparency_load(GPluginPlugin *plugin, GError **error) {
 	GApplication *application = NULL;
-	GtkWidget *window = NULL;
 
 	application = g_application_get_default();
 	g_signal_connect(application, "window-added",
@@ -618,45 +503,15 @@
 
 	update_existing_convs();
 
-	window = get_buddy_list_window();
-	if(window != NULL) {
-		blist_created_cb(NULL, NULL);
-	} else {
-		purple_signal_connect(pidgin_blist_get_handle(),
-			"gtkblist-created", plugin,
-			G_CALLBACK(blist_created_cb), NULL);
-	}
-
 	return TRUE;
 }
 
 static gboolean
 transparency_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) {
-	GtkWidget *window = NULL;
-
 	purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading transparency plugin\n");
 
 	remove_convs_wintrans(TRUE);
 
-	window = get_buddy_list_window();
-	if (window != NULL) {
-		GSettings *settings = NULL;
-		GtkEventController *focus = NULL;
-
-		settings = g_settings_new_with_backend(OPT_SCHEMA,
-		                                       purple_core_get_settings_backend());
-		if (g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) {
-			set_wintrans(window, 0, FALSE);
-		}
-		g_object_unref(settings);
-
-		/* Remove the focus cbs */
-		focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY);
-		if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) {
-			gtk_widget_remove_controller(window, focus);
-		}
-	}
-
 	return TRUE;
 }
 
--- a/pidgin/win32/gtkwin32dep.c	Fri Feb 17 19:34:51 2023 -0600
+++ b/pidgin/win32/gtkwin32dep.c	Fri Feb 17 19:37:13 2023 -0600
@@ -73,12 +73,13 @@
 static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 	if (msg == PIDGIN_WM_FOCUS_REQUEST) {
-		PidginBuddyList *blist;
-		purple_debug_info("winpidgin", "Got external Buddy List focus request.");
-		blist = pidgin_blist_get_default_gtk_blist();
-		if (blist != NULL && blist->window != NULL) {
-			gtk_window_present(GTK_WINDOW(blist->window));
-		}
+		GtkWidget *widget = NULL;
+
+		purple_debug_info("winpidgin", "Got external focus request.");
+
+		widget = pidgin_display_window_get_default();
+		gtk_window_present(GTK_WINDOW(widget));
+
 		return TRUE;
 	} else if (msg == PIDGIN_WM_PROTOCOL_HANDLE) {
 		char *proto_msg = (char *) lparam;

mercurial