# HG changeset patch
# Node ID c01068319853
# Parent  8dd430bd525b
diff --git a/pidgin/plugins/xmppconsole/console.ui b/pidgin/plugins/xmppconsole/console.ui
--- a/pidgin/plugins/xmppconsole/console.ui
+++ b/pidgin/plugins/xmppconsole/console.ui
@@ -21,12 +21,21 @@ Foundation, Inc., 51 Franklin Street, Fi
 -->
 <interface>
   <requires lib="gtk+" version="3.18"/>
+  <requires lib="pidgin" version="3.0"/>
   <!-- interface-license-type gplv2 -->
   <!-- interface-name Pidgin -->
   <!-- interface-description Internet Messenger -->
   <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <object class="PidginAccountStore" id="accounts"/>
+  <object class="PidginAccountFilterConnected" id="connected_accounts">
+    <property name="child-model">accounts</property>
+  </object>
+  <object class="PidginAccountFilterProtocol" id="connected_xmpp_accounts">
+    <property name="child-model">connected_accounts</property>
+    <property name="protocol-id">prpl-jabber</property>
+  </object>
   <object class="GtkTextBuffer" id="entry_buffer">
-    <signal name="changed" handler="entry_changed_cb" swapped="no"/>
+    <signal name="changed" handler="entry_changed_cb" object="PidginXmppConsole" swapped="no"/>
   </object>
   <object class="GtkAdjustment" id="presence.priority_adjustment">
     <property name="lower">-128</property>
@@ -82,22 +91,18 @@ Foundation, Inc., 51 Franklin Street, Fi
   <object class="GtkTextBuffer" id="buffer">
     <property name="tag_table">tags.table</property>
   </object>
-  <object class="GtkWindow" id="PidginXmppConsole">
+  <template parent="GtkWindow" class="PidginXmppConsole">
     <property name="can_focus">False</property>
     <property name="title" translatable="yes">XMPP Console</property>
     <property name="default_width">580</property>
     <property name="default_height">400</property>
-    <signal name="destroy" handler="console_destroy" swapped="no"/>
-    <child>
-      <placeholder/>
-    </child>
     <child>
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkBox" id="hbox">
+          <object class="GtkBox">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="margin_left">6</property>
@@ -115,10 +120,12 @@ Foundation, Inc., 51 Franklin Street, Fi
               </packing>
             </child>
             <child>
-              <object class="GtkComboBoxText" id="dropdown">
+              <object class="PidginAccountChooser">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <signal name="changed" handler="dropdown_changed_cb" swapped="no"/>
+                <property name="model">connected_xmpp_accounts</property>
+                <property name="active">0</property>
+                <signal name="changed" handler="dropdown_changed_cb" object="PidginXmppConsole" swapped="no"/>
               </object>
               <packing>
                 <property name="expand">True</property>
@@ -160,11 +167,17 @@ Foundation, Inc., 51 Franklin Street, Fi
             <property name="can_focus">False</property>
             <property name="toolbar_style">text</property>
             <child>
-              <object class="GtkToggleToolButton" id="iq">
+              <object class="GtkToolItem">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label">&lt;iq/&gt;</property>
-                <signal name="toggled" handler="toggle_button_toggled_cb" object="iq.popover" swapped="no"/>
+                <child>
+                  <object class="GtkMenuButton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">&lt;iq/&gt;</property>
+                    <property name="popover">iq.popover</property>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -172,11 +185,17 @@ Foundation, Inc., 51 Franklin Street, Fi
               </packing>
             </child>
             <child>
-              <object class="GtkToggleToolButton" id="presence">
+              <object class="GtkToolItem">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label">&lt;presence/&gt;</property>
-                <signal name="toggled" handler="toggle_button_toggled_cb" object="presence.popover" swapped="no"/>
+                <child>
+                  <object class="GtkMenuButton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">&lt;presence/&gt;</property>
+                    <property name="popover">presence.popover</property>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -184,11 +203,17 @@ Foundation, Inc., 51 Franklin Street, Fi
               </packing>
             </child>
             <child>
