view pidgin/gtkaccount.c @ 31712:8fc25b77295a

Hopefully fixes #13298, the bug about not being able to add MSN buddies. Here's what I think is happening: 1. Before we add the buddy we issue an FQY request to ask the server what network the username is for (either normal MSN or Yahoo) 2. When we get the response we add the buddy to our buddy list with the network ID given to us by the server 3. For some reason the server is now returning a network ID of 0 ("unknown") instead of 1 ("normal MSN") for normal passport buddies, and we bail out when we encounter this. QuLogic thinks the server used to return 1 in this case. My change is to just not bail out if the FQY response has network ID 0. Instead of treat 0 as 1 and continue with the add. It looks like our SOAP request to add the buddy to our address book will fail a big further down the road if the buddy doesn't exist--so we're still protecting against that.
author Mark Doliner <mark@kingant.net>
date Mon, 28 Feb 2011 08:26:40 +0000
parents a8cc50c2279f
children 917c597beb97
line wrap: on
line source

/**
 * @file gtkaccount.c GTK+ Account Editor UI
 * @ingroup pidgin
 */

/* 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 "account.h"
#include "accountopt.h"
#include "core.h"
#include "debug.h"
#include "notify.h"
#include "plugin.h"
#include "prefs.h"
#include "prpl.h"
#include "request.h"
#include "savedstatuses.h"
#include "signals.h"
#include "util.h"

#include "gtkaccount.h"
#include "gtkblist.h"
#include "gtkdialogs.h"
#include "gtkutils.h"
#include "gtkstatusbox.h"
#include "pidginstock.h"

enum
{
	COLUMN_ICON,
	COLUMN_BUDDYICON,
	COLUMN_USERNAME,
	COLUMN_ENABLED,
	COLUMN_PROTOCOL,
	COLUMN_DATA,
	NUM_COLUMNS
};

typedef struct
{
	PurpleAccount *account;
	char *username;
	char *alias;

} PidginAccountAddUserData;

typedef struct
{
	GtkWidget *window;
	GtkWidget *treeview;

	GtkWidget *modify_button;
	GtkWidget *delete_button;
	GtkWidget *notebook;

	GtkListStore *model;
	GtkTreeIter drag_iter;

	GtkTreeViewColumn *username_col;

} AccountsWindow;

typedef struct
{
	GtkWidget *widget;
	gchar *setting;
	PurplePrefType type;
} ProtocolOptEntry;

typedef struct
{
	PidginAccountDialogType type;

	PurpleAccount *account;
	char *protocol_id;
	PurplePlugin *plugin;
	PurplePluginProtocolInfo *prpl_info;

	PurpleProxyType new_proxy_type;

	GList *user_split_entries;
	GList *protocol_opt_entries;

	GtkSizeGroup *sg;
	GtkWidget *window;

	GtkWidget *notebook;
	GtkWidget *top_vbox;
	GtkWidget *ok_button;
	GtkWidget *register_button;

	/* Login Options */
	GtkWidget *login_frame;
	GtkWidget *protocol_menu;
	GtkWidget *password_box;
	GtkWidget *username_entry;
	GtkWidget *password_entry;
	GtkWidget *alias_entry;
	GtkWidget *remember_pass_check;

	/* User Options */
	GtkWidget *user_frame;
	GtkWidget *new_mail_check;
	GtkWidget *icon_hbox;
	GtkWidget *icon_check;
	GtkWidget *icon_entry;
	GtkWidget *icon_filesel;
	GtkWidget *icon_preview;
	GtkWidget *icon_text;
	PurpleStoredImage *icon_img;

	/* Protocol Options */
	GtkWidget *protocol_frame;

	/* Proxy Options */
	GtkWidget *proxy_frame;
	GtkWidget *proxy_vbox;
	GtkWidget *proxy_dropdown;
	GtkWidget *proxy_host_entry;
	GtkWidget *proxy_port_entry;
	GtkWidget *proxy_user_entry;
	GtkWidget *proxy_pass_entry;

} AccountPrefsDialog;

static AccountsWindow *accounts_window = NULL;
static GHashTable *account_pref_wins;

static void add_account_to_liststore(PurpleAccount *account, gpointer user_data);
static void set_account(GtkListStore *store, GtkTreeIter *iter,
						  PurpleAccount *account, GdkPixbuf *global_buddyicon);

/**************************************************************************
 * Add/Modify Account dialog
 **************************************************************************/
static void add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent);
static void add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent);
static void add_protocol_options(AccountPrefsDialog *dialog);
static void add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent);

static GtkWidget *
add_pref_box(AccountPrefsDialog *dialog, GtkWidget *parent,
			 const char *text, GtkWidget *widget)
{
	return pidgin_add_widget_to_vbox(GTK_BOX(parent), text, dialog->sg, widget, TRUE, NULL);
}

static void
set_dialog_icon(AccountPrefsDialog *dialog, gpointer data, size_t len, gchar *new_icon_path)
{
	GdkPixbuf *pixbuf = NULL;

	dialog->icon_img = purple_imgstore_unref(dialog->icon_img);
	if (data != NULL)
	{
		if (len > 0)
			dialog->icon_img = purple_imgstore_add(data, len, new_icon_path);
		else
			g_free(data);
	}

	if (dialog->icon_img != NULL) {
		pixbuf = pidgin_pixbuf_from_imgstore(dialog->icon_img);
	}

	if (pixbuf && dialog->prpl_info &&
	    (dialog->prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY))
	{
		/* Scale the icon to something reasonable */
		int width, height;
		GdkPixbuf *scale;

		pidgin_buddy_icon_get_scale_size(pixbuf, &dialog->prpl_info->icon_spec,
				PURPLE_ICON_SCALE_DISPLAY, &width, &height);
		scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);

		g_object_unref(G_OBJECT(pixbuf));
		pixbuf = scale;
	}

	if (pixbuf == NULL)
	{
		/* Show a placeholder icon */
		GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
		pixbuf = gtk_widget_render_icon(dialog->window, PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR,
		                                icon_size, "PidginAccount");
	}

	gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_entry), pixbuf);
	if (pixbuf != NULL)
		g_object_unref(G_OBJECT(pixbuf));
}

static void
set_account_protocol_cb(GtkWidget *item, const char *id,
						AccountPrefsDialog *dialog)
{
	PurplePlugin *new_plugin;

	new_plugin = purple_find_prpl(id);

	dialog->plugin = new_plugin;

	if (dialog->plugin != NULL)
	{
		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);

		g_free(dialog->protocol_id);
		dialog->protocol_id = g_strdup(dialog->plugin->info->id);
	}

	if (dialog->account != NULL)
		purple_account_clear_settings(dialog->account);

	add_login_options(dialog,    dialog->top_vbox);
	add_user_options(dialog,     dialog->top_vbox);
	add_protocol_options(dialog);

	gtk_widget_grab_focus(dialog->protocol_menu);

	if (!dialog->prpl_info || !dialog->prpl_info->register_user ||
	    g_object_get_data(G_OBJECT(item), "fake")) {
		gtk_widget_hide(dialog->register_button);
	} else {
		if (dialog->prpl_info != NULL &&
		   (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME)) {
			gtk_widget_set_sensitive(dialog->register_button, TRUE);
		} else {
			gtk_widget_set_sensitive(dialog->register_button, FALSE);
		}
		gtk_widget_show(dialog->register_button);
	}
}

static gboolean
username_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
{
	GHashTable *table;
	const char *label;

	table = dialog->prpl_info->get_account_text_table(NULL);
	label = g_hash_table_lookup(table, "login_label");

	if(!strcmp(gtk_entry_get_text(GTK_ENTRY(widget)), label)) {
		gtk_entry_set_text(GTK_ENTRY(widget), "");
		gtk_widget_modify_text(widget, GTK_STATE_NORMAL,NULL);
	}

	g_hash_table_destroy(table);

	return FALSE;
}

static void
username_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog)
{
	if (dialog->ok_button)
		gtk_widget_set_sensitive(dialog->ok_button,
				*gtk_entry_get_text(entry) != '\0');
	if (dialog->register_button) {
		if (dialog->prpl_info != NULL && (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME))
			gtk_widget_set_sensitive(dialog->register_button, TRUE);
		else
			gtk_widget_set_sensitive(dialog->register_button,
					*gtk_entry_get_text(entry) != '\0');
	}
}

static gboolean
username_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
{
	GdkColor color = {0, 34952, 35466, 34181};
	GHashTable *table = NULL;
	const char *label = NULL;

	if(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
		table = dialog->prpl_info->get_account_text_table(NULL);
		label = g_hash_table_lookup(table, "login_label");

		if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
			/* We have to avoid hitting the username_changed_cb function
			 * because it enables buttons we don't want enabled yet ;)
			 */
			g_signal_handlers_block_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
			gtk_entry_set_text(GTK_ENTRY(widget), label);
			/* Make sure we can hit it again */
			g_signal_handlers_unblock_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
			gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
		}

		g_hash_table_destroy(table);
	}

	return FALSE;
}

static void
icon_filesel_choose_cb(const char *filename, gpointer data)
{
	AccountPrefsDialog *dialog = data;

	if (filename != NULL)
	{
		size_t len;
		gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
		set_dialog_icon(dialog, data, len, g_strdup(filename));
	}

	dialog->icon_filesel = NULL;
}

static void
icon_select_cb(GtkWidget *button, AccountPrefsDialog *dialog)
{
	dialog->icon_filesel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(dialog->window), icon_filesel_choose_cb, dialog);
	gtk_widget_show_all(dialog->icon_filesel);
}

static void
icon_reset_cb(GtkWidget *button, AccountPrefsDialog *dialog)
{
	set_dialog_icon(dialog, NULL, 0, NULL);
}

