changeset 5228:1a53330dfd34

[gaim-migrate @ 5598] People have been talking about doing it, but nobody has done it. Since I want to clean up some header files today: list.[ch] -> blist.[ch] gtklist.h -> gtkblist.h buddy.c -> gtkblist.c committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Sat, 26 Apr 2003 17:56:23 +0000
parents 6d1707dc8c3d
children 8aafe8d7bcb8
files src/Makefile.am src/away.c src/blist.c src/blist.h src/buddy.c src/dialogs.c src/gaim.h src/gtkblist.c src/gtkblist.h src/gtkconv.c src/gtklist.h src/gtkpounce.c src/list.c src/list.h src/main.c src/multi.c src/prefs.c src/prpl.c
diffstat 18 files changed, 3990 insertions(+), 3990 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile.am	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/Makefile.am	Sat Apr 26 17:56:23 2003 +0000
@@ -6,6 +6,8 @@
 SUBDIRS = protocols
 
 CORESOURCES = \
+	blist.c \
+	blist.h \
 	conversation.c \
 	conversation.h \
 	core.c \
@@ -36,7 +38,6 @@
 	about.c \
 	away.c \
 	browser.c \
-	buddy.c \
 	buddy_chat.c \
 	dialogs.c \
 	dnd-hints.c \
@@ -48,6 +49,8 @@
 	gaimrc.c \
 	gtkcellrendererprogress.c \
 	gtkcellrendererprogress.h \
+	gtkblist.c \
+	gtkblist.h \
 	gtkconv.c \
 	gtkconv.h \
 	gtkdebug.c \
@@ -56,7 +59,6 @@
 	gtkft.h \
 	gtkimhtml.c \
 	gtkimhtml.h \
-	gtklist.h \
 	gtkplugin.c \
 	gtkplugin.h \
 	gtkpounce.c \
@@ -65,8 +67,6 @@
 	gtkutils.h \
 	html.c \
 	idle.c \
-	list.c \
-	list.h \
 	log.c \
 	main.c \
 	md5.c \
--- a/src/away.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/away.c	Sat Apr 26 17:56:23 2003 +0000
@@ -31,7 +31,7 @@
 #include "gaim.h"
 #include "prpl.h"
 #include "gtkimhtml.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "plugin.h"
 
 GtkWidget *imaway = NULL;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/blist.c	Sat Apr 26 17:56:23 2003 +0000
