diff pidgin/gtkblist.c @ 21420:1ce05db42eb3

propagate from branch 'im.pidgin.pidgin' (head 18b9b46acf72ed063fad2e2779a5202fd6d8126b) to branch 'im.pidgin.cpw.resiak.disconnectreason' (head 9ef6f213c70686d6e6f62c382a626a749bf4bf6c)
author Will Thompson <will.thompson@collabora.co.uk>
date Sun, 11 Nov 2007 14:19:55 +0000
parents 0bad71ee1465 38cc722159ff
children 3f87c15d8438
line wrap: on
line diff
--- a/pidgin/gtkblist.c	Sun Nov 11 14:18:23 2007 +0000
+++ b/pidgin/gtkblist.c	Sun Nov 11 14:19:55 2007 +0000
@@ -58,6 +58,7 @@
 #include "gtkstatusbox.h"
 #include "gtkscrollbook.h"
 #include "gtkutils.h"
+#include "pidgin/minidialog.h"
 
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtk.h>
@@ -107,6 +108,21 @@
 	GList *entries;
 } PidginJoinChatData;
 
+typedef struct
+{
+	/** PidginScrollBook used to hold error minidialogs.  Gets packed
+	 * inside PidginBuddyList.error_buttons
+	 */
+	GtkWidget *error_scrollbook;
+
+	/** Pointer to the mini-dialog about having signed on elsewhere, if one
+	 * is showing; @c NULL otherwise.
+	 */
+	PidginMiniDialog *signed_on_elsewhere;
+} PidginBuddyListPrivate;
+
+#define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
+	((PidginBuddyListPrivate *)((list)->priv))
 
 static GtkWidget *accountmenu = NULL;
 
@@ -4125,10 +4141,13 @@
 static void pidgin_blist_new_list(PurpleBuddyList *blist)
 {
 	PidginBuddyList *gtkblist;
+	PidginBuddyListPrivate *priv;
 
 	gtkblist = g_new0(PidginBuddyList, 1);
 	gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash,
 												g_direct_equal, NULL, g_free);
+	gtkblist->priv = priv = g_new0(PidginBuddyListPrivate, 1);
+
 	blist->ui_data = gtkblist;
 }
 
@@ -4284,6 +4303,13 @@
 					widget->allocation.width - 2 - HEADLINE_CLOSE_SIZE, 2,
 					HEADLINE_CLOSE_SIZE, HEADLINE_CLOSE_SIZE,
 					GDK_RGB_DITHER_NONE, 0, 0);
+#if 0
+		/* The presence of one opening paren in each branch of
+		 * GTK_CHECK_VERSION confuses vim's bracket matching for the
+		 * rest of the file.
+		 */
+		)
+#endif
 		gtk_paint_focus(widget->style, widget->window, GTK_STATE_PRELIGHT,
 				NULL, widget, NULL,
 				widget->allocation.width - HEADLINE_CLOSE_SIZE - 3, 1,
@@ -4354,124 +4380,363 @@
 /* Connection error handling stuff */
 /***********************************/
 
+#define OBJECT_DATA_KEY_ACCOUNT "account"
+
+static gboolean
+find_account_widget(GObject *widget,
+                    PurpleAccount *account)
+{
+	if (g_object_get_data(widget, OBJECT_DATA_KEY_ACCOUNT) == account)
+		return 0; /* found */
+	else
+		return 1;
+}
+
 static void
-ce_modify_account_cb(PurpleAccount *account)
+pack_prpl_icon_start(GtkWidget *box,
+                     PurpleAccount *account)
+{
+	GdkPixbuf *pixbuf;
+	GtkWidget *image;
+
+	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+	if (pixbuf != NULL) {
+		image = gtk_image_new_from_pixbuf(pixbuf);
+		g_object_unref(pixbuf);
+
+		gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
+	}
+}
+
+static void
+add_error_dialog(PidginBuddyList *gtkblist,
+                 GtkWidget *dialog)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	gtk_container_add(GTK_CONTAINER(priv->error_scrollbook), dialog);
+
+	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
+		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+}
+
+static GtkWidget *
+find_child_widget_by_account(GtkContainer *container,
+                             PurpleAccount *account)
+{
+	GList *l = NULL;
+	GList *children = gtk_container_get_children(container);
+	GtkWidget *ret = NULL;
+	l = g_list_find_custom(children, account, (GCompareFunc) find_account_widget);
+	if (l)
+		ret = GTK_WIDGET(l->data);
+	g_list_free(children);
+	return ret;
+}
+
+static void
+remove_child_widget_by_account(GtkContainer *container,
+                               PurpleAccount *account)
+{
+	GtkWidget *widget = find_child_widget_by_account(container, account);
+	if(widget)
+		gtk_widget_destroy(widget);
+}
+
+/* Generic error buttons */
+
+static void
+generic_error_modify_cb(PurpleAccount *account)
 {
 	pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
 }
 
 static void
