Merged in default (pull request #670)

Fri, 21 Feb 2020 02:45:13 +0000

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 21 Feb 2020 02:45:13 +0000
changeset 40303
0a01f2c891c2
parent 40295
90be91762626 (current diff)
parent 40302
28deeeb1ecdf (diff)
child 40304
a3d49ff045a8

Merged in default (pull request #670)

move PidginProtocolOptionMenu to its own widget

Approved-by: Elliott Sales de Andrade

--- a/ChangeLog.API	Fri Feb 14 22:55:41 2020 +0000
+++ b/ChangeLog.API	Fri Feb 21 02:45:13 2020 +0000
@@ -439,6 +439,7 @@
 		* purple_presence_new_for_buddy
 		* purple_print_utf8_to_console
 		* PurplePluginProtocolInfo
+		* pidgin_protocol_option_menu_new
 		* purple_proxy_connect_socks5
 		* purple_quotedp_decode. See the GMime library if needed.
 		* purple_request_certificate
--- a/pidgin/gtkaccount.c	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/gtkaccount.c	Fri Feb 21 02:45:13 2020 +0000
@@ -42,6 +42,7 @@
 #include "gtkstatusbox.h"
 #include "pidginstock.h"
 #include "minidialog.h"
+#include "pidginprotocolchooser.h"
 
 enum
 {
@@ -224,41 +225,34 @@
 }
 
 static void
-set_account_protocol_cb(GtkWidget *widget, const char *id,
-						AccountPrefsDialog *dialog)
-{
-	PurpleProtocol *new_protocol;
-
-	new_protocol = purple_protocols_find(id);
-
-	dialog->protocol = new_protocol;
-
-	if (dialog->protocol != NULL)
-	{
-		PurpleProtocol *old_protocol = NULL;
-
-		if (dialog->protocol_id)
-			old_protocol = purple_protocols_find(dialog->protocol_id);
-
-		if (old_protocol != new_protocol) {
-			g_free(dialog->protocol_id);
-			dialog->protocol_id = g_strdup(purple_protocol_get_id(dialog->protocol));
-		}
+set_account_protocol_cb(GtkWidget *widget, AccountPrefsDialog *dialog) {
+	PidginProtocolChooser *chooser = PIDGIN_PROTOCOL_CHOOSER(widget);
+	PurpleProtocol *protocol = pidgin_protocol_chooser_get_selected(chooser);
+
+	if(g_set_object(&dialog->protocol, protocol)) {
+		g_clear_pointer(&dialog->protocol_id, g_free);
 	}
-
-	if (dialog->account != NULL)
+	g_object_unref(G_OBJECT(protocol));
+
+	if(PURPLE_IS_PROTOCOL(dialog->protocol)) {
+		dialog->protocol_id = g_strdup(purple_protocol_get_id(dialog->protocol));
+	}
+
+	if (dialog->account != NULL) {
 		purple_account_clear_settings(dialog->account);
-
-	add_login_options(dialog,    dialog->top_vbox);
-	add_user_options(dialog,     dialog->top_vbox);
+	}
+
+	add_login_options(dialog, dialog->top_vbox);
+	add_user_options(dialog, dialog->top_vbox);
 	add_account_options(dialog);
 	add_voice_options(dialog);
 
 	gtk_widget_grab_focus(dialog->protocol_menu);
 
-	if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER, register_user)) {
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
-			dialog->register_button), FALSE);
+	if (!dialog->protocol ||
+	    !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER, register_user))
+	{
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->register_button), FALSE);
 		gtk_widget_hide(dialog->register_button);
 	} else {
 		if (purple_protocol_get_options(dialog->protocol) &
@@ -478,10 +472,12 @@
 	gtk_widget_show(vbox);
 
 	/* Protocol */
-	if (dialog->protocol_menu == NULL)
-	{
-		dialog->protocol_menu = pidgin_protocol_option_menu_new(
-				dialog->protocol_id, G_CALLBACK(set_account_protocol_cb), dialog);
+	if(dialog->protocol_menu == NULL) {
+		dialog->protocol_menu = pidgin_protocol_chooser_new();
+		gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->protocol_menu), 0);
+		g_signal_connect(G_OBJECT(dialog->protocol_menu), "changed",
+		                 G_CALLBACK(set_account_protocol_cb), dialog);
+		gtk_widget_show(dialog->protocol_menu);
 		g_object_ref(G_OBJECT(dialog->protocol_menu));
 	}
 
--- a/pidgin/gtkutils.c	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/gtkutils.c	Fri Feb 21 02:45:13 2020 +0000
@@ -440,25 +440,7 @@
 	return data;
 }
 