@@ -0,0 +1,1546 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 2003,      Sean Egan    <sean.egan@binghamton.edu>
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <unistd.h>
+#else
+#include <direct.h>
+#endif
+#include <ctype.h>
+#include "gaim.h"
+#include "prpl.h"
+#include "blist.h"
+
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+#define PATHSIZE 1024
+
+struct gaim_buddy_list *gaimbuddylist = NULL;
+static struct gaim_blist_ui_ops *blist_ui_ops = NULL;
+
+/*****************************************************************************
+ * Private Utility functions                                                 *
+ *****************************************************************************/
+static GaimBlistNode *gaim_blist_get_last_sibling(GaimBlistNode *node)
+{
+	GaimBlistNode *n = node;
+	if (!n)
+		return NULL;
+	while (n->next)
+		n = n->next;
+	return n;
+}
+static GaimBlistNode *gaim_blist_get_last_child(GaimBlistNode *node)
+{
+	if (!node)
+		return NULL;
+	return gaim_blist_get_last_sibling(node->child);
+}
+
+/*****************************************************************************
+ * Public API functions                                                      *
+ *****************************************************************************/
+
+struct gaim_buddy_list *gaim_blist_new()
+{
+	struct gaim_buddy_list *gbl = g_new0(struct gaim_buddy_list, 1);
+
+	gbl->ui_ops = gaim_get_blist_ui_ops();
+
+	if (gbl->ui_ops != NULL && gbl->ui_ops->new_list != NULL)
+		gbl->ui_ops->new_list(gbl);
+
+	return gbl;
+}
+
+void
+gaim_set_blist(struct gaim_buddy_list *list)
+{
+	gaimbuddylist = list;
+}
+
+struct gaim_buddy_list *
+gaim_get_blist(void)
+{
+	return gaimbuddylist;
+}
+
+void  gaim_blist_show () 
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	if (ops)
+		ops->show(gaimbuddylist);
+}
+
+void gaim_blist_destroy()
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	if (ops)
+		ops->destroy(gaimbuddylist);
+}
+
+void  gaim_blist_set_visible (gboolean show)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	if (ops)
+		ops->set_visible(gaimbuddylist, show);
+}
+
+void  gaim_blist_update_buddy_status (struct buddy *buddy, int status)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	buddy->uc = status;
+
+	if(!(status & UC_UNAVAILABLE))
+		gaim_event_broadcast(event_buddy_back, buddy->account->gc, buddy->name);
+	else
+		gaim_event_broadcast(event_buddy_away, buddy->account->gc, buddy->name);
+
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+}
+
+static gboolean presence_update_timeout_cb(struct buddy *buddy) {
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+
+	if(buddy->present == GAIM_BUDDY_SIGNING_ON) {
+		buddy->present = GAIM_BUDDY_ONLINE;
+		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
+	} else if(buddy->present == GAIM_BUDDY_SIGNING_OFF) {
+		buddy->present = GAIM_BUDDY_OFFLINE;
+	}
+
+	buddy->timer = 0;
+
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+
+	return FALSE;
+}
+
+void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence) {
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	gboolean do_timer = FALSE;
+
+	if (!GAIM_BUDDY_IS_ONLINE(buddy) && presence) {
+		buddy->present = GAIM_BUDDY_SIGNING_ON;
+		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
+		do_timer = TRUE;
+	} else if(GAIM_BUDDY_IS_ONLINE(buddy) && !presence) {
+		buddy->present = GAIM_BUDDY_SIGNING_OFF;
+		gaim_event_broadcast(event_buddy_signoff, buddy->account->gc, buddy->name);
+		do_timer = TRUE;
+	}
+
+	if(do_timer) {
+		if(buddy->timer > 0)
+			g_source_remove(buddy->timer);
+		buddy->timer = g_timeout_add(10000, (GSourceFunc)presence_update_timeout_cb, buddy);
+	}
+
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+}
+
+
+void  gaim_blist_update_buddy_idle (struct buddy *buddy, int idle)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	buddy->idle = idle;
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+}
+void  gaim_blist_update_buddy_evil (struct buddy *buddy, int warning)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	buddy->evil = warning;
+	if (ops)
+		ops->update(gaimbuddylist,(GaimBlistNode*)buddy);
+}
+void gaim_blist_update_buddy_icon(struct buddy *buddy) {
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	if(ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+}
+void  gaim_blist_rename_buddy (struct buddy *buddy, const char *name)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+       	g_free(buddy->name);
+	buddy->name = g_strdup(name);
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+}
+void  gaim_blist_alias_buddy (struct buddy *buddy, const char *alias)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	struct gaim_conversation *conv;
+
+	g_free(buddy->alias);
+
+	if(alias && strlen(alias))
+		buddy->alias = g_strdup(alias);
+	else
+		buddy->alias = NULL;
+
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+
+	conv = gaim_find_conversation_with_account(buddy->name, buddy->account);
+
+	if (conv)
+		gaim_conversation_autoset_title(conv);
+}
+
+void gaim_blist_rename_group(struct group *group, const char *name)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	g_free(group->name);
+	group->name = g_strdup(name);
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)group);
+}
+struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias)
+{
+	struct buddy *b;
+	struct gaim_blist_ui_ops *ops;
+
+	b = g_new0(struct buddy, 1);
+	b->account = account;
+	b->name  = g_strdup(screenname);
+	b->alias = g_strdup(alias);
+	b->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	((GaimBlistNode*)b)->type = GAIM_BLIST_BUDDY_NODE;
+
+	ops = gaim_get_blist_ui_ops();
+
+	if (ops != NULL && ops->new_node != NULL)
+		ops->new_node((GaimBlistNode *)b);
+
+	return b;
+}
+void  gaim_blist_add_buddy (struct buddy *buddy, struct group *group, GaimBlistNode *node)
+{
+	GaimBlistNode *n = node, *bnode = (GaimBlistNode*)buddy;
+	struct group *g = group;
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	gboolean save = FALSE;
+
+	if (!n) {
+		if (!g) {
+			g = gaim_group_new(_("Buddies"));
+			gaim_blist_add_group(g, NULL);
+		}
+		n = gaim_blist_get_last_child((GaimBlistNode*)g);
+	} else {
+		g = (struct group*)n->parent;
+	}
+
+	/* if we're moving to overtop of ourselves, do nothing */
+	if(bnode == n)
+		return;
+
+	if (bnode->parent) {
+		/* This buddy was already in the list and is
+		 * being moved.
+		 */
+		if(bnode->next)
+			bnode->next->prev = bnode->prev;
+		if(bnode->prev)
+			bnode->prev->next = bnode->next;
+		if(bnode->parent->child == bnode)
+			bnode->parent->child = bnode->next;
+
+		ops->remove(gaimbuddylist, bnode);
+
+		if (bnode->parent != ((GaimBlistNode*)g))
+			serv_move_buddy(buddy, (struct group*)bnode->parent, g);
+		save = TRUE;
+	}
+
+	if (n) {
+		if(n->next)
+			n->next->prev = (GaimBlistNode*)buddy;
+		((GaimBlistNode*)buddy)->next = n->next;
+		((GaimBlistNode*)buddy)->prev = n;
+		((GaimBlistNode*)buddy)->parent = n->parent;
+		n->next = (GaimBlistNode*)buddy;
+	} else {
+		((GaimBlistNode*)g)->child = (GaimBlistNode*)buddy;
+		((GaimBlistNode*)buddy)->next = NULL;
+		((GaimBlistNode*)buddy)->prev = NULL;
+		((GaimBlistNode*)buddy)->parent = (GaimBlistNode*)g;
+	}
+
+	if (ops)
+		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+	if (save)
+		gaim_blist_save();
+}
+
+struct group *gaim_group_new(const char *name)
+{
+	struct group *g = gaim_find_group(name);
+
+	if (!g) {
+		struct gaim_blist_ui_ops *ops;
+		g= g_new0(struct group, 1);
+		g->name = g_strdup(name);
+		g->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+				g_free, g_free);
+		((GaimBlistNode*)g)->type = GAIM_BLIST_GROUP_NODE;
+
+		ops = gaim_get_blist_ui_ops();
+
+		if (ops != NULL && ops->new_node != NULL)
+			ops->new_node((GaimBlistNode *)g);
+
+	}
+	return g;
+}
+
+void  gaim_blist_add_group (struct group *group, GaimBlistNode *node)
+{
+	struct gaim_blist_ui_ops *ops;
+	GaimBlistNode *gnode = (GaimBlistNode*)group;
+	gboolean save = FALSE;
+
+	if (!gaimbuddylist)
+		gaimbuddylist = gaim_blist_new();
+	ops = gaimbuddylist->ui_ops;
+
+	if (!gaimbuddylist->root) {
+		gaimbuddylist->root = gnode;
+		return;
+	}
+
+	if (!node)
+		node = gaim_blist_get_last_sibling(gaimbuddylist->root);
+
+	/* if we're moving to overtop of ourselves, do nothing */
+	if(gnode == node)
+		return;
+
+	if (gaim_find_group(group->name)) {
+		/* This is just being moved */
+
+		ops->remove(gaimbuddylist, (GaimBlistNode*)group);
+
+		if(gnode == gaimbuddylist->root)
+			gaimbuddylist->root = gnode->next;
+		if(gnode->prev)
+			gnode->prev->next = gnode->next;
+		if(gnode->next)
+			gnode->next->prev = gnode->prev;
+
+		save = TRUE;
+	}
+
+	gnode->next = node->next;
+	gnode->prev = node;
+	if(node->next)
+		node->next->prev = gnode;
+	node->next = gnode;
+
+	if (ops) {
+		ops->update(gaimbuddylist, gnode);
+		for(node = gnode->child; node; node = node->next)
+			ops->update(gaimbuddylist, node);
+	}
+	if (save)
+		gaim_blist_save();
+}
+
+void  gaim_blist_remove_buddy (struct buddy *buddy)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+
+	GaimBlistNode *gnode, *node = (GaimBlistNode*)buddy;
+	struct group *group;
+
+	gnode = node->parent;
+	group = (struct group *)gnode;
+
+	if(gnode->child == node)
+		gnode->child = node->next;
+	if (node->prev)
+		node->prev->next = node->next;
+	if (node->next)
+		node->next->prev = node->prev;
+
+	ops->remove(gaimbuddylist, node);
+	g_hash_table_destroy(buddy->settings);
+	g_free(buddy->name);
+	g_free(buddy->alias);
+	g_free(buddy);
+}
+
+void  gaim_blist_remove_group (struct group *group)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	GaimBlistNode *node = (GaimBlistNode*)group;
+
+	if(node->child) {
+		char *buf;
+		int count = 0;
+		GaimBlistNode *child = node->child;
+
+		while(child) {
+			count++;
+			child = child->next;
+		}
+
+		buf = g_strdup_printf(_("%d buddies from group %s were not "
+					"removed because their accounts were not logged in.  These "
+					"buddies, and the group were not removed.\n"),
+				count, group->name);
+		do_error_dialog(_("Group Not Removed"), buf, GAIM_ERROR);
+		g_free(buf);
+		return;
+	}
+
+	if(gaimbuddylist->root == node)
+		gaimbuddylist->root = node->next;
+	if (node->prev)
+		node->prev->next = node->next;
+	if (node->next)
+		node->next->prev = node->prev;
+
+	ops->remove(gaimbuddylist, node);
+	g_free(group->name);
+	g_free(group);
+}
+
+char *gaim_get_buddy_alias_only(struct buddy *b) {
+        if(!b)
+                return NULL;
+        if(b->alias && b->alias[0])
+                return b->alias;
+        else if((misc_options & OPT_MISC_USE_SERVER_ALIAS) && b->server_alias)
+                return b->server_alias;
+        return NULL;
+}
+
+char *  gaim_get_buddy_alias (struct buddy *buddy)
+{
+	char *ret = gaim_get_buddy_alias_only(buddy);
+        if(!ret)
+                return buddy ? buddy->name : _("Unknown");
+        return ret;
+
+}
+
+struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name)
+{
+	GaimBlistNode *group;
+	GaimBlistNode *buddy;
+	char *norm_name = g_strdup(normalize(name));
+
+	if (!gaimbuddylist)
+		return NULL;
+
+	group = gaimbuddylist->root;
+	while (group) {
+		buddy = group->child;
+		while (buddy) {
+			if (!gaim_utf8_strcasecmp(normalize(((struct buddy*)buddy)->name), norm_name) && account == ((struct buddy*)buddy)->account) {
+				g_free(norm_name);
+				return (struct buddy*)buddy;
+			}
+			buddy = buddy->next;
+		}
+		group = group->next;
+	}
+	g_free(norm_name);
+	return NULL;
+}
+
+struct group *gaim_find_group(const char *name)
+{
+	GaimBlistNode *node;
+	if (!gaimbuddylist)
+		return NULL;
+	node = gaimbuddylist->root;
+	while(node) {
+		if (!strcmp(((struct group*)node)->name, name))
+			return (struct group*)node;
+		node = node->next;
+	}
+	return NULL;
+}
+struct group *gaim_find_buddys_group(struct buddy *buddy)
+{
+	if (!buddy)
+		return NULL;
+	return (struct group*)(((GaimBlistNode*)buddy)->parent);
+}
+
+GSList *gaim_group_get_accounts(struct group *g)
+{
+	GSList *l = NULL;
+	GaimBlistNode *child = ((GaimBlistNode *)g)->child;
+
+	while (child) {
+		if (!g_slist_find(l, ((struct buddy*)child)->account))
+			l = g_slist_append(l, ((struct buddy*)child)->account);
+		child = child->next;
+	}
+	return l;
+}
+
+void gaim_blist_remove_account(struct gaim_account *account)
+{
+	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
+	GaimBlistNode *group = gaimbuddylist->root;
+	GaimBlistNode *buddy;
+	if (!gaimbuddylist)
+		return;
+	while (group) {
+		buddy = group->child;
+		while (buddy) {
+			if (account == ((struct buddy*)buddy)->account) {
+				((struct buddy*)buddy)->present = GAIM_BUDDY_OFFLINE;
+				if(ops)
+					ops->remove(gaimbuddylist, buddy);
+			}
+			buddy = buddy->next;
+		}
+		group = group->next;
+	}
+}
+
+void parse_toc_buddy_list(struct gaim_account *account, char *config)
+{
+	char *c;
+	char current[256];
+	GList *bud = NULL;
+
+
+	if (config != NULL) {
+
+		/* skip "CONFIG:" (if it exists) */
+		c = strncmp(config + 6 /* sizeof(struct sflap_hdr) */ , "CONFIG:", strlen("CONFIG:")) ?
+			strtok(config, "\n") :
+			strtok(config + 6 /* sizeof(struct sflap_hdr) */  + strlen("CONFIG:"), "\n");
+		do {
+			if (c == NULL)
+				break;
+			if (*c == 'g') {
+				char *utf8 = NULL;
+				utf8 = gaim_try_conv_to_utf8(c + 2);
+				if (utf8 == NULL) {
+					g_strlcpy(current, _("Invalid Groupname"), sizeof(current));
+				} else {
+					g_strlcpy(current, utf8, sizeof(current));
+					g_free(utf8);
+				}
+				if (!gaim_find_group(current)) {
+					struct group *g = gaim_group_new(current);
+					gaim_blist_add_group(g, NULL);
+				}
+			} else if (*c == 'b') { /*&& !gaim_find_buddy(user, c + 2)) {*/
+				char nm[80], sw[388], *a, *utf8 = NULL;
+
+				if ((a = strchr(c + 2, ':')) != NULL) {
+					*a++ = '\0';		/* nul the : */
+				}
+
+				g_strlcpy(nm, c + 2, sizeof(nm));
+				if (a) {
+					utf8 = gaim_try_conv_to_utf8(a);
+					if (utf8 == NULL) {
+						gaim_debug(GAIM_DEBUG_ERROR, "toc blist",
+								   "Failed to convert alias for "
+								   "'%s' to UTF-8\n", nm);
+					}
+				}
+				if (utf8 == NULL) {
+					sw[0] = '\0';
+				} else {
+					/* This can leave a partial sequence at the end,
+					 * but who cares? */
+					g_strlcpy(sw, utf8, sizeof(sw));
+					g_free(utf8);
+				}
+
+				if (!gaim_find_buddy(account, nm)) {
+					struct buddy *b = gaim_buddy_new(account, nm, sw);
+					struct group *g = gaim_find_group(current);
+					gaim_blist_add_buddy(b, g, NULL);
+					bud = g_list_append(bud, g_strdup(nm));
+				}
+			} else if (*c == 'p') {
+				gaim_privacy_permit_add(account, c + 2);
+			} else if (*c == 'd') {
+				gaim_privacy_deny_add(account, c + 2);
+			} else if (!strncmp("toc", c, 3)) {
+				sscanf(c + strlen(c) - 1, "%d", &account->permdeny);
+				gaim_debug(GAIM_DEBUG_MISC, "toc blist",
+						   "permdeny: %d\n", account->permdeny);
+				if (account->permdeny == 0)
+					account->permdeny = 1;
+			} else if (*c == 'm') {
+				sscanf(c + 2, "%d", &account->permdeny);
+				gaim_debug(GAIM_DEBUG_MISC, "toc blist",
+						   "permdeny: %d\n", account->permdeny);
+				if (account->permdeny == 0)
+					account->permdeny = 1;
+			}
+		} while ((c = strtok(NULL, "\n")));
+
+		if(account->gc) {
+			if(bud) {
+				GList *node = bud;
+				serv_add_buddies(account->gc, bud);
+				while(node) {
+					g_free(node->data);
+					node = node->next;
+				}
+			}
+			serv_set_permit_deny(account->gc);
+		}
+		g_list_free(bud);
+	}
+}
+
+#if 0
+/* translate an AIM 3 buddylist (*.lst) to a Gaim buddylist */
+static GString *translate_lst(FILE *src_fp)
+{
+	char line[BUF_LEN], *line2;
+	char *name;
+	int i;
+
+	GString *dest = g_string_new("m 1\n");
+
+	while (fgets(line, BUF_LEN, src_fp)) {
+		line2 = g_strchug(line);
+		if (strstr(line2, "group") == line2) {
+			name = strpbrk(line2, " \t\n\r\f") + 1;
+			dest = g_string_append(dest, "g ");
+			for (i = 0; i < strcspn(name, "\n\r"); i++)
+				if (name[i] != '\"')
+					dest = g_string_append_c(dest, name[i]);
+			dest = g_string_append_c(dest, '\n');
+		}
+		if (strstr(line2, "buddy") == line2) {
+			name = strpbrk(line2, " \t\n\r\f") + 1;
+			dest = g_string_append(dest, "b ");
+			for (i = 0; i < strcspn(name, "\n\r"); i++)
+				if (name[i] != '\"')
+					dest = g_string_append_c(dest, name[i]);
+			dest = g_string_append_c(dest, '\n');
+		}
+	}
+
+	return dest;
+}
+
+
+/* translate an AIM 4 buddylist (*.blt) to Gaim format */
+static GString *translate_blt(FILE *src_fp)
+{
+	int i;
+	char line[BUF_LEN];
+	char *buddy;
+
+	GString *dest = g_string_new("m 1\n");
+
+	while (strstr(fgets(line, BUF_LEN, src_fp), "Buddy") == NULL);
+	while (strstr(fgets(line, BUF_LEN, src_fp), "list") == NULL);
+
+	while (1) {
+		fgets(line, BUF_LEN, src_fp); g_strchomp(line);
+		if (strchr(line, '}') != NULL)
+			break;
+
+		if (strchr(line, '{') != NULL) {
+			/* Syntax starting with "<group> {" */
+
+			dest = g_string_append(dest, "g ");
+			buddy = g_strchug(strtok(line, "{"));
+			for (i = 0; i < strlen(buddy); i++)
+				if (buddy[i] != '\"')
+					dest = g_string_append_c(dest, buddy[i]);
+			dest = g_string_append_c(dest, '\n');
+			while (strchr(fgets(line, BUF_LEN, src_fp), '}') == NULL) {
+				gboolean pounce = FALSE;
+				char *e;
+				g_strchomp(line);
+				buddy = g_strchug(line);
+				gaim_debug(GAIM_DEBUG_MISC, "AIM 4 blt import",
+						   "buddy: \"%s\"\n", buddy);
+				dest = g_string_append(dest, "b ");
+				if (strchr(buddy, '{') != NULL) {
+					/* buddy pounce, etc */
+					char *pos = strchr(buddy, '{') - 1;
+					*pos = 0;
+					pounce = TRUE;
+				}
+				if ((e = strchr(buddy, '\"')) != NULL) {
+					*e = '\0';
+					buddy++;
+				}
+				dest = g_string_append(dest, buddy);
+				dest = g_string_append_c(dest, '\n');
+				if (pounce)
+					do
+						fgets(line, BUF_LEN, src_fp);
+					while (!strchr(line, '}'));
+			}
+		} else {
+
+			/* Syntax "group buddy buddy ..." */
+			buddy = g_strchug(strtok(line, " \n"));
+			dest = g_string_append(dest, "g ");
+			if (strchr(buddy, '\"') != NULL) {
+				dest = g_string_append(dest, &buddy[1]);
+				dest = g_string_append_c(dest, ' ');
+				buddy = g_strchug(strtok(NULL, " \n"));
+				while (strchr(buddy, '\"') == NULL) {
+					dest = g_string_append(dest, buddy);
+					dest = g_string_append_c(dest, ' ');
+					buddy = g_strchug(strtok(NULL, " \n"));
+				}
+				buddy[strlen(buddy) - 1] = '\0';
+				dest = g_string_append(dest, buddy);
+			} else {
+				dest = g_string_append(dest, buddy);
+			}
+			dest = g_string_append_c(dest, '\n');
+			while ((buddy = g_strchug(strtok(NULL, " \n"))) != NULL) {
+				dest = g_string_append(dest, "b ");
+				if (strchr(buddy, '\"') != NULL) {
+					dest = g_string_append(dest, &buddy[1]);
+					dest = g_string_append_c(dest, ' ');
+					buddy = g_strchug(strtok(NULL, " \n"));
+					while (strchr(buddy, '\"') == NULL) {
+						dest = g_string_append(dest, buddy);
+						dest = g_string_append_c(dest, ' ');
+						buddy = g_strchug(strtok(NULL, " \n"));
+					}
+					buddy[strlen(buddy) - 1] = '\0';
+					dest = g_string_append(dest, buddy);
+				} else {
+					dest = g_string_append(dest, buddy);
+				}
+				dest = g_string_append_c(dest, '\n');
+			}
+		}
+	}
+
+	return dest;
+}
+
+static GString *translate_gnomeicu(FILE *src_fp)
+{
+	char line[BUF_LEN];
+	GString *dest = g_string_new("m 1\ng Buddies\n");
+
+	while (strstr(fgets(line, BUF_LEN, src_fp), "NewContacts") == NULL);
+
+	while (fgets(line, BUF_LEN, src_fp)) {
+		char *eq;
+		g_strchomp(line);
+		if (line[0] == '\n' || line[0] == '[')
+			break;
+		eq = strchr(line, '=');
+		if (!eq)
+			break;
+		*eq = ':';
+		eq = strchr(eq, ',');
+		if (eq)
+			*eq = '\0';
+		dest = g_string_append(dest, "b ");
+		dest = g_string_append(dest, line);
+		dest = g_string_append_c(dest, '\n');
+	}
+
+	return dest;
+}
+#endif
+
+static gchar *get_screenname_filename(const char *name)
+{
+	gchar **split;
+	gchar *good;
+	gchar *ret;
+
+	split = g_strsplit(name, G_DIR_SEPARATOR_S, -1);
+	good = g_strjoinv(NULL, split);
+	g_strfreev(split);
+
+	ret = g_utf8_strup(good, -1);
+
+	g_free(good);
+
+	return ret;
+}
+
+static gboolean gaim_blist_read(const char *filename);
+
+
+static void do_import(struct gaim_account *account, const char *filename)
+{
+	GString *buf = NULL;
+	char first[64];
+	char path[PATHSIZE];
+	int len;
+	FILE *f;
+	struct stat st;
+
+	if (filename) {
+		g_snprintf(path, sizeof(path), "%s", filename);
+	} else {
+		char *g_screenname = get_screenname_filename(account->username);
+		char *file = gaim_user_dir();
+		int protocol = (account->protocol == GAIM_PROTO_OSCAR) ? (isalpha(account->username[0]) ? GAIM_PROTO_TOC : GAIM_PROTO_ICQ): account->protocol;
+
+		if (file != (char *)NULL) {
+			sprintf(path, "%s" G_DIR_SEPARATOR_S "%s.%d.blist", file, g_screenname, protocol);
+			g_free(g_screenname);
+		} else {
+			g_free(g_screenname);
+			return;
+		}
+	}
+
+	if (stat(path, &st)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to stat %s.\n",
+				   path);
+		return;
+	}
+
+	if (!(f = fopen(path, "r"))) {
+		gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to open %s.\n",
+				   path);
+		return;
+	}
+
+	fgets(first, 64, f);
+
+	if ((first[0] == '\n') || (first[0] == '\r' && first[1] == '\n'))
+		fgets(first, 64, f);
+
+#if 0
+	if (!g_strncasecmp(first, "<xml", strlen("<xml"))) {
+		/* new gaim XML buddy list */
+		gaim_blist_read(path);
+		
+		/* We really don't need to bother doing stuf like translating AIM 3 buddy lists anymore */
+		
+	} else if (!g_strncasecmp(first, "Config {", strlen("Config {"))) {
+		/* AIM 4 buddy list */
+		gaim_debug(GAIM_DEBUG_MISC, "blist import", "aim 4\n");
+		rewind(f);
+		buf = translate_blt(f);
+	} else if (strstr(first, "group") != NULL) {
+		/* AIM 3 buddy list */
+		gaim_debug(GAIM_DEBUG_MISC, "blist import", "aim 3\n");
+		rewind(f);
+		buf = translate_lst(f);
+	} else if (!g_strncasecmp(first, "[User]", strlen("[User]"))) {
+		/* GnomeICU (hopefully) */
+		gaim_debug(GAIM_DEBUG_MISC, "blist import", "gnomeicu\n");
+		rewind(f);
+		buf = translate_gnomeicu(f);
+
+	} else 
+#endif
+		if (first[0] == 'm') {
+			/* Gaim buddy list - no translation */
+			char buf2[BUF_LONG * 2];
+			buf = g_string_new("");
+			rewind(f);
+			while (1) {
+				len = fread(buf2, 1, BUF_LONG * 2 - 1, f);
+				if (len <= 0)
+					break;
+			buf2[len] = '\0';
+			buf = g_string_append(buf, buf2);
+			if (len != BUF_LONG * 2 - 1)
+				break;
+			}
+		}
+	
+	fclose(f);
+
+	if (buf) {
+		buf = g_string_prepend(buf, "toc_set_config {");
+		buf = g_string_append(buf, "}\n");
+		parse_toc_buddy_list(account, buf->str);
+		g_string_free(buf, TRUE);
+	}
+}
+
+gboolean gaim_group_on_account(struct group *g, struct gaim_account *account) {
+	GaimBlistNode *bnode;
+	for(bnode = g->node.child; bnode; bnode = bnode->next) {
+		struct buddy *b = (struct buddy *)bnode;
+		if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+			continue;
+		if((!account && b->account->gc) || b->account == account)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean blist_safe_to_write = FALSE;
+
+static char *blist_parser_group_name = NULL;
+static char *blist_parser_person_name = NULL;
+static char *blist_parser_account_name = NULL;
+static int blist_parser_account_protocol = 0;
+static char *blist_parser_buddy_name = NULL;
+static char *blist_parser_buddy_alias = NULL;
+static char *blist_parser_setting_name = NULL;
+static char *blist_parser_setting_value = NULL;
+static GHashTable *blist_parser_buddy_settings = NULL;
+static GHashTable *blist_parser_group_settings = NULL;
+static int blist_parser_privacy_mode = 0;
+static GList *tag_stack = NULL;
+enum {
+	BLIST_TAG_GAIM,
+	BLIST_TAG_BLIST,
+	BLIST_TAG_GROUP,
+	BLIST_TAG_PERSON,
+	BLIST_TAG_BUDDY,
+	BLIST_TAG_NAME,
+	BLIST_TAG_ALIAS,
+	BLIST_TAG_SETTING,
+	BLIST_TAG_PRIVACY,
+	BLIST_TAG_ACCOUNT,
+	BLIST_TAG_PERMIT,
+	BLIST_TAG_BLOCK,
+	BLIST_TAG_IGNORE
+};
+static gboolean blist_parser_error_occurred = FALSE;
+
+static void blist_start_element_handler (GMarkupParseContext *context,
+		const gchar *element_name,
+		const gchar **attribute_names,
+		const gchar **attribute_values,
+		gpointer user_data,
+		GError **error) {
+	int i;
+
+	if(!strcmp(element_name, "gaim")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GAIM));
+	} else if(!strcmp(element_name, "blist")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLIST));
+	} else if(!strcmp(element_name, "group")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GROUP));
+		for(i=0; attribute_names[i]; i++) {
+			if(!strcmp(attribute_names[i], "name")) {
+				g_free(blist_parser_group_name);
+				blist_parser_group_name = g_strdup(attribute_values[i]);
+			}
+		}
+		if(blist_parser_group_name) {
+			struct group *g = gaim_group_new(blist_parser_group_name);
+			gaim_blist_add_group(g,NULL);
+		}
+	} else if(!strcmp(element_name, "person")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERSON));
+		for(i=0; attribute_names[i]; i++) {
+			if(!strcmp(attribute_names[i], "name")) {
+				g_free(blist_parser_person_name);
+				blist_parser_person_name = g_strdup(attribute_values[i]);
+			}
+		}
+	} else if(!strcmp(element_name, "buddy")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BUDDY));
+		for(i=0; attribute_names[i]; i++) {
+			if(!strcmp(attribute_names[i], "account")) {
+				g_free(blist_parser_account_name);
+				blist_parser_account_name = g_strdup(attribute_values[i]);
+			} else if(!strcmp(attribute_names[i], "protocol")) {
+				blist_parser_account_protocol = atoi(attribute_values[i]);
+			}
+		}
+	} else if(!strcmp(element_name, "name")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_NAME));
+	} else if(!strcmp(element_name, "alias")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ALIAS));
+	} else if(!strcmp(element_name, "setting")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_SETTING));
+		for(i=0; attribute_names[i]; i++) {
+			if(!strcmp(attribute_names[i], "name")) {
+				g_free(blist_parser_setting_name);
+				blist_parser_setting_name = g_strdup(attribute_values[i]);
+			}
+		}
+	} else if(!strcmp(element_name, "privacy")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PRIVACY));
+	} else if(!strcmp(element_name, "account")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ACCOUNT));
+		for(i=0; attribute_names[i]; i++) {
+			if(!strcmp(attribute_names[i], "protocol"))
+				blist_parser_account_protocol = atoi(attribute_values[i]);
+			else if(!strcmp(attribute_names[i], "mode"))
+				blist_parser_privacy_mode = atoi(attribute_values[i]);
+			else if(!strcmp(attribute_names[i], "name")) {
+				g_free(blist_parser_account_name);
+				blist_parser_account_name = g_strdup(attribute_values[i]);
+			}
+		}
+	} else if(!strcmp(element_name, "permit")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERMIT));
+	} else if(!strcmp(element_name, "block")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLOCK));
+	} else if(!strcmp(element_name, "ignore")) {
+		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_IGNORE));
+	}
+}
+
+static void blist_end_element_handler(GMarkupParseContext *context,
+		const gchar *element_name, gpointer user_data, GError **error) {
+	if(!strcmp(element_name, "gaim")) {
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "blist")) {
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "group")) {
+		if(blist_parser_group_settings) {
+			struct group *g = gaim_find_group(blist_parser_group_name);
+			g_hash_table_destroy(g->settings);
+			g->settings = blist_parser_group_settings;
+		}
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+		blist_parser_group_settings = NULL;
+	} else if(!strcmp(element_name, "person")) {
+		g_free(blist_parser_person_name);
+		blist_parser_person_name = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "buddy")) {
+		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
+				blist_parser_account_protocol);
+		if(account) {
+			struct buddy *b = gaim_buddy_new(account, blist_parser_buddy_name, blist_parser_buddy_alias);
+			struct group *g = gaim_find_group(blist_parser_group_name);
+			gaim_blist_add_buddy(b,g,NULL);
+			if(blist_parser_buddy_settings) {
+				g_hash_table_destroy(b->settings);
+				b->settings = blist_parser_buddy_settings;
+			}
+		}
+		g_free(blist_parser_buddy_name);
+		blist_parser_buddy_name = NULL;
+		g_free(blist_parser_buddy_alias);
+		blist_parser_buddy_alias = NULL;
+		g_free(blist_parser_account_name);
+		blist_parser_account_name = NULL;
+		blist_parser_buddy_settings = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "name")) {
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "alias")) {
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "setting")) {
+		if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_BUDDY) {
+			if(!blist_parser_buddy_settings)
+				blist_parser_buddy_settings = g_hash_table_new_full(g_str_hash,
+						g_str_equal, g_free, g_free);
+			if(blist_parser_setting_name && blist_parser_setting_value) {
+				g_hash_table_replace(blist_parser_buddy_settings,
+						g_strdup(blist_parser_setting_name),
+						g_strdup(blist_parser_setting_value));
+			}
+		} else if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_GROUP) {
+			if(!blist_parser_group_settings)
+				blist_parser_group_settings = g_hash_table_new_full(g_str_hash,
+						g_str_equal, g_free, g_free);
+			if(blist_parser_setting_name && blist_parser_setting_value) {
+				g_hash_table_replace(blist_parser_group_settings,
+						g_strdup(blist_parser_setting_name),
+						g_strdup(blist_parser_setting_value));
+			}
+		}
+		g_free(blist_parser_setting_name);
+		g_free(blist_parser_setting_value);
+		blist_parser_setting_name = NULL;
+		blist_parser_setting_value = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "privacy")) {
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "account")) {
+		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
+				blist_parser_account_protocol);
+		if(account) {
+			account->permdeny = blist_parser_privacy_mode;
+		}
+		g_free(blist_parser_account_name);
+		blist_parser_account_name = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "permit")) {
+		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
+				blist_parser_account_protocol);
+		if(account) {
+			gaim_privacy_permit_add(account, blist_parser_buddy_name);
+		}
+		g_free(blist_parser_buddy_name);
+		blist_parser_buddy_name = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "block")) {
+		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
+				blist_parser_account_protocol);
+		if(account) {
+			gaim_privacy_deny_add(account, blist_parser_buddy_name);
+		}
+		g_free(blist_parser_buddy_name);
+		blist_parser_buddy_name = NULL;
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	} else if(!strcmp(element_name, "ignore")) {
+		/* we'll apparently do something with this later */
+		tag_stack = g_list_delete_link(tag_stack, tag_stack);
+	}
+}
+
+static void blist_text_handler(GMarkupParseContext *context, const gchar *text,
+		gsize text_len, gpointer user_data, GError **error) {
+	switch(GPOINTER_TO_INT(tag_stack->data)) {
+		case BLIST_TAG_NAME:
+			blist_parser_buddy_name = g_strndup(text, text_len);
+			break;
+		case BLIST_TAG_ALIAS:
+			blist_parser_buddy_alias = g_strndup(text, text_len);
+			break;
+		case BLIST_TAG_PERMIT:
+		case BLIST_TAG_BLOCK:
+		case BLIST_TAG_IGNORE:
+			blist_parser_buddy_name = g_strndup(text, text_len);
+			break;
+		case BLIST_TAG_SETTING:
+			blist_parser_setting_value = g_strndup(text, text_len);
+			break;
+		default:
+			break;
+	}
+}
+
+static void blist_error_handler(GMarkupParseContext *context, GError *error,
+		gpointer user_data) {
+	blist_parser_error_occurred = TRUE;
+	gaim_debug(GAIM_DEBUG_ERROR, "blist import",
+			   "Error parsing blist.xml: %s\n", error->message);
+}
+
+static GMarkupParser blist_parser = {
+	blist_start_element_handler,
+	blist_end_element_handler,
+	blist_text_handler,
+	NULL,
+	blist_error_handler
+};
+
+static gboolean gaim_blist_read(const char *filename) {
+	gchar *contents = NULL;
+	gsize length;
+	GMarkupParseContext *context;
+	GError *error = NULL;
+
+	gaim_debug(GAIM_DEBUG_INFO, "blist import",
+			   "Reading %s\n", filename);
+	if(!g_file_get_contents(filename, &contents, &length, &error)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "blist import",
+				   "Error reading blist: %s\n", error->message);
+		g_error_free(error);
+		return FALSE;
+	}
+
+	context = g_markup_parse_context_new(&blist_parser, 0, NULL, NULL);
+
+	if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
+		g_markup_parse_context_free(context);
+		g_free(contents);
+		return FALSE;
+	}
+
+	if(!g_markup_parse_context_end_parse(context, NULL)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "blist import",
+				   "Error parsing %s\n", filename);
+		g_markup_parse_context_free(context);
+		g_free(contents);
+		return FALSE;
+	}
+
+	g_markup_parse_context_free(context);
+	g_free(contents);
+
+	if(blist_parser_error_occurred)
+		return FALSE;
+
+	gaim_debug(GAIM_DEBUG_INFO, "blist import", "Finished reading %s\n",
+			   filename);
+
+	return TRUE;
+}
+
+void gaim_blist_load() {
+	GSList *accts;
+	char *user_dir = gaim_user_dir();
+	char *filename;
+	char *msg;
+
+	blist_safe_to_write = TRUE;
+
+	if(!user_dir)
+		return;
+
+	filename = g_build_filename(user_dir, "blist.xml", NULL);
+
+	if(g_file_test(filename, G_FILE_TEST_EXISTS)) {
+		if(!gaim_blist_read(filename)) {
+			msg = g_strdup_printf(_("An error was encountered parsing your "
+						"buddy list.  It has not been loaded."));
+			do_error_dialog(_("Buddy List Error"), msg, GAIM_ERROR);
+			g_free(msg);
+		}
+	} else if(g_slist_length(gaim_accounts)) {
+		/* rob wants to inform the user that their buddy lists are
+		 * being converted */
+		msg = g_strdup_printf(_("Gaim is converting your old buddy lists "
+					"to a new format, which will now be located at %s"),
+				filename);
+		do_error_dialog(_("Converting Buddy List"), msg, GAIM_INFO);
+		g_free(msg);
+
+		/* now, let gtk actually display the dialog before we start anything */
+		while(gtk_events_pending())
+			gtk_main_iteration();
+
+		/* read in the old lists, then save to the new format */
+		for(accts = gaim_accounts; accts; accts = accts->next) {
+			do_import(accts->data, NULL);
+		}
+		gaim_blist_save();
+	}
+
+	g_free(filename);
+}
+
+static void blist_print_group_settings(gpointer key, gpointer data,
+		gpointer user_data) {
+	char *key_val;
+	char *data_val;
+	FILE *file = user_data;
+
+	if(!key || !data)
+		return;
+
+	key_val = g_markup_escape_text(key, -1);
+	data_val = g_markup_escape_text(data, -1);
+
+	fprintf(file, "\t\t\t<setting name=\"%s\">%s</setting>\n", key_val,
+			data_val);
+	g_free(key_val);
+	g_free(data_val);
+}
+
+static void blist_print_buddy_settings(gpointer key, gpointer data,
+		gpointer user_data) {
+	char *key_val;
+	char *data_val;
+	FILE *file = user_data;
+
+	if(!key || !data)
+		return;
+
+	key_val = g_markup_escape_text(key, -1);
+	data_val = g_markup_escape_text(data, -1);
+
+	fprintf(file, "\t\t\t\t\t<setting name=\"%s\">%s</setting>\n", key_val,
+			data_val);
+	g_free(key_val);
+	g_free(data_val);
+}
+
+static void gaim_blist_write(FILE *file, struct gaim_account *exp_acct) {
+	GSList *accounts, *buds;
+	GaimBlistNode *gnode,*bnode;
+	struct group *group;
+	struct buddy *bud;
+	fprintf(file, "<?xml version='1.0' encoding='UTF-8' ?>\n");
+	fprintf(file, "<gaim version=\"1\">\n");
+	fprintf(file, "\t<blist>\n");
+
+	for(gnode = gaimbuddylist->root; gnode; gnode = gnode->next) {
+		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		group = (struct group *)gnode;
+		if(!exp_acct || gaim_group_on_account(group, exp_acct)) {
+			char *group_name = g_markup_escape_text(group->name, -1);
+			fprintf(file, "\t\t<group name=\"%s\">\n", group_name);
+			g_hash_table_foreach(group->settings, blist_print_group_settings, file);
+			for(bnode = gnode->child; bnode; bnode = bnode->next) {
+				if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+					continue;
+				bud = (struct buddy *)bnode;
+				if(!exp_acct || bud->account == exp_acct) {
+					char *bud_name = g_markup_escape_text(bud->name, -1);
+					char *bud_alias = NULL;
+					char *acct_name = g_markup_escape_text(bud->account->username, -1);
+					if(bud->alias)
+						bud_alias= g_markup_escape_text(bud->alias, -1);
+					fprintf(file, "\t\t\t<person name=\"%s\">\n",
+							bud_alias ? bud_alias : bud_name);
+					fprintf(file, "\t\t\t\t<buddy protocol=\"%d\" "
+							"account=\"%s\">\n", bud->account->protocol,
+							acct_name);
+					fprintf(file, "\t\t\t\t\t<name>%s</name>\n", bud_name);
+					if(bud_alias) {
+						fprintf(file, "\t\t\t\t\t<alias>%s</alias>\n",
+								bud_alias);
+					}
+					g_hash_table_foreach(bud->settings,
+							blist_print_buddy_settings, file);
+					fprintf(file, "\t\t\t\t</buddy>\n");
+					fprintf(file, "\t\t\t</person>\n");
+					g_free(bud_name);
+					g_free(bud_alias);
+					g_free(acct_name);
+				}
+			}
+			fprintf(file, "\t\t</group>\n");
+			g_free(group_name);
+		}
+	}
+
+	fprintf(file, "\t</blist>\n");
+	fprintf(file, "\t<privacy>\n");
+
+	for(accounts = gaim_accounts; accounts; accounts = accounts->next) {
+		struct gaim_account *account = accounts->data;
+		char *acct_name = g_markup_escape_text(account->username, -1);
+		if(!exp_acct || account == exp_acct) {
+			fprintf(file, "\t\t<account protocol=\"%d\" name=\"%s\" "
+					"mode=\"%d\">\n", account->protocol, acct_name, account->permdeny);
+			for(buds = account->permit; buds; buds = buds->next) {
+				char *bud_name = g_markup_escape_text(buds->data, -1);
+				fprintf(file, "\t\t\t<permit>%s</permit>\n", bud_name);
+				g_free(bud_name);
+			}
+			for(buds = account->deny; buds; buds = buds->next) {
+				char *bud_name = g_markup_escape_text(buds->data, -1);
+				fprintf(file, "\t\t\t<block>%s</block>\n", bud_name);
+				g_free(bud_name);
+			}
+			fprintf(file, "\t\t</account>\n");
+		}
+		g_free(acct_name);
+	}
+
+	fprintf(file, "\t</privacy>\n");
+	fprintf(file, "</gaim>\n");
+}
+
+void gaim_blist_save() {
+	FILE *file;
+	char *user_dir = gaim_user_dir();
+	char *filename;
+	char *filename_real;
+
+	if(!user_dir)
+		return;
+	if(!blist_safe_to_write) {
+		gaim_debug(GAIM_DEBUG_WARNING, "blist save",
+				   "AHH!! Tried to write the blist before we read it!\n");
+		return;
+	}
+
+	file = fopen(user_dir, "r");
+	if(!file)
+		mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
+	else
+		fclose(file);
+
+	filename = g_build_filename(user_dir, "blist.xml.save", NULL);
+
+	if((file = fopen(filename, "w"))) {
+		gaim_blist_write(file, NULL);
+		fclose(file);
+		chmod(filename, S_IRUSR | S_IWUSR);
+	} else {
+		gaim_debug(GAIM_DEBUG_ERROR, "blist save", "Unable to write %s\n",
+				   filename);
+	}
+
+	filename_real = g_build_filename(user_dir, "blist.xml", NULL);
+
+	if(rename(filename, filename_real) < 0)
+		gaim_debug(GAIM_DEBUG_ERROR, "blist save",
+				   "Error renaming %s to %s\n", filename, filename_real);
+
+
+	g_free(filename);
+	g_free(filename_real);
+}
+
+gboolean gaim_privacy_permit_add(struct gaim_account *account, const char *who) {
+	GSList *d = account->permit;
+	char *n = g_strdup(normalize(who));
+	while(d) {
+		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
+			break;
+		d = d->next;
+	}
+	g_free(n);
+	if(!d) {
+		account->permit = g_slist_append(account->permit, g_strdup(who));
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean gaim_privacy_permit_remove(struct gaim_account *account, const char *who) {
+	GSList *d = account->permit;
+	char *n = g_strdup(normalize(who));
+	while(d) {
+		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
+			break;
+		d = d->next;
+	}
+	g_free(n);
+	if(d) {
+		account->permit = g_slist_remove(account->permit, d->data);
+		g_free(d->data);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean gaim_privacy_deny_add(struct gaim_account *account, const char *who) {
+	GSList *d = account->deny;
+	char *n = g_strdup(normalize(who));
+	while(d) {
+		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
+			break;
+		d = d->next;
+	}
+	g_free(n);
+	if(!d) {
+		account->deny = g_slist_append(account->deny, g_strdup(who));
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean gaim_privacy_deny_remove(struct gaim_account *account, const char *who) {
+	GSList *d = account->deny;
+	char *n = g_strdup(normalize(who));
+	while(d) {
+		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
+			break;
+		d = d->next;
+	}
+	g_free(n);
+	if(d) {
+		account->deny = g_slist_remove(account->deny, d->data);
+		g_free(d->data);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+void gaim_group_set_setting(struct group *g, const char *key,
+		const char *value) {
+	if(!g)
+		return;
+	g_hash_table_replace(g->settings, g_strdup(key), g_strdup(value));
+}
+
+char *gaim_group_get_setting(struct group *g, const char *key) {
+	if(!g)
+		return NULL;
+	return g_strdup(g_hash_table_lookup(g->settings, key));
+}
+
+void gaim_buddy_set_setting(struct buddy *b, const char *key,
+		const char *value) {
+	if(!b)
+		return;
+	g_hash_table_replace(b->settings, g_strdup(key), g_strdup(value));
+}
+
+char *gaim_buddy_get_setting(struct buddy *b, const char *key) {
+	if(!b)
+		return NULL;
+	return g_strdup(g_hash_table_lookup(b->settings, key));
+}
+
+void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops)
+{
+	blist_ui_ops = ops;
+}
+
+struct gaim_blist_ui_ops *
+gaim_get_blist_ui_ops(void)
+{
+	return blist_ui_ops;
+}
+
+int gaim_blist_get_group_size(struct group *group, gboolean offline) {
+	GaimBlistNode *node;
+	int count = 0;
+
+	if(!group)
+		return 0;
+
+	for(node = group->node.child; node; node = node->next) {
+		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			struct buddy *b = (struct buddy *)node;
+			if(b->account->gc || offline)
+				count++;
+		}
+	}
+
+	return count;
+}
+
+int gaim_blist_get_group_online_count(struct group *group) {
+	GaimBlistNode *node;
+	int count = 0;
+
+	if(!group)
+		return 0;
+
+	for(node = group->node.child; node; node = node->next) {
+		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			struct buddy *b = (struct buddy *)node;
+			if(GAIM_BUDDY_IS_ONLINE(b))
+				count++;
+		}
+	}
+
+	return count;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/blist.h	Sat Apr 26 17:56:23 2003 +0000
@@ -0,0 +1,479 @@
+/**
+ * @file blist.h Buddy List API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003, Sean Egan <sean.egan@binghamton.edu>
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* I can't believe I let ChipX86 inspire me to write good code. -Sean */
+
+#ifndef _LIST_H_
+#define _LIST_H_
+
+#include <glib.h>
+
+/**************************************************************************/
+/* Enumerations                                                           */
+/**************************************************************************/
+enum gaim_blist_node_type {
+	GAIM_BLIST_GROUP_NODE,
+	GAIM_BLIST_BUDDY_NODE,
+	GAIM_BLIST_OTHER_NODE,
+};
+
+#define GAIM_BLIST_NODE_IS_BUDDY(n) ((n)->type == GAIM_BLIST_BUDDY_NODE)
+#define GAIM_BLIST_NODE_IS_GROUP(n) ((n)->type == GAIM_BLIST_GROUP_NODE)
+
+enum gaim_buddy_presence_state {
+	GAIM_BUDDY_SIGNING_OFF = -1,
+	GAIM_BUDDY_OFFLINE = 0,
+	GAIM_BUDDY_ONLINE,
+	GAIM_BUDDY_SIGNING_ON,
+};
+
+#define GAIM_BUDDY_IS_ONLINE(b) ((b)->present == GAIM_BUDDY_ONLINE || \
+		(b)->present == GAIM_BUDDY_SIGNING_ON)
+
+
+/**************************************************************************/
+/* Data Structures                                                        */
+/**************************************************************************/
+
+typedef struct _GaimBlistNode GaimBlistNode;
+/**
+ * A Buddy list node.  This can represent a group, a buddy, or anything else.  This is a base class for struct buddy and
+ * struct group and for anything else that wants to put itself in the buddy list. */
+struct _GaimBlistNode {
+	enum gaim_blist_node_type type;        /**< The type of node this is       */
+	GaimBlistNode *prev;                   /**< The sibling before this buddy. */
+	GaimBlistNode *next;                   /**< The sibling after this buddy.  */
+	GaimBlistNode *parent;                 /**< The parent of this node        */
+	GaimBlistNode *child;                  /**< The child of this node         */
+	void          *ui_data;                /**< The UI can put data here.      */
+};
+
+/**
+ * A buddy.  This contains everything Gaim will ever need to know about someone on the buddy list.  Everything.
+ */
+struct buddy {
+	GaimBlistNode node;                     /**< The node that this buddy inherits from */
+	char *name;                             /**< The screenname of the buddy. */
+	char *alias;                            /**< The user-set alias of the buddy */
+	char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */ 
+	enum gaim_buddy_presence_state present;                            /**< This is 0 if the buddy appears offline, 1 if he appears online, and 2 if
+						    he has recently signed on */
+	int evil;                               /**< The warning level */
+	time_t signon;                          /**< The time the buddy signed on. */
+	int idle;                               /**< The time the buddy has been idle in minutes. */
+	int uc;                                 /**< This is a cryptic bitmask that makes sense only to the prpl.  This will get changed */
+	void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
+	struct gaim_account *account;           /**< the account this buddy belongs to */ 
+	GHashTable *settings;                   /**< per-buddy settings from the XML buddy list, set by plugins and the likes. */
+	guint timer;							/**< The timer handle. */
+};
+
+/**
+ * A group.  This contains everything Gaim will ever need to know about a group.
+ */
+struct group {
+	GaimBlistNode node;                    /**< The node that this group inherits from */
+	char *name;                            /**< The name of this group. */
+	GHashTable *settings;                  /**< per-group settings from the XML buddy list, set by plugins and the likes. */
+};
+
+
+/**
+ * The Buddy List
+ */
+struct gaim_buddy_list {
+	GaimBlistNode *root;                    /**< The first node in the buddy list */
+	struct gaim_blist_ui_ops *ui_ops;       /**< The UI operations for the buddy list */
+
+	void *ui_data;                          /**< UI-specific data. */
+};
+
+/**
+ * Buddy list UI operations.
+ *
+ * Any UI representing a buddy list must assign a filled-out gaim_window_ops
+ * structure to the buddy list core.
+ */
+struct gaim_blist_ui_ops
+{
+	void (*new_list)(struct gaim_buddy_list *list); /**< Sets UI-specific data on a buddy list. */
+	void (*new_node)(GaimBlistNode *node);      /**< Sets UI-specific data on a node. */
+	void (*show)(struct gaim_buddy_list *list);     /**< The core will call this when its finished doing it's core stuff */
+	void (*update)(struct gaim_buddy_list *list, 
+		       GaimBlistNode *node);            /**< This will update a node in the buddy list. */
+	void (*remove)(struct gaim_buddy_list *list,
+		       GaimBlistNode *node);            /**< This removes a node from the list */
+	void (*destroy)(struct gaim_buddy_list *list);  /**< When the list gets destroyed, this gets called to destroy the UI. */
+	void (*set_visible)(struct gaim_buddy_list *list,
+			    gboolean show);             /**< Hides or unhides the buddy list */
+
+};
+
+/**************************************************************************/
+/** @name Buddy List API                                                  */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates a new buddy list
+ */
+struct gaim_buddy_list *gaim_blist_new();
+
+/**
+ * Sets the main buddy list.
+ *
+ * @return The main buddy list.
+ */
+void gaim_set_blist(struct gaim_buddy_list *blist);
+
+/**
+ * Returns the main buddy list.
+ *
+ * @return The main buddy list.
+ */
+struct gaim_buddy_list *gaim_get_blist(void);
+
+/**
+ * Shows the buddy list, creating a new one if necessary.
+ *
+ */
+void gaim_blist_show();
+
+
+/**
+ * Destroys the buddy list window.
+ */
+void gaim_blist_destroy();
+
+/**
+ * Hides or unhides the buddy list.
+ *
+ * @param show   Whether or not to show the buddy list
+ */
+void gaim_blist_set_visible(gboolean show);
+
+/**
+ * Updates a buddy's status.
+ * 
+ * This needs to not take an int.
+ *
+ * @param buddy   The buddy whose status has changed
+ * @param status  The new status in cryptic prpl-understood code
+ */
+void gaim_blist_update_buddy_status(struct buddy *buddy, int status);
+
+
+/**
+ * Updates a buddy's presence.
+ *
+ * @param buddy    The buddy whose presence has changed
+ * @param presence The new presence
+ */
+void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence);
+
+
+/**
+ * Updates a buddy's idle time.
+ *
+ * @param buddy  The buddy whose idle time has changed
+ * @param idle   The buddy's idle time in minutes.
+ */
+void gaim_blist_update_buddy_idle(struct buddy *buddy, int idle);
+
+
+/**
+ * Updates a buddy's warning level.
+ *
+ * @param buddy  The buddy whose warning level has changed
+ * @param evil   The warning level as an int from 0 to 100 (or higher, I guess... but that'd be weird)
+ */
+void gaim_blist_update_buddy_evil(struct buddy *buddy, int warning);
+
+/**
+ * Updates a buddy's icon.
+ *
+ * @param buddy  The buddy whose buddy icon has changed
+ */
+void gaim_blist_update_buddy_icon(struct buddy *buddy);
+
+
+
+/**
+ * Renames a buddy in the buddy list.
+ *
+ * @param buddy  The buddy whose name will be changed.
+ * @param name   The new name of the buddy.
+ */
+void gaim_blist_rename_buddy(struct buddy *buddy, const char *name);
+
+
+/**
+ * Aliases a buddy in the buddy list.
+ *
+ * @param buddy  The buddy whose alias will be changed.
+ * @param alias  The buddy's alias.
+ */
+void gaim_blist_alias_buddy(struct buddy *buddy, const char *alias);
+
+
+/**
+ * Renames a group
+ *
+ * @param group  The group to rename
+ * @param name   The new name
+ */
+void gaim_blist_rename_group(struct group *group, const char *name);
+
+
+/**
+ * Creates a new buddy
+ *
+ * @param account    The account this buddy will get added to
+ * @param screenname The screenname of the new buddy
+ * @param alias      The alias of the new buddy (or NULL if unaliased)
+ * @return           A newly allocated buddy
+ */
+struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias);
+
+/**
+ * Adds a new buddy to the buddy list.
+ *
+ * The buddy will be inserted right after node or appended to the end
+ * of group if node is NULL.  If both are NULL, the buddy will be added to
+ * the "Buddies" group.
+ *
+ * @param buddy  The new buddy who gets added
+ * @param group  The group to add the new buddy to.
+ * @param node   The insertion point 
+ */
+void gaim_blist_add_buddy(struct buddy *buddy, struct group *group, GaimBlistNode *node);
+
+/**
+ * Creates a new group
+ *
+ * You can't have more than one group with the same name.  Sorry.  If you pass this the 
+ * name of a group that already exists, it will return that group.
+ *
+ * @param name   The name of the new group
+ * @return       A new group struct 
+*/
+struct group *gaim_group_new(const char *name);
+
+/**
+ * Adds a new group to the buddy list.
+ *
+ * The new group will be inserted after insert or appended to the end of
+ * the list if node is NULL.
+ *
+ * @param group  The group to add the new buddy to.
+ * @param node   The insertion point 
+ */
+void gaim_blist_add_group(struct group *group, GaimBlistNode *node);
+
+/**
+ * Removes a buddy from the buddy list and frees the memory allocated to it.
+ *
+ * @param buddy   The buddy to be removed
+ */
+void gaim_blist_remove_buddy(struct buddy *buddy);
+
+/**
+ * Removes a group from the buddy list and frees the memory allocated to it and to
+ * its children
+ *
+ * @param group   The group to be removed
+ */
+void gaim_blist_remove_group(struct group *group);
+
+/**
+ * Returns the alias of a buddy.
+ *
+ * @param buddy   The buddy whose name will be returned.
+ * @return        The alias (if set), server alias (if option is set), or NULL.
+ */
+char *gaim_get_buddy_alias_only(struct buddy *buddy);
+
+
+/**
+ * Returns the correct name to display for a buddy.
+ *
+ * @param buddy   The buddy whose name will be returned.
+ * @return        The alias (if set), server alias (if option is set), screenname, or "Unknown"
+ */
+char *gaim_get_buddy_alias(struct buddy *buddy);
+
+/**
+ * Finds the buddy struct given a screenname and an account
+ *
+ * @param name    The buddy's screenname
+ * @param account The account this buddy belongs to
+ * @return        The buddy or NULL if the buddy does not exist
+ */
+struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name);   
+
+/**
+ * Finds a group by name
+ *
+ * @param name    The groups name
+ * @return        The group or NULL if the group does not exist
+ */
+struct group *gaim_find_group(const char *name);   
+
+/**
+ * Returns the group of which the buddy is a member.
+ *
+ * @param buddy   The buddy
+ * @return        The group or NULL if the buddy is not in a group
+ */
+struct group *gaim_find_buddys_group(struct buddy *buddy);
+
+
+/**
+ * Returns a list of accounts that have buddies in this group
+ *
+ * @param group   The group
+ * @return        A list of gaim_accounts
+ */
+GSList *gaim_group_get_accounts(struct group *g);
+
+/**
+ * Determines whether an account owns any buddies in a given group
+ *
+ * @param g       The group to search through.
+ * @param account The account.
+ */
+gboolean gaim_group_on_account(struct group *g, struct gaim_account *account);
+
+/**
+ * Called when an account gets signed off.  Sets the presence of all the buddies to 0
+ * and tells the UI to update them.
+ *
+ * @param account   The account 
+ */
+void gaim_blist_remove_account(struct gaim_account *account);
+
+
+/**
+ * Determines the total size of a group
+ *
+ * @param group  The group
+ * @param offline Count buddies in offline accounts
+ * @return The number of buddies in the group
+ */
+int gaim_blist_get_group_size(struct group *group, gboolean offline);
+
+/**
+ * Determines the number of online buddies in a group
+ *
+ * @param group The group
+ * @return The number of online buddies in the group, or 0 if the group is NULL
+ */
+int gaim_blist_get_group_online_count(struct group *group);
+
+/*@}*/
+
+/****************************************************************************************/
+/** @name Buddy list file management API                                                */
+/****************************************************************************************/
+
+/*@{*/
+/**
+ * Saves the buddy list to file
+ */
+void gaim_blist_save();
+
+/**
+ * Parses the toc-style buddy list used in older versions of Gaim and for SSI in toc.c
+ *
+ * @param account  This is the account that the buddies and groups from config will get added to
+ * @param config   This is the toc-style buddy list data
+ */
+void parse_toc_buddy_list(struct gaim_account *account, char *config);
+
+
+/**
+ * Loads the buddy list from ~/.gaim/blist.xml.
+ */
+void gaim_blist_load();
+
+/**
+ * Associates some data with the group in the xml buddy list
+ *
+ * @param g      The group the data is associated with
+ * @param key    The key used to retrieve the data
+ * @param value  The data to set
+ */
+void gaim_group_set_setting(struct group *g, const char *key, const char *value);
+
+/**
+ * Retrieves data from the XML buddy list set by gaim_group_set_setting())
+ *
+ * @param g      The group to retrieve data from
+ * @param key    The key to retrieve the data with
+ * @return       The associated data or NULL if no data is associated
+ */
+char *gaim_group_get_setting(struct group *g, const char *key);
+
+
+/**
+ * Associates some data with the buddy in the xml buddy list
+ *
+ * @param b      The buddy the data is associated with
+ * @param key    The key used to retrieve the data
+ * @param value  The data to set
+ */
+void gaim_buddy_set_setting(struct buddy *b, const char *key, const char *value);
+
+/**
+ * Retrieves data from the XML buddy list set by gaim_buddy_set_setting())
+ *
+ * @param b      The buddy to retrieve data from
+ * @param key    The key to retrieve the data with
+ * @return       The associated data or NULL if no data is associated
+ */
+char *gaim_buddy_get_setting(struct buddy *b, const char *key);
+
+/*@}*/
+
+/**************************************************************************/
+/** @name UI Registration Functions                                       */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Sets the UI operations structure to be used for the buddy list.
+ *
+ * @param ops The ops struct.
+ */
+void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops);
+
+/**
+ * Returns the UI operations structure to be used for the buddy list.
+ *
+ * @return The UI operations structure.
+ */
+struct gaim_blist_ui_ops *gaim_get_blist_ui_ops(void);
+
+/*@}*/
+
+#endif /* _LIST_H_ */
--- a/src/buddy.c	Sat Apr 26 17:36:52 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1824 +0,0 @@
-/*
- * gaim
- *
- * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef GAIM_PLUGINS
-#ifndef _WIN32
-#include <dlfcn.h>
-#endif
-#endif /* GAIM_PLUGINS */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <math.h>
-#include <time.h>
-#include <ctype.h>
-
-#ifdef _WIN32
-#include <gdk/gdkwin32.h>
-#else
-#include <unistd.h>
-#include <gdk/gdkx.h>
-#endif
-
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-#include "prpl.h"
-#include "sound.h"
-#include "gaim.h"
-#include "gtklist.h"
-#include "gtkpounce.h"
-#include "gtkft.h"
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-static struct gaim_gtk_buddy_list *gtkblist = NULL;
-
-/* part of the best damn Docklet code this side of Tahiti */
-static gboolean gaim_gtk_blist_obscured = FALSE;
-
-static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data);
-static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node);
-static char *gaim_get_tooltip_text(struct buddy *b);
-static char *item_factory_translate_func (const char *path, gpointer func_data);
-
-/***************************************************
- *              Callbacks                          *
- ***************************************************/
-
-static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
-{
-	if (docklet_count)
-		gaim_blist_set_visible(FALSE);
-	else
-		do_quit();
-
-	/* we handle everything, event should not propogate further */
-	return TRUE;
-}
-
-static gboolean gtk_blist_save_prefs_cb(gpointer data)
-{
-	save_prefs();
-
-	/* only run once */
-	return FALSE;
-}
-
-static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data)
-{
-	/* unfortunately GdkEventConfigure ignores the window gravity, but  *
-	 * the only way we have of setting the position doesn't. we have to *
-	 * call get_position and get_size because they do pay attention to  *
-	 * the gravity. this is inefficient and I agree it sucks, but it's  *
-	 * more likely to work correctly.                        - Robot101 */
-	gint x, y;
-
-	/* check for visibility because when we aren't visible, this will   *
-	 * give us bogus (0,0) coordinates.                      - xOr      */
-	if (GTK_WIDGET_VISIBLE(w)) {
-		gtk_window_get_position(GTK_WINDOW(w), &x, &y);
-
-		if (x != blist_pos.x ||
-		    y != blist_pos.y ||
-		    event->width != blist_pos.width ||
-		    event->height != blist_pos.height) {
-			blist_pos.x = x;
-			blist_pos.y = y;
-			blist_pos.width = event->width;
-			blist_pos.height = event->height;
-
-			if (!g_main_context_find_source_by_user_data(NULL, &gtk_blist_save_prefs_cb)) {
-				g_timeout_add(5000, gtk_blist_save_prefs_cb, &gtk_blist_save_prefs_cb);
-			}
-		}
-	}
-
-	/* continue to handle event normally */
-	return FALSE;
-}
-
-static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data)
-{
-	if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
-		gaim_gtk_blist_obscured = TRUE;
-	else
-		gaim_gtk_blist_obscured = FALSE;
-
-	/* continue to handle event normally */
-	return FALSE;
-}
-
-static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b)
-{
-	serv_get_info(b->account->gc, b->name);
-}
-
-static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b)
-{
-       gaim_conversation_new(GAIM_CONV_IM, b->account, b->name);
-}
-
-static void gtk_blist_menu_alias_cb(GtkWidget *w, struct buddy *b)
-{
-       alias_dialog_bud(b);
-}
-
-static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b)
-{
-	gaim_gtkpounce_dialog_show(b, NULL);
-}
-
-static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b)
-{
-       show_log(b->name);
-}
-
-static void gtk_blist_show_systemlog_cb()
-{
-       show_log(NULL);
-}
-
-static void gtk_blist_show_onlinehelp_cb()
-{
-       open_url(NULL, WEBSITE "documentation.php");
-}
-
-static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv)
-{
-	GtkTreeIter iter;
-	GtkTreeModel *model = gtk_tree_view_get_model(tv);
-	GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
-
-	if(gtk_tree_selection_get_selected(sel, &model, &iter)){
-		GaimBlistNode *node;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name);
-			return;
-		}
-	}
-	show_im_dialog();
-}
-
-static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv)
-{
-	GtkTreeIter iter;
-	GtkTreeModel *model = gtk_tree_view_get_model(tv);
-	GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
-
-	if(gtk_tree_selection_get_selected(sel, &model, &iter)){
-		GaimBlistNode *node;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
-			return;
-		}
-	}
-	show_info_dialog();
-}
-
-static void gtk_blist_button_chat_cb(GtkWidget *w, gpointer data)
-{
-	/* FIXME: someday, we can check to see if we've selected a chat node */
-	join_chat();
-}
-
-static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data)
-{
-	gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
-}
-
-static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
-	GaimBlistNode *node;
-	GValue val = {0,};
-
-	gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val);
-
-	node = g_value_get_pointer(&val);
-
-	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		gaim_group_set_setting((struct group *)node, "collapsed", NULL);
-		gaim_blist_save();
-	}
-}
-
-static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
-	GaimBlistNode *node;
-	GValue val = {0,};
-
-	gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val);
-
-	node = g_value_get_pointer(&val);
-
-	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		gaim_group_set_setting((struct group *)node, "collapsed", "true");
-		gaim_blist_save();
-	}
-}
-
-static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) {
-	GaimBlistNode *node;
-	GtkTreeIter iter;
-	GValue val = { 0, };
-
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-
-	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-	node = g_value_get_pointer(&val);
-
-	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		struct gaim_conversation *conv =
-			gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name);
-		if(conv) {
-			gaim_window_raise(gaim_conversation_get_window(conv));
-			gaim_window_switch_conversation(
-				gaim_conversation_get_window(conv),
-				gaim_conversation_get_index(conv));
-		}
-	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		if (gtk_tree_view_row_expanded(tv, path))
-			gtk_tree_view_collapse_row(tv, path);
-		else
-			gtk_tree_view_expand_row(tv,path,FALSE);
-	}
-}
-
-static void gaim_gtk_blist_add_buddy_cb()
-{
-	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
-	GtkTreeIter iter;
-	GaimBlistNode *node;
-
-	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (GAIM_BLIST_NODE_IS_BUDDY(node)) 
-			show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL);
-		else if (GAIM_BLIST_NODE_IS_GROUP(node))
-			show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL);
-	}
-	else {
-		show_add_buddy(NULL, NULL, NULL, NULL);
-	}
-}
-
-static void
-gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) 
-{
-	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		struct buddy *b = (struct buddy*)node;
-		show_confirm_del(b->account->gc, b->name);
-	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		struct group *g = (struct group*)node;
-		show_confirm_del_group(g);
-	}
-}
-
-static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b)
-{
-	struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
-	if (pbm->callback)
-		pbm->callback(pbm->gc, b->name);
-}
-
-static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null)
-{
-	GtkTreePath *path;
-	GaimBlistNode *node;
-	GValue val = { 0, };
-	GtkTreeIter iter;
-	GtkWidget *menu, *menuitem;
-	GtkTreeSelection *sel;
-	GList *list;
-	GaimPlugin *prpl = NULL;
-	GaimPluginProtocolInfo *prpl_info = NULL;
-
-	if (event->button != 3)
-		return FALSE;
-
-	/* Here we figure out which node was clicked */
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL))
-		return FALSE;
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-	node = g_value_get_pointer(&val);
-	menu = gtk_menu_new();
-
-	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		gaim_new_item_from_stock(menu, _("_Add a Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL);
-		gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL);
-		gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL);
-	} else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		/* Protocol specific options */
-		prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol);
-
-		if (prpl != NULL)
-			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
-
-		if (prpl && prpl_info->get_info)
-			gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL);
-
-		gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL);
-		gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL);
-		gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
-
-		if (prpl) {
-			list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
-			while (list) {
-				struct proto_buddy_menu *pbm = list->data;
-				menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
-				g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
-				g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node);
-				gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-				list = list->next;
-			}
-		}
-
-		gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name);
-
-		gaim_separator(menu);
-		gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
-		gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL);
-	}
-	
-	gtk_widget_show_all(menu);
-	
-	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
-
-#if (1)  /* This code only exists because GTK doesn't work.  If we return FALSE here, as would be normal
-	  * the event propoagates down and somehow gets interpreted as the start of a drag event. */
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
-	gtk_tree_selection_select_path(sel, path);
-	gtk_tree_path_free(path);
-	return TRUE;
-#endif
-}
-
-static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item)
-{
-	if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)))
-		blist_options &= ~OPT_BLIST_NO_MT_GRP;
-	else
-		blist_options |= OPT_BLIST_NO_MT_GRP;
-	save_prefs();
-	gaim_gtk_blist_refresh(gaim_get_blist());
-}
-
-static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action,
-		GtkWidget *checkitem) {
-	if(gtkblist->window->window) {
-		GdkCursor *cursor = gdk_cursor_new(GDK_WATCH);
-		gdk_window_set_cursor(gtkblist->window->window, cursor);
-		while (gtk_events_pending())
-			gtk_main_iteration();
-		gdk_cursor_unref(cursor);
-	}
-
-	if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem)))
-		blist_options |= OPT_BLIST_SHOW_OFFLINE;
-	else
-		blist_options &= ~OPT_BLIST_SHOW_OFFLINE;
-	save_prefs();
-
-	if(gtkblist->window->window) {
-		GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR);
-		gdk_window_set_cursor(gtkblist->window->window, cursor);
-		gdk_cursor_unref(cursor);
-	}
-
-	gaim_gtk_blist_refresh(gaim_get_blist());
-}
-
-static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget,
-					     GdkDragContext *dc,
-					     GtkSelectionData *data,
-					     guint info,
-					     guint time,
-					     gpointer *null)
-{
-	if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) {
-		GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row");
-		GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref);
-		GtkTreeIter iter;
-		GaimBlistNode *node = NULL;
-		GValue val = {0};
-		gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow);
-		gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-		node = g_value_get_pointer(&val);
-		gtk_selection_data_set (data,
-					gdk_atom_intern ("GAIM_BLIST_NODE", FALSE),
-					8, /* bits */
-					(void*)&node,
-					sizeof (node));
-		
-		gtk_tree_path_free(sourcerow);
-	}
-	
-}
-
-static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
-			  GtkSelectionData *sd, guint info, guint t)
-{	
-	if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) {
-		GaimBlistNode *n = NULL;
-		GtkTreePath *path = NULL;
-		GtkTreeViewDropPosition position;
-		memcpy(&n, sd->data, sizeof(n));
-		if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) {
-			/* if we're here, I think it means the drop is ok */
-			GtkTreeIter iter;
-			GaimBlistNode *node;
-			GValue val = {0};
-			gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-			gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-			node = g_value_get_pointer(&val);
-
-			if (GAIM_BLIST_NODE_IS_BUDDY(n)) {
-				struct buddy *b = (struct buddy*)n;
-				if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-					switch(position) {
-						case GTK_TREE_VIEW_DROP_AFTER:
-						case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
-							gaim_blist_add_buddy(b, (struct group*)node->parent, node);
-							break;
-						case GTK_TREE_VIEW_DROP_BEFORE:
-						case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
-							gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev);
-							break;
-					}
-				} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-					gaim_blist_add_buddy(b, (struct group*)node, NULL);
-				}
-			} else if (GAIM_BLIST_NODE_IS_GROUP(n)) {
-				struct group *g = (struct group*)n;
-				if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-					switch (position) {
-					case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
-					case GTK_TREE_VIEW_DROP_AFTER:
-						gaim_blist_add_group(g, node);
-						break;
-					case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
-					case GTK_TREE_VIEW_DROP_BEFORE:
-						gaim_blist_add_group(g, node->prev);
-						break;
-					}
-
-				} else if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-					gaim_blist_add_group(g, node->parent);
-				}
-
-			}
-
-			gtk_tree_path_free(path);
-			gaim_blist_save();
-		}
-	}
-}
-
-static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, struct buddy *b)
-{
-	GtkStyle *style;
-	GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(b, GAIM_STATUS_ICON_LARGE);
-	PangoLayout *layout;
-	char *tooltiptext = gaim_get_tooltip_text(b);
-
-	layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL);
-	pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext));
-	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(layout, 300000);
-	style = gtkblist->tipwindow->style;
-
-	gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-			    NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1);
-
-#if GTK_CHECK_VERSION(2,2,0)
-	gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf,
-			0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
-#else
-	gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
-#endif
-
-	gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE,
-			  NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout);
-
-	g_object_unref (pixbuf);
-	g_object_unref (layout);
-	g_free(tooltiptext);
-	return;
-}
-
-static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv)
-{
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	GaimBlistNode *node;
-	GValue val = {0};
-
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL))
-		return FALSE;
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-	node = g_value_get_pointer(&val);
-	
-	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		int scr_w,scr_h, w, h, x, y;
-		PangoLayout *layout;
-		struct buddy *buddy = (struct buddy*)node;
-		char *tooltiptext = gaim_get_tooltip_text(buddy);
-		gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
-		gtkblist->tipwindow->parent = tv;
-		gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE);
-		gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE);
-		gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips");
-		g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", 
-				 G_CALLBACK(gaim_gtk_blist_paint_tip), buddy);
-		gtk_widget_ensure_style (gtkblist->tipwindow);
-
-		layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL);
-		pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
-		pango_layout_set_width(layout, 300000);
-		pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext));
-		scr_w = gdk_screen_width();
-		scr_h = gdk_screen_height();
-		pango_layout_get_size (layout, &w, &h);
-		w = PANGO_PIXELS(w) + 8;
-		h = PANGO_PIXELS(h) + 8;
-
-		/* 38 is the size of a large status icon plus 4 pixels padding on each side.
-		   I should #define this or something */
-		w = w + 38;
-		h = MAX(h, 38);
-
-		gdk_window_get_pointer(NULL, &x, &y, NULL);
-		if (GTK_WIDGET_NO_WINDOW(gtkblist->window))
-			y+=gtkblist->window->allocation.y;
-	
-		x -= ((w >> 1) + 4);
-		
-		if ((x + w) > scr_w)
-			x -= (x + w) - scr_w;
-		else if (x < 0)
-			x = 0;
-
-		if ((y + h + 4) > scr_h)
-			y = y - h;
-		else
-			y = y + 6;
-		g_object_unref (layout);
-		g_free(tooltiptext);
-		gtk_widget_set_size_request(gtkblist->tipwindow, w, h);
-		gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
-		gtk_widget_show(gtkblist->tipwindow);
-	}
-
-	gtk_tree_path_free(path);
-	return FALSE;
-}
-
-static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
-{
-	GtkTreePath *path;
-	if (gtkblist->timeout) {
-		if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y))
-			return FALSE;
-		/* We've left the cell.  Remove the timeout and create a new one below */
-		if (gtkblist->tipwindow) {
-			gtk_widget_destroy(gtkblist->tipwindow);
-			gtkblist->tipwindow = NULL;
-		}
-		
-		g_source_remove(gtkblist->timeout);
-	}
-	
-	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
-	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->rect);
-	if (path)
-		gtk_tree_path_free(path);
-	gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv);
-	return FALSE;
-}
-
-static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n)
-{
-	if (gtkblist->timeout) {
-		g_source_remove(gtkblist->timeout);
-		gtkblist->timeout = 0;
-	}
-	if (gtkblist->tipwindow) {
-		gtk_widget_destroy(gtkblist->tipwindow);
-		gtkblist->tipwindow = NULL;
-	}
-}
-
-static void
-toggle_debug(void)
-{
-	misc_options ^= OPT_MISC_DEBUG;
-
-	if ((misc_options & OPT_MISC_DEBUG))
-		gaim_gtk_debug_window_show();
-	else
-		gaim_gtk_debug_window_hide();
-
-	save_prefs();
-}
-
-
-/***************************************************
- *            Crap                                 *
- ***************************************************/
-static GtkItemFactoryEntry blist_menu[] =
-{
-	/* Buddies menu */
-	{ N_("/_Buddies"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0, "<StockItem>", GAIM_STOCK_IM },
-	{ N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, "<StockItem>", GAIM_STOCK_CHAT },
-	{ N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0, "<StockItem>", GAIM_STOCK_INFO },
-	{ "/Buddies/sep1", NULL, NULL, 0, "<Separator>" },
-	{ N_("/Buddies/_Show Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, "<CheckItem>"},
-	{ N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"},
-	{ N_("/Buddies/_Add a Buddy..."), NULL, gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, 
-	{ N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL},
-	{ "/Buddies/sep2", NULL, NULL, 0, "<Separator>" },
-	{ N_("/Buddies/_Signoff"), "<CTL>D", signoff_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF },
-	{ N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0, "<StockItem>", GTK_STOCK_QUIT },
-
-	/* Tools */ 
-	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" },
-	{ "/Tools/sep1", NULL, NULL, 0, "<Separator>" },
-	{ N_("/Tools/A_ccounts..."), "<CTL>A", account_editor, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS },
-	{ N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER },
-	{ N_("/Tools/Preferences..."), "<CTL>P", show_prefs, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
-	{ N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "<StockItem>", GAIM_STOCK_PRIVACY },
-	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>" },
-	{ N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL },
-
-	/* Help */
-	{ N_("/_Help"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
-	{ N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL },
-	{ N_("/Help/_About..."), NULL, show_about, 0,  "<StockItem>", GAIM_STOCK_ABOUT },
-};
-
-/*********************************************************
- * Private Utility functions                             *
- *********************************************************/
-
-static char *gaim_get_tooltip_text(struct buddy *b)
-{
-	GaimPlugin *prpl;
-	GaimPluginProtocolInfo *prpl_info = NULL;
-	char *text = NULL;
-	char *statustext = NULL;
-	char *aliastext = NULL, *nicktext = NULL;
-	char *warning = NULL, *idletime = NULL;
-
-	prpl = gaim_find_prpl(b->account->protocol);
-	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info->tooltip_text) {
-		const char *end;
-		statustext = prpl_info->tooltip_text(b);
-
-		if(statustext && !g_utf8_validate(statustext, -1, &end)) {
-			char *new = g_strndup(statustext,
-					g_utf8_pointer_to_offset(statustext, end));
-			g_free(statustext);
-			statustext = new;
-		}
-	}
-
-	if (!statustext && !GAIM_BUDDY_IS_ONLINE(b))
-		statustext = g_strdup(_("<b>Status:</b> Offline"));
-
-	if (b->idle > 0) {
-		int ihrs, imin;
-		time_t t;
-		time(&t);
-		ihrs = (t - b->idle) / 3600;
-		imin = ((t - b->idle) / 60) % 60;
-		if (ihrs)
-			idletime = g_strdup_printf(_("%dh%02dm"), ihrs, imin);
-		else
-			idletime = g_strdup_printf(_("%dm"), imin);
-	}
-
-	if(b->alias && b->alias[0])
-		aliastext = g_markup_escape_text(b->alias, -1);
-
-	if(b->server_alias)
-		nicktext = g_markup_escape_text(b->server_alias, -1);
-
-	if (b->evil > 0)
-		warning = g_strdup_printf(_("%d%%"), b->evil);
-
-	text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>"
-			       "%s %s"  /* Alias */
-			       "%s %s"  /* Nickname */
-			       "%s %s"     /* Idle */
-			       "%s %s"     /* Warning */
-			       "%s%s"     /* Status */
-				   "%s",
-			       b->name,
-			       aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "",
-			       nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "",
-			       idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "",
-			       b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "",
-			       statustext ? "\n" : "", statustext ? statustext : "",
-				   !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : "");
-	
-	if(warning)
-		g_free(warning);
-	if(idletime)
-		g_free(idletime);
-	if(statustext)
-		g_free(statustext);
-	if(nicktext)
-		g_free(nicktext);
-	if(aliastext)
-		g_free(aliastext);
-
-	return text;
-
-}
-
-GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size)
-{
-	GdkPixbuf *status = NULL;
-	GdkPixbuf *scale = NULL;
-	GdkPixbuf *emblem = NULL;
-	gchar *filename = NULL;
-	const char *protoname = NULL;
-
-	char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL;
-
-	int scalesize = 30;
-
-	GaimPlugin *prpl;
-	GaimPluginProtocolInfo *prpl_info = NULL;
-
-	prpl = gaim_find_prpl(b->account->protocol);
-
-	if (!prpl) 
-		return NULL;
-
-	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info->list_icon)
-		protoname = prpl_info->list_icon(b->account, b);
-	if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl_info->list_emblems)
-		prpl_info->list_emblems(b, &se, &sw, &nw, &ne);
-
-	if (size == GAIM_STATUS_ICON_SMALL) {
-		scalesize = 15;
-		sw = nw = ne = NULL; /* So that only the se icon will composite */
-	}
-
-
-	if (b->present == GAIM_BUDDY_SIGNING_ON) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (b->present == GAIM_BUDDY_SIGNING_OFF) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-
-		/* "Hey, what's all this crap?" you ask.  Status icons will be themeable too, and
-		   then it will look up protoname from the theme */
-	} else {
-		char *image = g_strdup_printf("%s.png", protoname);
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(image);
-		g_free(filename);
-
-	}
-
-	if (!status)
-		return NULL;
-
-	scale =  gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR);
-
-	g_object_unref(G_OBJECT(status));
-
-	/* Emblems */
-
-	/* Each protocol can specify up to four "emblems" to composite over the base icon.  "away", "busy", "mobile user"
-	 * are all examples of states represented by emblems.  I'm not even really sure I like this yet. */
-
-	/* XXX Clean this crap up, yo. */
-	if (se) {
-		char *image = g_strdup_printf("%s.png", se);
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
-		g_free(image);
-		emblem = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-		if (emblem) {
-			if (size == GAIM_STATUS_ICON_LARGE)
-				gdk_pixbuf_composite (emblem,
-						      scale, 15, 15,
-						      15, 15,
-						      15, 15,
-						      1, 1,
-						      GDK_INTERP_BILINEAR,
-						      255);
-			else
-					gdk_pixbuf_composite (emblem,
-						      scale, 5, 5,
-						      10, 10,
-						      5, 5,
-						      .6, .6,
-						      GDK_INTERP_BILINEAR,
-						      255);
-			g_object_unref(G_OBJECT(emblem));
-		}
-	}
-	if (sw) {
-		char *image = g_strdup_printf("%s.png", sw);
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
-		g_free(image);
-		emblem = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-		if (emblem) {
-			gdk_pixbuf_composite (emblem,
-					scale, 0, 15,
-					15, 15,
-					0, 15,
-					1, 1,
-					GDK_INTERP_BILINEAR,
-					255);
-			g_object_unref(G_OBJECT(emblem));
-		}
-	}
-	if (nw) {
-		char *image = g_strdup_printf("%s.png", nw);
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
-		g_free(image);
-		emblem = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-		if (emblem) {
-			gdk_pixbuf_composite (emblem,
-					      scale, 0, 0,
-					      15, 15,
-					      0, 0,
-					      1, 1,
-					      GDK_INTERP_BILINEAR,
-					      255);
-			g_object_unref(G_OBJECT(emblem));
-		}
-	}
-	if (ne) {
-		char *image = g_strdup_printf("%s.png", ne);
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
-		g_free(image);
-		emblem = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-		if (emblem) {
-			gdk_pixbuf_composite (emblem,
-					      scale, 15, 0,
-					      15, 15,
-					      15, 0,
-					      1, 1,
-					      GDK_INTERP_BILINEAR,
-					      255);
-			g_object_unref(G_OBJECT(emblem));
-		}
-	}
-
-
-	/* Idle grey buddies affects the whole row.  This converts the status icon to greyscale. */
-	if (b->present == GAIM_BUDDY_OFFLINE)
-		gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
-	else if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS)
-		gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE);
-	return scale;
-}
-
-static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b)
-{
-	/* This just opens a file from ~/.gaim/icons/screenname.  This needs to change to be more gooder. */
-	char *file;
-	GdkPixbuf *buf, *ret;
-
-	if (!(blist_options & OPT_BLIST_SHOW_ICONS))
-		return NULL;
-
-	if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL)
-		return NULL;
-
-	buf = gdk_pixbuf_new_from_file(file, NULL);
-	g_free(file);
-
-
-	if (buf) {
-		if (!GAIM_BUDDY_IS_ONLINE(b))
-			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE);
-		if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS)
-			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE);
-
-		ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR);
-		g_object_unref(G_OBJECT(buf));
-		return ret;
-	}
-	return NULL;
-}
-
-static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected)
-{
-	char *name = gaim_get_buddy_alias(b);
-	char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL;
-	GaimPlugin *prpl;
-	GaimPluginProtocolInfo *prpl_info = NULL;
-	/* XXX Clean up this crap */
-
-	int ihrs, imin;
-	char *idletime = NULL, *warning = NULL, *statustext = NULL;
-	time_t t;
-
-	prpl = gaim_find_prpl(b->account->protocol);
-
-	if (prpl != NULL)
-		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
-		if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) {
-			text =  g_strdup_printf("<span color='dim grey'>%s</span>",
-						esc);
-			g_free(esc);
-			return text;
-		} else {
-			return esc;
-		}
-	}
-
-	time(&t);
-	ihrs = (t - b->idle) / 3600;
-	imin = ((t - b->idle) / 60) % 60;
-
-	if (prpl && prpl_info->status_text) {
-		char *tmp = prpl_info->status_text(b);
-		const char *end;
-
-		if(tmp && !g_utf8_validate(tmp, -1, &end)) {
-			char *new = g_strndup(tmp,
-					g_utf8_pointer_to_offset(tmp, end));
-			g_free(tmp);
-			tmp = new;
-		}
-
-		if(tmp) {
-			char buf[32];
-			char *c = tmp;
-			int length = 0, vis=0;
-			gboolean inside = FALSE;
-			g_strdelimit(tmp, "\n", ' ');
-
-			while(*c && vis < 20) {
-				if(*c == '&')
-					inside = TRUE;
-				else if(*c == ';')
-					inside = FALSE;
-				if(!inside)
-					vis++;
-				length++;
-				c++; /* this is fun */
-			}
-
-			if(vis == 20)
-				g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
-			else
-				g_snprintf(buf, sizeof(buf), "%%s ");
-
-			statustext = g_strdup_printf(buf, tmp);
-
-			g_free(tmp);
-		}
-	}
-
-	if (b->idle > 0) {
-		if (ihrs)
-			idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin);
-		else
-			idletime = g_strdup_printf(_("Idle (%dm) "), imin);
-	}
-
-	if (b->evil > 0)
-		warning = g_strdup_printf(_("Warned (%d%%) "), b->evil);
-
-	if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext)
-		statustext = g_strdup("Offline ");
-
-	if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) {
-		text =  g_strdup_printf("<span color='dim grey'>%s</span>\n"
-					"<span color='dim grey' size='smaller'>%s%s%s</span>",
-					esc,
-					statustext != NULL ? statustext : "",
-					idletime != NULL ? idletime : "",
-					warning != NULL ? warning : "");
-	} else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) {
-		text = g_strdup(esc);
-	} else {
-		text = g_strdup_printf("%s\n"
-				       "<span %s size='smaller'>%s%s%s</span>", esc,
-				       selected ? "" : "color='dim grey'",
-				       statustext != NULL ? statustext :  "",
-				       idletime != NULL ? idletime : "", 
-				       warning != NULL ? warning : "");
-	}
-	if (idletime)
-		g_free(idletime);
-	if (warning)
-		g_free(warning);
-	if (statustext)
-		g_free(statustext);
-	if (esc)
-		g_free(esc);
-
-	return text;
-}
-
-static void gaim_gtk_blist_restore_position()
-{
-	/* if the window exists, is hidden, we're saving positions, and the position is sane... */
-	if(gtkblist && gtkblist->window &&
-	   !GTK_WIDGET_VISIBLE(gtkblist->window) &&
-	   blist_pos.width != 0) {
-		/* ...check position is on screen... */
-		if (blist_pos.x >= gdk_screen_width())
-			blist_pos.x = gdk_screen_width() - 100;
-		else if (blist_pos.x < 0)
-			blist_pos.x = 100;
-		
-		if (blist_pos.y >= gdk_screen_height())
-			blist_pos.y = gdk_screen_height() - 100;
-		else if (blist_pos.y < 0)
-			blist_pos.y = 100;
-		
-		/* ...and move it back. */
-		gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y);
-		gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height);
-	}
-}
-
-static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list)
-{
-	GaimBlistNode *group = list->root;
-	GaimBlistNode *buddy;
-
-	while (group) {
-		buddy = group->child;
-		while (buddy) {
-			if (((struct buddy *)buddy)->idle)
-				gaim_gtk_blist_update(list, buddy);
-			buddy = buddy->next;
-		}
-		group = group->next;
-	}
-
-	/* keep on going */
-	return TRUE;
-}
-
-/**********************************************************************************
- * Public API Functions                                                           *
- **********************************************************************************/
-static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist)
-{
-	blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1);
-}
-
-void gaim_gtk_blist_update_columns()
-{
-	if(!gtkblist)
-		return;
-
-	if (blist_options & OPT_BLIST_SHOW_ICONS) {
-		gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE);
-		gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE);
-		gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE);
-	} else {
-		gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME);
-		gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN);
-		gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE);
-	}
-}
-
-enum {DRAG_BUDDY, DRAG_ROW};
-
-static char *
-item_factory_translate_func (const char *path, gpointer func_data)
-{
-	return _(path);
-}
-
-static void gaim_gtk_blist_show(struct gaim_buddy_list *list)
-{
-	GtkItemFactory *ift;
-	GtkCellRenderer *rend;
-	GtkTreeViewColumn *column;
-	GtkWidget *sw;
-	GtkWidget *button;
-	GtkSizeGroup *sg;
-	GtkAccelGroup *accel_group;
-	GtkTreeSelection *selection;
-	GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW},
-				{"application/x-im-contact", 0, DRAG_BUDDY}};
-
-	if (gtkblist && gtkblist->window) {
-		gtk_widget_show(gtkblist->window);
-		return;
-	}
-
-	gtkblist = GAIM_GTK_BLIST(list);
-
-	gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list");
-	gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));
-
-	GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
-
-	gtkblist->vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox);
-
-	g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL);
-	gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
-
-	/******************************* Menu bar *************************************/
-	accel_group = gtk_accel_group_new();
-	gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group);
-	g_object_unref(accel_group);
-	ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", accel_group);
-	gtk_item_factory_set_translate_func (ift,
-					     item_factory_translate_func,
-					     NULL, NULL);
-	gtk_item_factory_create_items(ift, sizeof(blist_menu) / sizeof(*blist_menu),
-				      blist_menu, NULL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(ift, "<GaimMain>"), FALSE, FALSE, 0);
-
-	awaymenu = gtk_item_factory_get_widget(ift, N_("/Tools/Away"));
-	do_away_menu();
-
-	gtkblist->bpmenu = gtk_item_factory_get_widget(ift, N_("/Tools/Buddy Pounce"));
-	gaim_gtkpounce_menu_build(gtkblist->bpmenu);
-
-	protomenu = gtk_item_factory_get_widget(ift, N_("/Tools/Protocol Actions"));
-	do_proto_menu();
-
-	/****************************** GtkTreeView **********************************/
-	sw = gtk_scrolled_window_new(NULL,NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
-	gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING,
-						 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
-
-	gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
-	gtk_widget_set_size_request(gtkblist->treeview, -1, 200);
-
-	/* Set up selection stuff */
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
-	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL);
-
-
-	/* Set up dnd */
-	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte,
-					       2, GDK_ACTION_COPY);
-	gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2,
-					     GDK_ACTION_COPY | GDK_ACTION_MOVE);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL);
-
-	/* Tooltips */
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL);
-
-	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
-
-	column = gtk_tree_view_column_new ();
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start (column, rend, FALSE);
-	gtk_tree_view_column_set_attributes (column, rend, 
-					     "pixbuf", STATUS_ICON_COLUMN,
-					     "visible", STATUS_ICON_VISIBLE_COLUMN,
-					     NULL);
-	g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL);
-
-	rend = gtk_cell_renderer_text_new();
-	gtk_tree_view_column_pack_start (column, rend, TRUE);
-	gtk_tree_view_column_set_attributes (column, rend, 
-					     "markup", NAME_COLUMN,
-					     NULL);
-	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
-
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
-
-	rend = gtk_cell_renderer_text_new();
-	gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL);
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column);
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-
-	rend = gtk_cell_renderer_text_new();
-	gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL);
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column);
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL);
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column);
-
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL);
-
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0);
-	gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
-	gaim_gtk_blist_update_columns();
-
-	/* set the Show Offline Buddies option. must be done
-	 * after the treeview or faceprint gets mad. -Robot101
-	 */
-	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Offline Buddies"))),
-			blist_options & OPT_BLIST_SHOW_OFFLINE);
-	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Empty Groups"))),
-			!(blist_options & OPT_BLIST_NO_MT_GRP));
-
-	/* OK... let's show this bad boy. */
-	gaim_gtk_blist_refresh(list);
-	gaim_gtk_blist_restore_position();
-	gtk_widget_show_all(gtkblist->window);
-
-	/**************************** Button Box **************************************/
-	/* add this afterwards so it doesn't force up the width of the window         */
-
-	gtkblist->tooltips = gtk_tooltips_new();
-
-	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
-	gtkblist->bbox = gtk_hbox_new(TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0);
-	gtk_widget_show(gtkblist->bbox);
-
-	button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
-	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-	gtk_size_group_add_widget(sg, button);
-	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb),
-			 gtkblist->treeview);
-	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL);
-	gtk_widget_show(button);
-
-	button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
-	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-	gtk_size_group_add_widget(sg, button);
-	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb),
-			 gtkblist->treeview);
-	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL);
-	gtk_widget_show(button);
-
-	button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
-	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-	gtk_size_group_add_widget(sg, button);
-	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), NULL);
-	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL);
-	gtk_widget_show(button);
-
-	button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
-	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-	gtk_size_group_add_widget(sg, button);
-	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL);
-	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL);
-	gtk_widget_show(button);
-
-	/* this will show the right image/label widgets for us */
-	gaim_gtk_blist_update_toolbar();
-
-	/* start the refresh timer */
-	gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list);
-}
-
-void gaim_gtk_blist_refresh(struct gaim_buddy_list *list)
-{
-	GaimBlistNode *group = list->root;
-	GaimBlistNode *buddy;
-
-	while (group) {
-		buddy = group->child;
-		gaim_gtk_blist_update(list, group);
-		while (buddy) {
-			gaim_gtk_blist_update(list, buddy);
-			buddy = buddy->next;
-		}
-		group = group->next;
-	}
-}
-
-static gboolean get_iter_from_node_helper(GaimBlistNode *node, GtkTreeIter *iter, GtkTreeIter *root) {
-
-	do {
-		GaimBlistNode *n;
-		GtkTreeIter child;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), root, NODE_COLUMN, &n, -1);
-		if(n == node) {
-			*iter = *root;
-			return TRUE;
-		}
-
-		if(gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &child, root)) {
-			if(get_iter_from_node_helper(node,iter,&child))
-				return TRUE;
-		}
-	} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), root));
-
-	return FALSE;
-}
-
-static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) {
-	GtkTreeIter root;
-
-	if (!gtkblist)
-		return FALSE;
-
-	if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkblist->treemodel), &root))
-		return FALSE;
-
-	return get_iter_from_node_helper(node, iter, &root);
-}
-
-/*
- * These state assignments suck. I'm sorry. They're for historical reasons.
- * Roll on new prefs. -Robot101
- * 
- * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image
- * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text
- * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images
- * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none
- */
-
-static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) {
-	if (GTK_IS_IMAGE(widget)) {
-		if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM)
-			gtk_widget_show(widget);
-		else
-			gtk_widget_hide(widget);
-	} else if (GTK_IS_LABEL(widget)) {
-		if (blist_options & OPT_BLIST_NO_BUTTON_TEXT)
-			gtk_widget_hide(widget);
-		else
-			gtk_widget_show(widget);
-	} else if (GTK_IS_CONTAINER(widget)) {
-		gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL);
-	}
-}
-
-void gaim_gtk_blist_update_toolbar() {
-	if (!gtkblist)
-		return;
-
-	if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM))
-		gtk_widget_hide(gtkblist->bbox);
-	else {
-		gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL);
-		gtk_widget_show(gtkblist->bbox);
-	}
-}
-
-static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node)
-{
-	GtkTreeIter iter;
-
-	/* For some reason, we're called before we have a buddy list sometimes */
-	if(!gtkblist)
-		return;
-
-	if(gtkblist->selected_node == node)
-		gtkblist->selected_node = NULL;
-
-	if (get_iter_from_node(node, &iter)) {
-		gtk_tree_store_remove(gtkblist->treemodel, &iter);
-		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			gaim_gtk_blist_update(list, node->parent);
-		}
-	}
-}
-
-static gboolean do_selection_changed(GaimBlistNode *new_selection)
-{
-	GaimBlistNode *old_selection = gtkblist->selected_node;
-
-	if(new_selection != gtkblist->selected_node) {
-		gtkblist->selected_node = new_selection;
-		if(new_selection)
-			gaim_gtk_blist_update(NULL, new_selection);
-		if(old_selection)
-			gaim_gtk_blist_update(NULL, old_selection);
-	}
-
-	return FALSE;
-}
-
-static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data)
-{
-	GaimBlistNode *new_selection = NULL;
-	GtkTreeIter iter;
-
-	if(gtk_tree_selection_get_selected(selection, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
-				NODE_COLUMN, &new_selection, -1);
-	}
-	/* we set this up as a timeout, otherwise the blist flickers */
-	g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
-}
-
-static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) {
-	GaimBlistNode *sibling;
-	GtkTreeIter siblingiter;
-	struct group *group = (struct group *)node;
-	char *esc = g_markup_escape_text(group->name, -1);
-	char *mark;
-
-	if(blist_options & OPT_BLIST_SHOW_GRPNUM)
-		mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE));
-	else
-		mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
-
-	g_free(esc);
-
-	sibling = node->prev;
-	while (sibling && !get_iter_from_node(sibling, &siblingiter)) {
-		sibling = sibling->prev;
-	}
-
-	gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL,
-			sibling ? &siblingiter : NULL);
-	gtk_tree_store_set(gtkblist->treemodel, iter,
-			STATUS_ICON_COLUMN, NULL,
-			STATUS_ICON_VISIBLE_COLUMN, FALSE,
-			NAME_COLUMN, mark,
-			NODE_COLUMN, node,
-			-1);
-	g_free(mark);
-}
-
-
-static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node)
-{
-	GtkTreeIter iter;
-	GtkTreePath *expand = NULL;
-	gboolean new_entry = FALSE;
-
-	if (!gtkblist)
-		return;
-
-	if (!get_iter_from_node(node, &iter)) { /* This is a newly added node */
-		new_entry = TRUE;
-		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) {
-				GtkTreeIter groupiter;
-				GaimBlistNode *oldersibling;
-				GtkTreeIter oldersiblingiter;
-				char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed");
-
-				if(node->parent &&
-						!get_iter_from_node(node->parent, &groupiter)) {
-					/* This buddy's group has not yet been added.
-					 * We do that here */
-					make_a_group(node->parent, &groupiter);
-				}
-				if(!collapsed)
-					expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter);
-				else
-					g_free(collapsed);
-
-				oldersibling = node->prev;
-				while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) {
-					oldersibling = oldersibling->prev;
-				}
-
-				gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL);
-
-				if (blist_options & OPT_BLIST_POPUP) {
-					gtk_widget_show(gtkblist->window);
-					gtk_window_deiconify(GTK_WINDOW(gtkblist->window));
-					gdk_window_raise(gtkblist->window->window);
-				}
-
-			}
-		}
-		else if (GAIM_BLIST_NODE_IS_GROUP(node) &&
-					((blist_options & OPT_BLIST_SHOW_OFFLINE) ||
-					!(blist_options & OPT_BLIST_NO_MT_GRP))) {
-			make_a_group(node, &iter);
-			expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
-		}
-	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
-		if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) {
-			gtk_tree_store_remove(gtkblist->treemodel, &iter);
-		} else {
-			struct group *group = (struct group *)node;
-			char *esc = g_markup_escape_text(group->name, -1);
-			char *mark;
-
-			if(blist_options & OPT_BLIST_SHOW_GRPNUM)
-				mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE));
-			else
-				mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
-
-			g_free(esc);
-			gtk_tree_store_set(gtkblist->treemodel, &iter,
-					NAME_COLUMN, mark,
-					-1);
-			g_free(mark);
-		}
-	}
-
-	if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) {
-		GdkPixbuf *status, *avatar;
-		char *mark;
-		char *warning = NULL, *idle = NULL;
-
-		gboolean selected = (gtkblist->selected_node == node);
-
-		status = gaim_gtk_blist_get_status_icon((struct buddy*)node,
-							blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL);
-		avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node);
-		mark   = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected);
-
-		if (((struct buddy*)node)->idle > 0) {
-			time_t t;
-			int ihrs, imin;
-			time(&t);
-			ihrs = (t - ((struct buddy *)node)->idle) / 3600;
-			imin = ((t - ((struct buddy*)node)->idle) / 60) % 60;
-			if(ihrs > 0)
-				idle = g_strdup_printf("(%d:%02d)", ihrs, imin);
-			else
-				idle = g_strdup_printf("(%d)", imin);
-		}
-
-		if (((struct buddy*)node)->evil > 0)
-			warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil);
-
-
-		if((blist_options & OPT_BLIST_GREY_IDLERS)
-				&& ((struct buddy *)node)->idle) {
-			if(warning && !selected) {
-				char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>",
-						warning);
-				g_free(warning);
-				warning = w2;
-			}
-
-			if(idle && !selected) {
-				char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>",
-						idle);
-				g_free(idle);
-				idle = i2;
-			}
-		}
-
-		gtk_tree_store_set(gtkblist->treemodel, &iter,
-				   STATUS_ICON_COLUMN, status,
-				   STATUS_ICON_VISIBLE_COLUMN, TRUE,
-				   NAME_COLUMN, mark,
-				   WARNING_COLUMN, warning,
-				   IDLE_COLUMN, idle,
-				   BUDDY_ICON_COLUMN, avatar,
-				   NODE_COLUMN, node,
-				   -1);
-
-		if (blist_options & OPT_BLIST_POPUP &&
-				((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) {
-			gtk_widget_show(gtkblist->window);
-			gtk_window_deiconify(GTK_WINDOW(gtkblist->window));
-			gdk_window_raise(gtkblist->window->window);
-		}
-
-		g_free(mark);
-		if (idle)
-			g_free(idle);
-		if (warning)
-			g_free(warning);
-
-		if (status != NULL)
-			g_object_unref(status);
-
-		if (avatar != NULL)
-			g_object_unref(avatar);
-
-	} else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) {
-		gaim_gtk_blist_remove(list, node);
-	}
-	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
-
-
-	if(expand) {
-		gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE);
-		gtk_tree_path_free(expand);
-	}
-
-	if(GAIM_BLIST_NODE_IS_BUDDY(node))
-		gaim_gtk_blist_update(list, node->parent);
-}
-
-static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list)
-{
-	if (!gtkblist)
-		return;
-
-	gtk_widget_destroy(gtkblist->window);
-	gtk_object_sink(GTK_OBJECT(gtkblist->tooltips));
-
-	if (gtkblist->refresh_timer)
-		g_source_remove(gtkblist->refresh_timer);
-	if (gtkblist->timeout)
-		g_source_remove(gtkblist->timeout);
-
-	gtkblist->refresh_timer = 0;
-	gtkblist->timeout = 0;
-	gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
-	gtkblist->treemodel = NULL;
-	gtkblist->idle_column = NULL;
-	gtkblist->warning_column = gtkblist->buddy_icon_column = NULL;
-	gtkblist->bbox = gtkblist->tipwindow = NULL;
-	protomenu = NULL;
-	awaymenu = NULL;
-	gtkblist = NULL;
-}
-
-static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show)
-{
-	if (!(gtkblist && gtkblist->window))
-		return;
-
-	if (show) {
-		gaim_gtk_blist_restore_position();
-		gtk_window_present(GTK_WINDOW(gtkblist->window));
-	} else {
-		if (!connections || docklet_count) {
-#ifdef _WIN32
-			wgaim_systray_minimize(gtkblist->window);
-#endif
-			gtk_widget_hide(gtkblist->window);
-		} else {
-			gtk_window_iconify(GTK_WINDOW(gtkblist->window));
-		}
-	}
-}
-
-void gaim_gtk_blist_docklet_toggle() {
-	/* Useful for the docklet plugin and also for the win32 tray icon*/
-	/* This is called when one of those is clicked--it will show/hide the 
-	   buddy list/login window--depending on which is active */
-	if (connections) {
-		if (gtkblist && gtkblist->window) {
-			if (GTK_WIDGET_VISIBLE(gtkblist->window)) {
-				gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured);
-			} else {
-#if _WIN32
-				wgaim_systray_maximize(gtkblist->window);
-#endif
-				gaim_blist_set_visible(TRUE);
-			}
-		} else {
-			/* we're logging in or something... do nothing */
-			/* or should I make the blist? */
-			gaim_debug(GAIM_DEBUG_WARNING, "blist",
-					   "docklet_toggle called with connections "
-					   "but no blist!\n");
-		}
-	} else if (mainwindow) {
-		if (GTK_WIDGET_VISIBLE(mainwindow)) {
-			if (GAIM_WINDOW_ICONIFIED(mainwindow)) {
-				gtk_window_present(GTK_WINDOW(mainwindow));
-			} else {
-#if _WIN32
-				wgaim_systray_minimize(mainwindow);
-#endif
-				gtk_widget_hide(mainwindow);
-			}
-		} else {
-#if _WIN32
-			wgaim_systray_maximize(mainwindow);
-#endif
-			show_login();
-		}
-	} else {
-		show_login();
-	}
-}
-
-void gaim_gtk_blist_docklet_add()
-{
-	docklet_count++;
-}
-
-void gaim_gtk_blist_docklet_remove()
-{
-	docklet_count--;
-	if (!docklet_count) {
-		if (connections)
-			gaim_blist_set_visible(TRUE);
-		else if (mainwindow)
-			gtk_window_present(GTK_WINDOW(mainwindow));
-		else
-			show_login();
-	}
-}
-
-static struct gaim_blist_ui_ops blist_ui_ops =
-{
-	gaim_gtk_blist_new_list,
-	NULL,
-	gaim_gtk_blist_show,
-	gaim_gtk_blist_update,
-	gaim_gtk_blist_remove,
-	gaim_gtk_blist_destroy,
-	gaim_gtk_blist_set_visible
-};
-
-
-struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops()
-{
-	return &blist_ui_ops;
-}
-
-
-
-/*********************************************************************
- * Public utility functions                                          *
- *********************************************************************/
-
-GdkPixbuf *
-create_prpl_icon(struct gaim_account *account)
-{
-	GaimPlugin *prpl;
-	GaimPluginProtocolInfo *prpl_info = NULL;
-	GdkPixbuf *status = NULL;
-	char *filename = NULL;
-	const char *protoname = NULL;
-	char buf[256];
-
-	prpl = gaim_find_prpl(account->protocol);
-
-	if (prpl != NULL) {
-		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
-
-		if (prpl_info->list_icon != NULL)
-			protoname = prpl_info->list_icon(account, NULL);
-	}
-
-	if (protoname == NULL)
-		return NULL;
-
-	/*
-	 * Status icons will be themeable too, and then it will look up
-	 * protoname from the theme
-	 */
-	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
-
-	filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
-								"default", buf, NULL);
-	status = gdk_pixbuf_new_from_file(filename, NULL);
-	g_free(filename);
-
-	return status;
-}
-
--- a/src/dialogs.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/dialogs.c	Sat Apr 26 17:56:23 2003 +0000
@@ -48,7 +48,7 @@
 #include "gaim.h"
 #include "gtkimhtml.h"
 #include "prpl.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 
 #ifdef _WIN32
 #include "win32dep.h"