static void
account_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
		 GtkSelectionData *sd, guint info, guint t, AccountPrefsDialog *dialog)
{
	gchar *name = (gchar *)sd->data;

	if ((sd->length >= 0) && (sd->format == 8)) {
		/* Well, it looks like the drag event was cool.
		 * Let's do something with it */
		if (!g_ascii_strncasecmp(name, "file://", 7)) {
			GError *converr = NULL;
			gchar *tmp, *rtmp;
			gpointer data;
			size_t len;

			/* It looks like we're dealing with a local file. */
			if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
				purple_debug(PURPLE_DEBUG_ERROR, "buddyicon", "%s\n",
					   (converr ? converr->message :
					    "g_filename_from_uri error"));
				return;
			}
			if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n')))
				*rtmp = '\0';

			data = pidgin_convert_buddy_icon(dialog->plugin, tmp, &len);
			/* This takes ownership of tmp */
			set_dialog_icon(dialog, data, len, tmp);
		}
		gtk_drag_finish(dc, TRUE, FALSE, t);
	}
	gtk_drag_finish(dc, FALSE, FALSE, t);
}

static void
update_editable(PurpleConnection *gc, AccountPrefsDialog *dialog)
{
	GtkStyle *style;
	gboolean set;
	GList *l;

	if (dialog->account == NULL)
		return;

	if (gc != NULL && dialog->account != purple_connection_get_account(gc))
		return;

	set = !(purple_account_is_connected(dialog->account) || purple_account_is_connecting(dialog->account));
	gtk_widget_set_sensitive(dialog->protocol_menu, set);
	gtk_editable_set_editable(GTK_EDITABLE(dialog->username_entry), set);
	style = set ? NULL : gtk_widget_get_style(dialog->username_entry);
	gtk_widget_modify_base(dialog->username_entry, GTK_STATE_NORMAL,
			style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);

	for (l = dialog->user_split_entries ; l != NULL ; l = l->next) {
		if (GTK_IS_EDITABLE(l->data)) {
			gtk_editable_set_editable(GTK_EDITABLE(l->data), set);
			style = set ? NULL : gtk_widget_get_style(GTK_WIDGET(l->data));
			gtk_widget_modify_base(GTK_WIDGET(l->data), GTK_STATE_NORMAL,
					style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);
		} else {
			gtk_widget_set_sensitive(GTK_WIDGET(l->data), set);
		}
	}
}

static void
add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
{
	GtkWidget *frame;
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkWidget *entry;
	GtkWidget *menu;
	GtkWidget *item;
	GList *user_splits;
	GList *l, *l2;
	char *username = NULL;

	if (dialog->protocol_menu != NULL)
	{
#if GTK_CHECK_VERSION(2,12,0)
		g_object_ref(G_OBJECT(dialog->protocol_menu));
#else
		gtk_widget_ref(dialog->protocol_menu);
#endif
		hbox = g_object_get_data(G_OBJECT(dialog->protocol_menu), "container");
		gtk_container_remove(GTK_CONTAINER(hbox), dialog->protocol_menu);
	}

	if (dialog->login_frame != NULL)
		gtk_widget_destroy(dialog->login_frame);

	/* Build the login options frame. */
	frame = pidgin_make_frame(parent, _("Login Options"));

	/* cringe */
	dialog->login_frame = gtk_widget_get_parent(gtk_widget_get_parent(frame));

	gtk_box_reorder_child(GTK_BOX(parent), dialog->login_frame, 0);
	gtk_widget_show(dialog->login_frame);

	/* Main vbox */
	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_container_add(GTK_CONTAINER(frame), vbox);
	gtk_widget_show(vbox);

	/* Protocol */
	if (dialog->protocol_menu == NULL)
	{
		dialog->protocol_menu = pidgin_protocol_option_menu_new(
				dialog->protocol_id, G_CALLBACK(set_account_protocol_cb), dialog);
#if GTK_CHECK_VERSION(2,12,0)
		g_object_ref(G_OBJECT(dialog->protocol_menu));
#else
		gtk_widget_ref(dialog->protocol_menu);
#endif
	}

	hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu);
	g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox);

#if GTK_CHECK_VERSION(2,12,0)
	g_object_unref(G_OBJECT(dialog->protocol_menu));
#else
	gtk_widget_unref(dialog->protocol_menu);
#endif

	/* Username */
	dialog->username_entry = gtk_entry_new();
	g_object_set(G_OBJECT(dialog->username_entry), "truncate-multiline", TRUE, NULL);

	add_pref_box(dialog, vbox, _("_Username:"), dialog->username_entry);

	if (dialog->account != NULL)
		username = g_strdup(purple_account_get_username(dialog->account));

	if (!username && dialog->prpl_info
			&& PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
		GdkColor color = {0, 34952, 35466, 34181};
		GHashTable *table;
		const char *label;
		table = dialog->prpl_info->get_account_text_table(NULL);
		label = g_hash_table_lookup(table, "login_label");

		gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), label);
		g_signal_connect(G_OBJECT(dialog->username_entry), "focus-in-event",
				G_CALLBACK(username_focus_cb), dialog);
		g_signal_connect(G_OBJECT(dialog->username_entry), "focus-out-event",
				G_CALLBACK(username_nofocus_cb), dialog);
		gtk_widget_modify_text(dialog->username_entry, GTK_STATE_NORMAL, &color);
		g_hash_table_destroy(table);
	}

	g_signal_connect(G_OBJECT(dialog->username_entry), "changed",
					 G_CALLBACK(username_changed_cb), dialog);

	/* Do the user split thang */
	if (dialog->prpl_info == NULL)
		user_splits = NULL;
	else
		user_splits = dialog->prpl_info->user_splits;

	if (dialog->user_split_entries != NULL) {
		g_list_free(dialog->user_split_entries);
		dialog->user_split_entries = NULL;
	}

	for (l = user_splits; l != NULL; l = l->next) {
		PurpleAccountUserSplit *split = l->data;
		char *buf;

		buf = g_strdup_printf("_%s:", purple_account_user_split_get_text(split));

		entry = gtk_entry_new();

		add_pref_box(dialog, vbox, buf, entry);

		g_free(buf);

		dialog->user_split_entries =
			g_list_append(dialog->user_split_entries, entry);
	}

	for (l = g_list_last(dialog->user_split_entries),
		 l2 = g_list_last(user_splits);
		 l != NULL && l2 != NULL;
		 l = l->prev, l2 = l2->prev) {

		GtkWidget *entry = l->data;
		PurpleAccountUserSplit *split = l2->data;
		const char *value = NULL;
		char *c;

		if (dialog->account != NULL) {
			if(purple_account_user_split_get_reverse(split))
				c = strrchr(username,
						purple_account_user_split_get_separator(split));
			else
				c = strchr(username,
						purple_account_user_split_get_separator(split));

			if (c != NULL) {
				*c = '\0';
				c++;

				value = c;
			}
		}
		if (value == NULL)
			value = purple_account_user_split_get_default_value(split);

		/* Google Talk default domain hackery! */
		menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
		item = gtk_menu_get_active(GTK_MENU(menu));
		if (value == NULL && g_object_get_data(G_OBJECT(item), "fakegoogle") &&
			!strcmp(purple_account_user_split_get_text(split), _("Domain")))
			value = "gmail.com";

		if (value == NULL && g_object_get_data(G_OBJECT(item), "fakefacebook") &&
			!strcmp(purple_account_user_split_get_text(split), _("Domain")))
			value = "chat.facebook.com";

		if (value != NULL)
			gtk_entry_set_text(GTK_ENTRY(entry), value);
	}

	if (username != NULL)
		gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), username);

	g_free(username);


	/* Password */
	dialog->password_entry = gtk_entry_new();
	gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE);
#if !GTK_CHECK_VERSION(2,16,0)
	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*')
		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR);
#endif /* Less than GTK+ 2.16 */
	dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
										  dialog->password_entry);

	/* Remember Password */
	dialog->remember_pass_check =
		gtk_check_button_new_with_mnemonic(_("Remember pass_word"));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
								 FALSE);
	gtk_box_pack_start(GTK_BOX(vbox), dialog->remember_pass_check,
					   FALSE, FALSE, 0);
	gtk_widget_show(dialog->remember_pass_check);

	/* Set the fields. */
	if (dialog->account != NULL) {
		if (purple_account_get_password(dialog->account) &&
		    purple_account_get_remember_password(dialog->account))
			gtk_entry_set_text(GTK_ENTRY(dialog->password_entry),
							   purple_account_get_password(dialog->account));

		gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
				purple_account_get_remember_password(dialog->account));
	}

	if (dialog->prpl_info != NULL &&
		(dialog->prpl_info->options & OPT_PROTO_NO_PASSWORD)) {

		gtk_widget_hide(dialog->password_box);
		gtk_widget_hide(dialog->remember_pass_check);
	}

	/* Do not let the user change the protocol/username while connected. */
	update_editable(NULL, dialog);
	purple_signal_connect(purple_connections_get_handle(), "signing-on", dialog,
					G_CALLBACK(update_editable), dialog);
	purple_signal_connect(purple_connections_get_handle(), "signed-off", dialog,
					G_CALLBACK(update_editable), dialog);
}

