Convert UI ops to PurpleBuddyListClass methods.

Wed, 03 Jul 2019 20:09:04 -0400

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Wed, 03 Jul 2019 20:09:04 -0400
changeset 39693
86f5c1fef140
parent 39692
6e8ebbefba1e
child 39694
4aed5fad4da9

Convert UI ops to PurpleBuddyListClass methods.

finch/gntblist.c file | annotate | diff | comparison | revisions
finch/gntblist.h file | annotate | diff | comparison | revisions
finch/gntui.c file | annotate | diff | comparison | revisions
libpurple/buddylist.c file | annotate | diff | comparison | revisions
libpurple/buddylist.h file | annotate | diff | comparison | revisions
pidgin/gtkblist.c file | annotate | diff | comparison | revisions
pidgin/gtkblist.h file | annotate | diff | comparison | revisions
pidgin/libpidgin.c file | annotate | diff | comparison | revisions
--- a/finch/gntblist.c	Wed Jul 03 04:02:46 2019 -0400
+++ b/finch/gntblist.c	Wed Jul 03 20:09:04 2019 -0400
@@ -65,8 +65,9 @@
 
 #define SHOW_EMPTY_GROUP_TIMEOUT  60
 
-typedef struct
-{
+struct _FinchBuddyList {
+	PurpleBuddyList parent;
+
 	GntWidget *window;
 	GntWidget *tree;
 
@@ -96,7 +97,7 @@
 	guint new_group_timeout;
 
 	FinchBlistManager *manager;
-} FinchBlist;
+};
 
 typedef struct
 {
@@ -122,23 +123,24 @@
 	} u;
 } StatusBoxItem;
 
-static FinchBlist *ggblist;
-
-static void add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist);
-static void add_contact(PurpleContact *contact, FinchBlist *ggblist);
-static void add_group(PurpleGroup *group, FinchBlist *ggblist);
-static void add_chat(PurpleChat *chat, FinchBlist *ggblist);
-static void add_node(PurpleBlistNode *node, FinchBlist *ggblist);
+static FinchBuddyList *ggblist;
+
+static void add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist);
+static void add_contact(PurpleContact *contact, FinchBuddyList *ggblist);
+static void add_group(PurpleGroup *group, FinchBuddyList *ggblist);
+static void add_chat(PurpleChat *chat, FinchBuddyList *ggblist);
+static void add_node(PurpleBlistNode *node, FinchBuddyList *ggblist);
 static void node_update(PurpleBuddyList *list, PurpleBlistNode *node);
-static void draw_tooltip(FinchBlist *ggblist);
+static void draw_tooltip(FinchBuddyList *ggblist);
 static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full);
 static gboolean remove_typing_cb(gpointer null);
-static void remove_peripherals(FinchBlist *ggblist);
+static void remove_peripherals(FinchBuddyList *ggblist);
 static const char * get_display_name(PurpleBlistNode *node);
 static void savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old);
 static void blist_show(PurpleBuddyList *list);
-static void update_node_display(PurpleBlistNode *buddy, FinchBlist *ggblist);
-static void update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist);
+static void update_node_display(PurpleBlistNode *buddy,
+                                FinchBuddyList *ggblist);
+static void update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist);
 static gboolean account_autojoin_cb(PurpleConnection *pc, gpointer null);
 static void finch_request_add_buddy(PurpleBuddyList *list,
                                     PurpleAccount *account,
@@ -405,7 +407,7 @@
 }
 
 static void
-add_node(PurpleBlistNode *node, FinchBlist *ggblist)
+add_node(PurpleBlistNode *node, FinchBuddyList *ggblist)
 {
 	if (purple_blist_node_get_ui_data(node))
 		return;
@@ -431,7 +433,7 @@
 }
 
 static void
-remove_tooltip(FinchBlist *ggblist)
+remove_tooltip(FinchBuddyList *ggblist)
 {
 	gnt_widget_destroy(ggblist->tooltip);
 	ggblist->tooltip = NULL;
@@ -441,7 +443,7 @@
 static void
 node_remove(PurpleBuddyList *list, PurpleBlistNode *node)
 {
-	FinchBlist *ggblist = purple_blist_get_ui_data();
+	FinchBuddyList *ggblist = purple_blist_get_ui_data();
 	PurpleBlistNode *parent;
 
 	if (ggblist == NULL || purple_blist_node_get_ui_data(node) == NULL)
@@ -525,7 +527,7 @@
 	if (ggblist)
 		return;
 
-	ggblist = g_new0(FinchBlist, 1);
+	ggblist = FINCH_BUDDY_LIST(list);
 	purple_blist_set_ui_data(ggblist);
 	ggblist->manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping"));
 	if (!ggblist->manager)
@@ -538,7 +540,6 @@
 		return;
 
 	gnt_widget_destroy(ggblist->window);
-	g_free(ggblist);
 	ggblist = NULL;
 }
 
@@ -793,24 +794,6 @@
 			NULL, NULL);
 }
 
-static PurpleBlistUiOps blist_ui_ops =
-{
-	new_list,
-	new_node,
-	blist_show,
-	node_update,
-	node_remove,
-	destroy_list,
-	NULL,
-	finch_request_add_buddy,
-	finch_request_add_chat,
-	finch_request_add_group,
-	NULL,
-	NULL,
-	NULL,
-	NULL, NULL, NULL, NULL
-};
-
 static gpointer
 finch_blist_get_handle(void)
 {
@@ -820,7 +803,7 @@
 }
 
 static void
-add_group(PurpleGroup *group, FinchBlist *ggblist)
+add_group(PurpleGroup *group, FinchBuddyList *ggblist)
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode *)group;
@@ -893,7 +876,7 @@
 }
 
 static void
-add_chat(PurpleChat *chat, FinchBlist *ggblist)
+add_chat(PurpleChat *chat, FinchBuddyList *ggblist)
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode *)chat;
@@ -910,7 +893,7 @@
 }
 
 static void
-add_contact(PurpleContact *contact, FinchBlist *ggblist)
+add_contact(PurpleContact *contact, FinchBuddyList *ggblist)
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode*)contact;
@@ -933,7 +916,7 @@
 }
 
 static void
-add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist)
+add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist)
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode *)buddy;
@@ -954,13 +937,8 @@
 		blist_update_row_flags((PurpleBlistNode*)contact);
 }
 
-PurpleBlistUiOps *finch_blist_get_ui_ops()
-{
-	return &blist_ui_ops;
-}
-
 static void
