Merged in default (pull request #643)

Fri, 22 Nov 2019 04:10:26 +0000

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 22 Nov 2019 04:10:26 +0000
changeset 40226
00908349dea9
parent 40215
f7cf821e15de (current diff)
parent 40225
b6f1592a07ec (diff)
child 40234
b82ecf28c2e4
child 40236
47904ebe816e

Merged in default (pull request #643)

Move the plugins dialog to glade using the gplugin-gtk widgets

Approved-by: Elliott Sales de Andrade

pidgin/gtkplugin.c file | annotate | diff | comparison | revisions
pidgin/gtkplugin.h file | annotate | diff | comparison | revisions
--- a/doc/reference/pidgin/pidgin-docs.xml	Thu Nov 21 21:41:21 2019 -0600
+++ b/doc/reference/pidgin/pidgin-docs.xml	Fri Nov 22 04:10:26 2019 +0000
@@ -35,7 +35,6 @@
       <xi:include href="xml/gtkmedia.xml" />
       <xi:include href="xml/gtknickcolors.xml" />
       <xi:include href="xml/gtknotify.xml" />
-      <xi:include href="xml/gtkplugin.xml" />
       <xi:include href="xml/gtkpluginpref.xml" />
       <xi:include href="xml/gtkpounce.xml" />
       <xi:include href="xml/gtkprefs.xml" />
@@ -61,6 +60,8 @@
       <xi:include href="xml/pidginlog.xml" />
       <xi:include href="xml/pidginmenutray.xml" />
       <xi:include href="xml/pidginmessage.xml" />
+      <xi:include href="xml/pidginplugininfo.xml" />
+      <xi:include href="xml/pidginpluginsdialog.xml" />
       <xi:include href="xml/pidginstock.xml" />
       <xi:include href="xml/pidgintalkatu.xml" />
       <xi:include href="xml/pidgintooltip.xml" />
--- a/meson.build	Thu Nov 21 21:41:21 2019 -0600
+++ b/meson.build	Fri Nov 22 04:10:26 2019 +0000
@@ -617,9 +617,9 @@
 endif
 
 #######################################################################
-# Check for GPlugin 0.28.0
+# Check for GPlugin
 #######################################################################
-gplugin_dep = dependency('gplugin', version : '>= 0.28.0', required : false)
+gplugin_dep = dependency('gplugin', version : '>= 0.29.0', required : false)
 if gplugin_dep.found()
 	if enable_introspection
 		gplugin_gir = 'GPlugin-0.0'
@@ -629,19 +629,43 @@
 	else
 		gplugin_include_directories = []
 	endif
+
+	if get_option('gtkui')
+		gplugin_gtk_dep = dependency('gplugin-gtk', required : true)
+
+		if enable_introspection
+			gplugin_gtk_gir = 'GPluginGtk-0.0'
+			gplugin_gtk_include_directories = include_directories(
+				join_paths(gplugin_gtk_dep.get_pkgconfig_variable('prefix'),
+				           'share/gir-1.0'))
+		else
+			gplugin_gtk_include_directories = []
+		endif
+	endif
 else
 	gplugin_proj = subproject('gplugin',
 	    default_options : [
 	        'doc=' + get_option('doc').to_string(),
 	        'gobject-introspection=' + enable_introspection.to_string(),
 	        'nls=' + get_option('nls').to_string(),
+	        'gtk3=' + get_option('gtkui').to_string(),
 	    ]
 	)
+
 	gplugin_dep = gplugin_proj.get_variable('gplugin_dep')
 	if enable_introspection
 		gplugin_gir = gplugin_proj.get_variable('gplugin_gir')[0]
 	endif
 	gplugin_include_directories = []
+
+	if get_option('gtkui')
+		gplugin_gtk_dep = gplugin_proj.get_variable('gplugin_gtk_dep')
+
+		if enable_introspection
+			gplugin_gtk_gir = gplugin_proj.get_variable('gplugin_gtk_gir')[0]
+		endif
+		gplugin_gtk_include_directories = []
+	endif
 endif
 
 PLUGINS = get_option('plugins')
--- a/pidgin/gtkblist.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/gtkblist.c	Fri Nov 22 04:10:26 2019 +0000
@@ -45,7 +45,6 @@
 #include "gtkdialogs.h"
 #include "gtkxfer.h"
 #include "gtkpounce.h"
-#include "gtkplugin.h"
 #include "gtkprefs.h"
 #include "gtkprivacy.h"
 #include "gtkroomlist.h"
@@ -63,6 +62,8 @@
 #include "pidgin/pidgindebugplugininfo.h"
 #include "pidgin/pidgingdkpixbuf.h"
 #include "pidgin/pidginlog.h"
+#include "pidgin/pidginplugininfo.h"
+#include "pidgin/pidginpluginsdialog.h"
 #include "pidgin/pidgintooltip.h"
 #include "pidginmenutray.h"
 
@@ -3617,6 +3618,15 @@
  *            Crap                                 *
  ***************************************************/
 static void
+pidgin_blist_plugins_dialog_cb(GtkAction *action, GtkWidget *window) {
+	GtkWidget *dialog = pidgin_plugins_dialog_new();
+
+	gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
+
+	gtk_widget_show_all(dialog);
+}
+
+static void
 _pidgin_about_cb(GtkAction *action, GtkWidget *window) {
 	GtkWidget *about = pidgin_about_dialog_new();
 
@@ -3650,7 +3660,7 @@
 	{ "ToolsMenu", NULL, N_("_Tools"), NULL, NULL, NULL },
 	{ "BuddyPounces", NULL, N_("Buddy _Pounces"), NULL, NULL, pidgin_pounces_manager_show },
 	{ "CustomSmileys", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("Custom Smile_ys"), "<control>Y", NULL, pidgin_smiley_manager_show },
-	{ "Plugins", PIDGIN_STOCK_TOOLBAR_PLUGINS, N_("Plu_gins"), "<control>U", NULL, pidgin_plugin_dialog_show },
+	{ "Plugins", PIDGIN_STOCK_TOOLBAR_PLUGINS, N_("Plu_gins"), "<control>U", NULL, pidgin_blist_plugins_dialog_cb },
 	{ "Preferences", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), "<control>P", NULL, pidgin_prefs_show },
 	{ "Privacy", NULL, N_("Pr_ivacy"), NULL, NULL, pidgin_privacy_dialog_show },
 	{ "SetMood", NULL, N_("Set _Mood"), "<control>D", NULL, set_mood_show },
--- a/pidgin/gtkplugin.c	Thu Nov 21 21:41:21 2019 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1074 +0,0 @@
-/* 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
- */
-#include "internal.h"
-#include "pidgin.h"
-#include "gtkplugin.h"
-#include "gtkpluginpref.h"
-#include "gtkutils.h"
-#include "debug.h"
-#include "prefs.h"
-#include "request.h"
-#include "pidgintooltip.h"
-
-#include <string.h>
-
-#define PIDGIN_RESPONSE_CONFIGURE 98121
-
-typedef struct
-{
-	PidginPluginConfigFrameCb config_frame_cb;
-} PidginPluginInfoPrivate;
-
-enum
-{
-	PROP_0,
-	PROP_GTK_CONFIG_FRAME_CB,
-	PROP_LAST
-};
-
-typedef struct
-{
-	enum
-	{
-		PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME,
-		PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
-	} type;
-
-	union
-	{
-		struct
-		{
-			GtkWidget *dialog;
-			PurplePluginPrefFrame *pref_frame;
-		} frame;
-
-		gpointer request_handle;
-	} u;
-} PidginPluginUiData;
-
-G_DEFINE_TYPE_WITH_PRIVATE(PidginPluginInfo, pidgin_plugin_info,
-		PURPLE_TYPE_PLUGIN_INFO);
-
-static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model,
-                                  GtkTreeIter *iter, GError *error, gboolean unload);
-
-static GtkWidget *expander = NULL;
-static GtkWidget *plugin_dialog = NULL;
-
-static GtkLabel *plugin_name = NULL;
-static GtkTextBuffer *plugin_desc = NULL;
-static GtkLabel *plugin_error = NULL;
-static GtkLabel *plugin_authors = NULL;
-static GtkLabel *plugin_website = NULL;
-static gchar *plugin_website_uri = NULL;
-static GtkLabel *plugin_filename = NULL;
-
-static GtkWidget *pref_button = NULL;
-
-/* Set method for GObject properties */
-static void
-pidgin_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
-		GParamSpec *pspec)
-{
-	PidginPluginInfoPrivate *priv =
-			pidgin_plugin_info_get_instance_private(
-					PIDGIN_PLUGIN_INFO(obj));
-
-	switch (param_id) {
-		case PROP_GTK_CONFIG_FRAME_CB:
-			priv->config_frame_cb = g_value_get_pointer(value);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* Get method for GObject properties */
-static void
-pidgin_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
-		GParamSpec *pspec)
-{
-	PidginPluginInfoPrivate *priv =
-			pidgin_plugin_info_get_instance_private(
-					PIDGIN_PLUGIN_INFO(obj));
-
-	switch (param_id) {
-		case PROP_GTK_CONFIG_FRAME_CB:
-			g_value_set_pointer(value, priv->config_frame_cb);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* Class initializer function */
-static void pidgin_plugin_info_class_init(PidginPluginInfoClass *klass)
-{
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-
-	/* Setup properties */
-	obj_class->get_property = pidgin_plugin_info_get_property;
-	obj_class->set_property = pidgin_plugin_info_set_property;
-
-	g_object_class_install_property(obj_class, PROP_GTK_CONFIG_FRAME_CB,
-		g_param_spec_pointer("gtk-config-frame-cb",
-		                     "GTK configuration frame callback",
-		                     "Callback that returns a GTK configuration frame",
-		                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                     G_PARAM_STATIC_STRINGS));
-}
-
-static void
-pidgin_plugin_info_init(PidginPluginInfo *info)
-{
-}
-
-PidginPluginInfo *
-pidgin_plugin_info_new(const char *first_property, ...)
-{
-	GObject *info;
-	va_list var_args;
-
-	/* at least ID is required */
-	if (!first_property)
-		return NULL;
-
-	va_start(var_args, first_property);
-	info = g_object_new_valist(PIDGIN_TYPE_PLUGIN_INFO, first_property,
-	                           var_args);
-	va_end(var_args);
-
-	g_object_set(info, "ui-requirement", PIDGIN_UI, NULL);
-
-	return PIDGIN_PLUGIN_INFO(info);
-}
-
-static gboolean
-pidgin_plugin_has_prefs(PurplePlugin *plugin)
-{
-	PurplePluginInfo *info = purple_plugin_get_info(plugin);
-	PidginPluginInfoPrivate *priv = NULL;
-	gboolean ret;
-
-	g_return_val_if_fail(plugin != NULL, FALSE);
-
-	if (!purple_plugin_is_loaded(plugin))
-		return FALSE;
-
-	if (PIDGIN_IS_PLUGIN_INFO(info))
-		priv = pidgin_plugin_info_get_instance_private(
-				PIDGIN_PLUGIN_INFO(info));
-
-	ret = ((priv && priv->config_frame_cb) ||
-			purple_plugin_info_get_pref_frame_cb(info) ||
-			purple_plugin_info_get_pref_request_cb(info));
-
-	return ret;
-}
-
-static GtkWidget *
-pidgin_plugin_get_config_frame(PurplePlugin *plugin,
-	PurplePluginPrefFrame **purple_pref_frame)
-{
-	GtkWidget *config = NULL;
-	PurplePluginInfo *info;
-	PurplePluginPrefFrameCb pref_frame_cb = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), NULL);
-
-	info = purple_plugin_get_info(plugin);
-	if(!PURPLE_IS_PLUGIN_INFO(info))
-		return NULL;
-
-	pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info);
-	if(pref_frame_cb) {
-		PurplePluginPrefFrame *frame = pref_frame_cb(plugin);
-
-		if(frame) {
-			config = pidgin_plugin_pref_create_frame(frame);
-
-			*purple_pref_frame = frame;
-		}
-	}
-
-	return config;
-}
-
-static void
-pref_dialog_close(PurplePlugin *plugin)
-{
-	PurplePluginInfo *info;
-	PidginPluginUiData *ui_data;
-
-	g_return_if_fail(plugin != NULL);
-
-	info = purple_plugin_get_info(plugin);
-
-	ui_data = purple_plugin_info_get_ui_data(info);
-	if (ui_data == NULL)
-		return;
-
-	if (ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST) {
-		purple_request_close(PURPLE_REQUEST_FIELDS,
-			ui_data->u.request_handle);
-		return;
-	}
-
-	g_return_if_fail(ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME);
-
-	gtk_widget_destroy(ui_data->u.frame.dialog);
-
-	if (ui_data->u.frame.pref_frame)
-		purple_plugin_pref_frame_destroy(ui_data->u.frame.pref_frame);
-
-	g_free(ui_data);
-	purple_plugin_info_set_ui_data(info, NULL);
-}
-
-
-static void
-pref_dialog_response_cb(GtkWidget *dialog, int response, PurplePlugin *plugin)
-{
-	if (response == GTK_RESPONSE_CLOSE ||
-		response == GTK_RESPONSE_DELETE_EVENT)
-	{
-		pref_dialog_close(plugin);
-	}
-}
-
-static void
-pidgin_plugin_open_config(PurplePlugin *plugin, GtkWindow *parent)
-{
-	PurplePluginInfo *info;
-	PidginPluginInfoPrivate *priv = NULL;
-	PidginPluginUiData *ui_data;
-	PurplePluginPrefFrameCb pref_frame_cb;
-	PurplePluginPrefRequestCb pref_request_cb;
-	PidginPluginConfigFrameCb get_pidgin_frame = NULL;
-	gint prefs_count;
-
-	g_return_if_fail(plugin != NULL);
-
-	info = purple_plugin_get_info(plugin);
-
-	if (!pidgin_plugin_has_prefs(plugin)) {
-		purple_debug_warning("gtkplugin", "Plugin has no prefs");
-		return;
-	}
-
-	if (purple_plugin_info_get_ui_data(info))
-		return;
-
-	if (PIDGIN_IS_PLUGIN_INFO(info))
-		priv = pidgin_plugin_info_get_instance_private(
-				PIDGIN_PLUGIN_INFO(info));
-
-	pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info);
-	pref_request_cb = purple_plugin_info_get_pref_request_cb(info);
-
-	if (priv)
-		get_pidgin_frame = priv->config_frame_cb;
-
-	prefs_count = 0;
-	if (pref_frame_cb)
-		prefs_count++;
-	if (pref_request_cb)
-		prefs_count++;
-	if (get_pidgin_frame)
-		prefs_count++;
-
-	if (prefs_count > 1) {
-		purple_debug_warning("gtkplugin",
-		                     "Plugin %s contains more than one prefs "
-		                     "callback, some will be ignored.",
-		                     gplugin_plugin_info_get_name(
-		                             GPLUGIN_PLUGIN_INFO(info)));
-	}
-	g_return_if_fail(prefs_count > 0);
-
-	ui_data = g_new0(PidginPluginUiData, 1);
-	purple_plugin_info_set_ui_data(info, ui_data);
-
-	/* Priority: pidgin frame > purple request > purple frame
-	 * Purple frame could be replaced with purple request some day.
-	 */
-	if (pref_request_cb && !get_pidgin_frame) {
-		ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST;
-		ui_data->u.request_handle = pref_request_cb(plugin);
-		purple_request_add_close_notify(ui_data->u.request_handle,
-			purple_callback_set_zero, &info->ui_data);
-		purple_request_add_close_notify(ui_data->u.request_handle,
-			g_free, ui_data);
-	} else {
-		GtkWidget *box, *dialog;
-
-		ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME;
-
-		box = pidgin_plugin_get_config_frame(plugin,
-			&ui_data->u.frame.pref_frame);
-		if (box == NULL) {
-			purple_debug_error("gtkplugin",
-				"Failed to display prefs frame");
-			g_free(ui_data);
-			purple_plugin_info_set_ui_data(info, NULL);
-			return;
-		}
-		gtk_widget_set_vexpand(box, TRUE);
-
-		ui_data->u.frame.dialog = dialog = gtk_dialog_new_with_buttons(
-			PIDGIN_ALERT_TITLE, parent,
-			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE,
-			GTK_RESPONSE_CLOSE, NULL);
-
-		g_signal_connect(G_OBJECT(dialog), "response",
-			G_CALLBACK(pref_dialog_response_cb), plugin);
-
-		gtk_container_add(GTK_CONTAINER(
-			gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
-			pidgin_make_scrollable(box, GTK_POLICY_NEVER,
-				GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, 400, 400));
-
-		gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
-		gtk_window_set_title(GTK_WINDOW(dialog),
-		                     _(gplugin_plugin_info_get_name(
-		                             GPLUGIN_PLUGIN_INFO(info))));
-		gtk_widget_show_all(dialog);
-	}
-}
-
-void
-pidgin_plugins_save(void)
-{
-	purple_plugins_save_loaded(PIDGIN_PREFS_ROOT "/plugins/loaded");
-}
-
-static void
-update_plugin_list(void *data)
-{
-	GtkListStore *ls = GTK_LIST_STORE(data);
-	GtkTreeIter iter;
-	GList *plugins, *l;
-	PurplePlugin *plug;
-	PurplePluginInfo *info;
-	GPluginPluginInfo *ginfo;
-
-	gtk_list_store_clear(ls);
-	purple_plugins_refresh();
-
-	plugins = purple_plugins_find_all();
-
-	for (l = plugins; l != NULL; l = l->next)
-	{
-		char *name;
-		char *version;
-		char *summary;
-		char *desc;
-		plug = PURPLE_PLUGIN(l->data);
-		info = purple_plugin_get_info(plug);
-		ginfo = GPLUGIN_PLUGIN_INFO(info);
-
-		if (purple_plugin_is_internal(plug))
-			continue;
-
-		gtk_list_store_append (ls, &iter);
-
-		if (gplugin_plugin_info_get_name(ginfo)) {
-			name = g_markup_escape_text(
-			        _(gplugin_plugin_info_get_name(ginfo)), -1);
-		} else {
-			char *tmp = g_path_get_basename(
-			        gplugin_plugin_get_filename(plug));
-			name = g_markup_escape_text(tmp, -1);
-			g_free(tmp);
-		}
-		version = g_markup_escape_text(
-		        gplugin_plugin_info_get_version(ginfo), -1);
-		summary = g_markup_escape_text(
-		        gplugin_plugin_info_get_summary(ginfo), -1);
-
-		desc = g_strdup_printf("<b>%s</b> %s\n%s", name,
-				       version,
-				       summary);
-		g_free(name);
-		g_free(version);
-		g_free(summary);
-
-		gtk_list_store_set(ls, &iter,
-				   0, purple_plugin_is_loaded(plug),
-				   1, desc,
-				   2, plug,
-				   3, purple_plugin_info_get_error(info),
-				   -1);
-		g_free(desc);
-	}
-
-	g_list_free(plugins);
-}
-
-static gboolean
-check_if_loaded(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
-{
-	PurplePlugin *plugin;
-	gtk_tree_model_get(model, iter, 2, &plugin, -1);
-	gtk_list_store_set(GTK_LIST_STORE(model), iter,
-					           0, purple_plugin_is_loaded(plugin),
-					           -1);
-	return FALSE;
-}
-
-static void
-update_loaded_plugins(GtkTreeModel *model)
-{
-	gtk_tree_model_foreach(model, check_if_loaded, NULL);
-}
-
-static void plugin_loading_common(PurplePlugin *plugin, GtkTreeView *view, gboolean loaded)
-{
-	GtkTreeIter iter;
-	GtkTreeModel *model = gtk_tree_view_get_model(view);
-
-	if (gtk_tree_model_get_iter_first(model, &iter)) {
-		do {
-			PurplePlugin *plug;
-			GtkTreeSelection *sel;
-
-			gtk_tree_model_get(model, &iter, 2, &plug, -1);
-
-			if (plug != plugin)
-				continue;
-
-			gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, loaded, -1);
-
-			/* If the loaded/unloaded plugin is the selected row,
-			 * update the pref_button. */
-			sel = gtk_tree_view_get_selection(view);
-			if (gtk_tree_selection_get_selected(sel, &model, &iter))
-			{
-				gtk_tree_model_get(model, &iter, 2, &plug, -1);
-				if (plug == plugin)
-				{
-					gtk_widget_set_sensitive(pref_button,
-						pidgin_plugin_has_prefs(plug));
-				}
-			}
-
-			break;
-		} while (gtk_tree_model_iter_next(model, &iter));
-	}
-}
-
-static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
-{
-	GtkTreeView *view = (GtkTreeView *)data;
-	plugin_loading_common(plugin, view, TRUE);
-}
-
-static void plugin_unload_cb(PurplePlugin *plugin, gpointer data)
-{
-	GtkTreeView *view = (GtkTreeView *)data;
-	plugin_loading_common(plugin, view, FALSE);
-}
-
-static void plugin_unload_confirm_cb(gpointer *data)
-{
-	PurplePlugin *plugin = (PurplePlugin *)data[0];
-	GtkTreeModel *model = (GtkTreeModel *)data[1];
-	GtkTreeIter *iter = (GtkTreeIter *)data[2];
-
-	plugin_toggled_stage_two(plugin, model, iter, NULL, TRUE);
-
-	g_free(data);
-}
-
-static void plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer data)
-{
-	GtkTreeModel *model = (GtkTreeModel *)data;
-	GtkTreeIter *iter = g_new(GtkTreeIter, 1);
-	GtkTreePath *path = gtk_tree_path_new_from_string(pth);
-	PurplePlugin *plug;
-	GError *error = NULL;
-
-	gtk_tree_model_get_iter(model, iter, path);
-	gtk_tree_path_free(path);
-	gtk_tree_model_get(model, iter, 2, &plug, -1);
-
-	if (!purple_plugin_is_loaded(plug))
-	{
-		pidgin_set_cursor(plugin_dialog, GDK_WATCH);
-
-		purple_plugin_load(plug, &error);
-		plugin_toggled_stage_two(plug, model, iter, error, FALSE);
-
-		pidgin_clear_cursor(plugin_dialog);
-	}
-	else
-	{
-		pref_dialog_close(plug);
-
-		if (purple_plugin_get_dependent_plugins(plug) != NULL)
-		{
-			GString *tmp = g_string_new(_("The following plugins will be unloaded."));
-			GSList *l;
-			gpointer *cb_data;
-
-			for (l = purple_plugin_get_dependent_plugins(plug); l != NULL ; l = l->next)
-			{
-				const char *dep_name = (const char *)l->data;
-				PurplePlugin *dep_plugin = purple_plugins_find_plugin(dep_name);
-				GPluginPluginInfo *dep_info;
-
-				if (dep_plugin == NULL) {
-					purple_debug_error("gtkplugin",
-					                   "The %s plugin could not be found.",
-					                   dep_name);
-					continue;
-				}
-
-				dep_info = GPLUGIN_PLUGIN_INFO(
-				        purple_plugin_get_info(dep_plugin));
-				g_string_append_printf(
-				        tmp, "\n\t%s\n",
-				        gplugin_plugin_info_get_name(dep_info));
-			}
-
-			cb_data = g_new(gpointer, 3);
-			cb_data[0] = plug;
-			cb_data[1] = model;
-			cb_data[2] = iter;
-
-			purple_request_action(plugin_dialog, NULL,
-				_("Multiple plugins will be unloaded."),
-				tmp->str, 0, NULL, cb_data, 2,
-				_("Unload Plugins"),
-				G_CALLBACK(plugin_unload_confirm_cb),
-				_("Cancel"), g_free);
-			g_string_free(tmp, TRUE);
-		}
-		else
-			plugin_toggled_stage_two(plug, model, iter, NULL, TRUE);
-	}
-}
-
-static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, GtkTreeIter *iter, GError *error, gboolean unload)
-{
-	GPluginPluginInfo *info =
-	        GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plug));
-
-	if (unload)
-	{
-		pidgin_set_cursor(plugin_dialog, GDK_WATCH);
-
-		if (!purple_plugin_unload(plug, &error))
-		{
-			const char *primary = _("Could not unload plugin");
-			const char *reload = _("The plugin could not be unloaded now, but will be disabled at the next startup.");
-
-			char *tmp = g_strdup_printf("%s\n\n%s", reload, error->message);
-			purple_notify_warning(NULL, NULL, primary, tmp, NULL);
-			g_free(tmp);
-
-			purple_plugin_disable(plug);
-		}
-
-		pidgin_clear_cursor(plugin_dialog);
-	} else if (error) {
-		purple_notify_warning(NULL, NULL, _("Could not load plugin"), error->message, NULL);
-	}
-
-	gtk_widget_set_sensitive(pref_button, pidgin_plugin_has_prefs(plug));
-
-	if (error != NULL)
-	{
-		gchar *name = g_markup_escape_text(
-		        gplugin_plugin_info_get_name(info), -1);
-
-		gchar *disp_error = g_markup_escape_text(error->message, -1);
-		gchar *text;
-
-		text = g_strdup_printf("<b>%s</b> %s\n<span weight=\"bold\" "
-		                       "color=\"red\">%s</span>",
-		                       gplugin_plugin_info_get_name(info),
-		                       gplugin_plugin_info_get_version(info),
-		                       disp_error);
-		gtk_list_store_set(GTK_LIST_STORE (model), iter,
-				   1, text,
-				   -1);
-		g_free(text);
-
-		text = g_strdup_printf(
-			"<span weight=\"bold\" color=\"red\">%s</span>",
-			disp_error);
-		gtk_label_set_markup(plugin_error, text);
-		g_free(text);
-
-		g_free(disp_error);
-		g_free(name);
-
-		g_error_free(error);
-	}
-
-	if ((unload && purple_plugin_get_dependent_plugins(plug)) ||
-	    (!unload && gplugin_plugin_info_get_dependencies(info))) {
-		update_loaded_plugins(model);
-	} else {
-		gtk_list_store_set(GTK_LIST_STORE (model), iter,
-			               0, purple_plugin_is_loaded(plug),
-			               -1);
-	}
-	g_free(iter);
-
-	pidgin_plugins_save();
-}
-
-static gboolean ensure_plugin_visible(void *data)
-{
-	GtkTreeSelection *sel = GTK_TREE_SELECTION(data);
-	GtkTreeView *tv = gtk_tree_selection_get_tree_view(sel);
-	GtkTreeModel *model = gtk_tree_view_get_model(tv);
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	if (!gtk_tree_selection_get_selected (sel, &model, &iter))
-		return FALSE;
-	path = gtk_tree_model_get_path(model, &iter);
-	gtk_tree_view_scroll_to_cell(gtk_tree_selection_get_tree_view(sel), path, NULL, FALSE, 0, 0);
-	gtk_tree_path_free(path);
-	return FALSE;
-}
-
-static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model)
-{
-	gchar *buf, *tmp, *name, *version;
-	gchar *authors = NULL;
-	const gchar * const *authorlist;
-	GtkTreeIter  iter;
-	GValue val;
-	PurplePlugin *plug;
-	PurplePluginInfo *info;
-	GPluginPluginInfo *ginfo;
-
-	if (!gtk_tree_selection_get_selected (sel, &model, &iter))
-	{
-		gtk_widget_set_sensitive(pref_button, FALSE);
-
-		/* Collapse and disable the expander widget */
-		gtk_expander_set_expanded(GTK_EXPANDER(expander), FALSE);
-		gtk_widget_set_sensitive(expander, FALSE);
-
-		return;
-	}
-
-	gtk_widget_set_sensitive(expander, TRUE);
-
-	val.g_type = 0;
-	gtk_tree_model_get_value (model, &iter, 2, &val);
-	plug = g_value_get_pointer(&val);
-	info = purple_plugin_get_info(plug);
-	ginfo = GPLUGIN_PLUGIN_INFO(info);
-
-	name = g_markup_escape_text(gplugin_plugin_info_get_name(ginfo), -1);
-	version = g_markup_escape_text(gplugin_plugin_info_get_version(ginfo),
-	                               -1);
-	buf = g_strdup_printf(
-		"<span size=\"larger\" weight=\"bold\">%s</span> "
-		"<span size=\"smaller\">%s</span>",
-		name, version);
-	gtk_label_set_markup(plugin_name, buf);
-	g_free(name);
-	g_free(version);
-	g_free(buf);
-
-	gtk_text_buffer_set_text(
-	        plugin_desc, gplugin_plugin_info_get_description(ginfo), -1);
-
-	authorlist = gplugin_plugin_info_get_authors(ginfo);
-	if (authorlist)
-		authors = g_strjoinv(",\n", (gchar **)authorlist);
-	gtk_label_set_text(plugin_authors, authors);
-	g_free(authors);
-
-	gtk_label_set_text(plugin_filename, gplugin_plugin_get_filename(plug));
-
-	g_free(plugin_website_uri);
-	plugin_website_uri = g_strdup(gplugin_plugin_info_get_website(ginfo));
-
-	if (plugin_website_uri)
-	{
-		tmp = g_markup_escape_text(plugin_website_uri, -1);
-		buf = g_strdup_printf("<span underline=\"single\" "
-			"foreground=\"blue\">%s</span>", tmp);
-		gtk_label_set_markup(plugin_website, buf);
-		g_free(tmp);
-		g_free(buf);
-	}
-	else
-	{
-		gtk_label_set_text(plugin_website, NULL);
-	}
-
-	if (purple_plugin_info_get_error(info) == NULL)
-	{
-		gtk_label_set_text(plugin_error, NULL);
-	}
-	else
-	{
-		tmp = g_markup_escape_text(purple_plugin_info_get_error(info), -1);
-		buf = g_strdup_printf(
-			_("<span foreground=\"red\" weight=\"bold\">"
-			  "Error: %s\n"
-			  "Check the plugin website for an update."
-			  "</span>"),
-			tmp);
-		gtk_label_set_markup(plugin_error, buf);
-		g_free(buf);
-		g_free(tmp);
-	}
-
-	gtk_widget_set_sensitive(pref_button, pidgin_plugin_has_prefs(plug));
-
-	/* Make sure the selected plugin is still visible */
-	g_idle_add(ensure_plugin_visible, sel);
-
-	g_value_unset(&val);
-}
-
-static void plugin_dialog_response_cb(GtkWidget *d, int response, GtkTreeSelection *sel)
-{
-	PurplePlugin *plugin;
-	GtkTreeModel *model;
-	GValue val;
-	GtkTreeIter iter;
-	GList *list;
-
-	g_return_if_fail(d != NULL);
-
-	switch (response) {
-	case GTK_RESPONSE_CLOSE:
-	case GTK_RESPONSE_DELETE_EVENT:
-		purple_request_close_with_handle(plugin_dialog);
-		purple_signals_disconnect_by_handle(plugin_dialog);
-		list = purple_plugins_find_all();
-		g_list_free_full(list, (GDestroyNotify)pref_dialog_close);
-		gtk_widget_destroy(d);
-		plugin_dialog = NULL;
-		break;
-	case PIDGIN_RESPONSE_CONFIGURE:
-		if (! gtk_tree_selection_get_selected (sel, &model, &iter))
-			return;
-		val.g_type = 0;
-		gtk_tree_model_get_value(model, &iter, 2, &val);
-		plugin = g_value_get_pointer(&val);
-		g_value_unset(&val);
-		if (plugin == NULL)
-			break;
-
-		pidgin_plugin_open_config(plugin, GTK_WINDOW(d));
-
-		break;
-	}
-}
-
-static void
-show_plugin_prefs_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *dialog)
-{
-	GtkTreeSelection *sel;
-	GtkTreeIter iter;
-	PurplePlugin *plugin;
-	GtkTreeModel *model;
-
-	sel = gtk_tree_view_get_selection(view);
-
-	if (!gtk_tree_selection_get_selected(sel, &model, &iter))
-		return;
-
-	gtk_tree_model_get(model, &iter, 2, &plugin, -1);
-
-	if (!purple_plugin_is_loaded(plugin))
-		return;
-
-	/* Now show the pref-dialog for the plugin */
-	plugin_dialog_response_cb(dialog, PIDGIN_RESPONSE_CONFIGURE, sel);
-}
-
-static gboolean
-pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, cairo_t *cr, gpointer data)
-{
-	PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
-	GtkStyleContext *context = gtk_widget_get_style_context(tipwindow);
-	gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
-	gtk_render_layout(context, cr, 6, 6, layout);
-	return TRUE;
-}
-
-static gboolean
-pidgin_plugins_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
-		gpointer data, int *w, int *h)
-{
-	GtkTreeIter iter;
-	GtkTreeView *treeview = GTK_TREE_VIEW(data);
-	PurplePlugin *plugin = NULL;
-	GPluginPluginInfo *info;
-	GtkTreeModel *model = gtk_tree_view_get_model(treeview);
-	PangoLayout *layout;
-	int width, height;
-	const char * const *authorlist;
-	char *markup, *name, *desc;
-	char *authors = NULL, *pauthors = NULL;
-
-	if (!gtk_tree_model_get_iter(model, &iter, path))
-		return FALSE;
-
-	gtk_tree_model_get(model, &iter, 2, &plugin, -1);
-	info = GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
-	authorlist = gplugin_plugin_info_get_authors(info);
-
-	if (authorlist)
-		authors = g_strjoinv(", ", (gchar **)authorlist);
-	if (authors)
-		pauthors = g_markup_escape_text(authors, -1);
-
-	markup = g_strdup_printf(
-	        "<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> "
-	        "%s\n<b>%s:</b> %s",
-	        name = g_markup_escape_text(gplugin_plugin_info_get_name(info), -1),
-	        _("Description"),
-	        desc = g_markup_escape_text(
-	                gplugin_plugin_info_get_description(info), -1),
-	        (authorlist && g_strv_length((gchar **)authorlist) > 1
-	                 ? _("Authors")
-	                 : _("Author")),
-	        pauthors);
-
-	layout = gtk_widget_create_pango_layout(tipwindow, NULL);
-	pango_layout_set_markup(layout, markup, -1);
-	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(layout, 600000);
-	pango_layout_get_size(layout, &width, &height);
-	g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref);
-
-	if (w)
-		*w = PANGO_PIXELS(width) + 12;
-	if (h)
-		*h = PANGO_PIXELS(height) + 12;
-
-	g_free(markup);
-	g_free(name);
-	g_free(desc);
-	g_free(pauthors);
-	g_free(authors);
-
-	return TRUE;
-}
-
-static gboolean
-website_button_motion_cb(GtkWidget *button, GdkEventCrossing *event,
-                          gpointer unused)
-{
-	if (plugin_website_uri) {
-		pidgin_set_cursor(button, GDK_HAND2);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-website_button_clicked_cb(GtkButton *button, GdkEventButton *event,
-                          gpointer unused)
-{
-	if (plugin_website_uri) {
-		purple_notify_uri(NULL, plugin_website_uri);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static GtkWidget *
-create_details()
-{
-	GtkBox *vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 3));
-	GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-	GtkWidget *label, *view, *website_button;
-
-	plugin_name = GTK_LABEL(gtk_label_new(NULL));
-	gtk_label_set_xalign(plugin_name, 0);
-	gtk_label_set_yalign(plugin_name, 0);
-	gtk_label_set_line_wrap(plugin_name, FALSE);
-	gtk_label_set_selectable(plugin_name, TRUE);
-	gtk_box_pack_start(vbox, GTK_WIDGET(plugin_name), FALSE, FALSE, 0);
-
-	view = gtk_text_view_new();
-	plugin_desc = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
-	g_object_set(view, "wrap-mode", GTK_WRAP_WORD,
-	                   "editable",  FALSE,
-	                   "left-margin",  PIDGIN_HIG_CAT_SPACE,
-	                   "right-margin", PIDGIN_HIG_CAT_SPACE,
-	                   NULL);
-	gtk_box_pack_start(vbox, view, TRUE, TRUE, 0);
-
-	plugin_error = GTK_LABEL(gtk_label_new(NULL));
-	gtk_label_set_xalign(plugin_error, 0);
-	gtk_label_set_yalign(plugin_error, 0);
-	gtk_label_set_line_wrap(plugin_error, FALSE);
-	gtk_label_set_selectable(plugin_error, TRUE);
-	gtk_box_pack_start(vbox, GTK_WIDGET(plugin_error), FALSE, FALSE, 0);
-
-	plugin_authors = GTK_LABEL(gtk_label_new(NULL));
-	gtk_label_set_line_wrap(plugin_authors, FALSE);
-	gtk_label_set_xalign(plugin_authors, 0);
-	gtk_label_set_yalign(plugin_authors, 0);
-	gtk_label_set_selectable(plugin_authors, TRUE);
-	pidgin_add_widget_to_vbox(vbox, "", sg,
-		GTK_WIDGET(plugin_authors), TRUE, &label);
-	gtk_label_set_markup(GTK_LABEL(label), _("<b>Written by:</b>"));
-	gtk_label_set_xalign(GTK_LABEL(label), 0);
-	gtk_label_set_yalign(GTK_LABEL(label), 0);
-
-	website_button = gtk_event_box_new();
-	gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button), FALSE);
-
-	plugin_website = GTK_LABEL(gtk_label_new(NULL));
-	g_object_set(G_OBJECT(plugin_website),
-		"ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
-	gtk_label_set_xalign(plugin_website, 0);
-	gtk_label_set_yalign(plugin_website, 0);
-	gtk_container_add(GTK_CONTAINER(website_button),
-		GTK_WIDGET(plugin_website));
-	g_signal_connect(website_button, "button-release-event",
-		G_CALLBACK(website_button_clicked_cb), NULL);
-	g_signal_connect(website_button, "enter-notify-event",
-		G_CALLBACK(website_button_motion_cb), NULL);
-	g_signal_connect(website_button, "leave-notify-event",
-		G_CALLBACK(pidgin_clear_cursor), NULL);
-
-	pidgin_add_widget_to_vbox(vbox, "", sg, website_button, TRUE, &label);
-	gtk_label_set_markup(GTK_LABEL(label), _("<b>Web site:</b>"));
-	gtk_label_set_xalign(GTK_LABEL(label), 0);
-
-	plugin_filename = GTK_LABEL(gtk_label_new(NULL));
-	gtk_label_set_line_wrap(plugin_filename, FALSE);
-	gtk_label_set_xalign(plugin_filename, 0);
-	gtk_label_set_yalign(plugin_filename, 0);
-	gtk_label_set_selectable(plugin_filename, TRUE);
-	pidgin_add_widget_to_vbox(vbox, "", sg,
-		GTK_WIDGET(plugin_filename), TRUE, &label);
-	gtk_label_set_markup(GTK_LABEL(label), _("<b>Filename:</b>"));
-	gtk_label_set_xalign(GTK_LABEL(label), 0);
-	gtk_label_set_yalign(GTK_LABEL(label), 0);
-
-	g_object_unref(sg);
-
-	return GTK_WIDGET(vbox);
-}
-
-
-void pidgin_plugin_dialog_show()
-{
-	GtkWidget *event_view;
-	GtkListStore *ls;
-	GtkCellRenderer *rend, *rendt;
-	GtkTreeViewColumn *col;
-	GtkTreeSelection *sel;
-
-	if (plugin_dialog != NULL) {
-		gtk_window_present(GTK_WINDOW(plugin_dialog));
-		return;
-	}
-
-	plugin_dialog = gtk_dialog_new();
-	gtk_window_set_title(GTK_WINDOW(plugin_dialog), _("Plugins"));
-	pref_button = gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
-						_("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE);
-	gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
-						GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
-	gtk_widget_set_sensitive(pref_button, FALSE);
-	gtk_window_set_role(GTK_WINDOW(plugin_dialog), "plugins");
-
-	ls = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
-	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),
-					     1, GTK_SORT_ASCENDING);
-
-	update_plugin_list(ls);
-
-	event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls));
-
-	g_signal_connect(G_OBJECT(event_view), "row-activated",
-				G_CALLBACK(show_plugin_prefs_cb), plugin_dialog);
-
-	purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin_dialog,
-	                    PURPLE_CALLBACK(plugin_load_cb), event_view);
-	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin_dialog,
-	                    PURPLE_CALLBACK(plugin_unload_cb), event_view);
-
-	rend = gtk_cell_renderer_toggle_new();
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
-
-	col = gtk_tree_view_column_new_with_attributes (_("Enabled"),
-							rend,
-							"active", 0,
-							NULL);
-	gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
-	gtk_tree_view_column_set_sort_column_id(col, 0);
-	g_signal_connect(G_OBJECT(rend), "toggled",
-			 G_CALLBACK(plugin_toggled), ls);
-
-	rendt = gtk_cell_renderer_text_new();
-	g_object_set(rendt,
-		     "foreground", "#c0c0c0",
-		     NULL);
-	col = gtk_tree_view_column_new_with_attributes (_("Name"),
-							rendt,
-							"markup", 1,
-							"foreground-set", 3,
-							NULL);
-	gtk_tree_view_column_set_expand (col, TRUE);
-	g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-	gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
-	gtk_tree_view_column_set_sort_column_id(col, 1);
-	g_object_unref(G_OBJECT(ls));
-	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog))),
-		pidgin_make_scrollable(event_view, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
-		TRUE, TRUE, 0);
-	gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view), 1);
-	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view),
-				pidgin_tree_view_search_equal_func, NULL, NULL);
-
-	pidgin_tooltip_setup_for_treeview(event_view, event_view,
-			pidgin_plugins_create_tooltip,
-			pidgin_plugins_paint_tooltip);
-
-
-	expander = gtk_expander_new(_("<b>Plugin Details</b>"));
-	gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
-	gtk_widget_set_sensitive(expander, FALSE);
-	gtk_container_add(GTK_CONTAINER(expander), create_details());
-	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog))),
-	                   expander, FALSE, FALSE, 0);
-
-
-	g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_plugin_sel), NULL);
-	g_signal_connect(G_OBJECT(plugin_dialog), "response", G_CALLBACK(plugin_dialog_response_cb), sel);
-	gtk_window_set_default_size(GTK_WINDOW(plugin_dialog), 430, 530);
-
-	pidgin_auto_parent_window(plugin_dialog);
-
-	gtk_widget_show_all(plugin_dialog);
-}
--- a/pidgin/gtkplugin.h	Thu Nov 21 21:41:21 2019 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/* 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 _PIDGINPLUGIN_H_
-#define _PIDGINPLUGIN_H_
-/**
- * SECTION:gtkplugin
- * @section_id: pidgin-gtkplugin
- * @short_description: <filename>gtkplugin.h</filename>
- * @title: Plugin API
- */
-
-#include "pidgin.h"
-#include "plugins.h"
-
-#define PIDGIN_TYPE_PLUGIN_INFO             (pidgin_plugin_info_get_type())
-#define PIDGIN_PLUGIN_INFO(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfo))
-#define PIDGIN_PLUGIN_INFO_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoClass))
-#define PIDGIN_IS_PLUGIN_INFO(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_PLUGIN_INFO))
-#define PIDGIN_IS_PLUGIN_INFO_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_PLUGIN_INFO))
-#define PIDGIN_PLUGIN_INFO_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoClass))
-
-typedef struct _PidginPluginInfo PidginPluginInfo;
-typedef struct _PidginPluginInfoClass PidginPluginInfoClass;
-
-typedef GtkWidget *(*PidginPluginConfigFrameCb)(PurplePlugin *plugin);
-
-/**
- * PidginPluginInfo:
- *
- * Extends #PurplePluginInfo to hold UI information for pidgin.
- */
-struct _PidginPluginInfo {
-	PurplePluginInfo parent;
-};
-
-/**
- * PidginPluginInfoClass:
- *
- * The base class for all #PidginPluginInfo's.
- */
-struct _PidginPluginInfoClass {
-	PurplePluginInfoClass parent_class;
-
-	/*< private >*/
-	void (*_pidgin_reserved1)(void);
-	void (*_pidgin_reserved2)(void);
-	void (*_pidgin_reserved3)(void);
-	void (*_pidgin_reserved4)(void);
-};
-
-G_BEGIN_DECLS
-
-/**
- * pidgin_plugin_info_get_type:
- *
- * Returns: The #GType for the #PidginPluginInfo object.
- */
-GType pidgin_plugin_info_get_type(void);
-
-/**
- * pidgin_plugin_info_new:
- * @first_property:  The first property name
- * @...:  The value of the first property, followed optionally by more
- *             name/value pairs, followed by %NULL
- *
- * Creates a new #PidginPluginInfo instance to be returned from
- * #plugin_query of a pidgin plugin, using the provided name/value
- * pairs.
- *
- * See purple_plugin_info_new() for a list of available property names.
- * Additionally, you can provide the property
- * <literal>"gtk-config-frame-cb"</literal>, which should be a callback that
- * returns a #GtkWidget for the plugin's configuration
- * (see #PidginPluginConfigFrameCb).
- *
- * See purple_plugin_info_new().
- *
- * Returns: A new #PidginPluginInfo instance.
- */
-PidginPluginInfo *pidgin_plugin_info_new(const char *first_property, ...)
-                  G_GNUC_NULL_TERMINATED;
-
-/**
- * pidgin_plugins_save:
- *
- * Saves all loaded plugins.
- */
-void pidgin_plugins_save(void);
-
-/**
- * pidgin_plugin_dialog_show:
- *
- * Shows the Plugins dialog
- */
-void pidgin_plugin_dialog_show(void);
-
-G_END_DECLS
-
-#endif /* _PIDGINPLUGIN_H_ */
--- a/pidgin/libpidgin.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/libpidgin.c	Fri Nov 22 04:10:26 2019 +0000
@@ -52,7 +52,6 @@
 #include "gtkidle.h"
 #include "gtkmedia.h"
 #include "gtknotify.h"