-              <object class="GtkToggleToolButton" id="message">
+              <object class="GtkToolItem">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label">&lt;message/&gt;</property>
-                <signal name="toggled" handler="toggle_button_toggled_cb" object="message.popover" swapped="no"/>
+                <child>
+                  <object class="GtkMenuButton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">&lt;message/&gt;</property>
+                    <property name="popover">message.popover</property>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -225,12 +250,10 @@ Foundation, Inc., 51 Franklin Street, Fi
         </child>
       </object>
     </child>
-  </object>
+  </template>
   <object class="GtkPopover" id="iq.popover">
     <property name="can_focus">False</property>
-    <property name="relative_to">iq</property>
     <property name="position">right</property>
-    <signal name="closed" handler="popover_closed_cb" object="iq" swapped="no"/>
     <child>
       <object class="GtkGrid">
         <property name="visible">True</property>
@@ -307,9 +330,7 @@ Foundation, Inc., 51 Franklin Street, Fi
   </object>
   <object class="GtkPopover" id="message.popover">
     <property name="can_focus">False</property>
-    <property name="relative_to">message</property>
     <property name="position">right</property>
-    <signal name="closed" handler="popover_closed_cb" object="message" swapped="no"/>
     <child>
       <object class="GtkGrid">
         <property name="visible">True</property>
@@ -458,9 +479,7 @@ Foundation, Inc., 51 Franklin Street, Fi
   </object>
   <object class="GtkPopover" id="presence.popover">
     <property name="can_focus">False</property>
-    <property name="relative_to">presence</property>
     <property name="position">right</property>
-    <signal name="closed" handler="popover_closed_cb" object="presence" swapped="no"/>
     <child>
       <object class="GtkGrid">
         <property name="visible">True</property>