--- a/src/gaim.h	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/gaim.h	Sat Apr 26 17:56:23 2003 +0000
@@ -27,7 +27,7 @@
 #endif
 
 #include "core.h"
-#include "list.h"
+#include "blist.h"
 #include "ui.h"
 #include "util.h"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkblist.c	Sat Apr 26 17:56:23 2003 +0000
@@ -0,0 +1,1824 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef GAIM_PLUGINS
+#ifndef _WIN32
+#include <dlfcn.h>
+#endif
+#endif /* GAIM_PLUGINS */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+#include <gdk/gdkwin32.h>
+#else
+#include <unistd.h>
+#include <gdk/gdkx.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include "prpl.h"
+#include "sound.h"
+#include "gaim.h"
+#include "gtkblist.h"
+#include "gtkpounce.h"
+#include "gtkft.h"
+
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+static struct gaim_gtk_buddy_list *gtkblist = NULL;
+
+/* part of the best damn Docklet code this side of Tahiti */
+static gboolean gaim_gtk_blist_obscured = FALSE;
+
+static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data);
+static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node);
+static char *gaim_get_tooltip_text(struct buddy *b);
+static char *item_factory_translate_func (const char *path, gpointer func_data);
+
+/***************************************************
+ *              Callbacks                          *
+ ***************************************************/
+
+static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
+{
+	if (docklet_count)
+		gaim_blist_set_visible(FALSE);
+	else
+		do_quit();
+
+	/* we handle everything, event should not propogate further */
+	return TRUE;
+}
+
+static gboolean gtk_blist_save_prefs_cb(gpointer data)
+{
+	save_prefs();
+
+	/* only run once */
+	return FALSE;
+}
+
+static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data)
+{
+	/* unfortunately GdkEventConfigure ignores the window gravity, but  *
+	 * the only way we have of setting the position doesn't. we have to *
+	 * call get_position and get_size because they do pay attention to  *
+	 * the gravity. this is inefficient and I agree it sucks, but it's  *
+	 * more likely to work correctly.                        - Robot101 */
+	gint x, y;
+
+	/* check for visibility because when we aren't visible, this will   *
+	 * give us bogus (0,0) coordinates.                      - xOr      */
+	if (GTK_WIDGET_VISIBLE(w)) {
+		gtk_window_get_position(GTK_WINDOW(w), &x, &y);
+
+		if (x != blist_pos.x ||
+		    y != blist_pos.y ||
+		    event->width != blist_pos.width ||
+		    event->height != blist_pos.height) {
+			blist_pos.x = x;
+			blist_pos.y = y;
+			blist_pos.width = event->width;
+			blist_pos.height = event->height;
+
+			if (!g_main_context_find_source_by_user_data(NULL, &gtk_blist_save_prefs_cb)) {
+				g_timeout_add(5000, gtk_blist_save_prefs_cb, &gtk_blist_save_prefs_cb);
+			}
+		}
+	}
+
+	/* continue to handle event normally */
+	return FALSE;
+}
+
+static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data)
+{
+	if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
+		gaim_gtk_blist_obscured = TRUE;
+	else
+		gaim_gtk_blist_obscured = FALSE;
+
+	/* continue to handle event normally */
+	return FALSE;
+}
+
+static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b)
+{
+	serv_get_info(b->account->gc, b->name);
+}
+
+static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b)
+{
+       gaim_conversation_new(GAIM_CONV_IM, b->account, b->name);
+}
+
+static void gtk_blist_menu_alias_cb(GtkWidget *w, struct buddy *b)
+{
+       alias_dialog_bud(b);
+}
+
+static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b)
+{
+	gaim_gtkpounce_dialog_show(b, NULL);
+}
+
+static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b)
+{
+       show_log(b->name);
+}
+
+static void gtk_blist_show_systemlog_cb()
+{
+       show_log(NULL);
+}
+
+static void gtk_blist_show_onlinehelp_cb()
+{
+       open_url(NULL, WEBSITE "documentation.php");
+}
+
+static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model = gtk_tree_view_get_model(tv);
+	GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
+
+	if(gtk_tree_selection_get_selected(sel, &model, &iter)){
+		GaimBlistNode *node;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
+		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name);
+			return;
+		}
+	}
+	show_im_dialog();
+}
+
+static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model = gtk_tree_view_get_model(tv);
+	GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
+
+	if(gtk_tree_selection_get_selected(sel, &model, &iter)){
+		GaimBlistNode *node;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
+		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
+			return;
+		}
+	}
+	show_info_dialog();
+}
+
+static void gtk_blist_button_chat_cb(GtkWidget *w, gpointer data)
+{
+	/* FIXME: someday, we can check to see if we've selected a chat node */
+	join_chat();
+}
+
+static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data)
+{
+	gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
+}
+
+static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
+	GaimBlistNode *node;
+	GValue val = {0,};
+
+	gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val);
+
+	node = g_value_get_pointer(&val);
+
+	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		gaim_group_set_setting((struct group *)node, "collapsed", NULL);
+		gaim_blist_save();
+	}
+}
+
+static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
+	GaimBlistNode *node;
+	GValue val = {0,};
+
+	gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val);
+
+	node = g_value_get_pointer(&val);
+
+	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		gaim_group_set_setting((struct group *)node, "collapsed", "true");
+		gaim_blist_save();
+	}
+}
+
+static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) {
+	GaimBlistNode *node;
+	GtkTreeIter iter;
+	GValue val = { 0, };
+
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+
+	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+	node = g_value_get_pointer(&val);
+
+	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		struct gaim_conversation *conv =
+			gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name);
+		if(conv) {
+			gaim_window_raise(gaim_conversation_get_window(conv));
+			gaim_window_switch_conversation(
+				gaim_conversation_get_window(conv),
+				gaim_conversation_get_index(conv));
+		}
+	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		if (gtk_tree_view_row_expanded(tv, path))
+			gtk_tree_view_collapse_row(tv, path);
+		else
+			gtk_tree_view_expand_row(tv,path,FALSE);
+	}
+}
+
+static void gaim_gtk_blist_add_buddy_cb()
+{
+	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
+	GtkTreeIter iter;
+	GaimBlistNode *node;
+
+	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
+		if (GAIM_BLIST_NODE_IS_BUDDY(node)) 
+			show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL);
+		else if (GAIM_BLIST_NODE_IS_GROUP(node))
+			show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL);
+	}
+	else {
+		show_add_buddy(NULL, NULL, NULL, NULL);
+	}
+}
+
+static void
+gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) 
+{
+	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		struct buddy *b = (struct buddy*)node;
+		show_confirm_del(b->account->gc, b->name);
+	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		struct group *g = (struct group*)node;
+		show_confirm_del_group(g);
+	}
+}
+
+static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b)
+{
+	struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
+	if (pbm->callback)
+		pbm->callback(pbm->gc, b->name);
+}
+
+static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null)
+{
+	GtkTreePath *path;
+	GaimBlistNode *node;
+	GValue val = { 0, };
+	GtkTreeIter iter;
+	GtkWidget *menu, *menuitem;
+	GtkTreeSelection *sel;
+	GList *list;
+	GaimPlugin *prpl = NULL;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (event->button != 3)
+		return FALSE;
+
+	/* Here we figure out which node was clicked */
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL))
+		return FALSE;
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+	node = g_value_get_pointer(&val);
+	menu = gtk_menu_new();
+
+	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		gaim_new_item_from_stock(menu, _("_Add a Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL);
+		gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL);
+		gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL);
+	} else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		/* Protocol specific options */
+		prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol);
+
+		if (prpl != NULL)
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl && prpl_info->get_info)
+			gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL);
+
+		gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL);
+		gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL);
+		gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
+
+		if (prpl) {
+			list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
+			while (list) {
+				struct proto_buddy_menu *pbm = list->data;
+				menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
+				g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
+				g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node);
+				gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+				list = list->next;
+			}
+		}
+
+		gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name);
+
+		gaim_separator(menu);
+		gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
+		gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL);
+	}
+	
+	gtk_widget_show_all(menu);
+	
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
+
+#if (1)  /* This code only exists because GTK doesn't work.  If we return FALSE here, as would be normal
+	  * the event propoagates down and somehow gets interpreted as the start of a drag event. */
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+	gtk_tree_selection_select_path(sel, path);
+	gtk_tree_path_free(path);
+	return TRUE;
+#endif
+}
+
+static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item)
+{
+	if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)))
+		blist_options &= ~OPT_BLIST_NO_MT_GRP;
+	else
+		blist_options |= OPT_BLIST_NO_MT_GRP;
+	save_prefs();
+	gaim_gtk_blist_refresh(gaim_get_blist());
+}
+
+static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action,
+		GtkWidget *checkitem) {
+	if(gtkblist->window->window) {
+		GdkCursor *cursor = gdk_cursor_new(GDK_WATCH);
+		gdk_window_set_cursor(gtkblist->window->window, cursor);
+		while (gtk_events_pending())
+			gtk_main_iteration();
+		gdk_cursor_unref(cursor);
+	}
+
+	if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem)))
+		blist_options |= OPT_BLIST_SHOW_OFFLINE;
+	else
+		blist_options &= ~OPT_BLIST_SHOW_OFFLINE;
+	save_prefs();
+
+	if(gtkblist->window->window) {
+		GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR);
+		gdk_window_set_cursor(gtkblist->window->window, cursor);
+		gdk_cursor_unref(cursor);
+	}
+
+	gaim_gtk_blist_refresh(gaim_get_blist());
+}
+
+static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget,
+					     GdkDragContext *dc,
+					     GtkSelectionData *data,
+					     guint info,
+					     guint time,
+					     gpointer *null)
+{
+	if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) {
+		GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row");
+		GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref);
+		GtkTreeIter iter;
+		GaimBlistNode *node = NULL;
+		GValue val = {0};
+		gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow);
+		gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+		node = g_value_get_pointer(&val);
+		gtk_selection_data_set (data,
+					gdk_atom_intern ("GAIM_BLIST_NODE", FALSE),
+					8, /* bits */
+					(void*)&node,
+					sizeof (node));
+		
+		gtk_tree_path_free(sourcerow);
+	}
+	
+}
+
+static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
+			  GtkSelectionData *sd, guint info, guint t)
+{	
+	if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) {
+		GaimBlistNode *n = NULL;
+		GtkTreePath *path = NULL;
+		GtkTreeViewDropPosition position;
+		memcpy(&n, sd->data, sizeof(n));
+		if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) {
+			/* if we're here, I think it means the drop is ok */
+			GtkTreeIter iter;
+			GaimBlistNode *node;
+			GValue val = {0};
+			gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+			gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+			node = g_value_get_pointer(&val);
+
+			if (GAIM_BLIST_NODE_IS_BUDDY(n)) {
+				struct buddy *b = (struct buddy*)n;
+				if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+					switch(position) {
+						case GTK_TREE_VIEW_DROP_AFTER:
+						case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
+							gaim_blist_add_buddy(b, (struct group*)node->parent, node);
+							break;
+						case GTK_TREE_VIEW_DROP_BEFORE:
+						case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+							gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev);
+							break;
+					}
+				} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+					gaim_blist_add_buddy(b, (struct group*)node, NULL);
+				}
+			} else if (GAIM_BLIST_NODE_IS_GROUP(n)) {
+				struct group *g = (struct group*)n;
+				if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+					switch (position) {
+					case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
+					case GTK_TREE_VIEW_DROP_AFTER:
+						gaim_blist_add_group(g, node);
+						break;
+					case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+					case GTK_TREE_VIEW_DROP_BEFORE:
+						gaim_blist_add_group(g, node->prev);
+						break;
+					}
+
+				} else if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+					gaim_blist_add_group(g, node->parent);
+				}
+
+			}
+
+			gtk_tree_path_free(path);
+			gaim_blist_save();
+		}
+	}
+}
+
+static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, struct buddy *b)
+{
+	GtkStyle *style;
+	GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(b, GAIM_STATUS_ICON_LARGE);
+	PangoLayout *layout;
+	char *tooltiptext = gaim_get_tooltip_text(b);
+
+	layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL);
+	pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext));
+	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+	pango_layout_set_width(layout, 300000);
+	style = gtkblist->tipwindow->style;
+
+	gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+			    NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1);
+
+#if GTK_CHECK_VERSION(2,2,0)
+	gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf,
+			0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
+#else
+	gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+#endif
+
+	gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE,
+			  NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout);
+
+	g_object_unref (pixbuf);
+	g_object_unref (layout);
+	g_free(tooltiptext);
+	return;
+}
+
+static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GaimBlistNode *node;
+	GValue val = {0};
+
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL))
+		return FALSE;
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+	node = g_value_get_pointer(&val);
+	
+	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		int scr_w,scr_h, w, h, x, y;
+		PangoLayout *layout;
+		struct buddy *buddy = (struct buddy*)node;
+		char *tooltiptext = gaim_get_tooltip_text(buddy);
+		gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
+		gtkblist->tipwindow->parent = tv;
+		gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE);
+		gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE);
+		gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips");
+		g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", 
+				 G_CALLBACK(gaim_gtk_blist_paint_tip), buddy);
+		gtk_widget_ensure_style (gtkblist->tipwindow);
+
+		layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL);
+		pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+		pango_layout_set_width(layout, 300000);
+		pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext));
+		scr_w = gdk_screen_width();
+		scr_h = gdk_screen_height();
+		pango_layout_get_size (layout, &w, &h);
+		w = PANGO_PIXELS(w) + 8;
+		h = PANGO_PIXELS(h) + 8;
+
+		/* 38 is the size of a large status icon plus 4 pixels padding on each side.
+		   I should #define this or something */
+		w = w + 38;
+		h = MAX(h, 38);
+
+		gdk_window_get_pointer(NULL, &x, &y, NULL);
+		if (GTK_WIDGET_NO_WINDOW(gtkblist->window))
+			y+=gtkblist->window->allocation.y;
+	
+		x -= ((w >> 1) + 4);
+		
+		if ((x + w) > scr_w)
+			x -= (x + w) - scr_w;
+		else if (x < 0)
+			x = 0;
+
+		if ((y + h + 4) > scr_h)
+			y = y - h;
+		else
+			y = y + 6;
+		g_object_unref (layout);
+		g_free(tooltiptext);
+		gtk_widget_set_size_request(gtkblist->tipwindow, w, h);
+		gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
+		gtk_widget_show(gtkblist->tipwindow);
+	}
+
+	gtk_tree_path_free(path);
+	return FALSE;
+}
+
+static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
+{
+	GtkTreePath *path;
+	if (gtkblist->timeout) {
+		if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y))
+			return FALSE;
+		/* We've left the cell.  Remove the timeout and create a new one below */
+		if (gtkblist->tipwindow) {
+			gtk_widget_destroy(gtkblist->tipwindow);
+			gtkblist->tipwindow = NULL;
+		}
+		
+		g_source_remove(gtkblist->timeout);
+	}
+	
+	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
+	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->rect);
+	if (path)
+		gtk_tree_path_free(path);
+	gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv);
+	return FALSE;
+}
+
+static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n)
+{
+	if (gtkblist->timeout) {
+		g_source_remove(gtkblist->timeout);
+		gtkblist->timeout = 0;
+	}
+	if (gtkblist->tipwindow) {
+		gtk_widget_destroy(gtkblist->tipwindow);
+		gtkblist->tipwindow = NULL;
+	}
+}
+
+static void
+toggle_debug(void)
+{
+	misc_options ^= OPT_MISC_DEBUG;
+
+	if ((misc_options & OPT_MISC_DEBUG))
+		gaim_gtk_debug_window_show();
+	else
+		gaim_gtk_debug_window_hide();
+
+	save_prefs();
+}
+
+
+/***************************************************
+ *            Crap                                 *
+ ***************************************************/
+static GtkItemFactoryEntry blist_menu[] =
+{
+	/* Buddies menu */
+	{ N_("/_Buddies"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0, "<StockItem>", GAIM_STOCK_IM },
+	{ N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, "<StockItem>", GAIM_STOCK_CHAT },
+	{ N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0, "<StockItem>", GAIM_STOCK_INFO },
+	{ "/Buddies/sep1", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Buddies/_Show Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, "<CheckItem>"},
+	{ N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"},
+	{ N_("/Buddies/_Add a Buddy..."), NULL, gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, 
+	{ N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL},
+	{ "/Buddies/sep2", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Buddies/_Signoff"), "<CTL>D", signoff_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF },
+	{ N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0, "<StockItem>", GTK_STOCK_QUIT },
+
+	/* Tools */ 
+	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" },
+	{ "/Tools/sep1", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Tools/A_ccounts..."), "<CTL>A", account_editor, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS },
+	{ N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER },
+	{ N_("/Tools/Preferences..."), "<CTL>P", show_prefs, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
+	{ N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "<StockItem>", GAIM_STOCK_PRIVACY },
+	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL },
+
+	/* Help */
+	{ N_("/_Help"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
+	{ N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL },
+	{ N_("/Help/_About..."), NULL, show_about, 0,  "<StockItem>", GAIM_STOCK_ABOUT },
+};
+
+/*********************************************************
+ * Private Utility functions                             *
+ *********************************************************/
+
+static char *gaim_get_tooltip_text(struct buddy *b)
+{
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+	char *text = NULL;
+	char *statustext = NULL;
+	char *aliastext = NULL, *nicktext = NULL;
+	char *warning = NULL, *idletime = NULL;
+
+	prpl = gaim_find_prpl(b->account->protocol);
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->tooltip_text) {
+		const char *end;
+		statustext = prpl_info->tooltip_text(b);
+
+		if(statustext && !g_utf8_validate(statustext, -1, &end)) {
+			char *new = g_strndup(statustext,
+					g_utf8_pointer_to_offset(statustext, end));
+			g_free(statustext);
+			statustext = new;
+		}
+	}
+
+	if (!statustext && !GAIM_BUDDY_IS_ONLINE(b))
+		statustext = g_strdup(_("<b>Status:</b> Offline"));
+
+	if (b->idle > 0) {
+		int ihrs, imin;
+		time_t t;
+		time(&t);
+		ihrs = (t - b->idle) / 3600;
+		imin = ((t - b->idle) / 60) % 60;
+		if (ihrs)
+			idletime = g_strdup_printf(_("%dh%02dm"), ihrs, imin);
+		else
+			idletime = g_strdup_printf(_("%dm"), imin);
+	}
+
+	if(b->alias && b->alias[0])
+		aliastext = g_markup_escape_text(b->alias, -1);
+
+	if(b->server_alias)
+		nicktext = g_markup_escape_text(b->server_alias, -1);
+
+	if (b->evil > 0)
+		warning = g_strdup_printf(_("%d%%"), b->evil);
+
+	text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>"
+			       "%s %s"  /* Alias */
+			       "%s %s"  /* Nickname */
+			       "%s %s"     /* Idle */
+			       "%s %s"     /* Warning */
+			       "%s%s"     /* Status */
+				   "%s",
+			       b->name,
+			       aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "",
+			       nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "",
+			       idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "",
+			       b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "",
+			       statustext ? "\n" : "", statustext ? statustext : "",
+				   !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : "");
+	
+	if(warning)
+		g_free(warning);
+	if(idletime)
+		g_free(idletime);
+	if(statustext)
+		g_free(statustext);
+	if(nicktext)
+		g_free(nicktext);
+	if(aliastext)
+		g_free(aliastext);
+
+	return text;
+
+}
+
+GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size)
+{
+	GdkPixbuf *status = NULL;
+	GdkPixbuf *scale = NULL;
+	GdkPixbuf *emblem = NULL;
+	gchar *filename = NULL;
+	const char *protoname = NULL;
+
+	char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL;
+
+	int scalesize = 30;
+
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	prpl = gaim_find_prpl(b->account->protocol);
+
+	if (!prpl) 
+		return NULL;
+
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->list_icon)
+		protoname = prpl_info->list_icon(b->account, b);
+	if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl_info->list_emblems)
+		prpl_info->list_emblems(b, &se, &sw, &nw, &ne);
+
+	if (size == GAIM_STATUS_ICON_SMALL) {
+		scalesize = 15;
+		sw = nw = ne = NULL; /* So that only the se icon will composite */
+	}
+
+
+	if (b->present == GAIM_BUDDY_SIGNING_ON) {
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL);
+		status = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+	} else if (b->present == GAIM_BUDDY_SIGNING_OFF) {
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL);
+		status = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+
+		/* "Hey, what's all this crap?" you ask.  Status icons will be themeable too, and
+		   then it will look up protoname from the theme */
+	} else {
+		char *image = g_strdup_printf("%s.png", protoname);
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
+		status = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(image);
+		g_free(filename);
+
+	}
+
+	if (!status)
+		return NULL;
+
+	scale =  gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR);
+
+	g_object_unref(G_OBJECT(status));
+
+	/* Emblems */
+
+	/* Each protocol can specify up to four "emblems" to composite over the base icon.  "away", "busy", "mobile user"
+	 * are all examples of states represented by emblems.  I'm not even really sure I like this yet. */
+
+	/* XXX Clean this crap up, yo. */
+	if (se) {
+		char *image = g_strdup_printf("%s.png", se);
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
+		g_free(image);
+		emblem = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+		if (emblem) {
+			if (size == GAIM_STATUS_ICON_LARGE)
+				gdk_pixbuf_composite (emblem,
+						      scale, 15, 15,
+						      15, 15,
+						      15, 15,
+						      1, 1,
+						      GDK_INTERP_BILINEAR,
+						      255);
+			else
+					gdk_pixbuf_composite (emblem,
+						      scale, 5, 5,
+						      10, 10,
+						      5, 5,
+						      .6, .6,
+						      GDK_INTERP_BILINEAR,
+						      255);
+			g_object_unref(G_OBJECT(emblem));
+		}
+	}
+	if (sw) {
+		char *image = g_strdup_printf("%s.png", sw);
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
+		g_free(image);
+		emblem = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+		if (emblem) {
+			gdk_pixbuf_composite (emblem,
+					scale, 0, 15,
+					15, 15,
+					0, 15,
+					1, 1,
+					GDK_INTERP_BILINEAR,
+					255);
+			g_object_unref(G_OBJECT(emblem));
+		}
+	}
+	if (nw) {
+		char *image = g_strdup_printf("%s.png", nw);
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
+		g_free(image);
+		emblem = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+		if (emblem) {
+			gdk_pixbuf_composite (emblem,
+					      scale, 0, 0,
+					      15, 15,
+					      0, 0,
+					      1, 1,
+					      GDK_INTERP_BILINEAR,
+					      255);
+			g_object_unref(G_OBJECT(emblem));
+		}
+	}
+	if (ne) {
+		char *image = g_strdup_printf("%s.png", ne);
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
+		g_free(image);
+		emblem = gdk_pixbuf_new_from_file(filename,NULL);
+		g_free(filename);
+		if (emblem) {
+			gdk_pixbuf_composite (emblem,
+					      scale, 15, 0,
+					      15, 15,
+					      15, 0,
+					      1, 1,
+					      GDK_INTERP_BILINEAR,
+					      255);
+			g_object_unref(G_OBJECT(emblem));
+		}
+	}
+
+
+	/* Idle grey buddies affects the whole row.  This converts the status icon to greyscale. */
+	if (b->present == GAIM_BUDDY_OFFLINE)
+		gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
+	else if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS)
+		gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE);
+	return scale;
+}
+
+static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b)
+{
+	/* This just opens a file from ~/.gaim/icons/screenname.  This needs to change to be more gooder. */
+	char *file;
+	GdkPixbuf *buf, *ret;
+
+	if (!(blist_options & OPT_BLIST_SHOW_ICONS))
+		return NULL;
+
+	if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL)
+		return NULL;
+
+	buf = gdk_pixbuf_new_from_file(file, NULL);
+	g_free(file);
+
+
+	if (buf) {
+		if (!GAIM_BUDDY_IS_ONLINE(b))
+			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE);
+		if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS)
+			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE);
+
+		ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR);
+		g_object_unref(G_OBJECT(buf));
+		return ret;
+	}
+	return NULL;
+}
+
+static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected)
+{
+	char *name = gaim_get_buddy_alias(b);
+	char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL;
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+	/* XXX Clean up this crap */
+
+	int ihrs, imin;
+	char *idletime = NULL, *warning = NULL, *statustext = NULL;
+	time_t t;
+
+	prpl = gaim_find_prpl(b->account->protocol);
+
+	if (prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
+		if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) {
+			text =  g_strdup_printf("<span color='dim grey'>%s</span>",
+						esc);
+			g_free(esc);
+			return text;
+		} else {
+			return esc;
+		}
+	}
+
+	time(&t);
+	ihrs = (t - b->idle) / 3600;
+	imin = ((t - b->idle) / 60) % 60;
+
+	if (prpl && prpl_info->status_text) {
+		char *tmp = prpl_info->status_text(b);
+		const char *end;
+
+		if(tmp && !g_utf8_validate(tmp, -1, &end)) {
+			char *new = g_strndup(tmp,
+					g_utf8_pointer_to_offset(tmp, end));
+			g_free(tmp);
+			tmp = new;
+		}
+
+		if(tmp) {
+			char buf[32];
+			char *c = tmp;
+			int length = 0, vis=0;
+			gboolean inside = FALSE;
+			g_strdelimit(tmp, "\n", ' ');
+
+			while(*c && vis < 20) {
+				if(*c == '&')
+					inside = TRUE;
+				else if(*c == ';')
+					inside = FALSE;
+				if(!inside)
+					vis++;
+				length++;
+				c++; /* this is fun */
+			}
+
+			if(vis == 20)
+				g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
+			else
+				g_snprintf(buf, sizeof(buf), "%%s ");
+
+			statustext = g_strdup_printf(buf, tmp);
+
+			g_free(tmp);
+		}
+	}
+
+	if (b->idle > 0) {
+		if (ihrs)
+			idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin);
+		else
+			idletime = g_strdup_printf(_("Idle (%dm) "), imin);
+	}
+
+	if (b->evil > 0)
+		warning = g_strdup_printf(_("Warned (%d%%) "), b->evil);
+
+	if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext)
+		statustext = g_strdup("Offline ");
+
+	if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) {
+		text =  g_strdup_printf("<span color='dim grey'>%s</span>\n"
+					"<span color='dim grey' size='smaller'>%s%s%s</span>",
+					esc,
+					statustext != NULL ? statustext : "",
+					idletime != NULL ? idletime : "",
+					warning != NULL ? warning : "");
+	} else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) {
+		text = g_strdup(esc);
+	} else {
+		text = g_strdup_printf("%s\n"
+				       "<span %s size='smaller'>%s%s%s</span>", esc,
+				       selected ? "" : "color='dim grey'",
+				       statustext != NULL ? statustext :  "",
+				       idletime != NULL ? idletime : "", 
+				       warning != NULL ? warning : "");
+	}
+	if (idletime)
+		g_free(idletime);
+	if (warning)
+		g_free(warning);
+	if (statustext)
+		g_free(statustext);
+	if (esc)
+		g_free(esc);
+
+	return text;
+}
+
+static void gaim_gtk_blist_restore_position()
+{
+	/* if the window exists, is hidden, we're saving positions, and the position is sane... */
+	if(gtkblist && gtkblist->window &&
+	   !GTK_WIDGET_VISIBLE(gtkblist->window) &&
+	   blist_pos.width != 0) {
+		/* ...check position is on screen... */
+		if (blist_pos.x >= gdk_screen_width())
+			blist_pos.x = gdk_screen_width() - 100;
+		else if (blist_pos.x < 0)
+			blist_pos.x = 100;
+		
+		if (blist_pos.y >= gdk_screen_height())
+			blist_pos.y = gdk_screen_height() - 100;
+		else if (blist_pos.y < 0)
+			blist_pos.y = 100;
+		
+		/* ...and move it back. */
+		gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y);
+		gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height);
+	}
+}
+
+static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list)
+{
+	GaimBlistNode *group = list->root;
+	GaimBlistNode *buddy;
+
+	while (group) {
+		buddy = group->child;
+		while (buddy) {
+			if (((struct buddy *)buddy)->idle)
+				gaim_gtk_blist_update(list, buddy);
+			buddy = buddy->next;
+		}
+		group = group->next;
+	}
+
+	/* keep on going */
+	return TRUE;
+}
+
+/**********************************************************************************
+ * Public API Functions                                                           *
+ **********************************************************************************/
+static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist)
+{
+	blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1);
+}
+
+void gaim_gtk_blist_update_columns()
+{
+	if(!gtkblist)
+		return;
+
+	if (blist_options & OPT_BLIST_SHOW_ICONS) {
+		gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE);
+		gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE);
+		gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE);
+	} else {
+		gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME);
+		gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN);
+		gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE);
+	}
+}
+
+enum {DRAG_BUDDY, DRAG_ROW};
+
+static char *
+item_factory_translate_func (const char *path, gpointer func_data)
+{
+	return _(path);
+}
+
+static void gaim_gtk_blist_show(struct gaim_buddy_list *list)
+{
+	GtkItemFactory *ift;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *column;
+	GtkWidget *sw;
+	GtkWidget *button;
+	GtkSizeGroup *sg;
+	GtkAccelGroup *accel_group;
+	GtkTreeSelection *selection;
+	GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW},
+				{"application/x-im-contact", 0, DRAG_BUDDY}};
+
+	if (gtkblist && gtkblist->window) {
+		gtk_widget_show(gtkblist->window);
+		return;
+	}
+
+	gtkblist = GAIM_GTK_BLIST(list);
+
+	gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list");
+	gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));
+
+	GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
+
+	gtkblist->vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox);
+
+	g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL);
+	gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
+
+	/******************************* Menu bar *************************************/
+	accel_group = gtk_accel_group_new();
+	gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group);
+	g_object_unref(accel_group);
+	ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", accel_group);
+	gtk_item_factory_set_translate_func (ift,
+					     item_factory_translate_func,
+					     NULL, NULL);
+	gtk_item_factory_create_items(ift, sizeof(blist_menu) / sizeof(*blist_menu),
+				      blist_menu, NULL);
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(ift, "<GaimMain>"), FALSE, FALSE, 0);
+
+	awaymenu = gtk_item_factory_get_widget(ift, N_("/Tools/Away"));
+	do_away_menu();
+
+	gtkblist->bpmenu = gtk_item_factory_get_widget(ift, N_("/Tools/Buddy Pounce"));
+	gaim_gtkpounce_menu_build(gtkblist->bpmenu);
+
+	protomenu = gtk_item_factory_get_widget(ift, N_("/Tools/Protocol Actions"));
+	do_proto_menu();
+
+	/****************************** GtkTreeView **********************************/
+	sw = gtk_scrolled_window_new(NULL,NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+	gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING,
+						 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
+
+	gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
+	gtk_widget_set_size_request(gtkblist->treeview, -1, 200);
+
+	/* Set up selection stuff */
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
+	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL);
+
+
+	/* Set up dnd */
+	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte,
+					       2, GDK_ACTION_COPY);
+	gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2,
+					     GDK_ACTION_COPY | GDK_ACTION_MOVE);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL);
+
+	/* Tooltips */
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL);
+
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
+
+	column = gtk_tree_view_column_new ();
+
+	rend = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start (column, rend, FALSE);
+	gtk_tree_view_column_set_attributes (column, rend, 
+					     "pixbuf", STATUS_ICON_COLUMN,
+					     "visible", STATUS_ICON_VISIBLE_COLUMN,
+					     NULL);
+	g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL);
+
+	rend = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start (column, rend, TRUE);
+	gtk_tree_view_column_set_attributes (column, rend, 
+					     "markup", NAME_COLUMN,
+					     NULL);
+	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
+
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
+
+	rend = gtk_cell_renderer_text_new();
+	gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column);
+	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+
+	rend = gtk_cell_renderer_text_new();
+	gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column);
+	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+
+	rend = gtk_cell_renderer_pixbuf_new();
+	gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL);
+	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column);
+
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL);
+
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0);
+	gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
+	gaim_gtk_blist_update_columns();
+
+	/* set the Show Offline Buddies option. must be done
+	 * after the treeview or faceprint gets mad. -Robot101
+	 */
+	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Offline Buddies"))),
+			blist_options & OPT_BLIST_SHOW_OFFLINE);
+	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Empty Groups"))),
+			!(blist_options & OPT_BLIST_NO_MT_GRP));
+
+	/* OK... let's show this bad boy. */
+	gaim_gtk_blist_refresh(list);
+	gaim_gtk_blist_restore_position();
+	gtk_widget_show_all(gtkblist->window);
+
+	/**************************** Button Box **************************************/
+	/* add this afterwards so it doesn't force up the width of the window         */
+
+	gtkblist->tooltips = gtk_tooltips_new();
+
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
+	gtkblist->bbox = gtk_hbox_new(TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0);
+	gtk_widget_show(gtkblist->bbox);
+
+	button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL);
+	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	gtk_size_group_add_widget(sg, button);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb),
+			 gtkblist->treeview);
+	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL);
+	gtk_widget_show(button);
+
+	button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL);
+	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	gtk_size_group_add_widget(sg, button);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb),
+			 gtkblist->treeview);
+	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL);
+	gtk_widget_show(button);
+
+	button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL);
+	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	gtk_size_group_add_widget(sg, button);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), NULL);
+	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL);
+	gtk_widget_show(button);
+
+	button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL);
+	gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	gtk_size_group_add_widget(sg, button);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL);
+	gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL);
+	gtk_widget_show(button);
+
+	/* this will show the right image/label widgets for us */
+	gaim_gtk_blist_update_toolbar();
+
+	/* start the refresh timer */
+	gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list);
+}
+
+void gaim_gtk_blist_refresh(struct gaim_buddy_list *list)
+{
+	GaimBlistNode *group = list->root;
+	GaimBlistNode *buddy;
+
+	while (group) {
+		buddy = group->child;
+		gaim_gtk_blist_update(list, group);
+		while (buddy) {
+			gaim_gtk_blist_update(list, buddy);
+			buddy = buddy->next;
+		}
+		group = group->next;
+	}
+}
+
+static gboolean get_iter_from_node_helper(GaimBlistNode *node, GtkTreeIter *iter, GtkTreeIter *root) {
+
+	do {
+		GaimBlistNode *n;
+		GtkTreeIter child;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), root, NODE_COLUMN, &n, -1);
+		if(n == node) {
+			*iter = *root;
+			return TRUE;
+		}
+
+		if(gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &child, root)) {
+			if(get_iter_from_node_helper(node,iter,&child))
+				return TRUE;
+		}
+	} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), root));
+
+	return FALSE;
+}
+
+static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) {
+	GtkTreeIter root;
+
+	if (!gtkblist)
+		return FALSE;
+
+	if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkblist->treemodel), &root))
+		return FALSE;
+
+	return get_iter_from_node_helper(node, iter, &root);
+}
+
+/*
+ * These state assignments suck. I'm sorry. They're for historical reasons.
+ * Roll on new prefs. -Robot101
+ * 
+ * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image
+ * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text
+ * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images
+ * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none
+ */
+
+static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) {
+	if (GTK_IS_IMAGE(widget)) {
+		if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM)
+			gtk_widget_show(widget);
+		else
+			gtk_widget_hide(widget);
+	} else if (GTK_IS_LABEL(widget)) {
+		if (blist_options & OPT_BLIST_NO_BUTTON_TEXT)
+			gtk_widget_hide(widget);
+		else
+			gtk_widget_show(widget);
+	} else if (GTK_IS_CONTAINER(widget)) {
+		gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL);
+	}
+}
+
+void gaim_gtk_blist_update_toolbar() {
+	if (!gtkblist)
+		return;
+
+	if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM))
+		gtk_widget_hide(gtkblist->bbox);
+	else {
+		gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL);
+		gtk_widget_show(gtkblist->bbox);
+	}
+}
+
+static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node)
+{
+	GtkTreeIter iter;
+
+	/* For some reason, we're called before we have a buddy list sometimes */
+	if(!gtkblist)
+		return;
+
+	if(gtkblist->selected_node == node)
+		gtkblist->selected_node = NULL;
+
+	if (get_iter_from_node(node, &iter)) {
+		gtk_tree_store_remove(gtkblist->treemodel, &iter);
+		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			gaim_gtk_blist_update(list, node->parent);
+		}
+	}
+}
+
+static gboolean do_selection_changed(GaimBlistNode *new_selection)
+{
+	GaimBlistNode *old_selection = gtkblist->selected_node;
+
+	if(new_selection != gtkblist->selected_node) {
+		gtkblist->selected_node = new_selection;
+		if(new_selection)
+			gaim_gtk_blist_update(NULL, new_selection);
+		if(old_selection)
+			gaim_gtk_blist_update(NULL, old_selection);
+	}
+
+	return FALSE;
+}
+
+static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data)
+{
+	GaimBlistNode *new_selection = NULL;
+	GtkTreeIter iter;
+
+	if(gtk_tree_selection_get_selected(selection, NULL, &iter)){
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
+				NODE_COLUMN, &new_selection, -1);
+	}
+	/* we set this up as a timeout, otherwise the blist flickers */
+	g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
+}
+
+static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) {
+	GaimBlistNode *sibling;
+	GtkTreeIter siblingiter;
+	struct group *group = (struct group *)node;
+	char *esc = g_markup_escape_text(group->name, -1);
+	char *mark;
+
+	if(blist_options & OPT_BLIST_SHOW_GRPNUM)
+		mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE));
+	else
+		mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
+
+	g_free(esc);
+
+	sibling = node->prev;
+	while (sibling && !get_iter_from_node(sibling, &siblingiter)) {
+		sibling = sibling->prev;
+	}
+
+	gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL,
+			sibling ? &siblingiter : NULL);
+	gtk_tree_store_set(gtkblist->treemodel, iter,
+			STATUS_ICON_COLUMN, NULL,
+			STATUS_ICON_VISIBLE_COLUMN, FALSE,
+			NAME_COLUMN, mark,
+			NODE_COLUMN, node,
+			-1);
+	g_free(mark);
+}
+
+
+static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node)
+{
+	GtkTreeIter iter;
+	GtkTreePath *expand = NULL;
+	gboolean new_entry = FALSE;
+
+	if (!gtkblist)
+		return;
+
+	if (!get_iter_from_node(node, &iter)) { /* This is a newly added node */
+		new_entry = TRUE;
+		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+			if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) {
+				GtkTreeIter groupiter;
+				GaimBlistNode *oldersibling;
+				GtkTreeIter oldersiblingiter;
+				char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed");
+
+				if(node->parent &&
+						!get_iter_from_node(node->parent, &groupiter)) {
+					/* This buddy's group has not yet been added.
+					 * We do that here */
+					make_a_group(node->parent, &groupiter);
+				}
+				if(!collapsed)
+					expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter);
+				else
+					g_free(collapsed);
+
+				oldersibling = node->prev;
+				while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) {
+					oldersibling = oldersibling->prev;
+				}
+
+				gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL);
+
+				if (blist_options & OPT_BLIST_POPUP) {
+					gtk_widget_show(gtkblist->window);
+					gtk_window_deiconify(GTK_WINDOW(gtkblist->window));
+					gdk_window_raise(gtkblist->window->window);
+				}
+
+			}
+		}
+		else if (GAIM_BLIST_NODE_IS_GROUP(node) &&
+					((blist_options & OPT_BLIST_SHOW_OFFLINE) ||
+					!(blist_options & OPT_BLIST_NO_MT_GRP))) {
+			make_a_group(node, &iter);
+			expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
+		}
+	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) {
+			gtk_tree_store_remove(gtkblist->treemodel, &iter);
+		} else {
+			struct group *group = (struct group *)node;
+			char *esc = g_markup_escape_text(group->name, -1);
+			char *mark;
+
+			if(blist_options & OPT_BLIST_SHOW_GRPNUM)
+				mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE));
+			else
+				mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
+
+			g_free(esc);
+			gtk_tree_store_set(gtkblist->treemodel, &iter,
+					NAME_COLUMN, mark,
+					-1);
+			g_free(mark);
+		}
+	}
+
+	if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) {
+		GdkPixbuf *status, *avatar;
+		char *mark;
+		char *warning = NULL, *idle = NULL;
+
+		gboolean selected = (gtkblist->selected_node == node);
+
+		status = gaim_gtk_blist_get_status_icon((struct buddy*)node,
+							blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL);
+		avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node);
+		mark   = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected);
+
+		if (((struct buddy*)node)->idle > 0) {
+			time_t t;
+			int ihrs, imin;
+			time(&t);
+			ihrs = (t - ((struct buddy *)node)->idle) / 3600;
+			imin = ((t - ((struct buddy*)node)->idle) / 60) % 60;
+			if(ihrs > 0)
+				idle = g_strdup_printf("(%d:%02d)", ihrs, imin);
+			else
+				idle = g_strdup_printf("(%d)", imin);
+		}
+
+		if (((struct buddy*)node)->evil > 0)
+			warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil);
+
+
+		if((blist_options & OPT_BLIST_GREY_IDLERS)
+				&& ((struct buddy *)node)->idle) {
+			if(warning && !selected) {
+				char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>",
+						warning);
+				g_free(warning);
+				warning = w2;
+			}
+
+			if(idle && !selected) {
+				char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>",
+						idle);
+				g_free(idle);
+				idle = i2;
+			}
+		}
+
+		gtk_tree_store_set(gtkblist->treemodel, &iter,
+				   STATUS_ICON_COLUMN, status,
+				   STATUS_ICON_VISIBLE_COLUMN, TRUE,
+				   NAME_COLUMN, mark,
+				   WARNING_COLUMN, warning,
+				   IDLE_COLUMN, idle,
+				   BUDDY_ICON_COLUMN, avatar,
+				   NODE_COLUMN, node,
+				   -1);
+
+		if (blist_options & OPT_BLIST_POPUP &&
+				((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) {
+			gtk_widget_show(gtkblist->window);
+			gtk_window_deiconify(GTK_WINDOW(gtkblist->window));
+			gdk_window_raise(gtkblist->window->window);
+		}
+
+		g_free(mark);
+		if (idle)
+			g_free(idle);
+		if (warning)
+			g_free(warning);
+
+		if (status != NULL)
+			g_object_unref(status);
+
+		if (avatar != NULL)
+			g_object_unref(avatar);
+
+	} else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) {
+		gaim_gtk_blist_remove(list, node);
+	}
+	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
+
+
+	if(expand) {
+		gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE);
+		gtk_tree_path_free(expand);
+	}
+
+	if(GAIM_BLIST_NODE_IS_BUDDY(node))
+		gaim_gtk_blist_update(list, node->parent);
+}
+
+static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list)
+{
+	if (!gtkblist)
+		return;
+
+	gtk_widget_destroy(gtkblist->window);
+	gtk_object_sink(GTK_OBJECT(gtkblist->tooltips));
+
+	if (gtkblist->refresh_timer)
+		g_source_remove(gtkblist->refresh_timer);
+	if (gtkblist->timeout)
+		g_source_remove(gtkblist->timeout);
+
+	gtkblist->refresh_timer = 0;
+	gtkblist->timeout = 0;
+	gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
+	gtkblist->treemodel = NULL;
+	gtkblist->idle_column = NULL;
+	gtkblist->warning_column = gtkblist->buddy_icon_column = NULL;
+	gtkblist->bbox = gtkblist->tipwindow = NULL;
+	protomenu = NULL;
+	awaymenu = NULL;
+	gtkblist = NULL;
+}
+
+static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show)
+{
+	if (!(gtkblist && gtkblist->window))
+		return;
+
+	if (show) {
+		gaim_gtk_blist_restore_position();
+		gtk_window_present(GTK_WINDOW(gtkblist->window));
+	} else {
+		if (!connections || docklet_count) {
+#ifdef _WIN32
+			wgaim_systray_minimize(gtkblist->window);
+#endif
+			gtk_widget_hide(gtkblist->window);
+		} else {
+			gtk_window_iconify(GTK_WINDOW(gtkblist->window));
+		}
+	}
+}
+
+void gaim_gtk_blist_docklet_toggle() {
+	/* Useful for the docklet plugin and also for the win32 tray icon*/
+	/* This is called when one of those is clicked--it will show/hide the 
+	   buddy list/login window--depending on which is active */
+	if (connections) {
+		if (gtkblist && gtkblist->window) {
+			if (GTK_WIDGET_VISIBLE(gtkblist->window)) {
+				gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured);
+			} else {
+#if _WIN32
+				wgaim_systray_maximize(gtkblist->window);
+#endif
+				gaim_blist_set_visible(TRUE);
+			}
+		} else {
+			/* we're logging in or something... do nothing */
+			/* or should I make the blist? */
+			gaim_debug(GAIM_DEBUG_WARNING, "blist",
+					   "docklet_toggle called with connections "
+					   "but no blist!\n");
+		}
+	} else if (mainwindow) {
+		if (GTK_WIDGET_VISIBLE(mainwindow)) {
+			if (GAIM_WINDOW_ICONIFIED(mainwindow)) {
+				gtk_window_present(GTK_WINDOW(mainwindow));
+			} else {
+#if _WIN32
+				wgaim_systray_minimize(mainwindow);
+#endif
+				gtk_widget_hide(mainwindow);
+			}
+		} else {
+#if _WIN32
+			wgaim_systray_maximize(mainwindow);
+#endif
+			show_login();
+		}
+	} else {
+		show_login();
+	}
+}
+
+void gaim_gtk_blist_docklet_add()
+{
+	docklet_count++;
+}
+
+void gaim_gtk_blist_docklet_remove()
+{
+	docklet_count--;
+	if (!docklet_count) {
+		if (connections)
+			gaim_blist_set_visible(TRUE);
+		else if (mainwindow)
+			gtk_window_present(GTK_WINDOW(mainwindow));
+		else
+			show_login();
+	}
+}
+
+static struct gaim_blist_ui_ops blist_ui_ops =
+{
+	gaim_gtk_blist_new_list,
+	NULL,
+	gaim_gtk_blist_show,
+	gaim_gtk_blist_update,
+	gaim_gtk_blist_remove,
+	gaim_gtk_blist_destroy,
+	gaim_gtk_blist_set_visible
+};
+
+
+struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops()
+{
+	return &blist_ui_ops;
+}
+
+
+
+/*********************************************************************
+ * Public utility functions                                          *
+ *********************************************************************/
+
+GdkPixbuf *
+create_prpl_icon(struct gaim_account *account)
+{
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+	GdkPixbuf *status = NULL;
+	char *filename = NULL;
+	const char *protoname = NULL;
+	char buf[256];
+
+	prpl = gaim_find_prpl(account->protocol);
+
+	if (prpl != NULL) {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl_info->list_icon != NULL)
+			protoname = prpl_info->list_icon(account, NULL);
+	}
+
+	if (protoname == NULL)
+		return NULL;
+
+	/*
+	 * Status icons will be themeable too, and then it will look up
+	 * protoname from the theme
+	 */
+	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
+
+	filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
+								"default", buf, NULL);
+	status = gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
+	return status;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkblist.h	Sat Apr 26 17:56:23 2003 +0000
@@ -0,0 +1,128 @@
+/**
+ * @file gtkblist.h GTK+ Buddy List API
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Copyright (C) 2002-2003, Sean Egan <sean.egan@binghamton.edu>
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _GAIM_GTK_LIST_H_
+#define _GAIM_GTK_LIST_H_
+
+enum {
+	STATUS_ICON_COLUMN,
+	STATUS_ICON_VISIBLE_COLUMN,
+	NAME_COLUMN,
+	WARNING_COLUMN,
+	IDLE_COLUMN,
+	BUDDY_ICON_COLUMN,
+	NODE_COLUMN,
+	BLIST_COLUMNS
+	};
+
+typedef enum {
+	GAIM_STATUS_ICON_LARGE,
+	GAIM_STATUS_ICON_SMALL
+} GaimStatusIconSize;
+/**************************************************************************
+ * @name Structures
+ **************************************************************************/
+/**
+ * Like, everything you need to know about the gtk buddy list
+ */
+struct gaim_gtk_buddy_list {
+	GtkWidget *window;
+	GtkWidget *vbox;                /**< This is the vbox that everything gets packed into.  Your plugin might
+					   want to pack something in it itself.  Go, plugins! */
+
+	GtkWidget *treeview;            /**< It's a treeview... d'uh. */
+	GtkTreeStore *treemodel;        /**< This is the treemodel.  */
+	GtkTreeViewColumn *idle_column, 
+		*warning_column, 
+		*buddy_icon_column;
+
+	GtkWidget *bpmenu;              /**< The buddy pounce menu. */
+
+	GtkWidget *bbox;                /**< A Button Box. */
+	GtkTooltips *tooltips;           /**< Tooltips for the buttons. */
+
+	guint refresh_timer;            /**< The timer for refreshing every 30 seconds */
+
+	guint      timeout;              /**< The timeout for the tooltip. */
+	GdkRectangle rect;               /**< This is the bounding rectangle of the
+					       cell we're currently hovering over.  This is 
+					       used for tooltips. */
+	GtkWidget *tipwindow;            /**< The window used by the tooltip */
+
+	GaimBlistNode *selected_node;   /**< The currently selected node */
+};
+
+#define GAIM_GTK_BLIST(list) ((struct gaim_gtk_buddy_list *)(list)->ui_data)
+#define GAIM_IS_GTK_BLIST(list) \
+	((list)->ui_ops == gaim_get_gtk_blist_ui_ops())
+
+/**************************************************************************
+ * @name GTK+ Conversation API
+ **************************************************************************/
+/**
+ * Returns the UI operations structure for the buddy list.
+ *
+ * @return The GTK list operations structure.
+ */
+struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops(void);
+
+/**
+ * Returns the base image to represent the account, based on the currently selected theme
+ *
+ * @param account  The account.
+ * 
+ * @return         The icon
+ */
+GdkPixbuf *create_prpl_icon(struct gaim_account *account);
+
+/**
+ * Refreshes all the nodes of the buddy list.
+ * This should only be called when something changes to affect most of the nodes (such as a ui preference changing)
+ *
+ * @param list   This is the core list that gets updated from
+ */
+void gaim_gtk_blist_refresh(struct gaim_buddy_list *list);
+
+/**
+ * Tells the buddy list to update its toolbar based on the preferences
+ *
+ */
+void gaim_gtk_blist_update_toolbar();
+
+/**
+ * Useful for the docklet plugin and also for the win32 tray icon
+ * This is called when one of those is clicked--it will show/hide the 
+ * buddy list/login window--depending on which is active 
+ */
+void gaim_gtk_blist_docklet_toggle();
+void gaim_gtk_blist_docklet_add();
+void gaim_gtk_blist_docklet_remove();
+void gaim_gtk_blist_update_columns();
+
+/**
+ * Useful for the buddy ticker
+ */
+GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b,
+		GaimStatusIconSize size);
+
+#endif /* _GAIM_GTK_LIST_H_ */
--- a/src/gtkconv.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/gtkconv.c	Sat Apr 26 17:56:23 2003 +0000
@@ -43,7 +43,7 @@
 #include "gtkimhtml.h"
 #include "dnd-hints.h"
 #include "sound.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 
 #ifdef _WIN32
 #include "win32dep.h"