static void
icon_check_cb(GtkWidget *checkbox, AccountPrefsDialog *dialog)
{
	gtk_widget_set_sensitive(dialog->icon_hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
}

static void
add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *vbox2;
	GtkWidget *hbox;
	GtkWidget *hbox2;
	GtkWidget *button;
	GtkWidget *label;

	if (dialog->user_frame != NULL)
		gtk_widget_destroy(dialog->user_frame);

	/* Build the user options frame. */
	frame = pidgin_make_frame(parent, _("User Options"));
	dialog->user_frame = gtk_widget_get_parent(gtk_widget_get_parent(frame));

	gtk_box_reorder_child(GTK_BOX(parent), dialog->user_frame, 1);
	gtk_widget_show(dialog->user_frame);

	/* Main vbox */
	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_container_add(GTK_CONTAINER(frame), vbox);
	gtk_widget_show(vbox);

	/* Alias */
	dialog->alias_entry = gtk_entry_new();
	add_pref_box(dialog, vbox, _("_Local alias:"), dialog->alias_entry);

	/* New mail notifications */
	dialog->new_mail_check =
		gtk_check_button_new_with_mnemonic(_("New _mail notifications"));
	gtk_box_pack_start(GTK_BOX(vbox), dialog->new_mail_check, FALSE, FALSE, 0);
	gtk_widget_show(dialog->new_mail_check);

	/* Buddy icon */
	dialog->icon_check = gtk_check_button_new_with_mnemonic(_("Use this buddy _icon for this account:"));
	g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog);
	gtk_widget_show(dialog->icon_check);
	gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0);

	dialog->icon_hbox = hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_widget_set_sensitive(hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	gtk_widget_show(hbox);

	label = gtk_label_new("    ");
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
	gtk_widget_show(label);

	button = gtk_button_new();
	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
	gtk_widget_show(button);
	g_signal_connect(G_OBJECT(button), "clicked",
	                 G_CALLBACK(icon_select_cb), dialog);

	dialog->icon_entry = gtk_image_new();
	gtk_container_add(GTK_CONTAINER(button), dialog->icon_entry);
	gtk_widget_show(dialog->icon_entry);
	/* TODO: Uh, isn't this next line pretty useless? */
	pidgin_set_accessible_label (dialog->icon_entry, label);
	purple_imgstore_unref(dialog->icon_img);
	dialog->icon_img = NULL;

	vbox2 = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
	gtk_widget_show(vbox2);

	hbox2 = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, PIDGIN_HIG_BORDER);
	gtk_widget_show(hbox2);

	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
	g_signal_connect(G_OBJECT(button), "clicked",
			 G_CALLBACK(icon_reset_cb), dialog);
	gtk_box_pack_start(GTK_BOX(hbox2), button, FALSE, FALSE, 0);
	gtk_widget_show(button);

	if (dialog->prpl_info != NULL) {
		if (!(dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK))
			gtk_widget_hide(dialog->new_mail_check);

		if (dialog->prpl_info->icon_spec.format == NULL) {
			gtk_widget_hide(dialog->icon_check);
			gtk_widget_hide(dialog->icon_hbox);
		}
	}

	if (dialog->account != NULL) {
		PurpleStoredImage *img;
		gpointer data = NULL;
		size_t len = 0;

		if (purple_account_get_alias(dialog->account))
			gtk_entry_set_text(GTK_ENTRY(dialog->alias_entry),
							   purple_account_get_alias(dialog->account));

		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->new_mail_check),
					     purple_account_get_check_mail(dialog->account));

		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->icon_check),
					     !purple_account_get_bool(dialog->account, "use-global-buddyicon",
								       TRUE));

		img = purple_buddy_icons_find_account_icon(dialog->account);
		if (img)
		{
			len = purple_imgstore_get_size(img);
			data = g_memdup(purple_imgstore_get_data(img), len);
		}
		set_dialog_icon(dialog, data, len,
		                g_strdup(purple_account_get_buddy_icon_path(dialog->account)));
	} else {
		set_dialog_icon(dialog, NULL, 0, NULL);
	}

#if 0
	if (!dialog->prpl_info ||
			(!(dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) &&
			 (dialog->prpl_info->icon_spec.format ==  NULL))) {

		/* Nothing to see :( aww. */
		gtk_widget_hide(dialog->user_frame);
	}
#endif
}

static void
add_protocol_options(AccountPrefsDialog *dialog)
{
	PurpleAccountOption *option;
	PurpleAccount *account;
	GtkWidget *vbox, *check, *entry, *combo, *menu, *item;
	GList *list, *node;
	gint i, idx, int_value;
	GtkListStore *model;
	GtkTreeIter iter;
	GtkCellRenderer *renderer;
	PurpleKeyValuePair *kvp;
	GList *l;
	char buf[1024];
	char *title, *tmp;
	const char *str_value;
	gboolean bool_value;
	ProtocolOptEntry *opt_entry;

	if (dialog->protocol_frame != NULL) {
		gtk_notebook_remove_page (GTK_NOTEBOOK(dialog->notebook), 1);
		dialog->protocol_frame = NULL;
	}

	while (dialog->protocol_opt_entries != NULL) {
		ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data;
		g_free(opt_entry->setting);
		g_free(opt_entry);
		dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries);
	}

	if (dialog->prpl_info == NULL ||
			dialog->prpl_info->protocol_options == NULL)
		return;

	account = dialog->account;

	/* Main vbox */
	dialog->protocol_frame = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
	gtk_notebook_insert_page(GTK_NOTEBOOK(dialog->notebook), vbox,
			gtk_label_new_with_mnemonic(_("Ad_vanced")), 1);
	gtk_widget_show(vbox);

	menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
	item = gtk_menu_get_active(GTK_MENU(menu));

	for (l = dialog->prpl_info->protocol_options; l != NULL; l = l->next)
	{
		option = (PurpleAccountOption *)l->data;

		opt_entry = g_new0(ProtocolOptEntry, 1);
		opt_entry->type = purple_account_option_get_type(option);
		opt_entry->setting = g_strdup(purple_account_option_get_setting(option));

		switch (opt_entry->type)
		{
			case PURPLE_PREF_BOOLEAN:
				if (account == NULL ||
					strcmp(purple_account_get_protocol_id(account),
						   dialog->protocol_id))
				{
					bool_value = purple_account_option_get_default_bool(option);
				}
				else
				{
					bool_value = purple_account_get_bool(account,
						purple_account_option_get_setting(option),
						purple_account_option_get_default_bool(option));
				}

				tmp = g_strconcat("_", purple_account_option_get_text(option), NULL);
				opt_entry->widget = check = gtk_check_button_new_with_mnemonic(tmp);
				g_free(tmp);

				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check),
											 bool_value);

				gtk_box_pack_start(GTK_BOX(vbox), check, FALSE, FALSE, 0);
				gtk_widget_show(check);
				break;

			case PURPLE_PREF_INT:
				if (account == NULL ||
					strcmp(purple_account_get_protocol_id(account),
						   dialog->protocol_id))
				{
					int_value = purple_account_option_get_default_int(option);
				}
				else
				{
					int_value = purple_account_get_int(account,
						purple_account_option_get_setting(option),
						purple_account_option_get_default_int(option));
				}

				g_snprintf(buf, sizeof(buf), "%d", int_value);

				opt_entry->widget = entry = gtk_entry_new();
				gtk_entry_set_text(GTK_ENTRY(entry), buf);

				title = g_strdup_printf("_%s:",
						purple_account_option_get_text(option));
				add_pref_box(dialog, vbox, title, entry);
				g_free(title);
				break;

			case PURPLE_PREF_STRING:
				if (account == NULL ||
					strcmp(purple_account_get_protocol_id(account),
						   dialog->protocol_id))
				{
					str_value = purple_account_option_get_default_string(option);
				}
				else
				{
					str_value = purple_account_get_string(account,
						purple_account_option_get_setting(option),
						purple_account_option_get_default_string(option));
				}

				opt_entry->widget = entry = gtk_entry_new();
				if (purple_account_option_get_masked(option))
				{
					gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
#if !GTK_CHECK_VERSION(2,16,0)
					if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
						gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
#endif /* Less than GTK+ 2.16 */
				}

				if (str_value != NULL)
					gtk_entry_set_text(GTK_ENTRY(entry), str_value);

				title = g_strdup_printf("_%s:",
						purple_account_option_get_text(option));
				add_pref_box(dialog, vbox, title, entry);
				g_free(title);
				break;

			case PURPLE_PREF_STRING_LIST:
				i = 0;
				idx = 0;

				if (account == NULL ||
					strcmp(purple_account_get_protocol_id(account),
						   dialog->protocol_id))
				{
					str_value = purple_account_option_get_default_list_value(option);
				}
				else
				{
					str_value = purple_account_get_string(account,
						purple_account_option_get_setting(option),
						purple_account_option_get_default_list_value(option));
				}

				list = purple_account_option_get_list(option);
				model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
				opt_entry->widget = combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));

				if (g_object_get_data(G_OBJECT(item), "fakefacebook") &&
					!strcmp(opt_entry->setting, "connection_security"))
					str_value = "opportunistic_tls";

				/* Loop through list of PurpleKeyValuePair items */
				for (node = list; node != NULL; node = node->next) {
					if (node->data != NULL) {
						kvp = (PurpleKeyValuePair *) node->data;
						if ((kvp->value != NULL) && (str_value != NULL) &&
						    !g_utf8_collate(kvp->value, str_value))
							idx = i;

						gtk_list_store_append(model, &iter);
						gtk_list_store_set(model, &iter,
								0, kvp->key,
								1, kvp->value,
								-1);
					}

					i++;
				}

				/* Set default */
				gtk_combo_box_set_active(GTK_COMBO_BOX(combo), idx);

				/* Define renderer */
				renderer = gtk_cell_renderer_text_new();
				gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer,
						TRUE);
				gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),
						renderer, "text", 0, NULL);

				title = g_strdup_printf("_%s:",
						purple_account_option_get_text(option));
				add_pref_box(dialog, vbox, title, combo);
				g_free(title);
				break;

			default:
				purple_debug_error("gtkaccount", "Invalid Account Option pref type (%d)\n",
						   opt_entry->type);
				g_free(opt_entry->setting);
				g_free(opt_entry);
				continue;
		}

		dialog->protocol_opt_entries =
			g_list_append(dialog->protocol_opt_entries, opt_entry);

	}
}