diff --git a/pidgin/plugins/xmppconsole/xmppconsole.h b/pidgin/plugins/xmppconsole/xmppconsole.h
new file mode 100644
--- /dev/null
+++ b/pidgin/plugins/xmppconsole/xmppconsole.h
@@ -0,0 +1,36 @@
+/*
+ * Pidgin - XMPP debugging tool
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PIDGIN_XMPP_CONSOLE_H
+#define PIDGIN_XMPP_CONSOLE_H
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_XMPP_CONSOLE (pidgin_xmpp_console_get_type())
+G_DECLARE_FINAL_TYPE(PidginXmppConsole, pidgin_xmpp_console, PIDGIN,
+                     XMPP_CONSOLE, GtkWindow)
+
+#endif /* PIDGIN_XMPP_CONSOLE_H */
diff --git a/pidgin/plugins/xmppconsole/xmppconsole.c b/pidgin/plugins/xmppconsole/xmppconsole.c
--- a/pidgin/plugins/xmppconsole/xmppconsole.c
+++ b/pidgin/plugins/xmppconsole/xmppconsole.c
@@ -1,5 +1,6 @@
 /*
  * Purple - XMPP debugging tool
+ * 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
@@ -16,27 +17,25 @@
  * 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
- *
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <glib/gi18n-lib.h>
 
 #include <gdk/gdkkeysyms.h>
-
+#include <gtk/gtk.h>
 #include <purple.h>
+#include <pidgin.h>
 
-#include <pidgin.h>
+#include "xmppconsole.h"
 
 #define PLUGIN_ID      "gtk-xmpp"
 #define PLUGIN_DOMAIN  (g_quark_from_static_string(PLUGIN_ID))
 
-typedef struct {
+struct _PidginXmppConsole {
+	GtkWindow parent;
+
 	PurpleConnection *gc;
-	GtkWidget *window;
-	GtkWidget *hbox;
-	GtkWidget *dropdown;
 	GtkTextBuffer *buffer;
 	struct {
 		GtkTextTag *info;
@@ -51,8 +50,6 @@ typedef struct {
 	GtkWidget *entry;
 	GtkTextBuffer *entry_buffer;
 	GtkWidget *sw;
-	int count;
-	GList *accounts;
 
 	struct {
 		GtkPopover *popover;
@@ -77,35 +74,19 @@ typedef struct {
 		GtkEntry *subject;
 		GtkEntry *thread;
 	} message;
-} XmppConsole;
-
-XmppConsole *console = NULL;
-static void *xmpp_console_handle = NULL;
-
-static const gchar *xmpp_prpls[] = {
-	"prpl-jabber", "prpl-gtalk", NULL
 };
 
-static gboolean
-xmppconsole_is_xmpp_account(PurpleAccount *account)
-{
-	const gchar *prpl_name;
-	int i;
+G_DEFINE_DYNAMIC_TYPE(PidginXmppConsole, pidgin_xmpp_console, GTK_TYPE_WINDOW)
 
-	prpl_name = purple_account_get_protocol_id(account);
+static PidginXmppConsole *console = NULL;
 
-	i = 0;
-	while (xmpp_prpls[i] != NULL) {
-		if (purple_strequal(xmpp_prpls[i], prpl_name))
-			return TRUE;
-		i++;
-	}
-
-	return FALSE;
-}
-
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
 static void
-purple_xmlnode_append_to_buffer(PurpleXmlNode *node, gint indent_level, GtkTextIter *iter, GtkTextTag *tag)
+xmppconsole_append_xmlnode(PidginXmppConsole *console, PurpleXmlNode *node,
+                           gint indent_level, GtkTextIter *iter,
+                           GtkTextTag *tag)
 {
 	PurpleXmlNode *c;
 	gboolean need_end = FALSE, pretty = TRUE;
@@ -171,7 +152,7 @@ purple_xmlnode_append_to_buffer(PurpleXm
 		for (c = node->child; c; c = c->next)
 		{
 			if (c->type == PURPLE_XMLNODE_TYPE_TAG) {
-				purple_xmlnode_append_to_buffer(c, indent_level + 1, iter, tag);
+				xmppconsole_append_xmlnode(console, c, indent_level + 1, iter, tag);
 			} else if (c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
 				gtk_text_buffer_insert_with_tags(console->buffer, iter, c->data, c->data_sz,
 				                                 tag, NULL);
@@ -208,11 +189,13 @@ purple_xmlnode_received_cb(PurpleConnect
 {
 	GtkTextIter iter;
 
-	if (!console || console->gc != gc)
+	if (console == NULL || console->gc != gc) {
 		return;
+	}
 
 	gtk_text_buffer_get_end_iter(console->buffer, &iter);
-	purple_xmlnode_append_to_buffer(*packet, 0, &iter, console->tags.incoming);
+	xmppconsole_append_xmlnode(console, *packet, 0, &iter,
+	                           console->tags.incoming);
 }
 
 static void
@@ -221,21 +204,23 @@ purple_xmlnode_sent_cb(PurpleConnection 
 	GtkTextIter iter;
 	PurpleXmlNode *node;
 
-	if (!console || console->gc != gc)
+	if (console == NULL || console->gc != gc) {
 		return;
+	}
 	node = purple_xmlnode_from_str(*packet, -1);
 
 	if (!node)
 		return;
 
 	gtk_text_buffer_get_end_iter(console->buffer, &iter);
-	purple_xmlnode_append_to_buffer(node, 0, &iter, console->tags.outgoing);
+	xmppconsole_append_xmlnode(console, node, 0, &iter,
+	                           console->tags.outgoing);
 	purple_xmlnode_free(node);
 }
 
 static gboolean
-message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer p)
-{
+message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) {
+	PidginXmppConsole *console = data;
 	PurpleProtocol *protocol = NULL;
 	PurpleConnection *gc;
 	gchar *text;
@@ -264,8 +249,8 @@ message_send_cb(GtkWidget *widget, GdkEv
 }
 
 static void
-entry_changed_cb(GtkTextBuffer *buffer, void *data)
-{
+entry_changed_cb(GtkTextBuffer *buffer, gpointer data) {
+	PidginXmppConsole *console = data;
 	GtkTextIter start, end;
 	char *xmlstr, *str;
 	GtkTextIter iter;
@@ -321,7 +306,8 @@ entry_changed_cb(GtkTextBuffer *buffer, 
 }
 
 static void
-load_text_and_set_caret(const gchar *pre_text, const gchar *post_text)
+load_text_and_set_caret(PidginXmppConsole *console, const gchar *pre_text,
+                        const gchar *post_text)
 {
 	GtkTextIter where;
 	GtkTextMark *mark;
@@ -343,25 +329,9 @@ load_text_and_set_caret(const gchar *pre
 }
 
 static void
-popover_closed_cb(GtkPopover *popover, gpointer data)
-{
-	GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON(data);
-
-	gtk_toggle_tool_button_set_active(button, FALSE);
-}
-
-static void
-toggle_button_toggled_cb(GtkToolButton *button, gpointer data)
-{
-	GtkPopover *popover = GTK_POPOVER(data);
-
-	gtk_popover_popup(popover);
-}
-
-static void
 iq_clicked_cb(GtkWidget *w, gpointer data)
 {
-	XmppConsole *console = (XmppConsole *)data;
+	PidginXmppConsole *console = data;
 	const gchar *to;
 	char *stanza;
 
@@ -370,7 +340,7 @@ iq_clicked_cb(GtkWidget *w, gpointer dat
 	        "<iq %s%s%s id='console%x' type='%s'>", to && *to ? "to='" : "",
 	        to && *to ? to : "", to && *to ? "'" : "", g_random_int(),
 	        gtk_combo_box_text_get_active_text(console->iq.type));
-	load_text_and_set_caret(stanza, "</iq>");
+	load_text_and_set_caret(console, stanza, "</iq>");
 	gtk_widget_grab_focus(console->entry);
 	g_free(stanza);
 
@@ -383,7 +353,7 @@ iq_clicked_cb(GtkWidget *w, gpointer dat
 static void
 presence_clicked_cb(GtkWidget *w, gpointer data)
 {
-	XmppConsole *console = (XmppConsole *)data;
+	PidginXmppConsole *console = data;
 	const gchar *to, *status, *priority;
 	gchar *type, *show;
 	char *stanza;
@@ -427,7 +397,7 @@ presence_clicked_cb(GtkWidget *w, gpoint
 	                         *priority ? priority : "",
 	                         *priority ? "</priority>" : "");
 
-	load_text_and_set_caret(stanza, "</presence>");
+	load_text_and_set_caret(console, stanza, "</presence>");
 	gtk_widget_grab_focus(console->entry);
 	g_free(stanza);
 	g_free(type);
@@ -445,7 +415,7 @@ presence_clicked_cb(GtkWidget *w, gpoint
 static void
 message_clicked_cb(GtkWidget *w, gpointer data)
 {
-	XmppConsole *console = (XmppConsole *)data;
+	PidginXmppConsole *console = data;
 	const gchar *to, *body, *thread, *subject;
 	char *stanza;
 
@@ -471,7 +441,7 @@ message_clicked_cb(GtkWidget *w, gpointe
 	        *thread ? "<thread>" : "", *thread ? thread : "",
 	        *thread ? "</thread>" : "");
 
-	load_text_and_set_caret(stanza, "</message>");
+	load_text_and_set_caret(console, stanza, "</message>");
 	gtk_widget_grab_focus(console->entry);
 	g_free(stanza);
 
@@ -485,184 +455,117 @@ message_clicked_cb(GtkWidget *w, gpointe
 }
 
 static void
-signing_on_cb(PurpleConnection *gc)
-{
-	PurpleAccount *account;
-
-	if (!console)
-		return;
-
-	account = purple_connection_get_account(gc);
-	if (!xmppconsole_is_xmpp_account(account))
-		return;
-
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
-		purple_account_get_username(account));
-	console->accounts = g_list_append(console->accounts, gc);
-	console->count++;
-
-	if (console->count == 1) {
-		console->gc = gc;
-		gtk_text_buffer_set_text(console->buffer, "", 0);
-		gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
-	} else
-		gtk_widget_show_all(console->hbox);
-}
-
-static void
-signed_off_cb(PurpleConnection *gc)
-{
-	int i;
-
-	if (!console)
-		return;
-
-	i = g_list_index(console->accounts, gc);
-	if (i == -1)
-		return;
-
-	gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(console->dropdown), i);
-	console->accounts = g_list_remove(console->accounts, gc);
-	console->count--;
-
-	if (gc == console->gc) {
-		GtkTextIter end;
-		gtk_text_buffer_get_end_iter(console->buffer, &end);
-		gtk_text_buffer_insert_with_tags(console->buffer, &end, _("Logged out."), -1,
-		                                 console->tags.info, NULL);
-		console->gc = NULL;
-	}
-}
-
-static void
-console_destroy(GtkWidget *window, gpointer nul)
-{
-	g_list_free(console->accounts);
-	g_free(console);
-	console = NULL;
-}
-
-static void
-dropdown_changed_cb(GtkComboBox *widget, gpointer nul)
-{
-	if (!console)
-		return;
+dropdown_changed_cb(GtkComboBox *widget, gpointer data) {
+	PidginXmppConsole *console = data;
+	PidginAccountChooser *chooser = PIDGIN_ACCOUNT_CHOOSER(widget);
+	PurpleAccount *account = NULL;
 
-	console->gc = g_list_nth_data(console->accounts, gtk_combo_box_get_active(GTK_COMBO_BOX(console->dropdown)));
-	gtk_text_buffer_set_text(console->buffer, "", 0);
-}
-
-static void
-create_console(PurplePluginAction *action)
-{
-	GtkBuilder *builder;
-	GList *connections;
-	GtkCssProvider *entry_css;
-	GtkStyleContext *context;
-
-	if (console) {
-		gtk_window_present(GTK_WINDOW(console->window));
-		return;
-	}
-
-	console = g_new0(XmppConsole, 1);
-
-	builder = gtk_builder_new_from_resource(
-	        "/im/pidgin/Pidgin3/Plugin/XMPPConsole/console.ui");
-	gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
-	console->window = GTK_WIDGET(
-	        gtk_builder_get_object(builder, "PidginXmppConsole"));
-	gtk_builder_add_callback_symbol(builder, "console_destroy",
-	                                G_CALLBACK(console_destroy));
-
-	console->hbox = GTK_WIDGET(gtk_builder_get_object(builder, "hbox"));
-	console->dropdown =
-	        GTK_WIDGET(gtk_builder_get_object(builder, "dropdown"));
-	for (connections = purple_connections_get_all(); connections; connections = connections->next) {
-		PurpleConnection *gc = connections->data;
-		if (xmppconsole_is_xmpp_account(purple_connection_get_account(gc))) {
-			console->count++;
-			console->accounts = g_list_append(console->accounts, gc);
-			gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
-						  purple_account_get_username(purple_connection_get_account(gc)));
-			if (!console->gc)
-				console->gc = gc;
-		}
-	}
-	gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
-	gtk_builder_add_callback_symbol(builder, "dropdown_changed_cb",
-	                                G_CALLBACK(dropdown_changed_cb));
-
-	console->buffer =
-	        GTK_TEXT_BUFFER(gtk_builder_get_object(builder, "buffer"));
-	console->tags.info =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.info"));
-	console->tags.incoming =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.incoming"));
-	console->tags.outgoing =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.outgoing"));
-	console->tags.bracket =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.bracket"));
-	console->tags.tag =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.tag"));
-	console->tags.attr =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.attr"));
-	console->tags.value =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.value"));
-	console->tags.xmlns =
-	        GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.xmlns"));
-
-	if (console->count == 0) {
+	account = pidgin_account_chooser_get_selected(chooser);
+	if(PURPLE_IS_ACCOUNT(account)) {
+		console->gc = purple_account_get_connection(account);
+		gtk_text_buffer_set_text(console->buffer, "", 0);
+	} else {
 		GtkTextIter start, end;
+		console->gc = NULL;
 		gtk_text_buffer_set_text(console->buffer, _("Not connected to XMPP"), -1);
 		gtk_text_buffer_get_bounds(console->buffer, &start, &end);
 		gtk_text_buffer_apply_tag(console->buffer, console->tags.info, &start, &end);
 	}
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_xmpp_console_class_finalize(PidginXmppConsoleClass *klass) {
+}
+
+static void
+pidgin_xmpp_console_class_init(PidginXmppConsoleClass *klass) {
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	gtk_widget_class_set_template_from_resource(
+	        widget_class,
+	        "/im/pidgin/Pidgin3/Plugin/XMPPConsole/console.ui"
+	);
+
+	gtk_widget_class_bind_template_callback(widget_class, dropdown_changed_cb);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     buffer);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.info);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.incoming);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.outgoing);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.bracket);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.tag);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.attr);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.value);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     tags.xmlns);
 
 	/* Popover for <iq/> button. */