-#include "gtkplugin.h"
 #include "gtkpounce.h"
 #include "gtkprefs.h"
 #include "gtkprivacy.h"
@@ -62,10 +61,11 @@
 #include "gtksmiley-theme.h"
 #include "gtksound.h"
 #include "gtkutils.h"
-#include "pidginstock.h"
 #include "gtkwhiteboard.h"
 #include "pidgindebug.h"
 #include "pidginlog.h"
+#include "pidginplugininfo.h"
+#include "pidginstock.h"
 
 #ifndef _WIN32
 #include <signal.h>
--- a/pidgin/meson.build	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/meson.build	Fri Nov 22 04:10:26 2019 +0000
@@ -14,7 +14,6 @@
 	'gtkidle.c',
 	'gtkmedia.c',
 	'gtknotify.c',
-	'gtkplugin.c',
 	'gtkpluginpref.c',
 	'gtkpounce.c',
 	'gtkprefs.c',
@@ -44,6 +43,8 @@
 	'pidginlog.c',
 	'pidginmenutray.c',
 	'pidginmessage.c',
+	'pidginplugininfo.c',
+	'pidginpluginsdialog.c',
 	'pidgintalkatu.c',
 	'pidgintooltip.c',
 ]
@@ -65,7 +66,6 @@
 	'gtkmedia.h',
 	'gtknickcolors.h',
 	'gtknotify.h',