-selection_activate(GntWidget *widget, FinchBlist *ggblist)
+selection_activate(GntWidget *widget, FinchBuddyList *ggblist)
 {
 	GntTree *tree = GNT_TREE(ggblist->tree);
 	PurpleBlistNode *node = gnt_tree_get_selection_data(tree);
@@ -1560,13 +1538,13 @@
 }
 
 static void
-context_menu_destroyed(GntWidget *widget, FinchBlist *ggblist)
+context_menu_destroyed(GntWidget *widget, FinchBuddyList *ggblist)
 {
 	ggblist->context = NULL;
 }
 
 static void
-draw_context_menu(FinchBlist *ggblist)
+draw_context_menu(FinchBuddyList *ggblist)
 {
 	PurpleBlistNode *node = NULL;
 	GntWidget *context = NULL;
@@ -1721,7 +1699,7 @@
 }
 
 static gboolean
-draw_tooltip_real(FinchBlist *ggblist)
+draw_tooltip_real(FinchBuddyList *ggblist)
 {
 	PurpleBlistNode *node;
 	int x, y, top, width, w, h;
@@ -1791,7 +1769,7 @@
 }
 
 static void
-draw_tooltip(FinchBlist *ggblist)
+draw_tooltip(FinchBuddyList *ggblist)
 {
 	/* When an account has signed off, it removes one buddy at a time.
 	 * Drawing the tooltip after removing each buddy is expensive. On
@@ -1804,21 +1782,22 @@
 }
 
 static void
-selection_changed(GntWidget *widget, gpointer old, gpointer current, FinchBlist *ggblist)
+selection_changed(GntWidget *widget, gpointer old, gpointer current,
+                  FinchBuddyList *ggblist)
 {
 	remove_peripherals(ggblist);
 	draw_tooltip(ggblist);
 }
 
 static gboolean
-context_menu(GntWidget *widget, FinchBlist *ggblist)
+context_menu(GntWidget *widget, FinchBuddyList *ggblist)
 {
 	draw_context_menu(ggblist);
 	return TRUE;
 }
 
 static gboolean
-key_pressed(GntWidget *widget, const char *text, FinchBlist *ggblist)
+key_pressed(GntWidget *widget, const char *text, FinchBuddyList *ggblist)
 {
 	if (text[0] == 27 && text[1] == 0) {
 		/* Escape was pressed */
@@ -1845,14 +1824,14 @@
 }
 
 static void
-update_node_display(PurpleBlistNode *node, FinchBlist *ggblist)
+update_node_display(PurpleBlistNode *node, FinchBuddyList *ggblist)
 {
 	GntTextFormatFlags flag = get_blist_node_flag(node);
 	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
 }
 
 static void
-update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist)
+update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist)
 {
 	PurpleContact *contact;
 
@@ -1870,19 +1849,21 @@
 }
 
 static void
-buddy_status_changed(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *now, FinchBlist *ggblist)
+buddy_status_changed(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *now,
+                     FinchBuddyList *ggblist)
 {
 	update_buddy_display(buddy, ggblist);
 }
 
 static void
-buddy_idle_changed(PurpleBuddy *buddy, int old, int new, FinchBlist *ggblist)
+buddy_idle_changed(PurpleBuddy *buddy, int old, int new,
+                   FinchBuddyList *ggblist)
 {
 	update_buddy_display(buddy, ggblist);
 }
 
 static void
-remove_peripherals(FinchBlist *ggblist)
+remove_peripherals(FinchBuddyList *ggblist)
 {
 	if (ggblist->tooltip)
 		remove_tooltip(ggblist);
@@ -1930,7 +1911,6 @@
 	if (ggblist->new_group)
 		g_list_free(ggblist->new_group);
 
-	g_free(ggblist);
 	ggblist = NULL;
 }
 
@@ -3165,6 +3145,33 @@
 }
 
 /**************************************************************************
+ * GObject code
+ **************************************************************************/
+G_DEFINE_TYPE(FinchBuddyList, finch_buddy_list, PURPLE_TYPE_BUDDY_LIST)
+
+static void
+finch_buddy_list_init(FinchBuddyList *self)
+{
+}
+
+static void
+finch_buddy_list_class_init(FinchBuddyListClass *klass)
+{
+	PurpleBuddyListClass *purple_blist_class;
+
+	purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
+	purple_blist_class->new_list = new_list;
+	purple_blist_class->new_node = new_node;
+	purple_blist_class->show = blist_show;
+	purple_blist_class->update = node_update;
+	purple_blist_class->remove = node_remove;
+	purple_blist_class->destroy = destroy_list;
+	purple_blist_class->request_add_buddy = finch_request_add_buddy;
+	purple_blist_class->request_add_chat = finch_request_add_chat;
+	purple_blist_class->request_add_group = finch_request_add_group;
+}
+
+/**************************************************************************
  * GBoxed code
  **************************************************************************/
 static FinchBlistManager *
--- a/finch/gntblist.h	Wed Jul 03 04:02:46 2019 -0400
+++ b/finch/gntblist.h	Wed Jul 03 20:09:04 2019 -0400
@@ -32,6 +32,7 @@
 #include "gnt.h"
 #include "gnttree.h"
 
+#define FINCH_TYPE_BUDDY_LIST (finch_buddy_list_get_type())
 #define FINCH_TYPE_BLIST_MANAGER (finch_blist_manager_get_type())
 
 /**********************************************************************
@@ -74,14 +75,8 @@
  */
 GType finch_blist_manager_get_type(void);
 
-/**
- * finch_blist_get_ui_ops:
- *
- * Get the ui-functions.
- *
- * Returns: The PurpleBlistUiOps structure populated with the appropriate functions.
- */
-PurpleBlistUiOps * finch_blist_get_ui_ops(void);
+G_DECLARE_FINAL_TYPE(FinchBuddyList, finch_buddy_list, FINCH, BUDDY_LIST,
+                     PurpleBuddyList)
 
 /**
  * finch_blist_init:
--- a/finch/gntui.c	Wed Jul 03 04:02:46 2019 -0400
+++ b/finch/gntui.c	Wed Jul 03 20:09:04 2019 -0400
@@ -65,7 +65,7 @@
 
 	/* Initialize the buddy list */
 	finch_blist_init();
-	purple_blist_set_ui_ops(finch_blist_get_ui_ops());
+	purple_blist_set_ui(FINCH_TYPE_BUDDY_LIST);
 
 	/* Initialize sound */
 	purple_sound_set_ui_ops(finch_sound_get_ui_ops());
@@ -122,7 +122,7 @@
 	purple_connections_set_ui_ops(NULL);
 	finch_connections_uninit();
 