-	console->iq.popover =
-	        GTK_POPOVER(gtk_builder_get_object(builder, "iq.popover"));
-	console->iq.to = GTK_ENTRY(gtk_builder_get_object(builder, "iq.to"));
-	console->iq.type =
-	        GTK_COMBO_BOX_TEXT(gtk_builder_get_object(builder, "iq.type"));
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     iq.popover);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     iq.to);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     iq.type);
+	gtk_widget_class_bind_template_callback(widget_class, iq_clicked_cb);
 
 	/* Popover for <presence/> button. */
-	console->presence.popover = GTK_POPOVER(
-	        gtk_builder_get_object(builder, "presence.popover"));
-	console->presence.to =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "presence.to"));
-	console->presence.type = GTK_COMBO_BOX_TEXT(
-	        gtk_builder_get_object(builder, "presence.type"));
-	console->presence.show = GTK_COMBO_BOX_TEXT(
-	        gtk_builder_get_object(builder, "presence.show"));
-	console->presence.status =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "presence.status"));
-	console->presence.priority =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "presence.priority"));
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.popover);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.to);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.type);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.show);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.status);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     presence.priority);
+	gtk_widget_class_bind_template_callback(widget_class, presence_clicked_cb);
 
 	/* Popover for <message/> button. */
-	console->message.popover =
-	        GTK_POPOVER(gtk_builder_get_object(builder, "message.popover"));
-	console->message.to =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "message.to"));
-	console->message.type = GTK_COMBO_BOX_TEXT(
-	        gtk_builder_get_object(builder, "message.type"));
-	console->message.body =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "message.body"));
-	console->message.subject =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "message.subject"));
-	console->message.thread =
-	        GTK_ENTRY(gtk_builder_get_object(builder, "message.thread"));
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.popover);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.to);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.type);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.body);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.subject);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     message.thread);
+	gtk_widget_class_bind_template_callback(widget_class, message_clicked_cb);
 