static GtkWidget *
make_proxy_dropdown(void)
{
	GtkWidget *dropdown;
	GtkListStore *model;
	GtkTreeIter iter;
	GtkCellRenderer *renderer;

	model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
	dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, purple_running_gnome() ? _("Use GNOME Proxy Settings")
			:_("Use Global Proxy Settings"),
			1, PURPLE_PROXY_USE_GLOBAL,
			-1);

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, _("No Proxy"),
			1, PURPLE_PROXY_NONE,
			-1);

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, _("HTTP"),
			1, PURPLE_PROXY_HTTP,
			-1);

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, _("SOCKS 4"),
			1, PURPLE_PROXY_SOCKS4,
			-1);

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, _("SOCKS 5"),
			1, PURPLE_PROXY_SOCKS5,
			-1);

	gtk_list_store_append(model, &iter);
	gtk_list_store_set(model, &iter,
			0, _("Use Environmental Settings"),
			1, PURPLE_PROXY_USE_ENVVAR,
			-1);

	renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE);
	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer,
			"text", 0, NULL);

	return dropdown;
}

static void
proxy_type_changed_cb(GtkWidget *menu, AccountPrefsDialog *dialog)
{
	dialog->new_proxy_type =
		gtk_combo_box_get_active(GTK_COMBO_BOX(menu)) - 1;

	if (dialog->new_proxy_type == PURPLE_PROXY_USE_GLOBAL ||
		dialog->new_proxy_type == PURPLE_PROXY_NONE ||
		dialog->new_proxy_type == PURPLE_PROXY_USE_ENVVAR) {

		gtk_widget_hide_all(dialog->proxy_vbox);
	}
	else
		gtk_widget_show_all(dialog->proxy_vbox);
}

static void
port_popup_cb(GtkWidget *w, GtkMenu *menu, gpointer data)
{
	GtkWidget *item1;
	GtkWidget *item2;

	/* This is an easter egg.
	   It means one of two things, both intended as humourus:
	   A) your network is really slow and you have nothing better to do than
	      look at butterflies.
	   B)You are looking really closely at something that shouldn't matter. */
	item1 = gtk_menu_item_new_with_label(_("If you look real closely"));

	/* This is an easter egg. See the comment on the previous line in the source. */
	item2 = gtk_menu_item_new_with_label(_("you can see the butterflies mating"));

	gtk_widget_show(item1);
	gtk_widget_show(item2);

	/* Prepend these in reverse order so they appear correctly. */
	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item2);
	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item1);
}

static void
add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent)
{
	PurpleProxyInfo *proxy_info;
	GtkWidget *vbox;
	GtkWidget *vbox2;

	if (dialog->proxy_frame != NULL)
		gtk_widget_destroy(dialog->proxy_frame);

	/* Main vbox */
	dialog->proxy_frame = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_container_add(GTK_CONTAINER(parent), vbox);
	gtk_widget_show(vbox);

	/* Proxy Type drop-down. */
	dialog->proxy_dropdown = make_proxy_dropdown();

	add_pref_box(dialog, vbox, _("Proxy _type:"), dialog->proxy_dropdown);

	/* Setup the second vbox, which may be hidden at times. */
	dialog->proxy_vbox = vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
	gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, PIDGIN_HIG_BORDER);
	gtk_widget_show(vbox2);

	/* Host */
	dialog->proxy_host_entry = gtk_entry_new();
	add_pref_box(dialog, vbox2, _("_Host:"), dialog->proxy_host_entry);

	/* Port */
	dialog->proxy_port_entry = gtk_entry_new();
	add_pref_box(dialog, vbox2, _("_Port:"), dialog->proxy_port_entry);

	g_signal_connect(G_OBJECT(dialog->proxy_port_entry), "populate-popup",
					 G_CALLBACK(port_popup_cb), NULL);

	/* User */
	dialog->proxy_user_entry = gtk_entry_new();

	add_pref_box(dialog, vbox2, _("_Username:"), dialog->proxy_user_entry);

	/* Password */
	dialog->proxy_pass_entry = gtk_entry_new();
	gtk_entry_set_visibility(GTK_ENTRY(dialog->proxy_pass_entry), FALSE);
#if !GTK_CHECK_VERSION(2,16,0)
	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry)) == '*')
		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry), PIDGIN_INVISIBLE_CHAR);
#endif /* Less than GTK+ 2.16 */
	add_pref_box(dialog, vbox2, _("Pa_ssword:"), dialog->proxy_pass_entry);

	if (dialog->account != NULL &&
		(proxy_info = purple_account_get_proxy_info(dialog->account)) != NULL) {

		PurpleProxyType type = purple_proxy_info_get_type(proxy_info);
		const char *value;
		int int_val;

		/* Hah! */
		/* I dunno what you're laughing about, fuzz ball. */
		dialog->new_proxy_type = type;
		gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->proxy_dropdown),
				type + 1);

		if (type == PURPLE_PROXY_USE_GLOBAL || type == PURPLE_PROXY_NONE ||
				type == PURPLE_PROXY_USE_ENVVAR)
			gtk_widget_hide_all(vbox2);


		if ((value = purple_proxy_info_get_host(proxy_info)) != NULL)
			gtk_entry_set_text(GTK_ENTRY(dialog->proxy_host_entry), value);

		if ((int_val = purple_proxy_info_get_port(proxy_info)) != 0) {
			char buf[11];

			g_snprintf(buf, sizeof(buf), "%d", int_val);

			gtk_entry_set_text(GTK_ENTRY(dialog->proxy_port_entry), buf);
		}

		if ((value = purple_proxy_info_get_username(proxy_info)) != NULL)
			gtk_entry_set_text(GTK_ENTRY(dialog->proxy_user_entry), value);

		if ((value = purple_proxy_info_get_password(proxy_info)) != NULL)
			gtk_entry_set_text(GTK_ENTRY(dialog->proxy_pass_entry), value);
	}
	else {
		dialog->new_proxy_type = PURPLE_PROXY_USE_GLOBAL;
		gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->proxy_dropdown),
				dialog->new_proxy_type + 1);
		gtk_widget_hide_all(vbox2);
	}

	/* Connect signals. */
	g_signal_connect(G_OBJECT(dialog->proxy_dropdown), "changed",
					 G_CALLBACK(proxy_type_changed_cb), dialog);
}

static gboolean
account_win_destroy_cb(GtkWidget *w, GdkEvent *event,
					   AccountPrefsDialog *dialog)
{
	g_hash_table_remove(account_pref_wins, dialog->account);

	gtk_widget_destroy(dialog->window);

	g_list_free(dialog->user_split_entries);
	while (dialog->protocol_opt_entries != NULL) {
		ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data;
		g_free(opt_entry->setting);
		g_free(opt_entry);
		dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries);
	}
	g_free(dialog->protocol_id);
	g_object_unref(dialog->sg);

	purple_imgstore_unref(dialog->icon_img);

	if (dialog->icon_filesel)
		gtk_widget_destroy(dialog->icon_filesel);

	purple_signals_disconnect_by_handle(dialog);

	g_free(dialog);
	return FALSE;
}

static void
cancel_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
{
	account_win_destroy_cb(NULL, NULL, dialog);
}