-ce_enable_account_cb(PurpleAccount *account)
+generic_error_enable_cb(PurpleAccount *account)
+{
+	purple_account_set_enabled(account, purple_core_get_ui(), TRUE);
+}
+
+static void
+generic_error_ignore_cb(PurpleAccount *account)
+{
+	purple_account_clear_current_error(account);
+}
+
+static void
+generic_error_destroy_cb(GtkObject *dialog,
+                         PurpleAccount *account)
+{
+	g_hash_table_remove(gtkblist->connection_errors, account);
+}
+
+static void
+add_generic_error_dialog(PurpleAccount *account,
+                         const PurpleConnectionErrorInfo *err)
+{
+	GtkWidget *mini_dialog;
+	const char *username = purple_account_get_username(account);
+	gboolean enabled =
+		purple_account_get_enabled(account, purple_core_get_ui());
+	char *primary;
+	
+	if (enabled)
+		primary = g_strdup_printf(_("%s disconnected"), username);
+	else
+		primary = g_strdup_printf(_("%s disabled"), username);
+
+	mini_dialog = pidgin_make_mini_dialog(NULL, PIDGIN_STOCK_DISCONNECT,
+		primary, err->description, account,
+		(enabled ? _("Reconnect") : _("Re-enable")),
+		(enabled ? PURPLE_CALLBACK(purple_account_connect)
+		         : PURPLE_CALLBACK(generic_error_enable_cb)),
+		_("Modify Account"), PURPLE_CALLBACK(generic_error_modify_cb),
+		_("Ignore"), PURPLE_CALLBACK(generic_error_ignore_cb),
+		NULL);
+
+	g_free(primary);
+
+	g_object_set_data(G_OBJECT(mini_dialog), OBJECT_DATA_KEY_ACCOUNT,
+		account);
+
+	g_signal_connect_after(mini_dialog, "destroy",
+		(GCallback)generic_error_destroy_cb,
+		account);
+
+	add_error_dialog(gtkblist, mini_dialog);
+}
+
+static void
+remove_generic_error_dialog(PurpleAccount *account)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	remove_child_widget_by_account(GTK_CONTAINER(priv->error_scrollbook),
+		account);
+}
+
+
+/* Notifications about accounts which were disconnected with
+ * PURPLE_CONNECTION_ERROR_NAME_IN_USE
+ */
+
+typedef void (*AccountFunction)(PurpleAccount *);
+
+static void
+elsewhere_foreach_account(PidginMiniDialog *mini_dialog,
+                          AccountFunction f)
+{
+	PurpleAccount *account;
+	GList *labels = gtk_container_get_children(
+		GTK_CONTAINER(mini_dialog->contents));
+	GList *l;
+
+	for (l = labels; l; l = l->next) {
+		account = g_object_get_data(G_OBJECT(l->data), OBJECT_DATA_KEY_ACCOUNT);
+		if (account)
+			f(account);
+		else
+			purple_debug_warning("gtkblist", "mini_dialog's child "
+				"didn't have an account stored in it!");
+	}
+	g_list_free(labels);
+}
+
+static void
+enable_account(PurpleAccount *account)
 {
 	purple_account_set_enabled(account, purple_core_get_ui(), TRUE);
 }
 
 static void