-	'gtkplugin.h',
 	'gtkpluginpref.h',
 	'gtkprefs.h',
 	'gtkprivacy.h',
@@ -96,6 +96,8 @@
 	'pidginlog.h',
 	'pidginmenutray.h',
 	'pidginmessage.h',
+	'pidginplugininfo.h',
+	'pidginpluginsdialog.h',
 	'pidgintalkatu.h',
 	'pidgintooltip.h',
 	'pidgin.h',
@@ -150,15 +152,16 @@
 	install_headers(libpidgin_headers, subdir : 'pidgin-3')
 
 	_libpidgin_dependencies = [
-	    	glib,
-	    	gstreamer_video,
-	    	gtk,
-	    	IOKIT,
-	    	json,
-	    	math,
+		glib,
+		gplugin_gtk_dep,
+		gstreamer_video,
+		gtk,
+		IOKIT,
+		json,
+		math,
 		libsoup,
-	        talkatu_dep,
-	    	libpurple_dep,
+		talkatu_dep,
+		libpurple_dep,
 	]
 	if x11.found()
 		_libpidgin_dependencies += x11
@@ -195,7 +198,7 @@
 	    filebase : 'pidgin-3',
 	    subdirs : 'pidgin-3',
 	    libraries : [libpidgin],
-	    requires : ['gtk+-3.0', 'purple-3', 'talkatu'],
+	    requires : ['gtk+-3.0', 'purple-3', 'talkatu', 'gplugin-gtk'],
 	    variables : ['plugindir=${libdir}/pidgin-@0@'.format(purple_major_version)])
 
 	if INSTALL_I18N