static void
ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
{
	PurpleProxyInfo *proxy_info = NULL;
	GList *l, *l2;
	const char *value;
	char *username;
	char *tmp;
	gboolean new_acct = FALSE, icon_change = FALSE;
	PurpleAccount *account;

	/* Build the username string. */
	username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry)));

	if (dialog->prpl_info != NULL)
	{
		for (l = dialog->prpl_info->user_splits,
			 l2 = dialog->user_split_entries;
			 l != NULL && l2 != NULL;
			 l = l->next, l2 = l2->next)
		{
			PurpleAccountUserSplit *split = l->data;
			GtkEntry *entry = l2->data;
			char sep[2] = " ";

			value = gtk_entry_get_text(entry);

			*sep = purple_account_user_split_get_separator(split);

			tmp = g_strconcat(username, sep,
					(*value ? value :
					 purple_account_user_split_get_default_value(split)),
					NULL);

			g_free(username);
			username = tmp;
		}
	}

	if (dialog->account == NULL)
	{
		if (purple_accounts_find(username, dialog->protocol_id) != NULL) {
			purple_debug_warning("gtkaccount", "Trying to add a duplicate %s account (%s).\n",
				dialog->protocol_id, username);

			purple_notify_error(NULL, NULL, _("Unable to save new account"),
				_("An account already exists with the specified criteria."));

			g_free(username);
			return;
		}

		if (purple_accounts_get_all() == NULL) {
			/* We're adding our first account.  Be polite and show the buddy list */
			purple_blist_set_visible(TRUE);
		}

		account = purple_account_new(username, dialog->protocol_id);
		new_acct = TRUE;
	}
	else
	{
		account = dialog->account;

		/* Protocol */
		purple_account_set_protocol_id(account, dialog->protocol_id);
	}

	/* Alias */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->alias_entry));

	if (*value != '\0')
		purple_account_set_alias(account, value);
	else
		purple_account_set_alias(account, NULL);

	/* Buddy Icon */
	if (dialog->prpl_info != NULL && dialog->prpl_info->icon_spec.format != NULL)
	{
		const char *filename;

		if (new_acct || purple_account_get_bool(account, "use-global-buddyicon", TRUE) ==
			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)))
		{
			icon_change = TRUE;
		}
		purple_account_set_bool(account, "use-global-buddyicon", !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));

		if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)))
		{
			if (dialog->icon_img)
			{
				size_t len = purple_imgstore_get_size(dialog->icon_img);
				purple_buddy_icons_set_account_icon(account,
				                                    g_memdup(purple_imgstore_get_data(dialog->icon_img), len),
				                                    len);
				purple_account_set_buddy_icon_path(account, purple_imgstore_get_filename(dialog->icon_img));
			}
			else
			{
				purple_buddy_icons_set_account_icon(account, NULL, 0);
				purple_account_set_buddy_icon_path(account, NULL);
			}
		}
		else if ((filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) && icon_change)
		{
			size_t len;
			gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
			purple_account_set_buddy_icon_path(account, filename);
			purple_buddy_icons_set_account_icon(account, data, len);
		}
	}


	/* Remember Password */
	purple_account_set_remember_password(account,
			gtk_toggle_button_get_active(
					GTK_TOGGLE_BUTTON(dialog->remember_pass_check)));

	/* Check Mail */
	if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK)
		purple_account_set_check_mail(account,
			gtk_toggle_button_get_active(
					GTK_TOGGLE_BUTTON(dialog->new_mail_check)));

	/* Password */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->password_entry));

	/*
	 * We set the password if this is a new account because new accounts
	 * will be set to online, and if the user has entered a password into
	 * the account editor (but has not checked the 'save' box), then we
	 * don't want to prompt them.
	 */
	if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0'))
		purple_account_set_password(account, value);
	else
		purple_account_set_password(account, NULL);

	purple_account_set_username(account, username);
	g_free(username);

	/* Add the protocol settings */
	if (dialog->prpl_info) {
		ProtocolOptEntry *opt_entry;
		GtkTreeIter iter;
		char *value2;
		int int_value;
		gboolean bool_value;

		for (l2 = dialog->protocol_opt_entries; l2; l2 = l2->next) {

			opt_entry = l2->data;

			switch (opt_entry->type) {
				case PURPLE_PREF_STRING:
					value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
					purple_account_set_string(account, opt_entry->setting, value);
					break;

				case PURPLE_PREF_INT:
					int_value = atoi(gtk_entry_get_text(GTK_ENTRY(opt_entry->widget)));
					purple_account_set_int(account, opt_entry->setting, int_value);
					break;

				case PURPLE_PREF_BOOLEAN:
					bool_value =
						gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_entry->widget));
					purple_account_set_bool(account, opt_entry->setting, bool_value);
					break;

				case PURPLE_PREF_STRING_LIST:
					value2 = NULL;
					if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(opt_entry->widget), &iter))
						gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(opt_entry->widget)), &iter, 1, &value2, -1);
					purple_account_set_string(account, opt_entry->setting, value2);
					break;

				default:
					break;
			}
		}
	}

	/* Set the proxy stuff. */
	proxy_info = purple_account_get_proxy_info(account);

	/* Create the proxy info if it doesn't exist. */
	if (proxy_info == NULL) {
		proxy_info = purple_proxy_info_new();
		purple_account_set_proxy_info(account, proxy_info);
	}

	/* Set the proxy info type. */
	purple_proxy_info_set_type(proxy_info, dialog->new_proxy_type);

	/* Host */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->proxy_host_entry));

	if (*value != '\0')
		purple_proxy_info_set_host(proxy_info, value);
	else
		purple_proxy_info_set_host(proxy_info, NULL);

	/* Port */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->proxy_port_entry));

	if (*value != '\0')
		purple_proxy_info_set_port(proxy_info, atoi(value));
	else
		purple_proxy_info_set_port(proxy_info, 0);

	/* Username */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->proxy_user_entry));

	if (*value != '\0')
		purple_proxy_info_set_username(proxy_info, value);
	else
		purple_proxy_info_set_username(proxy_info, NULL);

	/* Password */
	value = gtk_entry_get_text(GTK_ENTRY(dialog->proxy_pass_entry));

	if (*value != '\0')
		purple_proxy_info_set_password(proxy_info, value);
	else
		purple_proxy_info_set_password(proxy_info, NULL);

	/* If there are no values set then proxy_info NULL */
	if ((purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL) &&
		(purple_proxy_info_get_host(proxy_info) == NULL) &&
		(purple_proxy_info_get_port(proxy_info) == 0) &&
		(purple_proxy_info_get_username(proxy_info) == NULL) &&
		(purple_proxy_info_get_password(proxy_info) == NULL))
	{
		purple_account_set_proxy_info(account, NULL);
		proxy_info = NULL;
	}

	/* If this is a new account, add it to our list */
	if (new_acct)
		purple_accounts_add(account);
	else
		purple_signal_emit(pidgin_account_get_handle(), "account-modified", account);

	/* If this is a new account, then sign on! */
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->register_button))) {
		purple_account_register(account);
	} else if (new_acct) {
		const PurpleSavedStatus *saved_status;

		saved_status = purple_savedstatus_get_current();
		if (saved_status != NULL) {
			purple_savedstatus_activate_for_account(saved_status, account);
			purple_account_set_enabled(account, PIDGIN_UI, TRUE);
		}
	}

	/* We no longer need the data from the dialog window */
	account_win_destroy_cb(NULL, NULL, dialog);

}

static const GtkTargetEntry dnd_targets[] = {
	{"text/plain", 0, 0},
	{"text/uri-list", 0, 1},
	{"STRING", 0, 2}
};

void
pidgin_account_dialog_show(PidginAccountDialogType type,
							 PurpleAccount *account)
{
	AccountPrefsDialog *dialog;
	GtkWidget *win;
	GtkWidget *main_vbox;
	GtkWidget *vbox;
	GtkWidget *dbox;
	GtkWidget *notebook;
	GtkWidget *button;

	if (accounts_window != NULL && account != NULL &&
		(dialog = g_hash_table_lookup(account_pref_wins, account)) != NULL)
	{
		gtk_window_present(GTK_WINDOW(dialog->window));
		return;
	}

	dialog = g_new0(AccountPrefsDialog, 1);

	if (accounts_window != NULL && account != NULL)
	{
		g_hash_table_insert(account_pref_wins, account, dialog);
	}

	dialog->account = account;
	dialog->type    = type;
	dialog->sg      = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

	if (dialog->account == NULL) {
		/* Select the first prpl in the list*/
		GList *prpl_list = purple_plugins_get_protocols();
		if (prpl_list != NULL)
			dialog->protocol_id = g_strdup(((PurplePlugin *) prpl_list->data)->info->id);
	}
	else
	{
		dialog->protocol_id =
			g_strdup(purple_account_get_protocol_id(dialog->account));
	}

	if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL)
		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);

	dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
		PIDGIN_HIG_BOX_SPACE, "account", FALSE);

	g_signal_connect(G_OBJECT(win), "delete_event",
					 G_CALLBACK(account_win_destroy_cb), dialog);

	/* Setup the vbox */
	main_vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BOX_SPACE);

	dialog->notebook = notebook = gtk_notebook_new();
	gtk_box_pack_start(GTK_BOX(main_vbox), notebook, FALSE, FALSE, 0);
	gtk_widget_show(GTK_WIDGET(notebook));

	/* Setup the inner vbox */
	dialog->top_vbox = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox,
			gtk_label_new_with_mnemonic(_("_Basic")));
	gtk_widget_show(vbox);

	/* Setup the top frames. */
	add_login_options(dialog, vbox);
	add_user_options(dialog, vbox);

	button = gtk_check_button_new_with_mnemonic(
		_("Create _this new account on the server"));
	gtk_box_pack_start(GTK_BOX(main_vbox), button, FALSE, FALSE, 0);
	gtk_widget_show(button);
	dialog->register_button = button;
	if (dialog->account == NULL)
		gtk_widget_set_sensitive(button, FALSE);

	if (!dialog->prpl_info || !dialog->prpl_info->register_user)
		gtk_widget_hide(button);

	/* Setup the page with 'Advanced' (protocol options). */
	add_protocol_options(dialog);

	/* Setup the page with 'Proxy'. */
	dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
	gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dbox,
			gtk_label_new_with_mnemonic(_("P_roxy")));
	gtk_widget_show(dbox);
	add_proxy_options(dialog, dbox);

	/* Cancel button */
	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog);

	/* Save button */
	button = pidgin_dialog_add_button(GTK_DIALOG(win),
	                                  (type == PIDGIN_ADD_ACCOUNT_DIALOG) ? GTK_STOCK_ADD : GTK_STOCK_SAVE,
	                                  G_CALLBACK(ok_account_prefs_cb),
	                                  dialog);
	if (dialog->account == NULL)
		gtk_widget_set_sensitive(button, FALSE);
	dialog->ok_button = button;

	/* Set up DND */
	gtk_drag_dest_set(dialog->window,
			  GTK_DEST_DEFAULT_MOTION |
			  GTK_DEST_DEFAULT_DROP,
			  dnd_targets,
			  sizeof(dnd_targets) / sizeof(GtkTargetEntry),
			  GDK_ACTION_COPY);

	g_signal_connect(G_OBJECT(dialog->window), "drag_data_received",
			 G_CALLBACK(account_dnd_recv), dialog);

	/* Show the window. */
	gtk_widget_show(win);
	if (!account)
		gtk_widget_grab_focus(dialog->protocol_menu);
}

/**************************************************************************
 * Accounts Dialog
 **************************************************************************/
static void
signed_on_off_cb(PurpleConnection *gc, gpointer user_data)
{
	PurpleAccount *account;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GdkPixbuf *pixbuf;
	size_t index;

	/* Don't need to do anything if the accounts window is not visible */
	if (accounts_window == NULL)
		return;

	account = purple_connection_get_account(gc);
	model = GTK_TREE_MODEL(accounts_window->model);
	index = g_list_index(purple_accounts_get_all(), account);

	if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index))
	{
		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
		if ((pixbuf != NULL) && purple_account_is_disconnected(account))
			gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);

		gtk_list_store_set(accounts_window->model, &iter,
				   COLUMN_ICON, pixbuf,
				   -1);

		if (pixbuf != NULL)
			g_object_unref(G_OBJECT(pixbuf));
	}
}