-	purple_blist_set_ui_ops(NULL);
+	purple_blist_set_ui(G_TYPE_INVALID);
 	finch_blist_uninit();
 
 	purple_conversations_set_ui_ops(NULL);
--- a/libpurple/buddylist.c	Wed Jul 03 04:02:46 2019 -0400
+++ b/libpurple/buddylist.c	Wed Jul 03 20:09:04 2019 -0400
@@ -40,8 +40,7 @@
 	GHashTable *buddies;  /* Every buddy in this list */
 } PurpleBuddyListPrivate;
 
-static PurpleBlistUiOps *blist_ui_ops = NULL;
-
+static GType buddy_list_type = G_TYPE_INVALID;
 static PurpleBuddyList *purplebuddylist = NULL;
 
 G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddyList, purple_buddy_list, G_TYPE_OBJECT);
@@ -456,11 +455,15 @@
 
 void purple_blist_schedule_save()
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 
 	/* Save everything */
-	if (ops && ops->save_account) {
-		ops->save_account(purplebuddylist, NULL);
+	if (klass && klass->save_account) {
+		klass->save_account(purplebuddylist, NULL);
 	}
 }
 
@@ -709,13 +712,19 @@
  *****************************************************************************/
 
 void
+purple_blist_set_ui(GType type)
+{
+	g_return_if_fail(g_type_is_a(type, PURPLE_TYPE_BUDDY_LIST) ||
+	                 type == G_TYPE_INVALID);
+	buddy_list_type = type;
+}
+
+void
 purple_blist_boot(void)
 {
-	PurpleBlistUiOps *ui_ops;
 	GList *account;
-	PurpleBuddyList *gbl = g_object_new(PURPLE_TYPE_BUDDY_LIST, NULL);
-
-	ui_ops = purple_blist_get_ui_ops();
+	PurpleBuddyList *gbl = g_object_new(buddy_list_type, NULL);
+	PurpleBuddyListClass *klass = PURPLE_BUDDY_LIST_GET_CLASS(gbl);
 
 	buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
 					 NULL, (GDestroyNotify)g_hash_table_destroy);
@@ -729,10 +738,11 @@
 		purple_blist_buddies_cache_add_account(account->data);
 	}
 
-	if (ui_ops != NULL && ui_ops->new_list != NULL)
-		ui_ops->new_list(gbl);
+	purplebuddylist = gbl;
 
-	purplebuddylist = gbl;
+	if (klass && klass->new_list) {
+		klass->new_list(gbl);
+	}
 
 	load_blist();
 }
@@ -812,18 +822,26 @@
 
 void purple_blist_show()
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 
-	if (ops && ops->show)
-		ops->show(purplebuddylist);
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+
+	if (klass && klass->show) {
+		klass->show(purplebuddylist);
+	}
 }
 
 void purple_blist_set_visible(gboolean show)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 
-	if (ops && ops->set_visible)
-		ops->set_visible(purplebuddylist, show);
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+
+	if (klass && klass->set_visible) {
+		klass->set_visible(purplebuddylist, show);
+	}
 }
 
 void purple_blist_update_buddies_cache(PurpleBuddy *buddy, const char *new_name)
@@ -875,10 +893,12 @@
 void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
 {
 	PurpleBlistNode *cnode = PURPLE_BLIST_NODE(chat);
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 	PurpleCountingNode *group_counter;
 
 	g_return_if_fail(PURPLE_IS_CHAT(chat));
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 
 	if (node == NULL) {
 		if (group == NULL)
@@ -916,12 +936,13 @@
 		if (cnode->parent->child == cnode)
 			cnode->parent->child = cnode->next;
 
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, cnode);
+		if (klass && klass->remove) {
+			klass->remove(purplebuddylist, cnode);
+		}
 		/* ops->remove() cleaned up the cnode's ui_data, so we need to
 		 * reinitialize it */
-		if (ops && ops->new_node) {
-			ops->new_node(purplebuddylist, cnode);
+		if (klass && klass->new_node) {
+			klass->new_node(purplebuddylist, cnode);
 		}
 	}
 
@@ -953,12 +974,14 @@
 		}
 	}
 
-	if (ops) {
-		if (ops->save_node) {
-			ops->save_node(purplebuddylist, cnode);
+	if (klass) {
+		if (klass->save_node) {
+			klass->save_node(purplebuddylist, cnode);
 		}
-		if (ops->update)
-			ops->update(purplebuddylist, PURPLE_BLIST_NODE(cnode));
+		if (klass->update) {
+			klass->update(purplebuddylist,
+			              PURPLE_BLIST_NODE(cnode));
+		}
 	}
 
 	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
@@ -967,19 +990,21 @@
 
 void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
 {
+	PurpleBuddyListClass *klass = NULL;
+	PurpleBuddyListPrivate *priv = NULL;
 	PurpleBlistNode *cnode, *bnode;
 	PurpleCountingNode *contact_counter, *group_counter;
 	PurpleGroup *g;
 	PurpleContact *c;
 	PurpleAccount *account;
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
 	struct _purple_hbuddy *hb, *hb2;
 	GHashTable *account_buddies;
-	PurpleBuddyListPrivate *priv =
-			purple_buddy_list_get_instance_private(purplebuddylist);
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_BUDDY(buddy));
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	priv = purple_buddy_list_get_instance_private(purplebuddylist);
 	bnode = PURPLE_BLIST_NODE(buddy);
 	account = purple_buddy_get_account(buddy);
 
@@ -1040,8 +1065,9 @@
 		if (bnode->parent->child == bnode)
 			bnode->parent->child = bnode->next;
 
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, bnode);
+		if (klass && klass->remove) {
+			klass->remove(purplebuddylist, bnode);
+		}
 
 		if (bnode->parent->parent != (PurpleBlistNode*)g) {
 			struct _purple_hbuddy hb;
@@ -1060,8 +1086,9 @@
 		} else {
 			purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
 
-			if (ops && ops->update)
-				ops->update(purplebuddylist, bnode->parent);
+			if (klass && klass->update) {
+				klass->update(purplebuddylist, bnode->parent);
+			}
 		}
 	}
 
@@ -1114,13 +1141,15 @@
 
 	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
 