-static void
-aop_menu_cb(GtkWidget *optmenu, GCallback cb)
-{
-	if (cb != NULL) {
-		((void (*)(GtkWidget *, gpointer, gpointer))cb)(optmenu,
-			aop_option_menu_get_selected(optmenu),
-			g_object_get_data(G_OBJECT(optmenu), "user_data"));
-	}
-}
-
-static void
-aop_option_menu_replace_menu(GtkWidget *optmenu, AopMenu *new_aop_menu)
-{
-	gtk_combo_box_set_model(GTK_COMBO_BOX(optmenu), new_aop_menu->model);
-	gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), new_aop_menu->default_item);
-	g_free(new_aop_menu);
-}
-
-static GdkPixbuf *
+GdkPixbuf *
 pidgin_create_icon_from_protocol(PurpleProtocol *protocol, PidginProtocolIconSize size, PurpleAccount *account)
 {
 	const char *protoname = NULL;
@@ -490,27 +472,6 @@
 	return pixbuf;
 }
 
-static GtkWidget *
-aop_option_menu_new(AopMenu *aop_menu, GCallback cb, gpointer user_data)
-{
-	GtkWidget *optmenu = NULL;
-	GtkCellRenderer *cr = NULL;
-
-	optmenu = gtk_combo_box_new();
-	gtk_widget_show(optmenu);
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(optmenu), cr = gtk_cell_renderer_pixbuf_new(), FALSE);
-	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(optmenu), cr, "pixbuf", AOP_ICON_COLUMN);
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(optmenu), cr = gtk_cell_renderer_text_new(), TRUE);
-	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(optmenu), cr, "text", AOP_NAME_COLUMN);
-
-	aop_option_menu_replace_menu(optmenu, aop_menu);
-	g_object_set_data(G_OBJECT(optmenu), "user_data", user_data);
-
-	g_signal_connect(G_OBJECT(optmenu), "changed", G_CALLBACK(aop_menu_cb), cb);
-
-	return optmenu;
-}
-
 static void
 aop_option_menu_select_by_data(GtkWidget *optmenu, gpointer data)
 {
@@ -529,58 +490,6 @@
 	}
 }
 