@@ -224,20 +227,21 @@
 		introspection_sources = libpidgin_headers
 
 		gnome.generate_gir(libpidgin,
-		    sources : introspection_sources,
-		    includes : ['GObject-2.0', 'Gtk-3.0', libpurple_gir[0], talkatu_gir, gplugin_gir],
-		    namespace : 'Pidgin',
-		    symbol_prefix : 'pidgin',
-		    identifier_prefix : 'Pidgin',
-		    export_packages : 'pidgin-@0@'.format(purple_major_version),
-		    nsversion : '@0@.@1@'.format(purple_major_version,
-		                                 purple_minor_version),
-		    include_directories : [
-		        gplugin_include_directories,
-			talkatu_include_directories,
-		    ],
-		    install : true,
-		    extra_args : ['--quiet'])
+			sources : introspection_sources,
+			includes : ['GObject-2.0', 'Gtk-3.0', libpurple_gir[0], talkatu_gir, gplugin_gir, gplugin_gtk_gir],
+			namespace : 'Pidgin',
+			symbol_prefix : 'pidgin',
+			identifier_prefix : 'Pidgin',
+			export_packages : 'pidgin-@0@'.format(purple_major_version),
+			nsversion : '@0@.@1@'.format(purple_major_version,
+			                             purple_minor_version),
+			include_directories : [
+				gplugin_include_directories,
+				gplugin_gtk_include_directories,
+				talkatu_include_directories,
+			],
+			install : true,
+			extra_args : ['--quiet'])
 	endif
 
 	subdir('data')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginplugininfo.c	Fri Nov 22 04:10:26 2019 +0000