-connection_error_button_clicked_cb(GtkButton *widget, gpointer user_data)
-{
-	PurpleAccount *account;
-	char *primary;
-	const char *text;
-	gboolean enabled;
-	GList *list;
-
-	account = user_data;
-	primary = g_strdup_printf(_("%s disconnected"),
-							  purple_account_get_username(account));
-	text = g_hash_table_lookup(gtkblist->connection_errors, account);
-
-	enabled = purple_account_get_enabled(account, purple_core_get_ui());
-	purple_request_action_with_hint(account, _("Connection Error"), primary, text, 2,
-						account, NULL, NULL,
-						PURPLE_REQUEST_UI_HINT_ACCOUNT, account, 3,
-						_("OK"), NULL,
-						_("Modify Account"), PURPLE_CALLBACK(ce_modify_account_cb),
-						enabled ? _("Connect") : _("Re-enable Account"),
-						enabled ? PURPLE_CALLBACK(purple_account_connect) :
-									PURPLE_CALLBACK(ce_enable_account_cb));
-	g_free(primary);
-	gtk_widget_destroy(GTK_WIDGET(widget));
-	g_hash_table_remove(gtkblist->connection_errors, account);
-	if ((list = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons))) == NULL) {
-		gtk_widget_hide(gtkblist->error_buttons);
-	} else {
-		g_list_free(list);
-	}
-}
-
-/* Add some buttons that show connection errors */
+reconnect_elsewhere_accounts(PidginMiniDialog *mini_dialog,
+                             GtkButton *button,
+                             gpointer unused)
+{
+	elsewhere_foreach_account(mini_dialog, enable_account);
+}
+
+static void
+ignore_elsewhere_accounts(PidginMiniDialog *mini_dialog,
+                          GtkButton *button,
+                          gpointer unused)
+{
+	elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error);
+}
+
 static void