--- a/src/gtklist.h	Sat Apr 26 17:36:52 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-/**
- * @file gtklist.h GTK+ Buddy List API
- * @ingroup gtkui
- *
- * gaim
- *
- * Copyright (C) 2002-2003, Sean Egan <sean.egan@binghamton.edu>
- * 
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _GAIM_GTK_LIST_H_
-#define _GAIM_GTK_LIST_H_
-
-enum {
-	STATUS_ICON_COLUMN,
-	STATUS_ICON_VISIBLE_COLUMN,
-	NAME_COLUMN,
-	WARNING_COLUMN,
-	IDLE_COLUMN,
-	BUDDY_ICON_COLUMN,
-	NODE_COLUMN,
-	BLIST_COLUMNS
-	};
-
-typedef enum {
-	GAIM_STATUS_ICON_LARGE,
-	GAIM_STATUS_ICON_SMALL
-} GaimStatusIconSize;
-/**************************************************************************
- * @name Structures
- **************************************************************************/
-/**
- * Like, everything you need to know about the gtk buddy list
- */
-struct gaim_gtk_buddy_list {
-	GtkWidget *window;
-	GtkWidget *vbox;                /**< This is the vbox that everything gets packed into.  Your plugin might
-					   want to pack something in it itself.  Go, plugins! */
-
-	GtkWidget *treeview;            /**< It's a treeview... d'uh. */
-	GtkTreeStore *treemodel;        /**< This is the treemodel.  */
-	GtkTreeViewColumn *idle_column, 
-		*warning_column, 
-		*buddy_icon_column;
-
-	GtkWidget *bpmenu;              /**< The buddy pounce menu. */
-
-	GtkWidget *bbox;                /**< A Button Box. */
-	GtkTooltips *tooltips;           /**< Tooltips for the buttons. */
-
-	guint refresh_timer;            /**< The timer for refreshing every 30 seconds */
-
-	guint      timeout;              /**< The timeout for the tooltip. */
-	GdkRectangle rect;               /**< This is the bounding rectangle of the
-					       cell we're currently hovering over.  This is 
-					       used for tooltips. */
-	GtkWidget *tipwindow;            /**< The window used by the tooltip */
-
-	GaimBlistNode *selected_node;   /**< The currently selected node */
-};
-
-#define GAIM_GTK_BLIST(list) ((struct gaim_gtk_buddy_list *)(list)->ui_data)
-#define GAIM_IS_GTK_BLIST(list) \
-	((list)->ui_ops == gaim_get_gtk_blist_ui_ops())
-
-/**************************************************************************
- * @name GTK+ Conversation API
- **************************************************************************/
-/**
- * Returns the UI operations structure for the buddy list.
- *
- * @return The GTK list operations structure.
- */
-struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops(void);
-
-/**
- * Returns the base image to represent the account, based on the currently selected theme
- *
- * @param account  The account.
- * 
- * @return         The icon
- */
-GdkPixbuf *create_prpl_icon(struct gaim_account *account);
-
-/**
- * Refreshes all the nodes of the buddy list.
- * This should only be called when something changes to affect most of the nodes (such as a ui preference changing)
- *
- * @param list   This is the core list that gets updated from
- */
-void gaim_gtk_blist_refresh(struct gaim_buddy_list *list);
-
-/**
- * Tells the buddy list to update its toolbar based on the preferences
- *
- */
-void gaim_gtk_blist_update_toolbar();
-
-/**
- * Useful for the docklet plugin and also for the win32 tray icon
- * This is called when one of those is clicked--it will show/hide the 
- * buddy list/login window--depending on which is active 
- */
-void gaim_gtk_blist_docklet_toggle();
-void gaim_gtk_blist_docklet_add();
-void gaim_gtk_blist_docklet_remove();
-void gaim_gtk_blist_update_columns();
-
-/**
- * Useful for the buddy ticker
- */
-GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b,
-		GaimStatusIconSize size);
-
-#endif /* _GAIM_GTK_LIST_H_ */
--- a/src/gtkpounce.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/gtkpounce.c	Sat Apr 26 17:56:23 2003 +0000
@@ -23,7 +23,7 @@
 #include <unistd.h>
 #include "gaim.h"
 #include "gtkpounce.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "prpl.h"
 #include "sound.h"
 