/*
 * Get the GtkTreeIter of the specified account in the
 * GtkListStore
 */
static gboolean
accounts_window_find_account_in_treemodel(GtkTreeIter *iter, PurpleAccount *account)
{
	GtkTreeModel *model;
	PurpleAccount *cur;

	g_return_val_if_fail(account != NULL, FALSE);
	g_return_val_if_fail(accounts_window != NULL, FALSE);

	model = GTK_TREE_MODEL(accounts_window->model);

	if (!gtk_tree_model_get_iter_first(model, iter))
		return FALSE;

	gtk_tree_model_get(model, iter, COLUMN_DATA, &cur, -1);
	if (cur == account)
		return TRUE;

	while (gtk_tree_model_iter_next(model, iter))
	{
		gtk_tree_model_get(model, iter, COLUMN_DATA, &cur, -1);
		if (cur == account)
			return TRUE;
	}

	return FALSE;
}

static void
account_removed_cb(PurpleAccount *account, gpointer user_data)
{
	AccountPrefsDialog *dialog;
	GtkTreeIter iter;

	/* If the account was being modified, close the edit window */
	if ((dialog = g_hash_table_lookup(account_pref_wins, account)) != NULL)
		account_win_destroy_cb(NULL, NULL, dialog);

	if (accounts_window == NULL)
		return;

	/* Remove the account from the GtkListStore */
	if (accounts_window_find_account_in_treemodel(&iter, account))
		gtk_list_store_remove(accounts_window->model, &iter);

	if (purple_accounts_get_all() == NULL)
		gtk_notebook_set_current_page(GTK_NOTEBOOK(accounts_window->notebook), 0);
}

static void
account_abled_cb(PurpleAccount *account, gpointer user_data)
{
	GtkTreeIter iter;

	if (accounts_window == NULL)
		return;

	/* update the account in the GtkListStore */
	if (accounts_window_find_account_in_treemodel(&iter, account))
		gtk_list_store_set(accounts_window->model, &iter,
						   COLUMN_ENABLED, GPOINTER_TO_INT(user_data),
						   -1);
}

static void
drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx,
				 GtkSelectionData *data, guint info, guint time,
				 AccountsWindow *dialog)
{
	if (data->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) {
		GtkTreeRowReference *ref;
		GtkTreePath *source_row;
		GtkTreeIter iter;
		PurpleAccount *account = NULL;
		GValue val;

		ref = g_object_get_data(G_OBJECT(ctx), "gtk-tree-view-source-row");
		source_row = gtk_tree_row_reference_get_path(ref);

		if (source_row == NULL)
			return;

		gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter,
								source_row);
		val.g_type = 0;
		gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
								 COLUMN_DATA, &val);

		dialog->drag_iter = iter;

		account = g_value_get_pointer(&val);

		gtk_selection_data_set(data, gdk_atom_intern("PURPLE_ACCOUNT", FALSE),
							   8, (void *)&account, sizeof(account));

		gtk_tree_path_free(source_row);
	}
}

static void
move_account_after(GtkListStore *store, GtkTreeIter *iter,
				   GtkTreeIter *position)
{
	GtkTreeIter new_iter;
	PurpleAccount *account;

	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
					   COLUMN_DATA, &account,
					   -1);

	gtk_list_store_insert_after(store, &new_iter, position);

	set_account(store, &new_iter, account, NULL);

	gtk_list_store_remove(store, iter);
}

static void
move_account_before(GtkListStore *store, GtkTreeIter *iter,
					GtkTreeIter *position)
{
	GtkTreeIter new_iter;
	PurpleAccount *account;

	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
					   COLUMN_DATA, &account,
					   -1);

	gtk_list_store_insert_before(store, &new_iter, position);

	set_account(store, &new_iter, account, NULL);

	gtk_list_store_remove(store, iter);
}

static void
drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx,
					  guint x, guint y, GtkSelectionData *sd,
					  guint info, guint t, AccountsWindow *dialog)
{
	if (sd->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && sd->data) {
		gint dest_index;
		PurpleAccount *a = NULL;
		GtkTreePath *path = NULL;
		GtkTreeViewDropPosition position;

		memcpy(&a, sd->data, sizeof(a));

		if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y,
											  &path, &position)) {

			GtkTreeIter iter;
			PurpleAccount *account;
			GValue val;

			gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
			val.g_type = 0;
			gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
									 COLUMN_DATA, &val);

			account = g_value_get_pointer(&val);

			switch (position) {
				case GTK_TREE_VIEW_DROP_AFTER:
				case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
					move_account_after(dialog->model, &dialog->drag_iter,
									   &iter);
					dest_index = g_list_index(purple_accounts_get_all(),
											  account) + 1;
					break;

				case GTK_TREE_VIEW_DROP_BEFORE:
				case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
					dest_index = g_list_index(purple_accounts_get_all(),
											  account);

					move_account_before(dialog->model, &dialog->drag_iter,
										&iter);
					break;

				default:
					return;
			}

			purple_accounts_reorder(a, dest_index);
		}
	}
}

static gboolean
accedit_win_destroy_cb(GtkWidget *w, GdkEvent *event, AccountsWindow *dialog)
{
	dialog->window = NULL;

	pidgin_accounts_window_hide();

	return FALSE;
}

static void
add_account_cb(GtkWidget *w, AccountsWindow *dialog)
{
	pidgin_account_dialog_show(PIDGIN_ADD_ACCOUNT_DIALOG, NULL);
}

static void
modify_account_sel(GtkTreeModel *model, GtkTreePath *path,
				   GtkTreeIter *iter, gpointer data)
{
	PurpleAccount *account;

	gtk_tree_model_get(model, iter, COLUMN_DATA, &account, -1);

	if (account != NULL)
		pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
}

static void
modify_account_cb(GtkWidget *w, AccountsWindow *dialog)
{
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));

	gtk_tree_selection_selected_foreach(selection, modify_account_sel, dialog);
}

static void
delete_account_cb(PurpleAccount *account)
{
	purple_accounts_delete(account);
}

static void
ask_delete_account_sel(GtkTreeModel *model, GtkTreePath *path,
					   GtkTreeIter *iter, gpointer data)
{
	PurpleAccount *account;

	gtk_tree_model_get(model, iter, COLUMN_DATA, &account, -1);

	if (account != NULL) {
		char *buf;

		buf = g_strdup_printf(_("Are you sure you want to delete %s?"),
							  purple_account_get_username(account));

		purple_request_close_with_handle(account);
		purple_request_action(account, NULL, buf, NULL,
							PURPLE_DEFAULT_ACTION_NONE,
							account, NULL, NULL,
							account, 2,
							_("Delete"), delete_account_cb,
							_("Cancel"), NULL);
		g_free(buf);
	}
}

static void
ask_delete_account_cb(GtkWidget *w, AccountsWindow *dialog)
{
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));

	gtk_tree_selection_selected_foreach(selection, ask_delete_account_sel,
										dialog);
}

static void
close_accounts_cb(GtkWidget *w, AccountsWindow *dialog)
{
	pidgin_accounts_window_hide();
}


static void
enabled_cb(GtkCellRendererToggle *renderer, gchar *path_str,
			   gpointer data)
{
	AccountsWindow *dialog = (AccountsWindow *)data;
	PurpleAccount *account;
	GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
	GtkTreeIter iter;
	gboolean enabled;
	const PurpleSavedStatus *saved_status;

	gtk_tree_model_get_iter_from_string(model, &iter, path_str);
	gtk_tree_model_get(model, &iter,
					   COLUMN_DATA, &account,
					   COLUMN_ENABLED, &enabled,
					   -1);

	/*
	 * If we just enabled the account, then set the statuses
	 * to the current status.
	 */
	if (!enabled)
	{
		saved_status = purple_savedstatus_get_current();
		purple_savedstatus_activate_for_account(saved_status, account);
	}

	purple_account_set_enabled(account, PIDGIN_UI, !enabled);
}