-static AopMenu *
-create_protocols_menu(const char *default_proto_id)
-{
-	AopMenu *aop_menu = NULL;
-	PurpleProtocol *protocol;
-	GdkPixbuf *pixbuf = NULL;
-	GtkTreeIter iter;
-	GtkListStore *ls;
-	GList *list, *p;
-	int i;
-
-	ls = gtk_list_store_new(AOP_COLUMN_COUNT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
-
-	aop_menu = g_malloc0(sizeof(AopMenu));
-	aop_menu->default_item = 0;
-	aop_menu->model = GTK_TREE_MODEL(ls);
-
-	list = purple_protocols_get_all();
-
-	for (p = list, i = 0;
-		 p != NULL;
-		 p = p->next, i++) {
-
-		protocol = PURPLE_PROTOCOL(p->data);
-
-		pixbuf = pidgin_create_icon_from_protocol(protocol, PIDGIN_PROTOCOL_ICON_SMALL, NULL);
-
-		gtk_list_store_append(ls, &iter);
-		gtk_list_store_set(ls, &iter,
-		                   AOP_ICON_COLUMN, pixbuf,
-		                   AOP_NAME_COLUMN, purple_protocol_get_name(protocol),
-		                   AOP_DATA_COLUMN, purple_protocol_get_id(protocol),
-		                   -1);
-
-		if (pixbuf)
-			g_object_unref(pixbuf);
-
-		if (default_proto_id != NULL && purple_strequal(purple_protocol_get_id(protocol), default_proto_id))
-			aop_menu->default_item = i;
-	}
-	g_list_free(list);
-
-	return aop_menu;
-}
-
-GtkWidget *
-pidgin_protocol_option_menu_new(const char *id, GCallback cb,
-								  gpointer user_data)
-{
-	return aop_option_menu_new(create_protocols_menu(id), cb, user_data);
-}
-
 const char *
 pidgin_protocol_option_menu_get_selected(GtkWidget *optmenu)
 {
--- a/pidgin/gtkutils.h	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/gtkutils.h	Fri Feb 21 02:45:13 2020 +0000
@@ -259,20 +259,6 @@
 GtkWidget *pidgin_make_frame(GtkWidget *parent, const char *title);
 
 /**
- * pidgin_protocol_option_menu_new:
- * @id: The protocol to select by default.
- * @cb: (scope call): The callback to call when a protocol is selected.
- * @user_data: Data to pass to the callback function.
- *
- * Creates a drop-down option menu filled with protocols.
- *
- * Returns: (transfer full): The drop-down option menu.
- */
-GtkWidget *pidgin_protocol_option_menu_new(const char *id,
-											 GCallback cb,
-											 gpointer user_data);
-
-/**
  * pidgin_protocol_option_menu_get_selected:
  * @optmenu: The drop-down option menu created by
  *           pidgin_protocol_option_menu_new().
@@ -457,6 +443,27 @@
 GdkPixbuf *pidgin_create_protocol_icon(PurpleAccount *account, PidginProtocolIconSize size);
 
 /**
+ * pidgin_create_icon_from_protocol:
+ * @protocol: The #PurpleProtocol instance.
+ * @size: The size of the icon to return.
+ * @account: (nullable): An optional #PurpleAccount to use.
+ *
+ * Returns the base image to represent @protocol based on the currently
+ * selected theme.  If @account is not %NULL then the returned icon will
+ * represent the account.
+ *
+ * Returns: (transfer full): A newly-created pixbuf with a reference count of 1,
+ *         or NULL if any of several error conditions occurred:
+ *         the file could not be opened, there was no loader
+ *         for the file's format, there was not enough memory
+ *         to allocate the image buffer, or the image file
+ *         contained invalid data.
+ *
+ * Since: 3.0.0
+ */
+GdkPixbuf *pidgin_create_icon_from_protocol(PurpleProtocol *protocol, PidginProtocolIconSize size, PurpleAccount *account);
+
+/**
  * pidgin_create_status_icon:
  * @primitive:  The status primitive
  * @w:          The widget to render this
--- a/pidgin/meson.build	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/meson.build	Fri Feb 21 02:45:13 2020 +0000
@@ -44,6 +44,8 @@
 	'pidginmessage.c',
 	'pidginplugininfo.c',
 	'pidginpluginsdialog.c',
+	'pidginprotocolchooser.c',
+	'pidginprotocolstore.c',
 	'pidgintalkatu.c',
 	'pidgintooltip.c',
 ]
@@ -96,6 +98,8 @@
 	'pidginmessage.h',
 	'pidginplugininfo.h',
 	'pidginpluginsdialog.h',
+	'pidginprotocolchooser.h',
+	'pidginprotocolstore.h',
 	'pidgintalkatu.h',
 	'pidgintooltip.h',
 	'pidgin.h',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginprotocolchooser.c	Fri Feb 21 02:45:13 2020 +0000
@@ -0,0 +1,128 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 "pidginprotocolchooser.h"
+
+#include "pidginprotocolstore.h"
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+struct _PidginProtocolChooser {
+	GtkComboBox parent;
+
+	GtkListStore *model;
+};
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginProtocolChooser, pidgin_protocol_chooser,
+              GTK_TYPE_COMBO_BOX)
+
+static void
+pidgin_protocol_chooser_class_init(PidginProtocolChooserClass *klass)
+{
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	gtk_widget_class_set_template_from_resource(widget_class,
+	                                            "/im/pidgin/Pidgin/Protocols/chooser.ui");
+
+	gtk_widget_class_bind_template_child(widget_class, PidginProtocolChooser,
+	                                     model);
+}
+
+static void
+pidgin_protocol_chooser_init(PidginProtocolChooser *chooser) {
+	gtk_widget_init_template(GTK_WIDGET(chooser));
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_protocol_chooser_new(void) {
+	return GTK_WIDGET(g_object_new(PIDGIN_TYPE_PROTOCOL_CHOOSER, NULL));
+}
+
+PurpleProtocol *
+pidgin_protocol_chooser_get_selected(PidginProtocolChooser *chooser) {
+	GtkTreeIter iter;
+	PurpleProtocol *protocol = NULL;
+
+	g_return_val_if_fail(PIDGIN_IS_PROTOCOL_CHOOSER(chooser), NULL);
+
+	if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(chooser), &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(chooser->model), &iter,
+		                   PIDGIN_PROTOCOL_STORE_COLUMN_PROTOCOL, &protocol,
+		                   -1);
+	}
+
+	return protocol;
+}
+
+gchar *
+pidgin_protocol_chooser_get_selected_name(PidginProtocolChooser *chooser) {
+	GtkTreeIter iter;
+	gchar *name = NULL;
+
+	g_return_val_if_fail(PIDGIN_IS_PROTOCOL_CHOOSER(chooser), NULL);
+
+	if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(chooser), &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(chooser->model), &iter,
+		                   PIDGIN_PROTOCOL_STORE_COLUMN_NAME, &name,
+		                   -1);
+	}
+
+	return name;
+}
+
+void
+pidgin_protocol_chooser_set_selected_name(PidginProtocolChooser *chooser,
+                                          const gchar *name)
+{
+	GtkTreeIter iter;
+	gchar *iter_name = NULL;
+
+	g_return_if_fail(PIDGIN_IS_PROTOCOL_CHOOSER(chooser));
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(chooser->model), &iter)) {
+		do {
+			gtk_tree_model_get(GTK_TREE_MODEL(chooser->model), &iter,
+			                   PIDGIN_PROTOCOL_STORE_COLUMN_NAME, &iter_name,
+			                   -1);
+
+			if(purple_strequal(iter_name, name)) {
+				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(chooser), &iter);
+
+				g_free(iter_name);
+
+				return;
+			}
+
+			g_free(iter_name);
+		} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(chooser->model),
+		                                 &iter));
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginprotocolchooser.h	Fri Feb 21 02:45:13 2020 +0000
@@ -0,0 +1,95 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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
+ */
+
+#ifndef PIDGIN_PROTOCOL_CHOOSER_H
+#define PIDGIN_PROTOCOL_CHOOSER_H
+
+/**
+ * SECTION:pidginprotocolchooser
+ * @section_id: pidgin-protocol-chooser
+ * @short_description: A #GtkComboBox for choosing protocols
+ * @title: Pidgin Protocol Chooser Combo Box Widget
+ */
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_PROTOCOL_CHOOSER (pidgin_protocol_chooser_get_type())
+
+G_DECLARE_FINAL_TYPE(PidginProtocolChooser, pidgin_protocol_chooser, PIDGIN,
+                     PROTOCOL_CHOOSER, GtkComboBox)
+
+/**
+ * pidgin_protocol_chooser_new:
+ *
+ * Creates a combo box for a user to select a protocol from.
+ *
+ * Returns: (transfer full): The protocol chooser combo box.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_protocol_chooser_new(void);
+
+/**
+ * pidgin_protocol_chooser_get_selected:
+ * @chooser: The #PidginProtocolChooser instance.
+ *
+ * Gets the currently selected protocol from @chooser.
+ *
+ * Returns: (transfer full): The selected #PurpleProtocol or %NULL if nothing
+ *                           is selected
+ *
+ * Since: 3.0.0
+ */
+PurpleProtocol *pidgin_protocol_chooser_get_selected(PidginProtocolChooser *chooser);
+
+/**
+ * pidgin_protocol_chooser_get_selected_name:
+ * @chooser: The #PidginProtocolChooser instance.
+ *
+ * Gets the name of the currently selected protocol from @chooser.
+ *
+ * Returns: (transfer full): The selected #PurpleProtocol or %NULL if nothing
+ *                           is selected
+ *
+ * Since: 3.0.0
+ */
+gchar *pidgin_protocol_chooser_get_selected_name(PidginProtocolChooser *chooser);
+
+/**
+ * pidgin_protocol_chooser_set_selected_name:
+ * @chooser: The #PidginProtocolChooser instance.
+ * @name: The name of the protocol to select.
+ *
+ * Sets the currently selected protocol of @chooser to @protocol.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_protocol_chooser_set_selected_name(PidginProtocolChooser *chooser, const gchar *name);
+
+G_END_DECLS
+
+#endif /* PIDGIN_PROTOCOL_CHOOSER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginprotocolstore.c	Fri Feb 21 02:45:13 2020 +0000
@@ -0,0 +1,176 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 "pidgin/pidginprotocolstore.h"
+
+#include "gtkutils.h"
+
+struct _PidginProtocolStore {
+	GtkListStore parent;
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_protocol_store_add_protocol(PidginProtocolStore *store,
+                                   PurpleProtocol *protocol)
+{
+	GtkTreeIter iter;
+	GdkPixbuf *pixbuf = NULL;
+
+	pixbuf = pidgin_create_icon_from_protocol(protocol,
+	                                          PIDGIN_PROTOCOL_ICON_SMALL, NULL);
+
+	gtk_list_store_append(GTK_LIST_STORE(store), &iter);
+	gtk_list_store_set(
+		GTK_LIST_STORE(store),
+		&iter,
+		PIDGIN_PROTOCOL_STORE_COLUMN_PROTOCOL, protocol,
+		PIDGIN_PROTOCOL_STORE_COLUMN_NAME, purple_protocol_get_name(protocol),
+		PIDGIN_PROTOCOL_STORE_COLUMN_ICON, pixbuf,
+		-1
+	);
+
+	g_clear_object(&pixbuf);
+}
+
+static void
+pidgin_protocol_store_add_protocol_helper(gpointer data, gpointer user_data) {
+	if(PURPLE_IS_PROTOCOL(data)) {
+		pidgin_protocol_store_add_protocol(PIDGIN_PROTOCOL_STORE(user_data),
+		                                   PURPLE_PROTOCOL(data));
+	}
+}
+
+static void
+pidgin_protocol_store_add_protocols(PidginProtocolStore *store) {
+	g_list_foreach(purple_protocols_get_all(),
+	               pidgin_protocol_store_add_protocol_helper, store);
+}
+
+static void
+pidgin_protocol_store_remove_protocol(PidginProtocolStore *store,
+                                      PurpleProtocol *protocol)
+{
+	GtkTreeIter iter;
+
+	if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) == FALSE) {
+		purple_debug_warning("protocol-store",
+		                     "protocol %s was removed but not in the store",
+		                     purple_protocol_get_name(protocol));
+		return;
+	}
+
+	/* walk through the store and look for the protocol to remove */
+	do {
+		PurpleProtocol *prpl = NULL;
+
+		gtk_tree_model_get(
+			GTK_TREE_MODEL(store),
+			&iter,
+			PIDGIN_PROTOCOL_STORE_COLUMN_PROTOCOL, &prpl,
+			-1
+		);
+
+		if(prpl == protocol) {
+			g_object_unref(G_OBJECT(prpl));
+
+			gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
+
+			return;
+		}
+	} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_protocol_store_protocol_added_cb(PurpleProtocol *protocol,
+                                        gpointer data)
+{
+	pidgin_protocol_store_add_protocol(PIDGIN_PROTOCOL_STORE(data), protocol);
+}
+
+static void
+pidgin_protocol_store_protocol_removed_cb(PurpleProtocol *protocol,
+                                          gpointer data)
+{
+	pidgin_protocol_store_remove_protocol(PIDGIN_PROTOCOL_STORE(data), protocol);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginProtocolStore, pidgin_protocol_store, GTK_TYPE_LIST_STORE)
+
+static void
+pidgin_protocol_store_init(PidginProtocolStore *store) {
+	gpointer protocols_handle = NULL;
+	GType types[PIDGIN_PROTOCOL_STORE_N_COLUMNS] = {
+		PURPLE_TYPE_PROTOCOL,
+		G_TYPE_STRING,
+		GDK_TYPE_PIXBUF,
+	};
+
+	gtk_list_store_set_column_types(
+		GTK_LIST_STORE(store),
+		PIDGIN_PROTOCOL_STORE_N_COLUMNS,
+		types
+	);
+
+	/* add the known protocols */
+	pidgin_protocol_store_add_protocols(store);
+
+	/* add the signal handlers to dynamically add/remove protocols */
+	protocols_handle = purple_protocols_get_handle();
+	purple_signal_connect(protocols_handle, "protocol-added", store,
+	                      G_CALLBACK(pidgin_protocol_store_protocol_added_cb),
+	                      store);
+	purple_signal_connect(protocols_handle, "protocol-removed", store,
+	                      G_CALLBACK(pidgin_protocol_store_protocol_removed_cb),
+	                      store);
+}
+
+static void
+pidgin_protocol_store_finalize(GObject *obj) {
+	purple_signals_disconnect_by_handle(obj);
+
+	G_OBJECT_CLASS(pidgin_protocol_store_parent_class)->finalize(obj);
+}
+
+static void
+pidgin_protocol_store_class_init(PidginProtocolStoreClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->finalize = pidgin_protocol_store_finalize;
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkListStore *
+pidgin_protocol_store_new(void) {
+	return GTK_LIST_STORE(g_object_new(PIDGIN_TYPE_PROTOCOL_STORE, NULL));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginprotocolstore.h	Fri Feb 21 02:45:13 2020 +0000
@@ -0,0 +1,68 @@
+/* pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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
+ */
+
+#ifndef PIDGIN_PROTOCOL_STORE_H
+#define PIDGIN_PROTOCOL_STORE_H
+
+/**
+ * SECTION:pidginprotocolstore
+ * @section_id: pidgin-protocol-store
+ * @short_description: A GtkListStore that keeps track of protocols
+ * @title: Protocol Store
+ *
+ * #PidginProtocolStore is a #GtkListStore that automatically keeps track of
+ * what protocols are currently available in libpurple.  It's intended to be
+ * used any time that you need to present a protocol selection to the user.
+ */
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+typedef enum {
+	PIDGIN_PROTOCOL_STORE_COLUMN_PROTOCOL,
+	PIDGIN_PROTOCOL_STORE_COLUMN_NAME,
+	PIDGIN_PROTOCOL_STORE_COLUMN_ICON,
+	PIDGIN_PROTOCOL_STORE_N_COLUMNS,
+} PidginProtocolStoreColumn;
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_PROTOCOL_STORE pidgin_protocol_store_get_type()
+
+G_DECLARE_FINAL_TYPE(PidginProtocolStore, pidgin_protocol_store, PIDGIN,
+                     PROTOCOL_STORE, GtkListStore)
+
+/**
+ * pidgin_protocol_store_new:
+ *
+ * Creates a new #PidginProtocolStore that can be used anywhere a #GtkListStore
+ * can be used.
+ *
+ * Returns: (transfer full): The new #PidginProtocolStore instance.
+ *
+ * Since: 3.0.0
+ */
+GtkListStore *pidgin_protocol_store_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_PROTOCOL_STORE_H */
--- a/pidgin/plugins/meson.build	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/plugins/meson.build	Fri Feb 21 02:45:13 2020 +0000
@@ -85,4 +85,5 @@
 		    name_prefix : '',
 		    install : true, install_dir : PIDGIN_PLUGINDIR)
 	endif
+
 endif # PLUGINS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Protocols/chooser.ui	Fri Feb 21 02:45:13 2020 +0000
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 
+
+Pidgin - Internet Messenger
+Copyright (C) Pidgin Developers <devel@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  02110-1301, USA.
+
+-->
+<interface domain="pidgin">
+  <requires lib="gtk+" version="3.20"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <template class="PidginProtocolChooser" parent="GtkComboBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="model">model</property>
+    <child>
+      <object class="GtkCellRendererPixbuf" id="icon"/>
+      <attributes>
+        <attribute name="pixbuf">2</attribute>
+      </attributes>
+    </child>
+    <child>
+      <object class="GtkCellRendererText" id="name"/>
+      <attributes>
+        <attribute name="text">1</attribute>
+      </attributes>
+    </child>
+  </template>
+  <object class="PidginProtocolStore" id="model"/>
+</interface>
--- a/pidgin/resources/pidgin.gresource.xml	Fri Feb 14 22:55:41 2020 +0000
+++ b/pidgin/resources/pidgin.gresource.xml	Fri Feb 21 02:45:13 2020 +0000
@@ -14,6 +14,7 @@
     <file compressed="true">Prefs/prefs.ui</file>
     <file compressed="true">Prefs/vv.ui</file>
     <file compressed="true">Privacy/dialog.ui</file>
+    <file compressed="true">Protocols/chooser.ui</file>
     <file compressed="true">Roomlist/roomlist.ui</file>
     <file compressed="true">Whiteboard/whiteboard.ui</file>
     <file compressed="true">Xfer/xfer.ui</file>

mercurial