--- a/src/list.c	Sat Apr 26 17:36:52 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1546 +0,0 @@
-/*
- * gaim
- *
- * Copyright (C) 2003,      Sean Egan    <sean.egan@binghamton.edu>
- * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifndef _WIN32
-#include <unistd.h>
-#else
-#include <direct.h>
-#endif
-#include <ctype.h>
-#include "gaim.h"
-#include "prpl.h"
-#include "list.h"
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-#define PATHSIZE 1024
-
-struct gaim_buddy_list *gaimbuddylist = NULL;
-static struct gaim_blist_ui_ops *blist_ui_ops = NULL;
-
-/*****************************************************************************
- * Private Utility functions                                                 *
- *****************************************************************************/
-static GaimBlistNode *gaim_blist_get_last_sibling(GaimBlistNode *node)
-{
-	GaimBlistNode *n = node;
-	if (!n)
-		return NULL;
-	while (n->next)
-		n = n->next;
-	return n;
-}
-static GaimBlistNode *gaim_blist_get_last_child(GaimBlistNode *node)
-{
-	if (!node)
-		return NULL;
-	return gaim_blist_get_last_sibling(node->child);
-}
-
-/*****************************************************************************
- * Public API functions                                                      *
- *****************************************************************************/
-
-struct gaim_buddy_list *gaim_blist_new()
-{
-	struct gaim_buddy_list *gbl = g_new0(struct gaim_buddy_list, 1);
-
-	gbl->ui_ops = gaim_get_blist_ui_ops();
-
-	if (gbl->ui_ops != NULL && gbl->ui_ops->new_list != NULL)
-		gbl->ui_ops->new_list(gbl);
-
-	return gbl;
-}
-
-void
-gaim_set_blist(struct gaim_buddy_list *list)
-{
-	gaimbuddylist = list;
-}
-
-struct gaim_buddy_list *
-gaim_get_blist(void)
-{
-	return gaimbuddylist;
-}
-
-void  gaim_blist_show () 
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	if (ops)
-		ops->show(gaimbuddylist);
-}
-
-void gaim_blist_destroy()
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	if (ops)
-		ops->destroy(gaimbuddylist);
-}
-
-void  gaim_blist_set_visible (gboolean show)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	if (ops)
-		ops->set_visible(gaimbuddylist, show);
-}
-
-void  gaim_blist_update_buddy_status (struct buddy *buddy, int status)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	buddy->uc = status;
-
-	if(!(status & UC_UNAVAILABLE))
-		gaim_event_broadcast(event_buddy_back, buddy->account->gc, buddy->name);
-	else
-		gaim_event_broadcast(event_buddy_away, buddy->account->gc, buddy->name);
-
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-}
-
-static gboolean presence_update_timeout_cb(struct buddy *buddy) {
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-
-	if(buddy->present == GAIM_BUDDY_SIGNING_ON) {
-		buddy->present = GAIM_BUDDY_ONLINE;
-		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
-	} else if(buddy->present == GAIM_BUDDY_SIGNING_OFF) {
-		buddy->present = GAIM_BUDDY_OFFLINE;
-	}
-
-	buddy->timer = 0;
-
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-
-	return FALSE;
-}
-
-void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence) {
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	gboolean do_timer = FALSE;
-
-	if (!GAIM_BUDDY_IS_ONLINE(buddy) && presence) {
-		buddy->present = GAIM_BUDDY_SIGNING_ON;
-		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
-		do_timer = TRUE;
-	} else if(GAIM_BUDDY_IS_ONLINE(buddy) && !presence) {
-		buddy->present = GAIM_BUDDY_SIGNING_OFF;
-		gaim_event_broadcast(event_buddy_signoff, buddy->account->gc, buddy->name);
-		do_timer = TRUE;
-	}
-
-	if(do_timer) {
-		if(buddy->timer > 0)
-			g_source_remove(buddy->timer);
-		buddy->timer = g_timeout_add(10000, (GSourceFunc)presence_update_timeout_cb, buddy);
-	}
-
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-}
-
-
-void  gaim_blist_update_buddy_idle (struct buddy *buddy, int idle)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	buddy->idle = idle;
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-}
-void  gaim_blist_update_buddy_evil (struct buddy *buddy, int warning)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	buddy->evil = warning;
-	if (ops)
-		ops->update(gaimbuddylist,(GaimBlistNode*)buddy);
-}
-void gaim_blist_update_buddy_icon(struct buddy *buddy) {
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	if(ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-}
-void  gaim_blist_rename_buddy (struct buddy *buddy, const char *name)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-       	g_free(buddy->name);
-	buddy->name = g_strdup(name);
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-}
-void  gaim_blist_alias_buddy (struct buddy *buddy, const char *alias)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	struct gaim_conversation *conv;
-
-	g_free(buddy->alias);
-
-	if(alias && strlen(alias))
-		buddy->alias = g_strdup(alias);
-	else
-		buddy->alias = NULL;
-
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-
-	conv = gaim_find_conversation_with_account(buddy->name, buddy->account);
-
-	if (conv)
-		gaim_conversation_autoset_title(conv);
-}
-
-void gaim_blist_rename_group(struct group *group, const char *name)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	g_free(group->name);
-	group->name = g_strdup(name);
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)group);
-}
-struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias)
-{
-	struct buddy *b;
-	struct gaim_blist_ui_ops *ops;
-
-	b = g_new0(struct buddy, 1);
-	b->account = account;
-	b->name  = g_strdup(screenname);
-	b->alias = g_strdup(alias);
-	b->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	((GaimBlistNode*)b)->type = GAIM_BLIST_BUDDY_NODE;
-
-	ops = gaim_get_blist_ui_ops();
-
-	if (ops != NULL && ops->new_node != NULL)
-		ops->new_node((GaimBlistNode *)b);
-
-	return b;
-}
-void  gaim_blist_add_buddy (struct buddy *buddy, struct group *group, GaimBlistNode *node)
-{
-	GaimBlistNode *n = node, *bnode = (GaimBlistNode*)buddy;
-	struct group *g = group;
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	gboolean save = FALSE;
-
-	if (!n) {
-		if (!g) {
-			g = gaim_group_new(_("Buddies"));
-			gaim_blist_add_group(g, NULL);
-		}
-		n = gaim_blist_get_last_child((GaimBlistNode*)g);
-	} else {
-		g = (struct group*)n->parent;
-	}
-
-	/* if we're moving to overtop of ourselves, do nothing */
-	if(bnode == n)
-		return;
-
-	if (bnode->parent) {
-		/* This buddy was already in the list and is
-		 * being moved.
-		 */
-		if(bnode->next)
-			bnode->next->prev = bnode->prev;
-		if(bnode->prev)
-			bnode->prev->next = bnode->next;
-		if(bnode->parent->child == bnode)
-			bnode->parent->child = bnode->next;
-
-		ops->remove(gaimbuddylist, bnode);
-
-		if (bnode->parent != ((GaimBlistNode*)g))
-			serv_move_buddy(buddy, (struct group*)bnode->parent, g);
-		save = TRUE;
-	}
-
-	if (n) {
-		if(n->next)
-			n->next->prev = (GaimBlistNode*)buddy;
-		((GaimBlistNode*)buddy)->next = n->next;
-		((GaimBlistNode*)buddy)->prev = n;
-		((GaimBlistNode*)buddy)->parent = n->parent;
-		n->next = (GaimBlistNode*)buddy;
-	} else {
-		((GaimBlistNode*)g)->child = (GaimBlistNode*)buddy;
-		((GaimBlistNode*)buddy)->next = NULL;
-		((GaimBlistNode*)buddy)->prev = NULL;
-		((GaimBlistNode*)buddy)->parent = (GaimBlistNode*)g;
-	}
-
-	if (ops)
-		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
-	if (save)
-		gaim_blist_save();
-}
-
-struct group *gaim_group_new(const char *name)
-{
-	struct group *g = gaim_find_group(name);
-
-	if (!g) {
-		struct gaim_blist_ui_ops *ops;
-		g= g_new0(struct group, 1);
-		g->name = g_strdup(name);
-		g->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
-				g_free, g_free);
-		((GaimBlistNode*)g)->type = GAIM_BLIST_GROUP_NODE;
-
-		ops = gaim_get_blist_ui_ops();
-
-		if (ops != NULL && ops->new_node != NULL)
-			ops->new_node((GaimBlistNode *)g);
-
-	}
-	return g;
-}
-
-void  gaim_blist_add_group (struct group *group, GaimBlistNode *node)
-{
-	struct gaim_blist_ui_ops *ops;
-	GaimBlistNode *gnode = (GaimBlistNode*)group;
-	gboolean save = FALSE;
-
-	if (!gaimbuddylist)
-		gaimbuddylist = gaim_blist_new();
-	ops = gaimbuddylist->ui_ops;
-
-	if (!gaimbuddylist->root) {
-		gaimbuddylist->root = gnode;
-		return;
-	}
-
-	if (!node)
-		node = gaim_blist_get_last_sibling(gaimbuddylist->root);
-
-	/* if we're moving to overtop of ourselves, do nothing */
-	if(gnode == node)
-		return;
-
-	if (gaim_find_group(group->name)) {
-		/* This is just being moved */
-
-		ops->remove(gaimbuddylist, (GaimBlistNode*)group);
-
-		if(gnode == gaimbuddylist->root)
-			gaimbuddylist->root = gnode->next;
-		if(gnode->prev)
-			gnode->prev->next = gnode->next;
-		if(gnode->next)
-			gnode->next->prev = gnode->prev;
-
-		save = TRUE;
-	}
-
-	gnode->next = node->next;
-	gnode->prev = node;
-	if(node->next)
-		node->next->prev = gnode;
-	node->next = gnode;
-
-	if (ops) {
-		ops->update(gaimbuddylist, gnode);
-		for(node = gnode->child; node; node = node->next)
-			ops->update(gaimbuddylist, node);
-	}
-	if (save)
-		gaim_blist_save();
-}
-
-void  gaim_blist_remove_buddy (struct buddy *buddy)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-
-	GaimBlistNode *gnode, *node = (GaimBlistNode*)buddy;
-	struct group *group;
-
-	gnode = node->parent;
-	group = (struct group *)gnode;
-
-	if(gnode->child == node)
-		gnode->child = node->next;
-	if (node->prev)
-		node->prev->next = node->next;
-	if (node->next)
-		node->next->prev = node->prev;
-
-	ops->remove(gaimbuddylist, node);
-	g_hash_table_destroy(buddy->settings);
-	g_free(buddy->name);
-	g_free(buddy->alias);
-	g_free(buddy);
-}
-
-void  gaim_blist_remove_group (struct group *group)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	GaimBlistNode *node = (GaimBlistNode*)group;
-
-	if(node->child) {
-		char *buf;
-		int count = 0;
-		GaimBlistNode *child = node->child;
-
-		while(child) {
-			count++;
-			child = child->next;
-		}
-
-		buf = g_strdup_printf(_("%d buddies from group %s were not "
-					"removed because their accounts were not logged in.  These "
-					"buddies, and the group were not removed.\n"),
-				count, group->name);
-		do_error_dialog(_("Group Not Removed"), buf, GAIM_ERROR);
-		g_free(buf);
-		return;
-	}
-
-	if(gaimbuddylist->root == node)
-		gaimbuddylist->root = node->next;
-	if (node->prev)
-		node->prev->next = node->next;
-	if (node->next)
-		node->next->prev = node->prev;
-
-	ops->remove(gaimbuddylist, node);
-	g_free(group->name);
-	g_free(group);
-}
-
-char *gaim_get_buddy_alias_only(struct buddy *b) {
-        if(!b)
-                return NULL;
-        if(b->alias && b->alias[0])
-                return b->alias;
-        else if((misc_options & OPT_MISC_USE_SERVER_ALIAS) && b->server_alias)
-                return b->server_alias;
-        return NULL;
-}
-
-char *  gaim_get_buddy_alias (struct buddy *buddy)
-{
-	char *ret = gaim_get_buddy_alias_only(buddy);
-        if(!ret)
-                return buddy ? buddy->name : _("Unknown");
-        return ret;
-
-}
-
-struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name)
-{
-	GaimBlistNode *group;
-	GaimBlistNode *buddy;
-	char *norm_name = g_strdup(normalize(name));
-
-	if (!gaimbuddylist)
-		return NULL;
-
-	group = gaimbuddylist->root;
-	while (group) {
-		buddy = group->child;
-		while (buddy) {
-			if (!gaim_utf8_strcasecmp(normalize(((struct buddy*)buddy)->name), norm_name) && account == ((struct buddy*)buddy)->account) {
-				g_free(norm_name);
-				return (struct buddy*)buddy;
-			}
-			buddy = buddy->next;
-		}
-		group = group->next;
-	}
-	g_free(norm_name);
-	return NULL;
-}
-
-struct group *gaim_find_group(const char *name)
-{
-	GaimBlistNode *node;
-	if (!gaimbuddylist)
-		return NULL;
-	node = gaimbuddylist->root;
-	while(node) {
-		if (!strcmp(((struct group*)node)->name, name))
-			return (struct group*)node;
-		node = node->next;
-	}
-	return NULL;
-}
-struct group *gaim_find_buddys_group(struct buddy *buddy)
-{
-	if (!buddy)
-		return NULL;
-	return (struct group*)(((GaimBlistNode*)buddy)->parent);
-}
-
-GSList *gaim_group_get_accounts(struct group *g)
-{
-	GSList *l = NULL;
-	GaimBlistNode *child = ((GaimBlistNode *)g)->child;
-
-	while (child) {
-		if (!g_slist_find(l, ((struct buddy*)child)->account))
-			l = g_slist_append(l, ((struct buddy*)child)->account);
-		child = child->next;
-	}
-	return l;
-}
-
-void gaim_blist_remove_account(struct gaim_account *account)
-{
-	struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops;
-	GaimBlistNode *group = gaimbuddylist->root;
-	GaimBlistNode *buddy;
-	if (!gaimbuddylist)
-		return;
-	while (group) {
-		buddy = group->child;
-		while (buddy) {
-			if (account == ((struct buddy*)buddy)->account) {
-				((struct buddy*)buddy)->present = GAIM_BUDDY_OFFLINE;
-				if(ops)
-					ops->remove(gaimbuddylist, buddy);
-			}
-			buddy = buddy->next;
-		}
-		group = group->next;
-	}
-}
-
-void parse_toc_buddy_list(struct gaim_account *account, char *config)
-{
-	char *c;
-	char current[256];
-	GList *bud = NULL;
-
-
-	if (config != NULL) {
-
-		/* skip "CONFIG:" (if it exists) */
-		c = strncmp(config + 6 /* sizeof(struct sflap_hdr) */ , "CONFIG:", strlen("CONFIG:")) ?
-			strtok(config, "\n") :
-			strtok(config + 6 /* sizeof(struct sflap_hdr) */  + strlen("CONFIG:"), "\n");
-		do {
-			if (c == NULL)
-				break;
-			if (*c == 'g') {
-				char *utf8 = NULL;
-				utf8 = gaim_try_conv_to_utf8(c + 2);
-				if (utf8 == NULL) {
-					g_strlcpy(current, _("Invalid Groupname"), sizeof(current));
-				} else {
-					g_strlcpy(current, utf8, sizeof(current));
-					g_free(utf8);
-				}
-				if (!gaim_find_group(current)) {
-					struct group *g = gaim_group_new(current);
-					gaim_blist_add_group(g, NULL);
-				}
-			} else if (*c == 'b') { /*&& !gaim_find_buddy(user, c + 2)) {*/
-				char nm[80], sw[388], *a, *utf8 = NULL;
-
-				if ((a = strchr(c + 2, ':')) != NULL) {
-					*a++ = '\0';		/* nul the : */
-				}
-
-				g_strlcpy(nm, c + 2, sizeof(nm));
-				if (a) {
-					utf8 = gaim_try_conv_to_utf8(a);
-					if (utf8 == NULL) {
-						gaim_debug(GAIM_DEBUG_ERROR, "toc blist",
-								   "Failed to convert alias for "
-								   "'%s' to UTF-8\n", nm);
-					}
-				}
-				if (utf8 == NULL) {
-					sw[0] = '\0';
-				} else {
-					/* This can leave a partial sequence at the end,
-					 * but who cares? */
-					g_strlcpy(sw, utf8, sizeof(sw));
-					g_free(utf8);
-				}
-
-				if (!gaim_find_buddy(account, nm)) {
-					struct buddy *b = gaim_buddy_new(account, nm, sw);
-					struct group *g = gaim_find_group(current);
-					gaim_blist_add_buddy(b, g, NULL);
-					bud = g_list_append(bud, g_strdup(nm));
-				}
-			} else if (*c == 'p') {
-				gaim_privacy_permit_add(account, c + 2);
-			} else if (*c == 'd') {
-				gaim_privacy_deny_add(account, c + 2);
-			} else if (!strncmp("toc", c, 3)) {
-				sscanf(c + strlen(c) - 1, "%d", &account->permdeny);
-				gaim_debug(GAIM_DEBUG_MISC, "toc blist",
-						   "permdeny: %d\n", account->permdeny);
-				if (account->permdeny == 0)
-					account->permdeny = 1;
-			} else if (*c == 'm') {
-				sscanf(c + 2, "%d", &account->permdeny);
-				gaim_debug(GAIM_DEBUG_MISC, "toc blist",
-						   "permdeny: %d\n", account->permdeny);
-				if (account->permdeny == 0)
-					account->permdeny = 1;
-			}
-		} while ((c = strtok(NULL, "\n")));
-
-		if(account->gc) {
-			if(bud) {
-				GList *node = bud;
-				serv_add_buddies(account->gc, bud);
-				while(node) {
-					g_free(node->data);
-					node = node->next;
-				}
-			}
-			serv_set_permit_deny(account->gc);
-		}
-		g_list_free(bud);
-	}
-}
-
-#if 0
-/* translate an AIM 3 buddylist (*.lst) to a Gaim buddylist */
-static GString *translate_lst(FILE *src_fp)
-{
-	char line[BUF_LEN], *line2;
-	char *name;
-	int i;
-
-	GString *dest = g_string_new("m 1\n");
-
-	while (fgets(line, BUF_LEN, src_fp)) {
-		line2 = g_strchug(line);
-		if (strstr(line2, "group") == line2) {
-			name = strpbrk(line2, " \t\n\r\f") + 1;
-			dest = g_string_append(dest, "g ");
-			for (i = 0; i < strcspn(name, "\n\r"); i++)
-				if (name[i] != '\"')
-					dest = g_string_append_c(dest, name[i]);
-			dest = g_string_append_c(dest, '\n');
-		}
-		if (strstr(line2, "buddy") == line2) {
-			name = strpbrk(line2, " \t\n\r\f") + 1;
-			dest = g_string_append(dest, "b ");
-			for (i = 0; i < strcspn(name, "\n\r"); i++)
-				if (name[i] != '\"')
-					dest = g_string_append_c(dest, name[i]);
-			dest = g_string_append_c(dest, '\n');
-		}
-	}
-
-	return dest;
-}
-
-
-/* translate an AIM 4 buddylist (*.blt) to Gaim format */
-static GString *translate_blt(FILE *src_fp)
-{
-	int i;
-	char line[BUF_LEN];
-	char *buddy;
-
-	GString *dest = g_string_new("m 1\n");
-
-	while (strstr(fgets(line, BUF_LEN, src_fp), "Buddy") == NULL);
-	while (strstr(fgets(line, BUF_LEN, src_fp), "list") == NULL);
-
-	while (1) {
-		fgets(line, BUF_LEN, src_fp); g_strchomp(line);
-		if (strchr(line, '}') != NULL)
-			break;
-
-		if (strchr(line, '{') != NULL) {
-			/* Syntax starting with "<group> {" */
-
-			dest = g_string_append(dest, "g ");
-			buddy = g_strchug(strtok(line, "{"));
-			for (i = 0; i < strlen(buddy); i++)
-				if (buddy[i] != '\"')
-					dest = g_string_append_c(dest, buddy[i]);
-			dest = g_string_append_c(dest, '\n');
-			while (strchr(fgets(line, BUF_LEN, src_fp), '}') == NULL) {
-				gboolean pounce = FALSE;
-				char *e;
-				g_strchomp(line);
-				buddy = g_strchug(line);
-				gaim_debug(GAIM_DEBUG_MISC, "AIM 4 blt import",
-						   "buddy: \"%s\"\n", buddy);
-				dest = g_string_append(dest, "b ");
-				if (strchr(buddy, '{') != NULL) {
-					/* buddy pounce, etc */
-					char *pos = strchr(buddy, '{') - 1;
-					*pos = 0;
-					pounce = TRUE;
-				}
-				if ((e = strchr(buddy, '\"')) != NULL) {
-					*e = '\0';
-					buddy++;
-				}
-				dest = g_string_append(dest, buddy);
-				dest = g_string_append_c(dest, '\n');
-				if (pounce)
-					do
-						fgets(line, BUF_LEN, src_fp);
-					while (!strchr(line, '}'));
-			}
-		} else {
-
-			/* Syntax "group buddy buddy ..." */
-			buddy = g_strchug(strtok(line, " \n"));
-			dest = g_string_append(dest, "g ");
-			if (strchr(buddy, '\"') != NULL) {
-				dest = g_string_append(dest, &buddy[1]);
-				dest = g_string_append_c(dest, ' ');
-				buddy = g_strchug(strtok(NULL, " \n"));
-				while (strchr(buddy, '\"') == NULL) {
-					dest = g_string_append(dest, buddy);
-					dest = g_string_append_c(dest, ' ');
-					buddy = g_strchug(strtok(NULL, " \n"));
-				}
-				buddy[strlen(buddy) - 1] = '\0';
-				dest = g_string_append(dest, buddy);
-			} else {
-				dest = g_string_append(dest, buddy);
-			}
-			dest = g_string_append_c(dest, '\n');
-			while ((buddy = g_strchug(strtok(NULL, " \n"))) != NULL) {
-				dest = g_string_append(dest, "b ");
-				if (strchr(buddy, '\"') != NULL) {
-					dest = g_string_append(dest, &buddy[1]);
-					dest = g_string_append_c(dest, ' ');
-					buddy = g_strchug(strtok(NULL, " \n"));
-					while (strchr(buddy, '\"') == NULL) {
-						dest = g_string_append(dest, buddy);
-						dest = g_string_append_c(dest, ' ');
-						buddy = g_strchug(strtok(NULL, " \n"));
-					}
-					buddy[strlen(buddy) - 1] = '\0';
-					dest = g_string_append(dest, buddy);
-				} else {
-					dest = g_string_append(dest, buddy);
-				}
-				dest = g_string_append_c(dest, '\n');
-			}
-		}
-	}
-
-	return dest;
-}
-
-static GString *translate_gnomeicu(FILE *src_fp)
-{
-	char line[BUF_LEN];
-	GString *dest = g_string_new("m 1\ng Buddies\n");
-
-	while (strstr(fgets(line, BUF_LEN, src_fp), "NewContacts") == NULL);
-
-	while (fgets(line, BUF_LEN, src_fp)) {
-		char *eq;
-		g_strchomp(line);
-		if (line[0] == '\n' || line[0] == '[')
-			break;
-		eq = strchr(line, '=');
-		if (!eq)
-			break;
-		*eq = ':';
-		eq = strchr(eq, ',');
-		if (eq)
-			*eq = '\0';
-		dest = g_string_append(dest, "b ");
-		dest = g_string_append(dest, line);
-		dest = g_string_append_c(dest, '\n');
-	}
-
-	return dest;
-}
-#endif
-
-static gchar *get_screenname_filename(const char *name)
-{
-	gchar **split;
-	gchar *good;
-	gchar *ret;
-
-	split = g_strsplit(name, G_DIR_SEPARATOR_S, -1);
-	good = g_strjoinv(NULL, split);
-	g_strfreev(split);
-
-	ret = g_utf8_strup(good, -1);
-
-	g_free(good);
-
-	return ret;
-}
-
-static gboolean gaim_blist_read(const char *filename);
-
-
-static void do_import(struct gaim_account *account, const char *filename)
-{
-	GString *buf = NULL;
-	char first[64];
-	char path[PATHSIZE];
-	int len;
-	FILE *f;
-	struct stat st;
-
-	if (filename) {
-		g_snprintf(path, sizeof(path), "%s", filename);
-	} else {
-		char *g_screenname = get_screenname_filename(account->username);
-		char *file = gaim_user_dir();
-		int protocol = (account->protocol == GAIM_PROTO_OSCAR) ? (isalpha(account->username[0]) ? GAIM_PROTO_TOC : GAIM_PROTO_ICQ): account->protocol;
-
-		if (file != (char *)NULL) {
-			sprintf(path, "%s" G_DIR_SEPARATOR_S "%s.%d.blist", file, g_screenname, protocol);
-			g_free(g_screenname);
-		} else {
-			g_free(g_screenname);
-			return;
-		}
-	}
-
-	if (stat(path, &st)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to stat %s.\n",
-				   path);
-		return;
-	}
-
-	if (!(f = fopen(path, "r"))) {
-		gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to open %s.\n",
-				   path);
-		return;
-	}
-
-	fgets(first, 64, f);
-
-	if ((first[0] == '\n') || (first[0] == '\r' && first[1] == '\n'))
-		fgets(first, 64, f);
-
-#if 0
-	if (!g_strncasecmp(first, "<xml", strlen("<xml"))) {
-		/* new gaim XML buddy list */
-		gaim_blist_read(path);
-		
-		/* We really don't need to bother doing stuf like translating AIM 3 buddy lists anymore */
-		
-	} else if (!g_strncasecmp(first, "Config {", strlen("Config {"))) {
-		/* AIM 4 buddy list */
-		gaim_debug(GAIM_DEBUG_MISC, "blist import", "aim 4\n");
-		rewind(f);
-		buf = translate_blt(f);
-	} else if (strstr(first, "group") != NULL) {
-		/* AIM 3 buddy list */
-		gaim_debug(GAIM_DEBUG_MISC, "blist import", "aim 3\n");
-		rewind(f);
-		buf = translate_lst(f);
-	} else if (!g_strncasecmp(first, "[User]", strlen("[User]"))) {
-		/* GnomeICU (hopefully) */
-		gaim_debug(GAIM_DEBUG_MISC, "blist import", "gnomeicu\n");
-		rewind(f);
-		buf = translate_gnomeicu(f);
-
-	} else 
-#endif
-		if (first[0] == 'm') {
-			/* Gaim buddy list - no translation */
-			char buf2[BUF_LONG * 2];
-			buf = g_string_new("");
-			rewind(f);
-			while (1) {
-				len = fread(buf2, 1, BUF_LONG * 2 - 1, f);
-				if (len <= 0)
-					break;
-			buf2[len] = '\0';
-			buf = g_string_append(buf, buf2);
-			if (len != BUF_LONG * 2 - 1)
-				break;
-			}
-		}
-	
-	fclose(f);
-
-	if (buf) {
-		buf = g_string_prepend(buf, "toc_set_config {");
-		buf = g_string_append(buf, "}\n");
-		parse_toc_buddy_list(account, buf->str);
-		g_string_free(buf, TRUE);
-	}
-}
-
-gboolean gaim_group_on_account(struct group *g, struct gaim_account *account) {
-	GaimBlistNode *bnode;
-	for(bnode = g->node.child; bnode; bnode = bnode->next) {
-		struct buddy *b = (struct buddy *)bnode;
-		if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-			continue;
-		if((!account && b->account->gc) || b->account == account)
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean blist_safe_to_write = FALSE;
-
-static char *blist_parser_group_name = NULL;
-static char *blist_parser_person_name = NULL;
-static char *blist_parser_account_name = NULL;
-static int blist_parser_account_protocol = 0;
-static char *blist_parser_buddy_name = NULL;
-static char *blist_parser_buddy_alias = NULL;
-static char *blist_parser_setting_name = NULL;
-static char *blist_parser_setting_value = NULL;
-static GHashTable *blist_parser_buddy_settings = NULL;
-static GHashTable *blist_parser_group_settings = NULL;
-static int blist_parser_privacy_mode = 0;
-static GList *tag_stack = NULL;
-enum {
-	BLIST_TAG_GAIM,
-	BLIST_TAG_BLIST,
-	BLIST_TAG_GROUP,
-	BLIST_TAG_PERSON,
-	BLIST_TAG_BUDDY,
-	BLIST_TAG_NAME,
-	BLIST_TAG_ALIAS,
-	BLIST_TAG_SETTING,
-	BLIST_TAG_PRIVACY,
-	BLIST_TAG_ACCOUNT,
-	BLIST_TAG_PERMIT,
-	BLIST_TAG_BLOCK,
-	BLIST_TAG_IGNORE
-};
-static gboolean blist_parser_error_occurred = FALSE;
-
-static void blist_start_element_handler (GMarkupParseContext *context,
-		const gchar *element_name,
-		const gchar **attribute_names,
-		const gchar **attribute_values,
-		gpointer user_data,
-		GError **error) {
-	int i;
-
-	if(!strcmp(element_name, "gaim")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GAIM));
-	} else if(!strcmp(element_name, "blist")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLIST));
-	} else if(!strcmp(element_name, "group")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GROUP));
-		for(i=0; attribute_names[i]; i++) {
-			if(!strcmp(attribute_names[i], "name")) {
-				g_free(blist_parser_group_name);
-				blist_parser_group_name = g_strdup(attribute_values[i]);
-			}
-		}
-		if(blist_parser_group_name) {
-			struct group *g = gaim_group_new(blist_parser_group_name);
-			gaim_blist_add_group(g,NULL);
-		}
-	} else if(!strcmp(element_name, "person")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERSON));
-		for(i=0; attribute_names[i]; i++) {
-			if(!strcmp(attribute_names[i], "name")) {
-				g_free(blist_parser_person_name);
-				blist_parser_person_name = g_strdup(attribute_values[i]);
-			}
-		}
-	} else if(!strcmp(element_name, "buddy")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BUDDY));
-		for(i=0; attribute_names[i]; i++) {
-			if(!strcmp(attribute_names[i], "account")) {
-				g_free(blist_parser_account_name);
-				blist_parser_account_name = g_strdup(attribute_values[i]);
-			} else if(!strcmp(attribute_names[i], "protocol")) {
-				blist_parser_account_protocol = atoi(attribute_values[i]);
-			}
-		}
-	} else if(!strcmp(element_name, "name")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_NAME));
-	} else if(!strcmp(element_name, "alias")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ALIAS));
-	} else if(!strcmp(element_name, "setting")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_SETTING));
-		for(i=0; attribute_names[i]; i++) {
-			if(!strcmp(attribute_names[i], "name")) {
-				g_free(blist_parser_setting_name);
-				blist_parser_setting_name = g_strdup(attribute_values[i]);
-			}
-		}
-	} else if(!strcmp(element_name, "privacy")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PRIVACY));
-	} else if(!strcmp(element_name, "account")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ACCOUNT));
-		for(i=0; attribute_names[i]; i++) {
-			if(!strcmp(attribute_names[i], "protocol"))
-				blist_parser_account_protocol = atoi(attribute_values[i]);
-			else if(!strcmp(attribute_names[i], "mode"))
-				blist_parser_privacy_mode = atoi(attribute_values[i]);
-			else if(!strcmp(attribute_names[i], "name")) {
-				g_free(blist_parser_account_name);
-				blist_parser_account_name = g_strdup(attribute_values[i]);
-			}
-		}
-	} else if(!strcmp(element_name, "permit")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERMIT));
-	} else if(!strcmp(element_name, "block")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLOCK));
-	} else if(!strcmp(element_name, "ignore")) {
-		tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_IGNORE));
-	}
-}
-
-static void blist_end_element_handler(GMarkupParseContext *context,
-		const gchar *element_name, gpointer user_data, GError **error) {
-	if(!strcmp(element_name, "gaim")) {
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "blist")) {
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "group")) {
-		if(blist_parser_group_settings) {
-			struct group *g = gaim_find_group(blist_parser_group_name);
-			g_hash_table_destroy(g->settings);
-			g->settings = blist_parser_group_settings;
-		}
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-		blist_parser_group_settings = NULL;
-	} else if(!strcmp(element_name, "person")) {
-		g_free(blist_parser_person_name);
-		blist_parser_person_name = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "buddy")) {
-		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
-				blist_parser_account_protocol);
-		if(account) {
-			struct buddy *b = gaim_buddy_new(account, blist_parser_buddy_name, blist_parser_buddy_alias);
-			struct group *g = gaim_find_group(blist_parser_group_name);
-			gaim_blist_add_buddy(b,g,NULL);
-			if(blist_parser_buddy_settings) {
-				g_hash_table_destroy(b->settings);
-				b->settings = blist_parser_buddy_settings;
-			}
-		}
-		g_free(blist_parser_buddy_name);
-		blist_parser_buddy_name = NULL;
-		g_free(blist_parser_buddy_alias);
-		blist_parser_buddy_alias = NULL;
-		g_free(blist_parser_account_name);
-		blist_parser_account_name = NULL;
-		blist_parser_buddy_settings = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "name")) {
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "alias")) {
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "setting")) {
-		if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_BUDDY) {
-			if(!blist_parser_buddy_settings)
-				blist_parser_buddy_settings = g_hash_table_new_full(g_str_hash,
-						g_str_equal, g_free, g_free);
-			if(blist_parser_setting_name && blist_parser_setting_value) {
-				g_hash_table_replace(blist_parser_buddy_settings,
-						g_strdup(blist_parser_setting_name),
-						g_strdup(blist_parser_setting_value));
-			}
-		} else if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_GROUP) {
-			if(!blist_parser_group_settings)
-				blist_parser_group_settings = g_hash_table_new_full(g_str_hash,
-						g_str_equal, g_free, g_free);
-			if(blist_parser_setting_name && blist_parser_setting_value) {
-				g_hash_table_replace(blist_parser_group_settings,
-						g_strdup(blist_parser_setting_name),
-						g_strdup(blist_parser_setting_value));
-			}
-		}
-		g_free(blist_parser_setting_name);
-		g_free(blist_parser_setting_value);
-		blist_parser_setting_name = NULL;
-		blist_parser_setting_value = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "privacy")) {
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "account")) {
-		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
-				blist_parser_account_protocol);
-		if(account) {
-			account->permdeny = blist_parser_privacy_mode;
-		}
-		g_free(blist_parser_account_name);
-		blist_parser_account_name = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "permit")) {
-		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
-				blist_parser_account_protocol);
-		if(account) {
-			gaim_privacy_permit_add(account, blist_parser_buddy_name);
-		}
-		g_free(blist_parser_buddy_name);
-		blist_parser_buddy_name = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "block")) {
-		struct gaim_account *account = gaim_account_find(blist_parser_account_name,
-				blist_parser_account_protocol);
-		if(account) {
-			gaim_privacy_deny_add(account, blist_parser_buddy_name);
-		}
-		g_free(blist_parser_buddy_name);
-		blist_parser_buddy_name = NULL;
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	} else if(!strcmp(element_name, "ignore")) {
-		/* we'll apparently do something with this later */
-		tag_stack = g_list_delete_link(tag_stack, tag_stack);
-	}
-}
-
-static void blist_text_handler(GMarkupParseContext *context, const gchar *text,
-		gsize text_len, gpointer user_data, GError **error) {
-	switch(GPOINTER_TO_INT(tag_stack->data)) {
-		case BLIST_TAG_NAME:
-			blist_parser_buddy_name = g_strndup(text, text_len);
-			break;
-		case BLIST_TAG_ALIAS:
-			blist_parser_buddy_alias = g_strndup(text, text_len);
-			break;
-		case BLIST_TAG_PERMIT:
-		case BLIST_TAG_BLOCK:
-		case BLIST_TAG_IGNORE:
-			blist_parser_buddy_name = g_strndup(text, text_len);
-			break;
-		case BLIST_TAG_SETTING:
-			blist_parser_setting_value = g_strndup(text, text_len);
-			break;
-		default:
-			break;
-	}
-}
-
-static void blist_error_handler(GMarkupParseContext *context, GError *error,
-		gpointer user_data) {
-	blist_parser_error_occurred = TRUE;
-	gaim_debug(GAIM_DEBUG_ERROR, "blist import",
-			   "Error parsing blist.xml: %s\n", error->message);
-}
-
-static GMarkupParser blist_parser = {
-	blist_start_element_handler,
-	blist_end_element_handler,
-	blist_text_handler,
-	NULL,
-	blist_error_handler
-};
-
-static gboolean gaim_blist_read(const char *filename) {
-	gchar *contents = NULL;
-	gsize length;
-	GMarkupParseContext *context;
-	GError *error = NULL;
-
-	gaim_debug(GAIM_DEBUG_INFO, "blist import",
-			   "Reading %s\n", filename);
-	if(!g_file_get_contents(filename, &contents, &length, &error)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "blist import",
-				   "Error reading blist: %s\n", error->message);
-		g_error_free(error);
-		return FALSE;
-	}
-
-	context = g_markup_parse_context_new(&blist_parser, 0, NULL, NULL);
-
-	if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
-		g_markup_parse_context_free(context);
-		g_free(contents);
-		return FALSE;
-	}
-
-	if(!g_markup_parse_context_end_parse(context, NULL)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "blist import",
-				   "Error parsing %s\n", filename);
-		g_markup_parse_context_free(context);
-		g_free(contents);
-		return FALSE;
-	}
-
-	g_markup_parse_context_free(context);
-	g_free(contents);
-
-	if(blist_parser_error_occurred)
-		return FALSE;
-
-	gaim_debug(GAIM_DEBUG_INFO, "blist import", "Finished reading %s\n",
-			   filename);
-
-	return TRUE;
-}
-
-void gaim_blist_load() {
-	GSList *accts;
-	char *user_dir = gaim_user_dir();
-	char *filename;
-	char *msg;
-
-	blist_safe_to_write = TRUE;
-
-	if(!user_dir)
-		return;
-
-	filename = g_build_filename(user_dir, "blist.xml", NULL);
-
-	if(g_file_test(filename, G_FILE_TEST_EXISTS)) {
-		if(!gaim_blist_read(filename)) {
-			msg = g_strdup_printf(_("An error was encountered parsing your "
-						"buddy list.  It has not been loaded."));
-			do_error_dialog(_("Buddy List Error"), msg, GAIM_ERROR);
-			g_free(msg);
-		}
-	} else if(g_slist_length(gaim_accounts)) {
-		/* rob wants to inform the user that their buddy lists are
-		 * being converted */
-		msg = g_strdup_printf(_("Gaim is converting your old buddy lists "
-					"to a new format, which will now be located at %s"),
-				filename);
-		do_error_dialog(_("Converting Buddy List"), msg, GAIM_INFO);
-		g_free(msg);
-
-		/* now, let gtk actually display the dialog before we start anything */
-		while(gtk_events_pending())
-			gtk_main_iteration();
-
-		/* read in the old lists, then save to the new format */
-		for(accts = gaim_accounts; accts; accts = accts->next) {
-			do_import(accts->data, NULL);
-		}
-		gaim_blist_save();
-	}
-
-	g_free(filename);
-}
-
-static void blist_print_group_settings(gpointer key, gpointer data,
-		gpointer user_data) {
-	char *key_val;
-	char *data_val;
-	FILE *file = user_data;
-
-	if(!key || !data)
-		return;
-
-	key_val = g_markup_escape_text(key, -1);
-	data_val = g_markup_escape_text(data, -1);
-
-	fprintf(file, "\t\t\t<setting name=\"%s\">%s</setting>\n", key_val,
-			data_val);
-	g_free(key_val);
-	g_free(data_val);
-}
-
-static void blist_print_buddy_settings(gpointer key, gpointer data,
-		gpointer user_data) {
-	char *key_val;
-	char *data_val;
-	FILE *file = user_data;
-
-	if(!key || !data)
-		return;
-
-	key_val = g_markup_escape_text(key, -1);
-	data_val = g_markup_escape_text(data, -1);
-
-	fprintf(file, "\t\t\t\t\t<setting name=\"%s\">%s</setting>\n", key_val,
-			data_val);
-	g_free(key_val);
-	g_free(data_val);
-}
-
-static void gaim_blist_write(FILE *file, struct gaim_account *exp_acct) {
-	GSList *accounts, *buds;
-	GaimBlistNode *gnode,*bnode;
-	struct group *group;
-	struct buddy *bud;
-	fprintf(file, "<?xml version='1.0' encoding='UTF-8' ?>\n");
-	fprintf(file, "<gaim version=\"1\">\n");
-	fprintf(file, "\t<blist>\n");
-
-	for(gnode = gaimbuddylist->root; gnode; gnode = gnode->next) {
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		group = (struct group *)gnode;
-		if(!exp_acct || gaim_group_on_account(group, exp_acct)) {
-			char *group_name = g_markup_escape_text(group->name, -1);
-			fprintf(file, "\t\t<group name=\"%s\">\n", group_name);
-			g_hash_table_foreach(group->settings, blist_print_group_settings, file);
-			for(bnode = gnode->child; bnode; bnode = bnode->next) {
-				if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-					continue;
-				bud = (struct buddy *)bnode;
-				if(!exp_acct || bud->account == exp_acct) {
-					char *bud_name = g_markup_escape_text(bud->name, -1);
-					char *bud_alias = NULL;
-					char *acct_name = g_markup_escape_text(bud->account->username, -1);
-					if(bud->alias)
-						bud_alias= g_markup_escape_text(bud->alias, -1);
-					fprintf(file, "\t\t\t<person name=\"%s\">\n",
-							bud_alias ? bud_alias : bud_name);
-					fprintf(file, "\t\t\t\t<buddy protocol=\"%d\" "
-							"account=\"%s\">\n", bud->account->protocol,
-							acct_name);
-					fprintf(file, "\t\t\t\t\t<name>%s</name>\n", bud_name);
-					if(bud_alias) {
-						fprintf(file, "\t\t\t\t\t<alias>%s</alias>\n",
-								bud_alias);
-					}
-					g_hash_table_foreach(bud->settings,
-							blist_print_buddy_settings, file);
-					fprintf(file, "\t\t\t\t</buddy>\n");
-					fprintf(file, "\t\t\t</person>\n");
-					g_free(bud_name);
-					g_free(bud_alias);
-					g_free(acct_name);
-				}
-			}
-			fprintf(file, "\t\t</group>\n");
-			g_free(group_name);
-		}
-	}
-
-	fprintf(file, "\t</blist>\n");
-	fprintf(file, "\t<privacy>\n");
-
-	for(accounts = gaim_accounts; accounts; accounts = accounts->next) {
-		struct gaim_account *account = accounts->data;
-		char *acct_name = g_markup_escape_text(account->username, -1);
-		if(!exp_acct || account == exp_acct) {
-			fprintf(file, "\t\t<account protocol=\"%d\" name=\"%s\" "
-					"mode=\"%d\">\n", account->protocol, acct_name, account->permdeny);
-			for(buds = account->permit; buds; buds = buds->next) {
-				char *bud_name = g_markup_escape_text(buds->data, -1);
-				fprintf(file, "\t\t\t<permit>%s</permit>\n", bud_name);
-				g_free(bud_name);
-			}
-			for(buds = account->deny; buds; buds = buds->next) {
-				char *bud_name = g_markup_escape_text(buds->data, -1);
-				fprintf(file, "\t\t\t<block>%s</block>\n", bud_name);
-				g_free(bud_name);
-			}
-			fprintf(file, "\t\t</account>\n");
-		}
-		g_free(acct_name);
-	}
-
-	fprintf(file, "\t</privacy>\n");
-	fprintf(file, "</gaim>\n");
-}
-
-void gaim_blist_save() {
-	FILE *file;
-	char *user_dir = gaim_user_dir();
-	char *filename;
-	char *filename_real;
-
-	if(!user_dir)
-		return;
-	if(!blist_safe_to_write) {
-		gaim_debug(GAIM_DEBUG_WARNING, "blist save",
-				   "AHH!! Tried to write the blist before we read it!\n");
-		return;
-	}
-
-	file = fopen(user_dir, "r");
-	if(!file)
-		mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
-	else
-		fclose(file);
-
-	filename = g_build_filename(user_dir, "blist.xml.save", NULL);
-
-	if((file = fopen(filename, "w"))) {
-		gaim_blist_write(file, NULL);
-		fclose(file);
-		chmod(filename, S_IRUSR | S_IWUSR);
-	} else {
-		gaim_debug(GAIM_DEBUG_ERROR, "blist save", "Unable to write %s\n",
-				   filename);
-	}
-
-	filename_real = g_build_filename(user_dir, "blist.xml", NULL);
-
-	if(rename(filename, filename_real) < 0)
-		gaim_debug(GAIM_DEBUG_ERROR, "blist save",
-				   "Error renaming %s to %s\n", filename, filename_real);
-
-
-	g_free(filename);
-	g_free(filename_real);
-}
-
-gboolean gaim_privacy_permit_add(struct gaim_account *account, const char *who) {
-	GSList *d = account->permit;
-	char *n = g_strdup(normalize(who));
-	while(d) {
-		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
-			break;
-		d = d->next;
-	}
-	g_free(n);
-	if(!d) {
-		account->permit = g_slist_append(account->permit, g_strdup(who));
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-gboolean gaim_privacy_permit_remove(struct gaim_account *account, const char *who) {
-	GSList *d = account->permit;
-	char *n = g_strdup(normalize(who));
-	while(d) {
-		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
-			break;
-		d = d->next;
-	}
-	g_free(n);
-	if(d) {
-		account->permit = g_slist_remove(account->permit, d->data);
-		g_free(d->data);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-gboolean gaim_privacy_deny_add(struct gaim_account *account, const char *who) {
-	GSList *d = account->deny;
-	char *n = g_strdup(normalize(who));
-	while(d) {
-		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
-			break;
-		d = d->next;
-	}
-	g_free(n);
-	if(!d) {
-		account->deny = g_slist_append(account->deny, g_strdup(who));
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-gboolean gaim_privacy_deny_remove(struct gaim_account *account, const char *who) {
-	GSList *d = account->deny;
-	char *n = g_strdup(normalize(who));
-	while(d) {
-		if(!gaim_utf8_strcasecmp(n, normalize(d->data)))
-			break;
-		d = d->next;
-	}
-	g_free(n);
-	if(d) {
-		account->deny = g_slist_remove(account->deny, d->data);
-		g_free(d->data);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-void gaim_group_set_setting(struct group *g, const char *key,
-		const char *value) {
-	if(!g)
-		return;
-	g_hash_table_replace(g->settings, g_strdup(key), g_strdup(value));
-}
-
-char *gaim_group_get_setting(struct group *g, const char *key) {
-	if(!g)
-		return NULL;
-	return g_strdup(g_hash_table_lookup(g->settings, key));
-}
-
-void gaim_buddy_set_setting(struct buddy *b, const char *key,
-		const char *value) {
-	if(!b)
-		return;
-	g_hash_table_replace(b->settings, g_strdup(key), g_strdup(value));
-}
-
-char *gaim_buddy_get_setting(struct buddy *b, const char *key) {
-	if(!b)
-		return NULL;
-	return g_strdup(g_hash_table_lookup(b->settings, key));
-}
-
-void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops)
-{
-	blist_ui_ops = ops;
-}
-
-struct gaim_blist_ui_ops *
-gaim_get_blist_ui_ops(void)
-{
-	return blist_ui_ops;
-}
-
-int gaim_blist_get_group_size(struct group *group, gboolean offline) {
-	GaimBlistNode *node;
-	int count = 0;
-
-	if(!group)
-		return 0;
-
-	for(node = group->node.child; node; node = node->next) {
-		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			struct buddy *b = (struct buddy *)node;
-			if(b->account->gc || offline)
-				count++;
-		}
-	}
-
-	return count;
-}
-
-int gaim_blist_get_group_online_count(struct group *group) {
-	GaimBlistNode *node;
-	int count = 0;
-
-	if(!group)
-		return 0;
-
-	for(node = group->node.child; node; node = node->next) {
-		if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			struct buddy *b = (struct buddy *)node;
-			if(GAIM_BUDDY_IS_ONLINE(b))
-				count++;
-		}
-	}
-
-	return count;
-}
-
-
--- a/src/list.h	Sat Apr 26 17:36:52 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,479 +0,0 @@
-/**
- * @file list.h Buddy List API
- * @ingroup core
- *
- * gaim
- *
- * Copyright (C) 2003, Sean Egan <sean.egan@binghamton.edu>
- * 
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* I can't believe I let ChipX86 inspire me to write good code. -Sean */
-
-#ifndef _LIST_H_
-#define _LIST_H_
-
-#include <glib.h>
-
-/**************************************************************************/
-/* Enumerations                                                           */
-/**************************************************************************/
-enum gaim_blist_node_type {
-	GAIM_BLIST_GROUP_NODE,
-	GAIM_BLIST_BUDDY_NODE,
-	GAIM_BLIST_OTHER_NODE,
-};
-
-#define GAIM_BLIST_NODE_IS_BUDDY(n) ((n)->type == GAIM_BLIST_BUDDY_NODE)
-#define GAIM_BLIST_NODE_IS_GROUP(n) ((n)->type == GAIM_BLIST_GROUP_NODE)
-
-enum gaim_buddy_presence_state {
-	GAIM_BUDDY_SIGNING_OFF = -1,
-	GAIM_BUDDY_OFFLINE = 0,
-	GAIM_BUDDY_ONLINE,
-	GAIM_BUDDY_SIGNING_ON,
-};
-
-#define GAIM_BUDDY_IS_ONLINE(b) ((b)->present == GAIM_BUDDY_ONLINE || \
-		(b)->present == GAIM_BUDDY_SIGNING_ON)
-
-
-/**************************************************************************/
-/* Data Structures                                                        */
-/**************************************************************************/
-
-typedef struct _GaimBlistNode GaimBlistNode;
-/**
- * A Buddy list node.  This can represent a group, a buddy, or anything else.  This is a base class for struct buddy and
- * struct group and for anything else that wants to put itself in the buddy list. */
-struct _GaimBlistNode {
-	enum gaim_blist_node_type type;        /**< The type of node this is       */
-	GaimBlistNode *prev;                   /**< The sibling before this buddy. */
-	GaimBlistNode *next;                   /**< The sibling after this buddy.  */
-	GaimBlistNode *parent;                 /**< The parent of this node        */
-	GaimBlistNode *child;                  /**< The child of this node         */
-	void          *ui_data;                /**< The UI can put data here.      */
-};
-
-/**
- * A buddy.  This contains everything Gaim will ever need to know about someone on the buddy list.  Everything.
- */
-struct buddy {
-	GaimBlistNode node;                     /**< The node that this buddy inherits from */
-	char *name;                             /**< The screenname of the buddy. */
-	char *alias;                            /**< The user-set alias of the buddy */
-	char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */ 
-	enum gaim_buddy_presence_state present;                            /**< This is 0 if the buddy appears offline, 1 if he appears online, and 2 if
-						    he has recently signed on */
-	int evil;                               /**< The warning level */
-	time_t signon;                          /**< The time the buddy signed on. */
-	int idle;                               /**< The time the buddy has been idle in minutes. */
-	int uc;                                 /**< This is a cryptic bitmask that makes sense only to the prpl.  This will get changed */
-	void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
-	struct gaim_account *account;           /**< the account this buddy belongs to */ 
-	GHashTable *settings;                   /**< per-buddy settings from the XML buddy list, set by plugins and the likes. */
-	guint timer;							/**< The timer handle. */
-};
-
-/**
- * A group.  This contains everything Gaim will ever need to know about a group.
- */
-struct group {
-	GaimBlistNode node;                    /**< The node that this group inherits from */
-	char *name;                            /**< The name of this group. */
-	GHashTable *settings;                  /**< per-group settings from the XML buddy list, set by plugins and the likes. */
-};
-
-
-/**
- * The Buddy List
- */
-struct gaim_buddy_list {
-	GaimBlistNode *root;                    /**< The first node in the buddy list */
-	struct gaim_blist_ui_ops *ui_ops;       /**< The UI operations for the buddy list */
-
-	void *ui_data;                          /**< UI-specific data. */
-};
-
-/**
- * Buddy list UI operations.
- *
- * Any UI representing a buddy list must assign a filled-out gaim_window_ops
- * structure to the buddy list core.
- */
-struct gaim_blist_ui_ops
-{
-	void (*new_list)(struct gaim_buddy_list *list); /**< Sets UI-specific data on a buddy list. */
-	void (*new_node)(GaimBlistNode *node);      /**< Sets UI-specific data on a node. */
-	void (*show)(struct gaim_buddy_list *list);     /**< The core will call this when its finished doing it's core stuff */
-	void (*update)(struct gaim_buddy_list *list, 
-		       GaimBlistNode *node);            /**< This will update a node in the buddy list. */
-	void (*remove)(struct gaim_buddy_list *list,
-		       GaimBlistNode *node);            /**< This removes a node from the list */
-	void (*destroy)(struct gaim_buddy_list *list);  /**< When the list gets destroyed, this gets called to destroy the UI. */
-	void (*set_visible)(struct gaim_buddy_list *list,
-			    gboolean show);             /**< Hides or unhides the buddy list */
-
-};
-
-/**************************************************************************/
-/** @name Buddy List API                                                  */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new buddy list
- */
-struct gaim_buddy_list *gaim_blist_new();
-
-/**
- * Sets the main buddy list.
- *
- * @return The main buddy list.
- */
-void gaim_set_blist(struct gaim_buddy_list *blist);
-
-/**
- * Returns the main buddy list.
- *
- * @return The main buddy list.
- */
-struct gaim_buddy_list *gaim_get_blist(void);
-
-/**
- * Shows the buddy list, creating a new one if necessary.
- *
- */
-void gaim_blist_show();
-
-
-/**
- * Destroys the buddy list window.
- */
-void gaim_blist_destroy();
-
-/**
- * Hides or unhides the buddy list.
- *
- * @param show   Whether or not to show the buddy list
- */
-void gaim_blist_set_visible(gboolean show);
-
-/**
- * Updates a buddy's status.
- * 
- * This needs to not take an int.
- *
- * @param buddy   The buddy whose status has changed
- * @param status  The new status in cryptic prpl-understood code
- */
-void gaim_blist_update_buddy_status(struct buddy *buddy, int status);
-
-
-/**
- * Updates a buddy's presence.
- *
- * @param buddy    The buddy whose presence has changed
- * @param presence The new presence
- */
-void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence);
-
-
-/**
- * Updates a buddy's idle time.
- *
- * @param buddy  The buddy whose idle time has changed
- * @param idle   The buddy's idle time in minutes.
- */
-void gaim_blist_update_buddy_idle(struct buddy *buddy, int idle);
-
-
-/**
- * Updates a buddy's warning level.
- *
- * @param buddy  The buddy whose warning level has changed
- * @param evil   The warning level as an int from 0 to 100 (or higher, I guess... but that'd be weird)
- */
-void gaim_blist_update_buddy_evil(struct buddy *buddy, int warning);
-
-/**
- * Updates a buddy's icon.
- *
- * @param buddy  The buddy whose buddy icon has changed
- */
-void gaim_blist_update_buddy_icon(struct buddy *buddy);
-
-
-
-/**
- * Renames a buddy in the buddy list.
- *
- * @param buddy  The buddy whose name will be changed.
- * @param name   The new name of the buddy.
- */
-void gaim_blist_rename_buddy(struct buddy *buddy, const char *name);
-
-
-/**
- * Aliases a buddy in the buddy list.
- *
- * @param buddy  The buddy whose alias will be changed.
- * @param alias  The buddy's alias.
- */
-void gaim_blist_alias_buddy(struct buddy *buddy, const char *alias);
-
-
-/**
- * Renames a group
- *
- * @param group  The group to rename
- * @param name   The new name
- */
-void gaim_blist_rename_group(struct group *group, const char *name);
-
-
-/**
- * Creates a new buddy
- *
- * @param account    The account this buddy will get added to
- * @param screenname The screenname of the new buddy
- * @param alias      The alias of the new buddy (or NULL if unaliased)
- * @return           A newly allocated buddy
- */
-struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias);
-
-/**
- * Adds a new buddy to the buddy list.
- *
- * The buddy will be inserted right after node or appended to the end
- * of group if node is NULL.  If both are NULL, the buddy will be added to
- * the "Buddies" group.
- *
- * @param buddy  The new buddy who gets added
- * @param group  The group to add the new buddy to.
- * @param node   The insertion point 
- */
-void gaim_blist_add_buddy(struct buddy *buddy, struct group *group, GaimBlistNode *node);
-
-/**
- * Creates a new group
- *
- * You can't have more than one group with the same name.  Sorry.  If you pass this the 
- * name of a group that already exists, it will return that group.
- *
- * @param name   The name of the new group
- * @return       A new group struct 
-*/
-struct group *gaim_group_new(const char *name);
-
-/**
- * Adds a new group to the buddy list.
- *
- * The new group will be inserted after insert or appended to the end of
- * the list if node is NULL.
- *
- * @param group  The group to add the new buddy to.
- * @param node   The insertion point 
- */
-void gaim_blist_add_group(struct group *group, GaimBlistNode *node);
-
-/**
- * Removes a buddy from the buddy list and frees the memory allocated to it.
- *
- * @param buddy   The buddy to be removed
- */
-void gaim_blist_remove_buddy(struct buddy *buddy);
-
-/**
- * Removes a group from the buddy list and frees the memory allocated to it and to
- * its children
- *
- * @param group   The group to be removed
- */
-void gaim_blist_remove_group(struct group *group);
-
-/**
- * Returns the alias of a buddy.
- *
- * @param buddy   The buddy whose name will be returned.
- * @return        The alias (if set), server alias (if option is set), or NULL.
- */
-char *gaim_get_buddy_alias_only(struct buddy *buddy);
-
-
-/**
- * Returns the correct name to display for a buddy.
- *
- * @param buddy   The buddy whose name will be returned.
- * @return        The alias (if set), server alias (if option is set), screenname, or "Unknown"
- */
-char *gaim_get_buddy_alias(struct buddy *buddy);
-
-/**
- * Finds the buddy struct given a screenname and an account
- *
- * @param name    The buddy's screenname
- * @param account The account this buddy belongs to
- * @return        The buddy or NULL if the buddy does not exist
- */
-struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name);   
-
-/**
- * Finds a group by name
- *
- * @param name    The groups name
- * @return        The group or NULL if the group does not exist
- */
-struct group *gaim_find_group(const char *name);   
-
-/**
- * Returns the group of which the buddy is a member.
- *
- * @param buddy   The buddy
- * @return        The group or NULL if the buddy is not in a group
- */
-struct group *gaim_find_buddys_group(struct buddy *buddy);
-
-
-/**
- * Returns a list of accounts that have buddies in this group
- *
- * @param group   The group
- * @return        A list of gaim_accounts
- */
-GSList *gaim_group_get_accounts(struct group *g);
-
-/**
- * Determines whether an account owns any buddies in a given group
- *
- * @param g       The group to search through.
- * @param account The account.
- */
-gboolean gaim_group_on_account(struct group *g, struct gaim_account *account);
-
-/**
- * Called when an account gets signed off.  Sets the presence of all the buddies to 0
- * and tells the UI to update them.
- *
- * @param account   The account 
- */
-void gaim_blist_remove_account(struct gaim_account *account);
-
-
-/**
- * Determines the total size of a group
- *
- * @param group  The group
- * @param offline Count buddies in offline accounts
- * @return The number of buddies in the group
- */
-int gaim_blist_get_group_size(struct group *group, gboolean offline);
-
-/**
- * Determines the number of online buddies in a group
- *
- * @param group The group
- * @return The number of online buddies in the group, or 0 if the group is NULL
- */
-int gaim_blist_get_group_online_count(struct group *group);
-
-/*@}*/
-
-/****************************************************************************************/
-/** @name Buddy list file management API                                                */
-/****************************************************************************************/
-
-/*@{*/
-/**
- * Saves the buddy list to file
- */
-void gaim_blist_save();
-
-/**
- * Parses the toc-style buddy list used in older versions of Gaim and for SSI in toc.c
- *
- * @param account  This is the account that the buddies and groups from config will get added to
- * @param config   This is the toc-style buddy list data
- */
-void parse_toc_buddy_list(struct gaim_account *account, char *config);
-
-
-/**
- * Loads the buddy list from ~/.gaim/blist.xml.
- */
-void gaim_blist_load();
-
-/**
- * Associates some data with the group in the xml buddy list
- *
- * @param g      The group the data is associated with
- * @param key    The key used to retrieve the data
- * @param value  The data to set
- */
-void gaim_group_set_setting(struct group *g, const char *key, const char *value);
-
-/**
- * Retrieves data from the XML buddy list set by gaim_group_set_setting())
- *
- * @param g      The group to retrieve data from
- * @param key    The key to retrieve the data with
- * @return       The associated data or NULL if no data is associated
- */
-char *gaim_group_get_setting(struct group *g, const char *key);
-
-
-/**
- * Associates some data with the buddy in the xml buddy list
- *
- * @param b      The buddy the data is associated with
- * @param key    The key used to retrieve the data
- * @param value  The data to set
- */
-void gaim_buddy_set_setting(struct buddy *b, const char *key, const char *value);
-
-/**
- * Retrieves data from the XML buddy list set by gaim_buddy_set_setting())
- *
- * @param b      The buddy to retrieve data from
- * @param key    The key to retrieve the data with
- * @return       The associated data or NULL if no data is associated
- */
-char *gaim_buddy_get_setting(struct buddy *b, const char *key);
-
-/*@}*/
-
-/**************************************************************************/
-/** @name UI Registration Functions                                       */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Sets the UI operations structure to be used for the buddy list.
- *
- * @param ops The ops struct.
- */
-void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops);
-
-/**
- * Returns the UI operations structure to be used for the buddy list.
- *
- * @return The UI operations structure.
- */
-struct gaim_blist_ui_ops *gaim_get_blist_ui_ops(void);
-
-/*@}*/
-
-#endif /* _LIST_H_ */
--- a/src/main.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/main.c	Sat Apr 26 17:56:23 2003 +0000
@@ -50,7 +50,7 @@
 #include "sound.h"
 #include "gaim.h"
 #include "gaim-socket.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "gtkdebug.h"
 #if HAVE_SIGNAL_H
 #include <signal.h>
--- a/src/multi.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/multi.c	Sat Apr 26 17:56:23 2003 +0000
@@ -27,7 +27,7 @@
 #include "multi.h"
 #include "gaim.h"
 #include "conversation.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "gaim-disclosure.h"
 
 #ifdef _WIN32
--- a/src/prefs.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/prefs.c	Sat Apr 26 17:56:23 2003 +0000
@@ -36,7 +36,7 @@
 #include <gtk/gtk.h>
 #include "gtkimhtml.h"
 #include "gaim.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "gtkplugin.h"
 #include "prpl.h"
 #include "proxy.h"
--- a/src/prpl.c	Sat Apr 26 17:36:52 2003 +0000
+++ b/src/prpl.c	Sat Apr 26 17:56:23 2003 +0000
@@ -21,7 +21,7 @@
 
 #include "gaim.h"
 #include "gtkutils.h"
-#include "gtklist.h"
+#include "gtkblist.h"
 #include "prpl.h"
 #include <sys/types.h>
 #include <sys/stat.h>