-	if (ops) {
-		if (ops->save_node) {
-			ops->save_node(purplebuddylist,
-			               (PurpleBlistNode *)buddy);
+	if (klass) {
+		if (klass->save_node) {
+			klass->save_node(purplebuddylist,
+			                 (PurpleBlistNode *)buddy);
 		}
-		if (ops->update)
-			ops->update(purplebuddylist, PURPLE_BLIST_NODE(buddy));
+		if (klass->update) {
+			klass->update(purplebuddylist,
+			              PURPLE_BLIST_NODE(buddy));
+		}
 	}
 
 	/* Signal that the buddy has been added */
@@ -1130,18 +1159,21 @@
 
 void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
+	PurpleBuddyListPrivate *priv = NULL;
 	PurpleGroup *g;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleCountingNode *contact_counter, *group_counter;
-	PurpleBuddyListPrivate *priv =
-			purple_buddy_list_get_instance_private(purplebuddylist);
 
 	g_return_if_fail(PURPLE_IS_CONTACT(contact));
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 
 	if (PURPLE_BLIST_NODE(contact) == node)
 		return;
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	priv = purple_buddy_list_get_instance_private(purplebuddylist);
+
 	if (node && (PURPLE_IS_CONTACT(node) ||
 				PURPLE_IS_CHAT(node)))
 		g = PURPLE_GROUP(node->parent);
@@ -1227,11 +1259,12 @@
 			purple_counting_node_change_current_size(group_counter, -1);
 		purple_counting_node_change_total_size(group_counter, -1);
 
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, cnode);
+		if (klass && klass->remove) {
+			klass->remove(purplebuddylist, cnode);
+		}
 
-		if (ops && ops->remove_node) {
-			ops->remove_node(purplebuddylist, cnode);
+		if (klass && klass->remove_node) {
+			klass->remove_node(purplebuddylist, cnode);
 		}
 	}
 
@@ -1261,36 +1294,37 @@
 		purple_counting_node_change_current_size(group_counter, +1);
 	purple_counting_node_change_total_size(group_counter, +1);
 
-	if (ops && ops->save_node)
-	{
+	if (klass && klass->save_node) {
 		if (cnode->child) {
-			ops->save_node(purplebuddylist, cnode);
+			klass->save_node(purplebuddylist, cnode);
 		}
 		for (bnode = cnode->child; bnode; bnode = bnode->next) {
-			ops->save_node(purplebuddylist, bnode);
+			klass->save_node(purplebuddylist, bnode);
 		}
 	}
 
-	if (ops && ops->update)
-	{
-		if (cnode->child)
-			ops->update(purplebuddylist, cnode);
+	if (klass && klass->update) {
+		if (cnode->child) {
+			klass->update(purplebuddylist, cnode);
+		}
 
-		for (bnode = cnode->child; bnode; bnode = bnode->next)
-			ops->update(purplebuddylist, bnode);
+		for (bnode = cnode->child; bnode; bnode = bnode->next) {
+			klass->update(purplebuddylist, bnode);
+		}
 	}
 }
 
 void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
 {
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBuddyListPrivate *priv = NULL;
-	PurpleBlistUiOps *ops;
 	PurpleBlistNode *gnode = (PurpleBlistNode*)group;
 	gchar* key;
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_GROUP(group));
 
-	ops = purple_blist_get_ui_ops();
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 	priv = purple_buddy_list_get_instance_private(purplebuddylist);
 
 	/* if we're moving to overtop of ourselves, do nothing */
@@ -1305,8 +1339,10 @@
 	if (purple_blist_find_group(purple_group_get_name(group))) {
 		/* This is just being moved */
 
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, (PurpleBlistNode *)group);
+		if (klass && klass->remove) {
+			klass->remove(purplebuddylist,
+			              (PurpleBlistNode *)group);
+		}
 
 		if (gnode == priv->root) {
 			priv->root = gnode->next;
@@ -1335,17 +1371,18 @@
 		priv->root = gnode;
 	}
 
-	if (ops && ops->save_node) {
-		ops->save_node(purplebuddylist, gnode);
+	if (klass && klass->save_node) {
+		klass->save_node(purplebuddylist, gnode);
 		for (node = gnode->child; node; node = node->next) {
-			ops->save_node(purplebuddylist, node);
+			klass->save_node(purplebuddylist, node);
 		}
 	}
 
-	if (ops && ops->update) {
-		ops->update(purplebuddylist, gnode);
-		for (node = gnode->child; node; node = node->next)
-			ops->update(purplebuddylist, node);
+	if (klass && klass->update) {
+		klass->update(purplebuddylist, gnode);
+		for (node = gnode->child; node; node = node->next) {
+			klass->update(purplebuddylist, node);
+		}
 	}
 
 	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
@@ -1354,12 +1391,14 @@
 
 void purple_blist_remove_contact(PurpleContact *contact)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBlistNode *node, *gnode;
 	PurpleGroup *group;
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_CONTACT(contact));
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 	node = (PurpleBlistNode *)contact;
 	gnode = node->parent;
 	group = PURPLE_GROUP(gnode);
@@ -1390,11 +1429,12 @@
 		purple_counting_node_change_total_size(PURPLE_COUNTING_NODE(group), -1);
 
 		/* Update the UI */
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, node);
+		if (klass && klass->remove) {
+			klass->remove(purplebuddylist, node);
+		}
 
-		if (ops && ops->remove_node) {
-			ops->remove_node(purplebuddylist, node);
+		if (klass && klass->remove_node) {
+			klass->remove_node(purplebuddylist, node);
 		}
 
 		purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
@@ -1407,9 +1447,8 @@
 
 void purple_blist_remove_buddy(PurpleBuddy *buddy)
 {
-	PurpleBuddyListPrivate *priv =
-			purple_buddy_list_get_instance_private(purplebuddylist);
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
+	PurpleBuddyListPrivate *priv = NULL;
 	PurpleBlistNode *node, *cnode, *gnode;
 	PurpleCountingNode *contact_counter, *group_counter;
 	PurpleContact *contact;
@@ -1418,8 +1457,11 @@
 	GHashTable *account_buddies;
 	PurpleAccount *account;
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_BUDDY(buddy));
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	priv = purple_buddy_list_get_instance_private(purplebuddylist);
 	account = purple_buddy_get_account(buddy);
 	node = PURPLE_BLIST_NODE(buddy);
 	cnode = node->parent;
@@ -1456,8 +1498,9 @@
 		if (cnode->child && purple_contact_get_priority_buddy(contact) == buddy) {
 			purple_contact_invalidate_priority_buddy(contact);
 
-			if (ops && ops->update)
-				ops->update(purplebuddylist, cnode);
+			if (klass && klass->update) {
+				klass->update(purplebuddylist, cnode);
+			}
 		}
 	}
 
@@ -1471,11 +1514,12 @@
 	g_hash_table_remove(account_buddies, &hb);
 
 	/* Update the UI */
-	if (ops && ops->remove)
-		ops->remove(purplebuddylist, node);
+	if (klass && klass->remove) {
+		klass->remove(purplebuddylist, node);
+	}
 
-	if (ops && ops->remove_node) {
-		ops->remove_node(purplebuddylist, node);
+	if (klass && klass->remove_node) {
+		klass->remove_node(purplebuddylist, node);
 	}
 
 	/* Remove this buddy's pounces */
@@ -1494,13 +1538,15 @@
 
 void purple_blist_remove_chat(PurpleChat *chat)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBlistNode *node, *gnode;
 	PurpleGroup *group;
 	PurpleCountingNode *group_counter;
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_CHAT(chat));
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 	node = (PurpleBlistNode *)chat;
 	gnode = node->parent;
 	group = (PurpleGroup *)gnode;
@@ -1525,11 +1571,12 @@
 	}
 
 	/* Update the UI */
-	if (ops && ops->remove)
-		ops->remove(purplebuddylist, node);
+	if (klass && klass->remove) {
+		klass->remove(purplebuddylist, node);
+	}
 
-	if (ops && ops->remove_node) {
-		ops->remove_node(purplebuddylist, node);
+	if (klass && klass->remove_node) {
+		klass->remove_node(purplebuddylist, node);
 	}
 
 	purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
@@ -1541,18 +1588,20 @@
 
 void purple_blist_remove_group(PurpleGroup *group)
 {
-	PurpleBuddyListPrivate *priv =
-	        purple_buddy_list_get_instance_private(purplebuddylist);
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
+	PurpleBuddyListPrivate *priv = NULL;
 	PurpleBlistNode *node;
 	GList *l;
 	gchar* key;
 
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 	g_return_if_fail(PURPLE_IS_GROUP(group));
 
 	if (group == purple_blist_get_default_group())
 		purple_debug_warning("buddylist", "cannot remove default group");
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	priv = purple_buddy_list_get_instance_private(purplebuddylist);
 	node = (PurpleBlistNode *)group;
 
 	/* Make sure the group is empty */
@@ -1573,11 +1622,12 @@
 	g_free(key);
 
 	/* Update the UI */
-	if (ops && ops->remove)
-		ops->remove(purplebuddylist, node);
+	if (klass && klass->remove) {
+		klass->remove(purplebuddylist, node);
+	}
 
-	if (ops && ops->remove_node) {
-		ops->remove_node(purplebuddylist, node);
+	if (klass && klass->remove_node) {
+		klass->remove_node(purplebuddylist, node);
 	}
 
 	purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
@@ -1778,14 +1828,16 @@
 
 void purple_blist_add_account(PurpleAccount *account)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleCountingNode *contact_counter, *group_counter;
 
 	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 
-	if (!ops || !ops->update)
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	if (!klass || !klass->update) {
 		return;
+	}
 
 	for (gnode = purple_blist_get_default_root(); gnode;
 	     gnode = gnode->next) {
@@ -1803,29 +1855,34 @@
 							purple_counting_node_change_current_size(contact_counter, +1);
 							if (purple_counting_node_get_current_size(contact_counter) == 1)
 								purple_counting_node_change_current_size(group_counter, +1);
-							ops->update(purplebuddylist, bnode);
-						}
-					}
-					if (recompute ||
-							purple_blist_node_get_bool(cnode, "show_offline")) {
-						purple_contact_invalidate_priority_buddy((PurpleContact*)cnode);
-						ops->update(purplebuddylist, cnode);
-					}
+						        klass->update(
+						                purplebuddylist,
+						                bnode);
+					        }
+				        }
+				        if (recompute ||
+				            purple_blist_node_get_bool(
+				                    cnode, "show_offline")) {
+					        purple_contact_invalidate_priority_buddy(
+					                (PurpleContact *)cnode);
+					        klass->update(purplebuddylist,
+					                      cnode);
+				        }
 			} else if (PURPLE_IS_CHAT(cnode) &&
 					purple_chat_get_account(PURPLE_CHAT(cnode)) == account) {
 				group_counter = PURPLE_COUNTING_NODE(gnode);
 				purple_counting_node_change_online_count(group_counter, +1);
 				purple_counting_node_change_current_size(group_counter, +1);
-				ops->update(purplebuddylist, cnode);
+				klass->update(purplebuddylist, cnode);
 			}
 		}