-create_connection_error_buttons(gpointer key, gpointer value,
-                                gpointer user_data)
-{
-	PurpleAccount *account;
-	gchar *escaped, *text;
-	GtkWidget *button, *label, *image, *hbox;
-	GdkPixbuf *pixbuf;
-
-	account = key;
-	escaped = g_markup_escape_text((const gchar *)value, -1);
-	text = g_strdup_printf(_("<span color=\"red\">%s disconnected: %s</span>"),
-	                       purple_account_get_username(account),
-	                       escaped);
-	g_free(escaped);
+ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PidginMiniDialog *mini_dialog;
+
+	if(priv->signed_on_elsewhere)
+		return;
+	
+	mini_dialog = priv->signed_on_elsewhere =
+		pidgin_mini_dialog_new(NULL, NULL, PIDGIN_STOCK_DISCONNECT);
+
+	pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"),
+		reconnect_elsewhere_accounts, NULL);
+
+	pidgin_mini_dialog_add_button(mini_dialog, _("Ignore"),
+		ignore_elsewhere_accounts, NULL);
+
+	add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog));
+
+	/* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */
+	g_signal_connect(G_OBJECT(mini_dialog), "destroy",
+		(GCallback) gtk_widget_destroyed, &(priv->signed_on_elsewhere));
+}
+
+static void
+update_signed_on_elsewhere_minidialog_title(void)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere;
+	guint accounts;
+	char *title;
+
+	if (mini_dialog == NULL)
+		return;
+
+	accounts = pidgin_mini_dialog_get_num_children(mini_dialog);
+
+	title = g_strdup_printf(
+		ngettext("%d account was disabled because you signed on from another location.",
+			 "%d accounts were disabled because you signed on from another location.",
+			 accounts),
+		accounts);
+	pidgin_mini_dialog_set_title(mini_dialog, title);
+	g_free(title);
+}
+
+static GtkWidget *
+create_account_label(PurpleAccount *account)
+{
+	GtkWidget *hbox, *label;
+	const char *username = purple_account_get_username(account);
+	char *markup;
 
 	hbox = gtk_hbox_new(FALSE, 6);
-
-	/* Create the icon */
-	if (purple_account_get_status_type_with_primitive(account,
-							PURPLE_STATUS_OFFLINE) != NULL) {
-		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
-		if (pixbuf != NULL) {
-			image = gtk_image_new_from_pixbuf(pixbuf);
-			g_object_unref(pixbuf);
-
-			gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
-		}
-	}
-
-	/* Create the text */
-	label = gtk_label_new("");
-	gtk_label_set_markup(GTK_LABEL(label), text);
-	g_free(text);
+	g_object_set_data(G_OBJECT(hbox), OBJECT_DATA_KEY_ACCOUNT, account);
+
+	pack_prpl_icon_start(hbox, account);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<span size=\"smaller\">%s</span>", username);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 #if GTK_CHECK_VERSION(2,6,0)
-	g_object_set(label, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+	g_object_set(G_OBJECT(label), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif
+#if GTK_CHECK_VERSION(2,12,0)
+	{ /* avoid unused variable warnings on pre-2.12 Gtk */
+		char *description =
+			purple_account_get_current_error(account)->description;
+		if (description != NULL && *description != '\0')
+			gtk_widget_set_tooltip_text(label, description);
+	}
 #endif
 	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 
-	/* Create the actual button and put the icon and text on it */
-	button = gtk_button_new();
-	gtk_container_add(GTK_CONTAINER(button), hbox);
-	g_signal_connect(G_OBJECT(button), "clicked",
-	                 G_CALLBACK(connection_error_button_clicked_cb),
-	                 account);
-	gtk_widget_show_all(button);
-	gtk_box_pack_end(GTK_BOX(gtkblist->error_buttons), button,
-	                 FALSE, FALSE, 0);
-	gtk_widget_show_all(gtkblist->error_buttons);
+	return hbox;
+}
+
+static void
+add_to_signed_on_elsewhere(PurpleAccount *account)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PidginMiniDialog *mini_dialog;
+	GtkWidget *account_label;
+
+	ensure_signed_on_elsewhere_minidialog(gtkblist);
+	mini_dialog = priv->signed_on_elsewhere;
+
+	if(find_child_widget_by_account(GTK_CONTAINER(mini_dialog->contents), account))
+		return;
+
+	account_label = create_account_label(account);
+	gtk_box_pack_start(mini_dialog->contents, account_label, FALSE, FALSE, 0);
+	gtk_widget_show_all(account_label);
+
+	update_signed_on_elsewhere_minidialog_title();
+
+	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
+		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+}
+
+static void
+remove_from_signed_on_elsewhere(PurpleAccount *account)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere;
+	if(mini_dialog == NULL)
+		return;
+
+	remove_child_widget_by_account(GTK_CONTAINER(mini_dialog->contents), account);
+
+	update_signed_on_elsewhere_minidialog_title();
+}
+
+
+/* Call appropriate error notification code based on error types */
+static void
+update_account_error_state(PurpleAccount *account,
+                           const PurpleConnectionErrorInfo *old,
+                           const PurpleConnectionErrorInfo *new,
+                           PidginBuddyList *gtkblist)
+{
+	/* For backwards compatibility: */
+	if (new)
+		pidgin_blist_update_account_error_state(account, new->description);
+	else
+		pidgin_blist_update_account_error_state(account, NULL);
+
+	if (old) {
+		if(old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
+			remove_from_signed_on_elsewhere(account);
+		else
+			remove_generic_error_dialog(account);
+	}
+
+	if (new) {
+		if(new->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
+			add_to_signed_on_elsewhere(account);
+		else
+			add_generic_error_dialog(account, new);
+	}
+}
+
+/* In case accounts are loaded before the blist (which they currently are),
+ * let's call update_account_error_state ourselves on every account's current
+ * state when the blist starts.
+ */
+static void
+show_initial_account_errors(PidginBuddyList *gtkblist)
+{
+	GList *l = purple_accounts_get_all();
+	PurpleAccount *account;
+	const PurpleConnectionErrorInfo *err;
+
+	for (; l; l = l->next)
+	{
+		account = l->data;
+		err = purple_account_get_current_error(account);
+
+		update_account_error_state(account, NULL, err, gtkblist);
+	}
 }
 
 void
 pidgin_blist_update_account_error_state(PurpleAccount *account, const char *text)
 {
-	GList *l;
-
+	/* connection_errors isn't actually used anywhere; it's just kept in
+	 * sync with reality in case a plugin uses it.
+	 */
 	if (text == NULL)
 		g_hash_table_remove(gtkblist->connection_errors, account);
 	else
 		g_hash_table_insert(gtkblist->connection_errors, account, g_strdup(text));
-
-	/* Remove the old error buttons */
-	for (l = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons));
-			l != NULL;
-			l = g_list_delete_link(l, l))
-	{
-		gtk_widget_destroy(GTK_WIDGET(l->data));
-	}
-
-	/* Add new error buttons */
-	g_hash_table_foreach(gtkblist->connection_errors,
-			create_connection_error_buttons, NULL);
 }
 
 static gboolean