static void
add_columns(GtkWidget *treeview, AccountsWindow *dialog)
{
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	/* Enabled */
	renderer = gtk_cell_renderer_toggle_new();

	g_signal_connect(G_OBJECT(renderer), "toggled",
			 G_CALLBACK(enabled_cb), dialog);

	column = gtk_tree_view_column_new_with_attributes(_("Enabled"),
			 renderer, "active", COLUMN_ENABLED, NULL);

	gtk_tree_view_column_set_resizable(column, FALSE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

	/* Username column */
	column = gtk_tree_view_column_new();
	gtk_tree_view_column_set_title(column, _("Username"));
	gtk_tree_view_column_set_resizable(column, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

	/* Buddy Icon */
	renderer = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(column, renderer, FALSE);
	gtk_tree_view_column_add_attribute(column, renderer,
					   "pixbuf", COLUMN_BUDDYICON);

	/* Username */
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(column, renderer, TRUE);
	gtk_tree_view_column_add_attribute(column, renderer,
					   "text", COLUMN_USERNAME);
	dialog->username_col = column;


	/* Protocol name */
	column = gtk_tree_view_column_new();
	gtk_tree_view_column_set_title(column, _("Protocol"));
	gtk_tree_view_column_set_resizable(column, FALSE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

	/* Icon */
	renderer = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(column, renderer, FALSE);
	gtk_tree_view_column_add_attribute(column, renderer,
					   "pixbuf", COLUMN_ICON);

	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(column, renderer, TRUE);
	gtk_tree_view_column_add_attribute(column, renderer,
					   "text", COLUMN_PROTOCOL);
}

static void
set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account, GdkPixbuf *global_buddyicon)
{
	GdkPixbuf *pixbuf, *buddyicon = NULL;
	PurpleStoredImage *img = NULL;
	PurplePlugin *prpl;
	PurplePluginProtocolInfo *prpl_info = NULL;

	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
	if ((pixbuf != NULL) && purple_account_is_disconnected(account))
		gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);

	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
	if (prpl != NULL)
		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
	if (prpl_info != NULL && prpl_info->icon_spec.format != NULL) {
		if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) {
			if (global_buddyicon != NULL)
				buddyicon = g_object_ref(G_OBJECT(global_buddyicon));
			else {
				/* This is for when set_account() is called for a single account */
				const char *path;
				path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
				if ((path != NULL) && (*path != '\0')) {
					img = purple_imgstore_new_from_file(path);
				}
			}
		} else {
			img = purple_buddy_icons_find_account_icon(account);
		}
	}

	if (img != NULL) {
		GdkPixbuf *buddyicon_pixbuf;
		buddyicon_pixbuf = pidgin_pixbuf_from_imgstore(img);
		purple_imgstore_unref(img);

		if (buddyicon_pixbuf != NULL) {
			buddyicon = gdk_pixbuf_scale_simple(buddyicon_pixbuf, 22, 22, GDK_INTERP_HYPER);
			g_object_unref(G_OBJECT(buddyicon_pixbuf));
		}
	}

	gtk_list_store_set(store, iter,
			COLUMN_ICON, pixbuf,
			COLUMN_BUDDYICON, buddyicon,
			COLUMN_USERNAME, purple_account_get_username(account),
			COLUMN_ENABLED, purple_account_get_enabled(account, PIDGIN_UI),
			COLUMN_PROTOCOL, purple_account_get_protocol_name(account),
			COLUMN_DATA, account,
			-1);

	if (pixbuf != NULL)
		g_object_unref(G_OBJECT(pixbuf));
	if (buddyicon != NULL)
		g_object_unref(G_OBJECT(buddyicon));
}

static void
add_account_to_liststore(PurpleAccount *account, gpointer user_data)
{
	GtkTreeIter iter;
	GdkPixbuf *global_buddyicon = user_data;

	if (accounts_window == NULL)
		return;

	gtk_list_store_append(accounts_window->model, &iter);
	gtk_notebook_set_current_page(GTK_NOTEBOOK(accounts_window->notebook),1);

	set_account(accounts_window->model, &iter, account, global_buddyicon);
}

static gboolean
populate_accounts_list(AccountsWindow *dialog)
{
	GList *l;
	gboolean ret = FALSE;
	GdkPixbuf *global_buddyicon = NULL;
	const char *path;

	gtk_list_store_clear(dialog->model);

	if ((path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) != NULL) {
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL);
		if (pixbuf != NULL) {
			global_buddyicon = gdk_pixbuf_scale_simple(pixbuf, 22, 22, GDK_INTERP_HYPER);
			g_object_unref(G_OBJECT(pixbuf));
		}
	}

	for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
		ret = TRUE;
		add_account_to_liststore((PurpleAccount *)l->data, global_buddyicon);
	}

	if (global_buddyicon != NULL)
		g_object_unref(G_OBJECT(global_buddyicon));

	return ret;
}

static void
account_selected_cb(GtkTreeSelection *sel, AccountsWindow *dialog)
{
	gboolean selected = FALSE;

	selected = (gtk_tree_selection_count_selected_rows(sel) > 0);

	gtk_widget_set_sensitive(dialog->modify_button, selected);
	gtk_widget_set_sensitive(dialog->delete_button, selected);
}

static gboolean
account_treeview_double_click_cb(GtkTreeView *treeview, GdkEventButton *event, gpointer user_data)
{
	AccountsWindow *dialog;
	GtkTreePath *path;
	GtkTreeViewColumn *column;
	GtkTreeIter iter;
	PurpleAccount *account;

	dialog = (AccountsWindow *)user_data;

	if (event->window != gtk_tree_view_get_bin_window(treeview))
	    return FALSE;

	/* Figure out which node was clicked */
	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dialog->treeview), event->x, event->y, &path, &column, NULL, NULL))
		return FALSE;
	if (column == gtk_tree_view_get_column(treeview, 0)) {
		gtk_tree_path_free(path);
		return FALSE;
	}

	gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
	gtk_tree_path_free(path);
	gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &account, -1);

	if ((account != NULL) && (event->button == 1) &&
		(event->type == GDK_2BUTTON_PRESS))
	{
		pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
		return TRUE;
	}

	return FALSE;
}

static GtkWidget *
create_accounts_list(AccountsWindow *dialog)
{
	GtkWidget *frame;
	GtkWidget *sw;
	GtkWidget *label;
	GtkWidget *treeview;
	GtkTreeSelection *sel;
	GtkTargetEntry gte[] = {{"PURPLE_ACCOUNT", GTK_TARGET_SAME_APP, 0}};
	char *pretty, *tmp;

	frame = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);

	accounts_window->notebook = gtk_notebook_new();
	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(accounts_window->notebook), FALSE);
	gtk_notebook_set_show_border(GTK_NOTEBOOK(accounts_window->notebook), FALSE);
	gtk_container_add(GTK_CONTAINER(frame), accounts_window->notebook);

	/* Create a helpful first-time-use label */
	label = gtk_label_new(NULL);
	/* Translators: Please maintain the use of -> or <- to represent the menu heirarchy */
	tmp = g_strdup_printf(_(
						 "<span size='larger' weight='bold'>Welcome to %s!</span>\n\n"

						 "You have no IM accounts configured. To start connecting with %s "
						 "press the <b>Add...</b> button below and configure your first "
						 "account. If you want %s to connect to multiple IM accounts, "
						 "press <b>Add...</b> again to configure them all.\n\n"

						 "You can come back to this window to add, edit, or remove "
						 "accounts from <b>Accounts->Manage Accounts</b> in the Buddy "
						 "List window"), PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
	pretty = pidgin_make_pretty_arrows(tmp);
	g_free(tmp);
	gtk_label_set_markup(GTK_LABEL(label), pretty);
	g_free(pretty);

	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
	gtk_widget_show(label);

	gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
	gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook), label, NULL);

	/* Create the scrolled window. */
	sw = gtk_scrolled_window_new(0, 0);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
					GTK_SHADOW_NONE);
	gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook), sw, NULL);
	gtk_widget_show(sw);

	/* Create the list model. */
	dialog->model = gtk_list_store_new(NUM_COLUMNS,
					GDK_TYPE_PIXBUF,   /* COLUMN_ICON */
					GDK_TYPE_PIXBUF,   /* COLUMN_BUDDYICON */
					G_TYPE_STRING,     /* COLUMN_USERNAME */
					G_TYPE_BOOLEAN,    /* COLUMN_ENABLED */
					G_TYPE_STRING,     /* COLUMN_PROTOCOL */
					G_TYPE_POINTER     /* COLUMN_DATA */
					);

	/* And now the actual treeview */
	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
	dialog->treeview = treeview;
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
	g_object_unref(G_OBJECT(dialog->model));

	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
	gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
	g_signal_connect(G_OBJECT(sel), "changed",
					 G_CALLBACK(account_selected_cb), dialog);

	/* Handle double-clicking */
	g_signal_connect(G_OBJECT(treeview), "button_press_event",
					 G_CALLBACK(account_treeview_double_click_cb), dialog);

	gtk_container_add(GTK_CONTAINER(sw), treeview);

	add_columns(treeview, dialog);
	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(treeview));

	if (populate_accounts_list(dialog))
		gtk_notebook_set_current_page(GTK_NOTEBOOK(accounts_window->notebook), 1);
	else
		gtk_notebook_set_current_page(GTK_NOTEBOOK(accounts_window->notebook), 0);

	/* Setup DND. I wanna be an orc! */
	gtk_tree_view_enable_model_drag_source(
			GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte,
			1, GDK_ACTION_COPY);
	gtk_tree_view_enable_model_drag_dest(
			GTK_TREE_VIEW(treeview), gte, 1,
			GDK_ACTION_COPY | GDK_ACTION_MOVE);

	g_signal_connect(G_OBJECT(treeview), "drag-data-received",
					 G_CALLBACK(drag_data_received_cb), dialog);
	g_signal_connect(G_OBJECT(treeview), "drag-data-get",
					 G_CALLBACK(drag_data_get_cb), dialog);

	gtk_widget_show_all(frame);
	return frame;
}

static void
account_modified_cb(PurpleAccount *account, AccountsWindow *window)
{
	GtkTreeIter iter;

	if (!accounts_window_find_account_in_treemodel(&iter, account))
		return;

	set_account(window->model, &iter, account, NULL);
}

static void
global_buddyicon_changed(const char *name, PurplePrefType type,
			gconstpointer value, gpointer window)
{
	GList *list;
	for (list = purple_accounts_get_all(); list; list = list->next) {
		account_modified_cb(list->data, window);
	}
}