-		ops->update(purplebuddylist, gnode);
+		klass->update(purplebuddylist, gnode);
 	}
 }
 
 void purple_blist_remove_account(PurpleAccount *account)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleCountingNode *contact_counter, *group_counter;
 	PurpleBuddy *buddy;
@@ -1835,6 +1892,7 @@
 	GList *list = NULL, *iter = NULL;
 
 	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 
 	for (gnode = purple_blist_get_default_root(); gnode;
 	     gnode = gnode->next) {
@@ -1881,16 +1939,20 @@
 						else
 							recompute = TRUE;
 
-						if (ops && ops->remove) {
-							ops->remove(purplebuddylist, bnode);
+						if (klass && klass->remove) {
+							klass->remove(
+							        purplebuddylist,
+							        bnode);
 						}
 					}
 				}
 				if (recompute) {
 					purple_contact_invalidate_priority_buddy(contact);
 
-					if (ops && ops->update)
-						ops->update(purplebuddylist, cnode);
+					if (klass && klass->update) {
+						klass->update(purplebuddylist,
+						              cnode);
+					}
 				}
 			} else if (PURPLE_IS_CHAT(cnode)) {
 				chat = PURPLE_CHAT(cnode);
@@ -1900,8 +1962,10 @@
 					purple_counting_node_change_current_size(group_counter, -1);
 					purple_counting_node_change_online_count(group_counter, -1);
 
-					if (ops && ops->remove)
-						ops->remove(purplebuddylist, cnode);
+					if (klass && klass->remove) {
+						klass->remove(purplebuddylist,
+						              cnode);
+					}
 				}
 			}
 		}