@@ -4598,6 +4863,7 @@
 
 static void pidgin_blist_show(PurpleBuddyList *list)
 {
+	PidginBuddyListPrivate *priv;
 	void *handle;
 	GtkCellRenderer *rend;
 	GtkTreeViewColumn *column;
@@ -4624,6 +4890,7 @@
 	}
 
 	gtkblist = PIDGIN_BLIST(list);
+	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
 
 	gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
 	gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000);
@@ -4926,11 +5193,19 @@
 	gtkblist->scrollbook = pidgin_scroll_book_new();
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->scrollbook, FALSE, FALSE, 0);
 
-	/* Create an empty vbox used for showing connection errors */
+	/* Create an vbox which holds the scrollbook which is actually used to
+	 * display connection errors.  The vbox needs to still exist for
+	 * backwards compatibility.
+	 */
 	gtkblist->error_buttons = gtk_vbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->error_buttons, FALSE, FALSE, 0);
-        gtk_container_set_border_width(GTK_CONTAINER(gtkblist->error_buttons), 3);
-	
+	gtk_container_set_border_width(GTK_CONTAINER(gtkblist->error_buttons), 0);
+
+	priv->error_scrollbook = pidgin_scroll_book_new();
+	gtk_box_pack_start(GTK_BOX(gtkblist->error_buttons),
+		priv->error_scrollbook, FALSE, FALSE, 0);
+
+
 	/* Add the statusbox */
 	gtkblist->statusbox = pidgin_status_box_new();
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0);
@@ -5001,43 +5276,56 @@
 	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/sound/method",
 			pidgin_blist_sound_method_pref_cb, NULL);
 
+
 	/* Setup some purple signal handlers. */