-	gtk_builder_add_callback_symbols(
-	        builder, "toggle_button_toggled_cb",
-	        G_CALLBACK(toggle_button_toggled_cb), "popover_closed_cb",
-	        G_CALLBACK(popover_closed_cb), "iq_clicked_cb",
-	        G_CALLBACK(iq_clicked_cb), "presence_clicked_cb",
-	        G_CALLBACK(presence_clicked_cb), "message_clicked_cb",
-	        G_CALLBACK(message_clicked_cb), NULL);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     entry);
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole,
+	                                     entry_buffer);
+	gtk_widget_class_bind_template_callback(widget_class, message_send_cb);
 
-	console->entry = GTK_WIDGET(gtk_builder_get_object(builder, "entry"));
+	gtk_widget_class_bind_template_child(widget_class, PidginXmppConsole, sw);
+	gtk_widget_class_bind_template_callback(widget_class, entry_changed_cb);
+}
+
+static void
+pidgin_xmpp_console_init(PidginXmppConsole *console) {
+	GtkCssProvider *entry_css;
+	GtkStyleContext *context;
+
+	gtk_widget_init_template(GTK_WIDGET(console));
+
 	entry_css = gtk_css_provider_new();
 	gtk_css_provider_load_from_data(entry_css,
 	                                "textview." GTK_STYLE_CLASS_ERROR " text {background-color:#ffcece;}",
@@ -670,23 +573,24 @@ create_console(PurplePluginAction *actio
 	context = gtk_widget_get_style_context(console->entry);
 	gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(entry_css),
 	                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-	console->entry_buffer = GTK_TEXT_BUFFER(
-	        gtk_builder_get_object(builder, "entry_buffer"));
-	gtk_builder_add_callback_symbol(builder, "message_send_cb",
-	                                G_CALLBACK(message_send_cb));
+
+	entry_changed_cb(console->entry_buffer, console);
 
-	console->sw = GTK_WIDGET(gtk_builder_get_object(builder, "sw"));
-	gtk_builder_add_callback_symbol(builder, "entry_changed_cb",
-	                                G_CALLBACK(entry_changed_cb));
+	gtk_widget_show(GTK_WIDGET(console));
+}
 
-	entry_changed_cb(console->entry_buffer, NULL);
+/******************************************************************************
+ * Plugin implementation
+ *****************************************************************************/
+static void
+create_console(PurplePluginAction *action)
+{
+	if (console == NULL) {
+		console = g_object_new(PIDGIN_TYPE_XMPP_CONSOLE, NULL);
+		g_object_add_weak_pointer(G_OBJECT(console), (gpointer)&console);
+	}
 
-	gtk_widget_show_all(console->window);
-	if (console->count < 2)
-		gtk_widget_hide(console->hbox);
-
-	gtk_builder_connect_signals(builder, console);
-	g_object_unref(builder);
+	gtk_window_present(GTK_WINDOW(console));
 }
 
 static GList *
@@ -728,42 +632,23 @@ xmpp_console_query(GError **error)
 static gboolean
 xmpp_console_load(GPluginPlugin *plugin, GError **error)
 {
-	int i;
-	gboolean any_registered = FALSE;
-
-	xmpp_console_handle = plugin;
-
-	i = 0;
-	while (xmpp_prpls[i] != NULL) {
-		PurpleProtocol *xmpp;
-		PurpleProtocolManager *manager;
-
-		manager = purple_protocol_manager_get_default();
-		xmpp = purple_protocol_manager_find(manager, xmpp_prpls[i]);
-		i++;
+	PurpleProtocolManager *manager = NULL;
+	PurpleProtocol *xmpp = NULL;
 
-		if (!xmpp)
-			continue;
-		any_registered = TRUE;
+	pidgin_xmpp_console_register_type(G_TYPE_MODULE(plugin));
 
-		purple_signal_connect(xmpp, "jabber-receiving-xmlnode",
-			xmpp_console_handle,
-			G_CALLBACK(purple_xmlnode_received_cb), NULL);
-		purple_signal_connect(xmpp, "jabber-sending-text",
-			xmpp_console_handle,
-			G_CALLBACK(purple_xmlnode_sent_cb), NULL);
-	}
-
-	if (!any_registered) {
+	manager = purple_protocol_manager_get_default();
+	xmpp = purple_protocol_manager_find(manager, "prpl-jabber");
+	if (!PURPLE_IS_PROTOCOL(xmpp)) {
 		g_set_error_literal(error, PLUGIN_DOMAIN, 0,
 		                    _("No XMPP protocol is loaded."));
 		return FALSE;
 	}
 
-	purple_signal_connect(purple_connections_get_handle(), "signing-on",
-		plugin, G_CALLBACK(signing_on_cb), NULL);
-	purple_signal_connect(purple_connections_get_handle(), "signed-off",
-		plugin, G_CALLBACK(signed_off_cb), NULL);
+	purple_signal_connect(xmpp, "jabber-receiving-xmlnode", plugin,
+	                      G_CALLBACK(purple_xmlnode_received_cb), NULL);
+	purple_signal_connect(xmpp, "jabber-sending-text", plugin,
+	                      G_CALLBACK(purple_xmlnode_sent_cb), NULL);
 
 	return TRUE;
 }
@@ -771,8 +656,9 @@ xmpp_console_load(GPluginPlugin *plugin,
 static gboolean
 xmpp_console_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
 {
-	if (console)
-		gtk_widget_destroy(console->window);
+	if (console) {
+		gtk_widget_destroy(GTK_WIDGET(console));
+	}
 	return TRUE;
 }
 