void
pidgin_accounts_window_show(void)
{
	AccountsWindow *dialog;
	GtkWidget *win;
	GtkWidget *vbox;
	GtkWidget *sw;
	GtkWidget *button;
	int width, height;

	if (accounts_window != NULL) {
		gtk_window_present(GTK_WINDOW(accounts_window->window));
		return;
	}

	accounts_window = dialog = g_new0(AccountsWindow, 1);

	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width");
	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height");

	dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
	gtk_window_set_default_size(GTK_WINDOW(win), width, height);

	g_signal_connect(G_OBJECT(win), "delete_event",
					 G_CALLBACK(accedit_win_destroy_cb), accounts_window);

	/* Setup the vbox */
	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);

	/* Setup the scrolled window that will contain the list of accounts. */
	sw = create_accounts_list(dialog);
	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	gtk_widget_show(sw);

	/* Add button */
	pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_ADD, G_CALLBACK(add_account_cb), dialog);

	/* Modify button */
	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(modify_account_cb), dialog);
	dialog->modify_button = button;
	gtk_widget_set_sensitive(button, FALSE);

	/* Delete button */
	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(ask_delete_account_cb), dialog);
	dialog->delete_button = button;
	gtk_widget_set_sensitive(button, FALSE);

	/* Close button */
	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog);

	purple_signal_connect(pidgin_account_get_handle(), "account-modified",
	                    accounts_window,
	                    PURPLE_CALLBACK(account_modified_cb), accounts_window);
	purple_prefs_connect_callback(accounts_window,
	                    PIDGIN_PREFS_ROOT "/accounts/buddyicon",
	                    global_buddyicon_changed, accounts_window);

	gtk_widget_show(win);
}

void
pidgin_accounts_window_hide(void)
{
	if (accounts_window == NULL)
		return;

	if (accounts_window->window != NULL)
		gtk_widget_destroy(accounts_window->window);

	purple_signals_disconnect_by_handle(accounts_window);
	purple_prefs_disconnect_by_handle(accounts_window);

	g_free(accounts_window);
	accounts_window = NULL;
}

static void
free_add_user_data(PidginAccountAddUserData *data)
{
	g_free(data->username);
	g_free(data->alias);
	g_free(data);
}

static void
add_user_cb(PidginAccountAddUserData *data)
{
	PurpleConnection *gc = purple_account_get_connection(data->account);

	if (g_list_find(purple_connections_get_all(), gc))
	{
		purple_blist_request_add_buddy(data->account, data->username,
									 NULL, data->alias);
	}

	free_add_user_data(data);
}

static char *
make_info(PurpleAccount *account, PurpleConnection *gc, const char *remote_user,
          const char *id, const char *alias, const char *msg)
{
	if (msg != NULL && *msg == '\0')
		msg = NULL;

	return g_strdup_printf(_("%s%s%s%s has made %s his or her buddy%s%s"),
	                       remote_user,
	                       (alias != NULL ? " ("  : ""),
	                       (alias != NULL ? alias : ""),
	                       (alias != NULL ? ")"   : ""),
	                       (id != NULL
	                        ? id
	                        : (purple_connection_get_display_name(gc) != NULL
	                           ? purple_connection_get_display_name(gc)
	                           : purple_account_get_username(account))),
	                       (msg != NULL ? ": " : "."),
	                       (msg != NULL ? msg  : ""));
}

static void
pidgin_accounts_notify_added(PurpleAccount *account, const char *remote_user,
                               const char *id, const char *alias,
                               const char *msg)
{
	char *buffer;
	PurpleConnection *gc;
	GtkWidget *alert;

	gc = purple_account_get_connection(account);

	buffer = make_info(account, gc, remote_user, id, alias, msg);
	alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_INFO, buffer,
					  NULL, NULL, _("Close"), NULL, NULL);
	pidgin_blist_add_alert(alert);

	g_free(buffer);
}

static void
pidgin_accounts_request_add(PurpleAccount *account, const char *remote_user,
                              const char *id, const char *alias,
                              const char *msg)
{
	char *buffer;
	PurpleConnection *gc;
	PidginAccountAddUserData *data;
	GtkWidget *alert;

	gc = purple_account_get_connection(account);

	data = g_new0(PidginAccountAddUserData, 1);
	data->account  = account;
	data->username = g_strdup(remote_user);
	data->alias    = g_strdup(alias);

	buffer = make_info(account, gc, remote_user, id, alias, msg);
	alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_QUESTION,
					  _("Add buddy to your list?"), buffer, data,
					  _("Add"), G_CALLBACK(add_user_cb),
					  _("Cancel"), G_CALLBACK(free_add_user_data), NULL);
	pidgin_blist_add_alert(alert);

	g_free(buffer);
}

struct auth_request
{
	PurpleAccountRequestAuthorizationCb auth_cb;
	PurpleAccountRequestAuthorizationCb deny_cb;
	void *data;
	char *username;
	char *alias;
	PurpleAccount *account;
	gboolean add_buddy_after_auth;
};

static void
free_auth_request(struct auth_request *ar)
{
	g_free(ar->username);
	g_free(ar->alias);
	g_free(ar);
}

static void
authorize_and_add_cb(struct auth_request *ar)
{
	ar->auth_cb(ar->data);
	if (ar->add_buddy_after_auth) {
		purple_blist_request_add_buddy(ar->account, ar->username, NULL, ar->alias);
	}
}

static void
deny_no_add_cb(struct auth_request *ar)
{
	ar->deny_cb(ar->data);
}

static void *
pidgin_accounts_request_authorization(PurpleAccount *account,
                                      const char *remote_user,
                                      const char *id,
                                      const char *alias,
                                      const char *message,
                                      gboolean on_list,
                                      PurpleAccountRequestAuthorizationCb auth_cb,
                                      PurpleAccountRequestAuthorizationCb deny_cb,
                                      void *user_data)
{
	char *buffer;
	PurpleConnection *gc;
	GtkWidget *alert;
	GdkPixbuf *prpl_icon;
	struct auth_request *aa;
	gboolean have_valid_alias = alias && *alias;

	gc = purple_account_get_connection(account);
	if (message != NULL && *message == '\0')
		message = NULL;

	buffer = g_strdup_printf(_("%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"),
				remote_user,
				(have_valid_alias ? " ("  : ""),
				(have_valid_alias ? alias : ""),
				(have_valid_alias ? ")"   : ""),
				(id != NULL
				? id
				: (purple_connection_get_display_name(gc) != NULL
				? purple_connection_get_display_name(gc)
				: purple_account_get_username(account))),
				(message != NULL ? ": " : "."),
				(message != NULL ? message  : ""));


	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);

	aa = g_new0(struct auth_request, 1);
	aa->auth_cb = auth_cb;
	aa->deny_cb = deny_cb;
	aa->data = user_data;
	aa->username = g_strdup(remote_user);
	aa->alias = g_strdup(alias);
	aa->account = account;
	aa->add_buddy_after_auth = !on_list;

	alert = pidgin_make_mini_dialog_with_custom_icon(
		gc, prpl_icon,
		_("Authorize buddy?"), buffer, aa,
		_("Authorize"), authorize_and_add_cb,
		_("Deny"), deny_no_add_cb,
		NULL);

	g_signal_connect_swapped(G_OBJECT(alert), "destroy", G_CALLBACK(free_auth_request), aa);
	g_signal_connect(G_OBJECT(alert), "destroy", G_CALLBACK(purple_account_request_close), NULL);
	pidgin_blist_add_alert(alert);

	g_free(buffer);

	return alert;
}

static void
pidgin_accounts_request_close(void *ui_handle)
{
	gtk_widget_destroy(GTK_WIDGET(ui_handle));
}

static PurpleAccountUiOps ui_ops =
{
	pidgin_accounts_notify_added,
	NULL,
	pidgin_accounts_request_add,
	pidgin_accounts_request_authorization,
	pidgin_accounts_request_close,
	NULL,
	NULL,
	NULL,
	NULL
};

PurpleAccountUiOps *
pidgin_accounts_get_ui_ops(void)
{
	return &ui_ops;
}

void *
pidgin_account_get_handle(void) {
	static int handle;

	return &handle;
}

void
pidgin_account_init(void)
{
	char *default_avatar = NULL;
	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/accounts");
	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/accounts/dialog");
	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width",  520);
	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height", 321);
	default_avatar = g_build_filename(g_get_home_dir(), ".face.icon", NULL);
	if (!g_file_test(default_avatar, G_FILE_TEST_EXISTS)) {
		g_free(default_avatar);
		default_avatar = g_build_filename(g_get_home_dir(), ".face", NULL);
		if (!g_file_test(default_avatar, G_FILE_TEST_EXISTS)) {
			g_free(default_avatar);
			default_avatar = NULL;
		}
	}

	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon", default_avatar);
	g_free(default_avatar);

	purple_signal_register(pidgin_account_get_handle(), "account-modified",
						 purple_marshal_VOID__POINTER, NULL, 1,
						 purple_value_new(PURPLE_TYPE_SUBTYPE,
										PURPLE_SUBTYPE_ACCOUNT));

	/* Setup some purple signal handlers. */
	purple_signal_connect(purple_connections_get_handle(), "signed-on",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(signed_on_off_cb), NULL);
	purple_signal_connect(purple_connections_get_handle(), "signed-off",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(signed_on_off_cb), NULL);
	purple_signal_connect(purple_accounts_get_handle(), "account-added",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(add_account_to_liststore), NULL);
	purple_signal_connect(purple_accounts_get_handle(), "account-removed",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(account_removed_cb), NULL);
	purple_signal_connect(purple_accounts_get_handle(), "account-disabled",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(FALSE));
	purple_signal_connect(purple_accounts_get_handle(), "account-enabled",
						pidgin_account_get_handle(),
						PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(TRUE));

	account_pref_wins =
		g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
}

void
pidgin_account_uninit(void)
{
	/*
	 * TODO: Need to free all the dialogs in here.  Could probably create
	 *       a callback function to use for the free-some-data-function
	 *       parameter of g_hash_table_new_full, above.
	 */
	g_hash_table_destroy(account_pref_wins);

	purple_signals_disconnect_by_handle(pidgin_account_get_handle());
	purple_signals_unregister_by_instance(pidgin_account_get_handle());
}