@@ -0,0 +1,62 @@
+/* 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
+ */
+#include "pidginplugininfo.h"
+
+struct _PidginPluginInfo {
+	PurplePluginInfo parent;
+};
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginPluginInfo, pidgin_plugin_info, PURPLE_TYPE_PLUGIN_INFO);
+
+static void
+pidgin_plugin_info_init(PidginPluginInfo *info) {
+}
+
+static void
+pidgin_plugin_info_class_init(PidginPluginInfoClass *klass) {
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+PidginPluginInfo *
+pidgin_plugin_info_new(const char *first_property, ...)
+{
+	GObject *info;
+	va_list var_args;
+
+	/* at least ID is required */
+	if (!first_property) {
+		return NULL;
+	}
+
+	va_start(var_args, first_property);
+	info = g_object_new_valist(PIDGIN_TYPE_PLUGIN_INFO, first_property,
+	                           var_args);
+	va_end(var_args);
+
+	g_object_set(info, "ui-requirement", PIDGIN_UI, NULL);
+
+	return PIDGIN_PLUGIN_INFO(info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginplugininfo.h	Fri Nov 22 04:10:26 2019 +0000
@@ -0,0 +1,62 @@
+/* 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_PLUGIN_INFO_H
+#define PIDGIN_PLUGIN_INFO_H
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+#include "pidgin.h"
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_PLUGIN_INFO (pidgin_plugin_info_get_type())
+G_DECLARE_FINAL_TYPE(PidginPluginInfo, pidgin_plugin_info, PIDGIN, PLUGIN_INFO, PurplePluginInfo)
+
+/**
+ * pidgin_plugin_info_new:
+ * @first_property:  The first property name
+ * @...:  The value of the first property, followed optionally by more
+ *        name/value pairs, followed by %NULL
+ *
+ * Creates a new #PidginPluginInfo instance to be returned from
+ * #plugin_query of a pidgin plugin, using the provided name/value
+ * pairs.
+ *
+ * See purple_plugin_info_new() for a list of available property names.
+ * Additionally, you can provide the property
+ * <literal>"gtk-config-frame-cb"</literal>, which should be a callback that
+ * returns a #GtkWidget for the plugin's configuration
+ * (see #PidginPluginConfigFrameCb).
+ *
+ * See purple_plugin_info_new().
+ *
+ * Returns: A new #PidginPluginInfo instance.
+ *
+ * Since: 3.0.0
+ */
+PidginPluginInfo *pidgin_plugin_info_new(const char *first_property, ...)
+                  G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* PIDGIN_PLUGIN_INFO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginpluginsdialog.c	Fri Nov 22 04:10:26 2019 +0000
@@ -0,0 +1,345 @@
+/*
+ * 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
+ *
+ */
+
+#include "pidginpluginsdialog.h"
+
+#include <glib/gi18n.h>
+
+#include <gplugin.h>
+#include <gplugin-gtk.h>
+
+#include <purple.h>
+
+#include "gtkpluginpref.h"
+
+struct _PidginPluginsDialog {
+	GtkDialog parent;
+
+	GtkWidget *tree_view;
+	GtkWidget *configure_plugin_button;
+	GtkWidget *close_button;
+	GtkWidget *plugin_info;
+
+	GtkListStore *plugin_store;
+};
+
+/* this has a short life left to it... */
+typedef struct
+{
+	enum
+	{
+		PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME,
+		PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
+	} type;
+
+	union
+	{
+		struct
+		{
+			GtkWidget *dialog;
+			PurplePluginPrefFrame *pref_frame;
+		} frame;
+
+		gpointer request_handle;
+	} u;
+} PidginPluginUiData;
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+pidgin_plugins_dialog_plugin_has_config(GPluginPlugin *plugin) {
+	GPluginPluginInfo *ginfo = gplugin_plugin_get_info(plugin);
+	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(ginfo);
+	GPluginPluginState state;
+
+	g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE);
+
+	state = gplugin_plugin_get_state(plugin);
+
+	if (state != GPLUGIN_PLUGIN_STATE_LOADED) {
+		return FALSE;
+	}
+
+	return (purple_plugin_info_get_pref_frame_cb(info) ||
+	       purple_plugin_info_get_pref_request_cb(info));
+}
+
+static GPluginPlugin *
+pidgin_plugins_dialog_get_selected(PidginPluginsDialog *dialog) {
+	GPluginPlugin *plugin = NULL;
+	GtkTreeSelection *selection = NULL;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+
+	g_return_val_if_fail(PIDGIN_IS_PLUGINS_DIALOG(dialog), NULL);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree_view));
+	/* not sure if this is necessary, but playing defense. - grim 20191112 */
+	if(selection == NULL) {
+		return NULL;
+	}
+
+	if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		gtk_tree_model_get(model, &iter,
+		                   GPLUGIN_GTK_STORE_PLUGIN_COLUMN, &plugin,
+		                   -1);
+	}
+
+	return plugin;
+}
+
+static void
+pidgin_plugins_dialog_pref_dialog_close(GPluginPlugin *plugin) {
+	GPluginPluginInfo *ginfo = gplugin_plugin_get_info(plugin);
+	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(ginfo);
+	PidginPluginUiData *ui_data;
+
+	ui_data = purple_plugin_info_get_ui_data(info);
+	if (ui_data == NULL) {
+		purple_debug_info("PidginPluginsDialog", "failed to find uidata\n");
+		return;
+	}
+
+	if (ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST) {
+		purple_request_close(PURPLE_REQUEST_FIELDS,
+			ui_data->u.request_handle);
+		return;
+	}
+
+	g_return_if_fail(ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME);
+
+	gtk_widget_destroy(ui_data->u.frame.dialog);
+
+	if (ui_data->u.frame.pref_frame) {
+		purple_plugin_pref_frame_destroy(ui_data->u.frame.pref_frame);
+	}
+
+	g_free(ui_data);
+	purple_plugin_info_set_ui_data(info, NULL);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_plugins_dialog_close(GtkWidget *b, gpointer data) {
+	gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+pidgin_plugins_dialog_selection_cb(GtkTreeSelection *sel, gpointer data) {
+	PidginPluginsDialog *dialog = PIDGIN_PLUGINS_DIALOG(data);
+	GPluginPlugin *plugin = NULL;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+
+	if(gtk_tree_selection_get_selected(sel, &model, &iter)) {
+		gtk_tree_model_get(model, &iter,
+		                   GPLUGIN_GTK_STORE_PLUGIN_COLUMN, &plugin,
+		                   -1);
+	}
+
+	gplugin_gtk_plugin_info_set_plugin(
+		GPLUGIN_GTK_PLUGIN_INFO(dialog->plugin_info),
+		plugin
+	);
+
+	gtk_widget_set_sensitive(
+		GTK_WIDGET(dialog->configure_plugin_button),
+		pidgin_plugins_dialog_plugin_has_config(plugin)
+	);
+
+	g_object_unref(G_OBJECT(plugin));
+}
+
+static void
+pidgin_plugins_dialog_pref_dialog_response_cb(GtkWidget *dialog, int response,
+                                              gpointer data)
+{
+	if (response == GTK_RESPONSE_CLOSE ||
+		response == GTK_RESPONSE_DELETE_EVENT)
+	{
+		pidgin_plugins_dialog_pref_dialog_close(GPLUGIN_PLUGIN(data));
+	}
+}
+
+static void
+pidgin_plugins_dialog_config_plugin_cb(GtkWidget *button, gpointer data) {
+	PidginPluginsDialog *dialog = PIDGIN_PLUGINS_DIALOG(data);
+	PidginPluginUiData *ui_data;
+	PurplePluginInfo *info;
+	PurplePluginPrefFrameCb pref_frame_cb;
+	PurplePluginPrefRequestCb pref_request_cb;
+	GPluginPlugin *plugin = NULL;
+	GPluginPluginInfo *ginfo = NULL;
+	gint prefs_count;
+
+	plugin = pidgin_plugins_dialog_get_selected(dialog);
+	if(!GPLUGIN_IS_PLUGIN(plugin)) {
+		return;
+	}
+
+	ginfo = gplugin_plugin_get_info(plugin);
+	info = PURPLE_PLUGIN_INFO(ginfo);
+
+	if(purple_plugin_info_get_ui_data(info)) {
+		g_object_unref(G_OBJECT(plugin));
+		return;
+	}
+
+	pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info);
+	pref_request_cb = purple_plugin_info_get_pref_request_cb(info);
+
+	ui_data = g_new0(PidginPluginUiData, 1);
+	purple_plugin_info_set_ui_data(info, ui_data);
+
+	prefs_count = 0;
+	if (pref_frame_cb) {
+		prefs_count++;
+
+		ui_data->u.frame.pref_frame = pref_frame_cb(plugin);
+	}
+
+	if (pref_request_cb) {
+		prefs_count++;
+	}
+
+	if (prefs_count > 1) {
+		purple_debug_warning("gtkplugin",
+		                     "Plugin %s contains more than one prefs "
+		                     "callback, some will be ignored.",
+		                     gplugin_plugin_info_get_name(ginfo));
+	}
+	g_return_if_fail(prefs_count > 0);
+
+
+	/* Priority: pidgin frame > purple request > purple frame
+	 * Purple frame could be replaced with purple request some day.
+	 */
+	if (pref_request_cb) {
+		ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST;
+		ui_data->u.request_handle = pref_request_cb(plugin);
+		purple_request_add_close_notify(ui_data->u.request_handle,
+			purple_callback_set_zero, &info->ui_data);
+		purple_request_add_close_notify(ui_data->u.request_handle,
+			g_free, ui_data);
+	} else {
+		GtkWidget *box, *pdialog, *content, *sw;
+
+		ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME;
+
+		box = pidgin_plugin_pref_create_frame(ui_data->u.frame.pref_frame);
+		if (box == NULL) {
+			purple_debug_error("gtkplugin",
+				"Failed to display prefs frame");
+			g_free(ui_data);
+			purple_plugin_info_set_ui_data(info, NULL);
+			g_object_unref(G_OBJECT(plugin));
+			return;
+		}
+		gtk_widget_set_vexpand(box, TRUE);
+
+		ui_data->u.frame.dialog = pdialog = gtk_dialog_new_with_buttons(
+			PIDGIN_ALERT_TITLE, GTK_WINDOW(dialog),
+			GTK_DIALOG_DESTROY_WITH_PARENT,
+			_("Close"), GTK_RESPONSE_CLOSE,
+			NULL);
+
+		g_signal_connect(G_OBJECT(pdialog), "response",
+			G_CALLBACK(pidgin_plugins_dialog_pref_dialog_response_cb), plugin);
+
+		content = gtk_dialog_get_content_area(GTK_DIALOG(pdialog));
+
+		sw = gtk_scrolled_window_new(NULL, NULL);
+		gtk_container_add(GTK_CONTAINER(content), sw);
+		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+		                               GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+		gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+		                                    GTK_SHADOW_IN);
+		gtk_widget_set_size_request(sw, 400, 400);
+
+		gtk_container_add(GTK_CONTAINER(sw), box);
+
+		gtk_window_set_role(GTK_WINDOW(pdialog), "plugin_config");
+		gtk_window_set_title(GTK_WINDOW(pdialog),
+		                     _(gplugin_plugin_info_get_name(
+		                             GPLUGIN_PLUGIN_INFO(info))));
+		gtk_widget_show_all(pdialog);
+	}
+	g_object_unref(G_OBJECT(plugin));
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginPluginsDialog, pidgin_plugins_dialog, GTK_TYPE_DIALOG);
+
+static void
+pidgin_plugins_dialog_class_init(PidginPluginsDialogClass *klass) {
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	gtk_widget_class_set_template_from_resource(
+		widget_class,
+		"/im/pidgin/Pidgin/Plugins/dialog.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginPluginsDialog, tree_view);
+	gtk_widget_class_bind_template_child(widget_class, PidginPluginsDialog, configure_plugin_button);
+	gtk_widget_class_bind_template_child(widget_class, PidginPluginsDialog, close_button);
+	gtk_widget_class_bind_template_child(widget_class, PidginPluginsDialog, plugin_info);
+	gtk_widget_class_bind_template_child(widget_class, PidginPluginsDialog, plugin_store);
+
+	gtk_widget_class_bind_template_callback(widget_class, pidgin_plugins_dialog_selection_cb);
+	gtk_widget_class_bind_template_callback(widget_class, pidgin_plugins_dialog_config_plugin_cb);
+}
+
+static void
+pidgin_plugins_dialog_init(PidginPluginsDialog *dialog) {
+	gtk_widget_init_template(GTK_WIDGET(dialog));
+
+	/* wire up the close button */
+	g_signal_connect(
+		dialog->close_button,
+		"clicked",
+		G_CALLBACK(pidgin_plugins_dialog_close),
+		dialog
+	);
+
+	/* set the sort column for the plugin_store */
+	gtk_tree_sortable_set_sort_column_id(
+		GTK_TREE_SORTABLE(dialog->plugin_store),
+		GPLUGIN_GTK_STORE_MARKUP_COLUMN,
+		GTK_SORT_ASCENDING
+	);
+}
+
+GtkWidget *
+pidgin_plugins_dialog_new(void) {
+	return g_object_new(
+		PIDGIN_TYPE_PLUGINS_DIALOG,
+		"title", _("Plugins"),
+		NULL
+	);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginpluginsdialog.h	Fri Nov 22 04:10:26 2019 +0000
@@ -0,0 +1,55 @@
+/* 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_PLUGINS_DIALOG_H
+#define PIDGIN_PLUGINS_DIALOG_H
+
+/**
+ * SECTION:pidginpluginsdialog
+ * @section_id: pidgin-plugins-dialog
+ * @short_description: A widget to display available plugins
+ * @title: Plugins Dialog
+ */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_PLUGINS_DIALOG (pidgin_plugins_dialog_get_type())
+G_DECLARE_FINAL_TYPE(PidginPluginsDialog, pidgin_plugins_dialog, PIDGIN,
+		PLUGINS_DIALOG, GtkDialog)
+
+/**
+ * pidgin_plugins_dialog_new:
+ *
+ * Creates a new instance of #PidginPluginsDialog.
+ *
+ * Returns: (transfer full): The new #PidginPluginsDialog.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_plugins_dialog_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_PLUGINS_DIALOG_H */
+
--- a/pidgin/plugins/contact_priority.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/contact_priority.c	Fri Nov 22 04:10:26 2019 +0000
@@ -20,8 +20,8 @@
 
 #include "internal.h"
 #include "pidgin.h"
-#include "gtkplugin.h"
 #include "gtkutils.h"
+#include "pidginplugininfo.h"
 #include "prefs.h"
 #include "version.h"
 
--- a/pidgin/plugins/disco/xmppdisco.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/disco/xmppdisco.c	Fri Nov 22 04:10:26 2019 +0000
@@ -44,7 +44,7 @@
 #include "version.h"
 
 #include "gtkconv.h"
-#include "gtkplugin.h"
+#include "pidginplugininfo.h"
 
 #include "xmppdisco.h"
 #include "gtkdisco.h"
--- a/pidgin/plugins/extplacement.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/extplacement.c	Fri Nov 22 04:10:26 2019 +0000
@@ -24,7 +24,7 @@
 #include "pidgin.h"
 #include "conversation.h"
 #include "version.h"
-#include "gtkplugin.h"
+#include "pidginplugininfo.h"
 #include "gtkconv.h"
 #include "gtkconvwin.h"
 
--- a/pidgin/plugins/gtk-signals-test.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/gtk-signals-test.c	Fri Nov 22 04:10:26 2019 +0000
@@ -30,7 +30,7 @@
 #include "gtkaccount.h"
 #include "gtkblist.h"
 #include "gtkconv.h"
-#include "gtkplugin.h"
+#include "pidginplugininfo.h"
 
 /**************************************************************************
  * Account subsystem signal callbacks
--- a/pidgin/plugins/gtkbuddynote.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/gtkbuddynote.c	Fri Nov 22 04:10:26 2019 +0000
@@ -19,7 +19,7 @@
 #include "internal.h"
 
 #include <gtkblist.h>
-#include <gtkplugin.h>
+#include <pidginplugininfo.h>
 
 #include <debug.h>
 #include <version.h>
--- a/pidgin/plugins/iconaway.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/iconaway.c	Fri Nov 22 04:10:26 2019 +0000
@@ -26,7 +26,7 @@
 #include "version.h"
 
 #include "gtkconv.h"
-#include "gtkplugin.h"
+#include "pidginplugininfo.h"
 
 #define ICONAWAY_PLUGIN_ID "gtk-iconaway"
 
--- a/pidgin/plugins/relnot.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/relnot.c	Fri Nov 22 04:10:26 2019 +0000
@@ -32,10 +32,10 @@
 #include "core.h"
 #include "debug.h"
 #include "gtkblist.h"
-#include "gtkplugin.h"
 #include "gtkutils.h"
 #include "notify.h"
 #include "pidginicon.h"
+#include "pidginplugininfo.h"
 #include "prefs.h"
 #include "util.h"
 #include "version.h"
--- a/pidgin/plugins/ticker/ticker.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/ticker/ticker.c	Fri Nov 22 04:10:26 2019 +0000
@@ -35,8 +35,8 @@
 #include "version.h"
 
 #include "gtkblist.h"
-#include "gtkplugin.h"
 #include "gtkutils.h"
+#include "pidginplugininfo.h"
 #include "pidginstock.h"
 
 #include "gtkticker.h"
--- a/pidgin/plugins/xmppconsole/xmppconsole.c	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/plugins/xmppconsole/xmppconsole.c	Fri Nov 22 04:10:26 2019 +0000
@@ -22,7 +22,7 @@
  */
 
 #include "internal.h"
-#include "gtkplugin.h"
+#include "pidginplugininfo.h"
 #include "version.h"
 #include "protocol.h"
 #include "xmlnode.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Plugins/dialog.ui	Fri Nov 22 04:10:26 2019 +0000
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gplugin-gtk" version="0.28"/>
+  <object class="GPluginGtkStore" id="raw_plugin_store"/>
+  <object class="GtkTreeModelSort" id="plugin_store">
+    <property name="model">raw_plugin_store</property>
+  </object>
+  <template class="PidginPluginsDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="type_hint">dialog</property>
+    <child>
+      <placeholder/>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="configure_plugin_button">
+                <property name="label" translatable="yes">Configure Plugin</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <signal name="clicked" handler="pidgin_plugins_dialog_config_plugin_cb" object="PidginPluginsDialog" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label" translatable="yes">Close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GPluginGtkView" id="tree_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="model">plugin_store</property>
+                    <property name="headers_clickable">False</property>
+                    <child internal-child="selection">
+                      <object class="GtkTreeSelection">
+                        <signal name="changed" handler="pidgin_plugins_dialog_selection_cb" object="PidginPluginsDialog" swapped="no"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GPluginGtkPluginInfo" id="plugin_info">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
--- a/pidgin/resources/pidgin.gresource.xml	Thu Nov 21 21:41:21 2019 -0600
+++ b/pidgin/resources/pidgin.gresource.xml	Fri Nov 22 04:10:26 2019 +0000
@@ -10,6 +10,7 @@
     <file compressed="true">Debug/debug.ui</file>
     <file compressed="true">Debug/plugininfo.ui</file>
     <file compressed="true">Log/log-viewer.ui</file>
+    <file compressed="true">Plugins/dialog.ui</file>
     <file compressed="true">Prefs/prefs.ui</file>
     <file compressed="true">Prefs/vv.ui</file>
     <file compressed="true">Whiteboard/whiteboard.ui</file>

mercurial