-	purple_signal_connect(purple_accounts_get_handle(), "account-enabled",
-			gtkblist, PURPLE_CALLBACK(account_modified), gtkblist);
-	purple_signal_connect(purple_accounts_get_handle(), "account-disabled",
-			gtkblist, PURPLE_CALLBACK(account_modified), gtkblist);
-	purple_signal_connect(purple_accounts_get_handle(), "account-removed",
-			gtkblist, PURPLE_CALLBACK(account_modified), gtkblist);
-	purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
-			gtkblist, PURPLE_CALLBACK(account_status_changed), gtkblist);
-
-	purple_signal_connect(pidgin_account_get_handle(), "account-modified",
-			gtkblist, PURPLE_CALLBACK(account_modified), gtkblist);
-
-	purple_signal_connect(purple_connections_get_handle(), "signed-on",
-						gtkblist, PURPLE_CALLBACK(sign_on_off_cb), list);
-	purple_signal_connect(purple_connections_get_handle(), "signed-off",
-						gtkblist, PURPLE_CALLBACK(sign_on_off_cb), list);
-
-	purple_signal_connect(purple_plugins_get_handle(), "plugin-load",
-			gtkblist, PURPLE_CALLBACK(plugin_changed_cb), NULL);
-	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload",
-			gtkblist, PURPLE_CALLBACK(plugin_changed_cb), NULL);
-
-	purple_signal_connect(purple_conversations_get_handle(), "conversation-updated",
-						gtkblist, PURPLE_CALLBACK(conversation_updated_cb),
-						gtkblist);
-	purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
-						gtkblist, PURPLE_CALLBACK(conversation_deleting_cb),
-						gtkblist);
-	purple_signal_connect(purple_conversations_get_handle(), "conversation-created",
-			gtkblist, PURPLE_CALLBACK(conversation_created_cb),
-			gtkblist);
+
+	handle = purple_accounts_get_handle();
+	purple_signal_connect(handle, "account-enabled", gtkblist,
+	                      PURPLE_CALLBACK(account_modified), gtkblist);
+	purple_signal_connect(handle, "account-disabled", gtkblist,
+	                      PURPLE_CALLBACK(account_modified), gtkblist);
+	purple_signal_connect(handle, "account-removed", gtkblist,
+	                      PURPLE_CALLBACK(account_modified), gtkblist);
+	purple_signal_connect(handle, "account-status-changed", gtkblist,
+	                      PURPLE_CALLBACK(account_status_changed),
+	                      gtkblist);
+	purple_signal_connect(handle, "account-error-changed", gtkblist,
+	                      PURPLE_CALLBACK(update_account_error_state),
+	                      gtkblist);
+
+	handle = pidgin_account_get_handle();
+	purple_signal_connect(handle, "account-modified", gtkblist,
+	                      PURPLE_CALLBACK(account_modified), gtkblist);
+
+	handle = purple_connections_get_handle();
+	purple_signal_connect(handle, "signed-on", gtkblist,
+	                      PURPLE_CALLBACK(sign_on_off_cb), list);
+	purple_signal_connect(handle, "signed-off", gtkblist,
+	                      PURPLE_CALLBACK(sign_on_off_cb), list);
+
+	handle = purple_plugins_get_handle();
+	purple_signal_connect(handle, "plugin-load", gtkblist,
+	                      PURPLE_CALLBACK(plugin_changed_cb), NULL);
+	purple_signal_connect(handle, "plugin-unload", gtkblist,
+	                      PURPLE_CALLBACK(plugin_changed_cb), NULL);
+
+	handle = purple_conversations_get_handle();
+	purple_signal_connect(handle, "conversation-updated", gtkblist,
+	                      PURPLE_CALLBACK(conversation_updated_cb),
+	                      gtkblist);
+	purple_signal_connect(handle, "deleting-conversation", gtkblist,
+	                      PURPLE_CALLBACK(conversation_deleting_cb),
+	                      gtkblist);
+	purple_signal_connect(handle, "conversation-created", gtkblist,
+	                      PURPLE_CALLBACK(conversation_created_cb),
+	                      gtkblist);
 
 	gtk_widget_hide(gtkblist->headline_hbox);
-	gtk_widget_hide(gtkblist->error_buttons);
+
+	show_initial_account_errors(gtkblist);
 
 	/* emit our created signal */
+	handle = pidgin_blist_get_handle();
 	purple_signal_emit(handle, "gtkblist-created", list);
 }
 
@@ -5660,6 +5948,8 @@
 
 static void pidgin_blist_destroy(PurpleBuddyList *list)
 {
+	PidginBuddyListPrivate *priv;
+
 	if (!gtkblist)
 		return;
 
@@ -5693,6 +5983,9 @@
 	gtkblist->hand_cursor = NULL;
 	gtkblist->arrow_cursor = NULL;
 
+	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	g_free(priv);
+
 	g_free(gtkblist);
 	accountmenu = NULL;
 	gtkblist = NULL;