@@ -1954,13 +2018,14 @@
 purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
 							 const char *group, const char *alias)
 {
-	PurpleBlistUiOps *ui_ops;
+	PurpleBuddyListClass *klass = NULL;
 
-	ui_ops = purple_blist_get_ui_ops();
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 
-	if (ui_ops != NULL && ui_ops->request_add_buddy != NULL) {
-		ui_ops->request_add_buddy(purplebuddylist, account, username,
-		                          group, alias);
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	if (klass != NULL && klass->request_add_buddy != NULL) {
+		klass->request_add_buddy(purplebuddylist, account, username,
+		                         group, alias);
 	}
 }
 
@@ -1968,98 +2033,80 @@
 purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
 							const char *alias, const char *name)
 {
-	PurpleBlistUiOps *ui_ops;
+	PurpleBuddyListClass *klass = NULL;
 
-	ui_ops = purple_blist_get_ui_ops();
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 
-	if (ui_ops != NULL && ui_ops->request_add_chat != NULL) {
-		ui_ops->request_add_chat(purplebuddylist, account, group, alias,
-		                         name);
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	if (klass != NULL && klass->request_add_chat != NULL) {
+		klass->request_add_chat(purplebuddylist, account, group, alias,
+		                        name);
 	}
 }
 
 void
 purple_blist_request_add_group(void)
 {
-	PurpleBlistUiOps *ui_ops;
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
 
-	ui_ops = purple_blist_get_ui_ops();
-
-	if (ui_ops != NULL && ui_ops->request_add_group != NULL) {
-		ui_ops->request_add_group(purplebuddylist);
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
+	if (klass != NULL && klass->request_add_group != NULL) {
+		klass->request_add_group(purplebuddylist);
 	}
 }
 
 void
 purple_blist_new_node(PurpleBuddyList *list, PurpleBlistNode *node)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	if (ops && ops->new_node) {
-		ops->new_node(list, node);
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
+
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
+	if (klass && klass->new_node) {
+		klass->new_node(list, node);
 	}
 }
 
 void
 purple_blist_update_node(PurpleBuddyList *list, PurpleBlistNode *node)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	if (ops && ops->update) {
-		ops->update(list, node);
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
+
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
+	if (klass && klass->update) {
+		klass->update(list, node);
 	}
 }
 
 void
 purple_blist_save_node(PurpleBuddyList *list, PurpleBlistNode *node)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	if (ops && ops->save_node) {
-		ops->save_node(list, node);
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
+
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
+	if (klass && klass->save_node) {
+		klass->save_node(list, node);
 	}
 }
 
 void
 purple_blist_save_account(PurpleBuddyList *list, PurpleAccount *account)
 {
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	if (ops && ops->save_account) {
-		ops->save_account(list, account);
-	}
-}
+	PurpleBuddyListClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
 
-void
-purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
-{
-	gboolean overrode = FALSE;
-	blist_ui_ops = ops;
-
-	if (!ops)
-		return;
-
-	if (!ops->save_node) {
-		ops->save_node = purple_blist_real_save_node;
-		overrode = TRUE;
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
+	if (klass && klass->save_account) {
+		klass->save_account(list, account);
 	}
-	if (!ops->remove_node) {
-		ops->remove_node = purple_blist_real_save_node;
-		overrode = TRUE;
-	}
-	if (!ops->save_account) {
-		ops->save_account = purple_blist_real_save_account;
-		overrode = TRUE;
-	}
-
-	if (overrode && (ops->save_node != purple_blist_real_save_node ||
-	                 ops->remove_node != purple_blist_real_save_node ||
-	                 ops->save_account != purple_blist_real_save_account)) {
-		purple_debug_warning("buddylist", "Only some of the blist saving UI ops "
-				"were overridden. This probably is not what you want!\n");
-	}
-}
-
-PurpleBlistUiOps *
-purple_blist_get_ui_ops(void)
-{
-	return blist_ui_ops;
 }
 
 const gchar *
@@ -2081,6 +2128,9 @@
 {
 	void *handle = purple_blist_get_handle();
 
+	/* Set a default, which can't be done as a static initializer. */
+	buddy_list_type = PURPLE_TYPE_BUDDY_LIST;
+
 	purple_signal_register(handle, "buddy-status-changed",
 	                     purple_marshal_VOID__POINTER_POINTER_POINTER,
 	                     G_TYPE_NONE, 3, PURPLE_TYPE_BUDDY, PURPLE_TYPE_STATUS, 
@@ -2151,10 +2201,10 @@
 static void
 blist_node_destroy(PurpleBlistNode *node)
 {
-	PurpleBlistUiOps *ui_ops;
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBlistNode *child, *next_child;
 
-	ui_ops = purple_blist_get_ui_ops();
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 	child = node->child;
 	while (child) {
 		next_child = child->next;
@@ -2167,8 +2217,9 @@
 	node->child  = NULL;
 	node->next   = NULL;
 	node->prev   = NULL;
-	if (ui_ops && ui_ops->remove)
-		ui_ops->remove(purplebuddylist, node);
+	if (klass && klass->remove) {
+		klass->remove(purplebuddylist, node);
+	}
 
 	g_object_unref(node);
 }
@@ -2176,14 +2227,15 @@
 void
 purple_blist_uninit(void)
 {
+	PurpleBuddyListClass *klass = NULL;
 	PurpleBuddyListPrivate *priv = NULL;
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
 	PurpleBlistNode *node, *next_node;
 
 	/* This happens if we quit before purple_set_blist is called. */
 	if (purplebuddylist == NULL)
 		return;
 
+	klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
 	priv = purple_buddy_list_get_instance_private(purplebuddylist);
 
 	if (save_timer != 0) {
@@ -2194,8 +2246,9 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "buddylist", "Destroying\n");
 
-	if (ops && ops->destroy)
-		ops->destroy(purplebuddylist);
+	if (klass && klass->destroy) {
+		klass->destroy(purplebuddylist);
+	}
 
 	node = priv->root;
 	while (node) {
@@ -2222,36 +2275,6 @@
 }
 
 /**************************************************************************
- * GBoxed code
- **************************************************************************/
-static PurpleBlistUiOps *
-purple_blist_ui_ops_copy(PurpleBlistUiOps *ops)
-{
-	PurpleBlistUiOps *ops_new;
-
-	g_return_val_if_fail(ops != NULL, NULL);
-
-	ops_new = g_new(PurpleBlistUiOps, 1);
-	*ops_new = *ops;
-
-	return ops_new;
-}
-
-GType
-purple_blist_ui_ops_get_type(void)
-{
-	static GType type = 0;
-
-	if (type == 0) {
-		type = g_boxed_type_register_static("PurpleBlistUiOps",
-				(GBoxedCopyFunc)purple_blist_ui_ops_copy,
-				(GBoxedFreeFunc)g_free);
-	}
-
-	return type;
-}
-
-/**************************************************************************
  * GObject code
  **************************************************************************/
 
@@ -2285,5 +2308,8 @@
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
 	obj_class->finalize = purple_buddy_list_finalize;
+
+	klass->save_node = purple_blist_real_save_node;
+	klass->remove_node = purple_blist_real_save_node;
+	klass->save_account = purple_blist_real_save_account;
 }
-
--- a/libpurple/buddylist.h	Wed Jul 03 04:02:46 2019 -0400
+++ b/libpurple/buddylist.h	Wed Jul 03 20:09:04 2019 -0400
@@ -33,19 +33,8 @@
 
 #include "buddy.h"
 
-#define PURPLE_TYPE_BUDDY_LIST             (purple_buddy_list_get_type())
-#define PURPLE_BUDDY_LIST(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyList))
-#define PURPLE_BUDDY_LIST_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyListClass))
-#define PURPLE_IS_BUDDY_LIST(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_BUDDY_LIST))
-#define PURPLE_IS_BUDDY_LIST_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_BUDDY_LIST))
-#define PURPLE_BUDDY_LIST_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyListClass))
-
-typedef struct _PurpleBuddyList       PurpleBuddyList;
-typedef struct _PurpleBuddyListClass  PurpleBuddyListClass;
-
-#define PURPLE_TYPE_BLIST_UI_OPS (purple_blist_ui_ops_get_type())
-
-typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
+#define PURPLE_TYPE_BUDDY_LIST (purple_buddy_list_get_type())
+typedef struct _PurpleBuddyList PurpleBuddyList;
 
 #define PURPLE_BLIST_DEFAULT_GROUP_NAME _("Buddies")
 
@@ -70,45 +59,31 @@
  *
  * The Buddy List
  */
-struct _PurpleBuddyList {
-	GObject gparent;
-};
-
-struct _PurpleBuddyListClass {
-	GObjectClass gparent_class;
-
-	/*< private >*/
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
-};
-
 /**
- * PurpleBlistUiOps:
+ * PurpleBuddyListClass:
  * @new_list:     Sets UI-specific data on a buddy list.
  * @new_node:     Sets UI-specific data on a node.
  * @show:         The core will call this when it's finished doing its core
- *                stuff
+ *                stuff.
  * @update:       This will update a node in the buddy list.
  * @remove:       This removes a node from the list
  * @destroy:      When the list is destroyed, this is called to destroy the UI.
- * @set_visible:  Hides or unhides the buddy list
+ * @set_visible:  Hides or unhides the buddy list.
  * @save_node:    This is called when a node has been modified and should be
  *                saved.
- *                <sbr/>Implementation of this UI op is
+ *                <sbr/>Implementation of this method is
  *                <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
  *                set to a fallback function that saves data to
  *                <filename>blist.xml</filename> like in previous libpurple
  *                versions.
  *                <sbr/>@node: The node which has been modified.
  * @remove_node:  Called when a node is about to be removed from the buddy list.
- *                The UI op should update the relevant data structures to remove
- *                this node (for example, removing a buddy from the group this
- *                node is in).
- *                <sbr/>Implementation of this UI op is
- *                <emphasis>OPTIONAL</emphasis>. If not implemented,
- *                it will be set to a fallback function that saves data to
+ *                The method should update the relevant data structures to
+ *                remove this node (for example, removing a buddy from the
+ *                group this node is in).
+ *                <sbr/>Implementation of this method is
+ *                <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
+ *                set to a fallback function that saves data to
  *                <filename>blist.xml</filename> like in previous libpurple
  *                versions.
  *                <sbr/>@node: The node which has been modified.
@@ -116,7 +91,7 @@
  *                this, the callback must save the privacy and buddy list data
  *                for an account. If the account is %NULL, save the data for all
  *                accounts.
- *                <sbr/>Implementation of this UI op is
+ *                <sbr/>Implementation of this method is
  *                <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
  *                set to a fallback function that saves data to
  *                <filename>blist.xml</filename> like in previous
@@ -124,13 +99,15 @@
  *                <sbr/>@account: The account whose data to save. If %NULL,
  *                                save all data for all accounts.
  *
- * Buddy list UI operations.
+ * Buddy list operations.
  *
- * Any UI representing a buddy list must assign a filled-out PurpleBlistUiOps
- * structure to the buddy list core.
+ * Any UI representing a buddy list must derive a filled-out
+ * @PurpleBuddyListClass and set the GType using purple_blist_set_ui() before a
+ * buddy list is created.
  */
-struct _PurpleBlistUiOps
-{
+struct _PurpleBuddyListClass {
+	GObjectClass gparent_class;
+
 	void (*new_list)(PurpleBuddyList *list);
 	void (*new_node)(PurpleBuddyList *list, PurpleBlistNode *node);
 	void (*show)(PurpleBuddyList *list);
@@ -155,10 +132,7 @@
 	void (*save_account)(PurpleBuddyList *list, PurpleAccount *account);
 
 	/*< private >*/
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	gpointer reserved[4];
 };
 
 G_BEGIN_DECLS
@@ -172,7 +146,8 @@
  *
  * Returns: The #GType for the #PurpleBuddyList object.
  */
-GType purple_buddy_list_get_type(void);
+G_DECLARE_DERIVABLE_TYPE(PurpleBuddyList, purple_buddy_list, PURPLE, BUDDY_LIST,
+                         GObject)
 
 /**
  * purple_blist_get_default:
@@ -517,10 +492,6 @@
  */
 void purple_blist_request_add_group(void);
 
-/**************************************************************************/
-/* Buddy list UI Functions                                                */
-/**************************************************************************/
-
 /**
  * purple_blist_new_node:
  * @list: The list that contains the node.
@@ -576,36 +547,21 @@
 void purple_blist_save_account(PurpleBuddyList *list, PurpleAccount *account);
 
 /**************************************************************************/
-/* UI Registration Functions                                              */
+/* Buddy List Subsystem                                                   */
 /**************************************************************************/
 
 /**
- * purple_blist_ui_ops_get_type:
+ * purple_blist_set_ui:
+ * @type: The @GType of a derived UI implementation of @PurpleBuddyList.
+ *
+ * Set the UI implementation of the buddy list.
  *
- * Returns: The #GType for the #PurpleBlistUiOps boxed structure.
- */
-GType purple_blist_ui_ops_get_type(void);
-
-/**
- * purple_blist_set_ui_ops:
- * @ops: The ops struct.
+ * This must be called before the buddy list is created or you will get the
+ * default libpurple implementation.
  *
- * Sets the UI operations structure to be used for the buddy list.
+ * Since: 3.0.0
  */
-void purple_blist_set_ui_ops(PurpleBlistUiOps *ops);
-
-/**
- * purple_blist_get_ui_ops:
- *
- * Returns the UI operations structure to be used for the buddy list.
- *
- * Returns: The UI operations structure.
- */
-PurpleBlistUiOps *purple_blist_get_ui_ops(void);
-
-/**************************************************************************/
-/* Buddy List Subsystem                                                   */
-/**************************************************************************/
+void purple_blist_set_ui(GType type);
 
 /**
  * purple_blist_get_handle:
--- a/pidgin/gtkblist.c	Wed Jul 03 04:02:46 2019 -0400
+++ b/pidgin/gtkblist.c	Wed Jul 03 20:09:04 2019 -0400
@@ -4793,51 +4793,6 @@
 	}
 }
 
-/**************************************************************************
- * GTK Buddy list GBoxed code
- **************************************************************************/
-static PidginBuddyList *
-pidgin_buddy_list_ref(PidginBuddyList *gtkblist)
-{
-	PidginBuddyListPrivate *priv;
-
-	g_return_val_if_fail(gtkblist != NULL, NULL);
-
-	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
-	priv->box_count++;
-
-	return gtkblist;
-}
-
-static void
-pidgin_buddy_list_unref(PidginBuddyList *gtkblist)
-{
-	PidginBuddyListPrivate *priv;
-
-	g_return_if_fail(gtkblist != NULL);
-
-	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
-
-	g_return_if_fail(priv->box_count >= 0);
-
-	if (!priv->box_count--)
-		purple_core_quit();
-}
-
-GType
-pidgin_buddy_list_get_type(void)
-{
-	static GType type = 0;
-
-	if (type == 0) {
-		type = g_boxed_type_register_static("PidginBuddyList",
-				(GBoxedCopyFunc)pidgin_buddy_list_ref,
-				(GBoxedFreeFunc)pidgin_buddy_list_unref);
-	}
-
-	return type;
-}
-
 /**********************************************************************************
  * Public API Functions                                                           *
  **********************************************************************************/
@@ -4846,7 +4801,7 @@
 {
 	PidginBuddyList *gtkblist;
 
-	gtkblist = g_new0(PidginBuddyList, 1);
+	gtkblist = PIDGIN_BUDDY_LIST(blist);
 	gtkblist->priv = g_new0(PidginBuddyListPrivate, 1);
 
 	purple_blist_set_ui_data(gtkblist);
@@ -6892,7 +6847,6 @@
 		g_source_remove(priv->select_notebook_page_timeout);
 	g_free(priv);
 
-	g_free(gtkblist);
 	accountmenu = NULL;
 	gtkblist = NULL;
 	purple_prefs_disconnect_by_handle(pidgin_blist_get_handle());
@@ -7382,31 +7336,6 @@
 		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
 }
 
-static PurpleBlistUiOps blist_ui_ops =
-{
-	pidgin_blist_new_list,
-	pidgin_blist_new_node,
-	pidgin_blist_show,
-	pidgin_blist_update,
-	pidgin_blist_remove,
-	pidgin_blist_destroy,
-	pidgin_blist_set_visible,
-	pidgin_blist_request_add_buddy,
-	pidgin_blist_request_add_chat,
-	pidgin_blist_request_add_group,
-	NULL,
-	NULL,
-	NULL,
-	NULL, NULL, NULL, NULL
-};
-
-
-PurpleBlistUiOps *
-pidgin_blist_get_ui_ops(void)
-{
-	return &blist_ui_ops;
-}
-
 PidginBuddyList *pidgin_blist_get_default_gtk_blist()
 {
 	return gtkblist;
@@ -7578,6 +7507,34 @@
 	purple_signals_disconnect_by_handle(pidgin_blist_get_handle());
 }
 
+/**************************************************************************
+ * GTK Buddy list GObject code
+ **************************************************************************/
+G_DEFINE_TYPE(PidginBuddyList, pidgin_buddy_list, PURPLE_TYPE_BUDDY_LIST)
+
+static void
+pidgin_buddy_list_init(PidginBuddyList *self)
+{
+}
+
+static void
+pidgin_buddy_list_class_init(PidginBuddyListClass *klass)
+{
+	PurpleBuddyListClass *purple_blist_class;
+
+	purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
+	purple_blist_class->new_list = pidgin_blist_new_list;
+	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->destroy = pidgin_blist_destroy;
+	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                                      *
  *********************************************************************/
--- a/pidgin/gtkblist.h	Wed Jul 03 04:02:46 2019 -0400
+++ b/pidgin/gtkblist.h	Wed Jul 03 20:09:04 2019 -0400
@@ -106,6 +106,8 @@
  * Like, everything you need to know about the gtk buddy list
  */
 struct _PidginBuddyList {
+	PurpleBuddyList parent;
+
 	GtkWidget *window;
 	GtkWidget *notebook;
 	GtkWidget *main_vbox;
@@ -149,8 +151,6 @@
 };
 
 #define PIDGIN_BLIST(list) ((PidginBuddyList *)purple_blist_get_ui_data())
-#define PIDGIN_IS_PIDGIN_BLIST(list) \
-	(purple_blist_get_ui_ops() == pidgin_blist_get_ui_ops())
 
 G_BEGIN_DECLS
 
@@ -163,7 +163,8 @@
  *
  * Returns: The #GType for the #PidginBuddyList boxed structure.
  */
-GType pidgin_buddy_list_get_type(void);
+G_DECLARE_FINAL_TYPE(PidginBuddyList, pidgin_buddy_list, PIDGIN, BUDDY_LIST,
+                     PurpleBuddyList)
 
 /**
  * pidgin_blist_get_handle:
@@ -189,15 +190,6 @@
 void pidgin_blist_uninit(void);
 
 /**
- * pidgin_blist_get_ui_ops:
- *
- * Returns the UI operations structure for the buddy list.
- *
- * Returns: The GTK+ list operations structure.
- */
-PurpleBlistUiOps *pidgin_blist_get_ui_ops(void);
-
-/**
  * pidgin_blist_get_default_gtk_blist:
  *
  * Returns the default gtk buddy list
--- a/pidgin/libpidgin.c	Wed Jul 03 04:02:46 2019 -0400
+++ b/pidgin/libpidgin.c	Wed Jul 03 20:09:04 2019 -0400
@@ -225,7 +225,7 @@
 	/* Set the UI operation structures. */
 	purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
 	purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
-	purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
+	purple_blist_set_ui(PIDGIN_TYPE_BUDDY_LIST);
 	purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
 	purple_request_set_ui_ops(pidgin_request_get_ui_ops());
 	purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());

mercurial