changeset 22012:0503cd74cb56

merge of '418bc7dd4e063c50039bba0d51b489b568f9aaaa' and '4df361b4c7c545ea522212487ebfd8e4bda0345f'
author Richard Laager <rlaager@wiktel.com>
date Sat, 05 Jan 2008 18:07:16 +0000
parents 485383451769 (diff) 76e0463db3aa (current diff)
children fefe61275687
files COPYRIGHT ChangeLog configure.ac finch/gntblist.c finch/libgnt/gntwm.c pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/plugins/spellchk.c
diffstat 34 files changed, 1242 insertions(+), 790 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Jan 05 18:01:12 2008 +0000
+++ b/COPYRIGHT	Sat Jan 05 18:07:16 2008 +0000
@@ -151,6 +151,7 @@
 Michael Golden
 Charlie Gordon
 Ryan C. Gordon
+Konrad Gräfe
 Miah Gregory
 David Grohmann
 Christian Hammond
--- a/ChangeLog	Sat Jan 05 18:01:12 2008 +0000
+++ b/ChangeLog	Sat Jan 05 18:07:16 2008 +0000
@@ -7,6 +7,8 @@
 	* Eliminated unmaintained Howl backend implementation for the
 	  Bonjour protocol.  Avahi (or Apple's Bonjour runtime on win32) is
 	  now required to use Bonjour.
+	* Partial support for viewing ICQ status notes (Collin from
+	  ComBOTS GmbH).
 
 	Pidgin:
 	* Added the ability to theme conversation name colors (red and blue)
--- a/ChangeLog.API	Sat Jan 05 18:01:12 2008 +0000
+++ b/ChangeLog.API	Sat Jan 05 18:07:16 2008 +0000
@@ -8,6 +8,7 @@
 		* purple_major_version, purple_minor_version,
 		  purple_micro_version variables are exported by version.h,
 		  giving the version of libpurple in use at runtime.
+		* purple_util_set_current_song, purple_util_format_song_info
 
 	Pidgin:
 		Added:
@@ -24,9 +25,11 @@
 		* pidgin_tooltip_setup_for_treeview, pidgin_tooltip_destroy,
 		  pidgin_tooltip_show and pidgin_tooltip_setup_for_widget to simplify
 		  the process of drawing tooltips.
+		* pidgin_add_widget_to_vbox to simplify adding a labeled widget to a
+		  window.
 
-	        Deprecated:
-	        * PIDGIN_DIALOG
+		Deprecated:
+		* PIDGIN_DIALOG
 
 	Finch:
 		libgnt:
--- a/configure.ac	Sat Jan 05 18:01:12 2008 +0000
+++ b/configure.ac	Sat Jan 05 18:07:16 2008 +0000
@@ -142,7 +142,7 @@
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
 dnl AM_GLIB_GNU_GETTEXT found it.
 
-if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT = x
+if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
 then
 	AC_ERROR([
 
--- a/doc/finch.1.in	Sat Jan 05 18:01:12 2008 +0000
+++ b/doc/finch.1.in	Sat Jan 05 18:07:16 2008 +0000
@@ -105,6 +105,12 @@
 .B Alt \+ 1 2 ... 0
 Jump to the 1st, 2nd ... 10th window.
 .TP
+.B Alt \+ Tab
+Jump to the next URGENT (highlighted) window.
+.TP
+.B Alt \+ Shift \+ Tab
+Jump to the previous URGENT (highlighted) window.
+.TP
 .B Ctrl \+ o
 Bring up the menu (if there is one) for a window.
 .TP
@@ -460,6 +466,7 @@
 .br
 # switch-window-n
 .br
+# Other actions: window-next-urgent, window-prev-urgent
 
 # For the sample custom window manager
 .br
--- a/finch/gntblist.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/finch/gntblist.c	Sat Jan 05 18:07:16 2008 +0000
@@ -83,6 +83,12 @@
 	GntMenuItem *plugins;
 } FinchBlist;
 
+typedef struct
+{
+	gpointer row;                /* the row in the GntTree */
+	guint signed_timer;          /* used when 'recently' signed on/off */
+} FinchBlistNode;
+
 typedef enum
 {
 	STATUS_PRIMITIVE = 0,
@@ -131,6 +137,31 @@
 static int color_offline;
 static int color_idle;
 
+static FinchBlistNode *
+create_finch_blist_node(PurpleBlistNode *node, gpointer row)
+{
+	FinchBlistNode *fnode = node->ui_data;
+	if (!fnode) {
+		fnode = g_new0(FinchBlistNode, 1);
+		fnode->signed_timer = 0;
+		node->ui_data = fnode;
+	}
+	fnode->row = row;
+	return fnode;
+}
+
+static void
+reset_blist_node_ui_data(PurpleBlistNode *node)
+{
+	FinchBlistNode *fnode = node->ui_data;
+	if (fnode == NULL)
+		return;
+	if (fnode->signed_timer)
+		purple_timeout_remove(fnode->signed_timer);
+	g_free(fnode);
+	node->ui_data = NULL;
+}
+
 static int
 get_display_color(PurpleBlistNode  *node)
 {
@@ -157,6 +188,34 @@
 	return color;
 }
 
+static GntTextFormatFlags
+get_blist_node_flag(PurpleBlistNode *node)
+{
+	GntTextFormatFlags flag = 0;
+	FinchBlistNode *fnode = node->ui_data;
+
+	if (ggblist->tagged && g_list_find(ggblist->tagged, node))
+		flag |= GNT_TEXT_FLAG_BOLD;
+
+	if (fnode && fnode->signed_timer)
+		flag |= GNT_TEXT_FLAG_BLINK;
+	else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact *)node);
+		fnode = node->ui_data;
+		if (fnode && fnode->signed_timer)
+			flag |= GNT_TEXT_FLAG_BLINK;
+	}
+
+	return flag;
+}
+
+static void
+blist_update_row_flags(PurpleBlistNode *node)
+{
+	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, get_blist_node_flag(node));
+	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
+}
+
 static gboolean
 is_contact_online(PurpleContact *contact)
 {
@@ -217,7 +276,7 @@
 		return;
 
 	gnt_tree_remove(GNT_TREE(ggblist->tree), node);
-	node->ui_data = NULL;
+	reset_blist_node_ui_data(node);
 	if (ggblist->tagged)
 		ggblist->tagged = g_list_remove(ggblist->tagged, node);
 
@@ -234,7 +293,7 @@
 				(!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)))
 			node_remove(list, node->parent);
 		for (node = node->child; node; node = node->next)
-			node->ui_data = NULL;
+			reset_blist_node_ui_data(node);
 	} else {
 		for (node = node->child; node; node = node->next)
 			node_remove(list, node);
@@ -261,7 +320,7 @@
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
 		gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
+		blist_update_row_flags(node);
 	}
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -269,8 +328,6 @@
 		if (purple_account_is_connected(buddy->account) &&
 				(PURPLE_BUDDY_IS_ONLINE(buddy) || purple_prefs_get_bool(PREF_ROOT "/showoffline")))
 			add_node((PurpleBlistNode*)buddy, list->ui_data);
-		else
-			node_remove(purple_get_blist(), node);
 
 		node_update(list, node->parent);
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
@@ -279,7 +336,7 @@
 		PurpleContact *contact = (PurpleContact*)node;
 		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) ||
 				contact->currentsize < 1)
-			node_remove(purple_get_blist(), node);
+			/* nothing */;
 		else {
 			if (node->ui_data == NULL) {
 				/* The core seems to expect the UI to add the buddies. */
@@ -524,8 +581,8 @@
 	PurpleBlistNode *node = (PurpleBlistNode *)group;
 	if (node->ui_data)
 		return;
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
-			gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL);
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
+			gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL));
 	gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node,
 		!purple_blist_node_get_bool(node, "collapsed"));
 }
@@ -539,7 +596,7 @@
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
 		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);  /* XXX: this can return NULL?! */
-	
+
 	if (node == NULL)
 		return NULL;
 
@@ -550,7 +607,7 @@
 		PurplePresence *presence;
 		PurpleStatus *now;
 		gboolean ascii = gnt_ascii_only();
-		
+
 		presence = purple_buddy_get_presence(buddy);
 		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
 			strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1);
@@ -601,9 +658,9 @@
 	group = purple_chat_get_group(chat);
 	add_node((PurpleBlistNode*)group, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
-				group, NULL);
+				group, NULL));
 }
 
 static void
@@ -623,9 +680,9 @@
 	group = (PurpleGroup*)node->parent;
 	add_node((PurpleBlistNode*)group, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), name),
-				group, NULL);
+				group, NULL));
 
 	gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE);
 }
@@ -635,7 +692,7 @@
 {
 	PurpleContact *contact;
 	PurpleBlistNode *node = (PurpleBlistNode *)buddy;
-	int color = 0;
+
 	if (node->ui_data)
 		return;
 
@@ -647,14 +704,13 @@
 		return;
 	add_node((PurpleBlistNode*)contact, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
-				contact, NULL);
-
-	color = get_display_color((PurpleBlistNode*)buddy);
-	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
+				contact, NULL));
+
+	blist_update_row_flags((PurpleBlistNode*)buddy);
 	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
+		blist_update_row_flags((PurpleBlistNode*)contact);
 }
 
 #if 0
@@ -1564,9 +1620,7 @@
 static void
 update_node_display(PurpleBlistNode *node, FinchBlist *ggblist)
 {
-	GntTextFormatFlags flag = 0;
-	if (ggblist->tagged && g_list_find(ggblist->tagged, node))
-		flag |= GNT_TEXT_FLAG_BOLD;
+	GntTextFormatFlags flag = get_blist_node_flag(node);
 	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
 }
 
@@ -1574,33 +1628,18 @@
 update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist)
 {
 	PurpleContact *contact;
-	GntTextFormatFlags bflag = 0, cflag = 0;
-	int color = 0;
 
 	contact = purple_buddy_get_contact(buddy);
 
 	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
 	gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact));
 
-	if (ggblist->tagged && g_list_find(ggblist->tagged, buddy))
-		bflag |= GNT_TEXT_FLAG_BOLD;
-	if (ggblist->tagged && g_list_find(ggblist->tagged, contact))
-		cflag |= GNT_TEXT_FLAG_BOLD;
+	blist_update_row_flags((PurpleBlistNode *)buddy);
+	if (buddy == purple_contact_get_priority_buddy(contact))
+		blist_update_row_flags((PurpleBlistNode *)contact);
 
 	if (ggblist->tnode == (PurpleBlistNode*)buddy)
 		draw_tooltip(ggblist);
-
-	color = get_display_color((PurpleBlistNode*)buddy);
-	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
-	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
-
-	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag);
-	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag);
-
-	if (buddy != purple_contact_get_priority_buddy(contact))
-			update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist);
 }
 
 static void
@@ -1649,7 +1688,7 @@
 
 	node = purple_blist_get_root();
 	while (node) {
-		node->ui_data = NULL;
+		reset_blist_node_ui_data(node);
 		node = purple_blist_node_next(node, TRUE);
 	}
 
@@ -1762,7 +1801,7 @@
 	gnt_tree_remove_all(GNT_TREE(ggblist->tree));
 	node = purple_blist_get_root();
 	for (; node; node = purple_blist_node_next(node, TRUE))
-		node->ui_data = NULL;
+		reset_blist_node_ui_data(node);
 	populate_buddylist();
 	gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel);
 	draw_tooltip(ggblist);
@@ -2122,6 +2161,51 @@
 	}
 }
 
+static gboolean
+buddy_recent_signed_on_off(gpointer data)
+{
+	PurpleBlistNode *node = data;
+	FinchBlistNode *fnode = node->ui_data;
+	PurpleBuddy *buddy = (PurpleBuddy*)node;
+
+	purple_timeout_remove(fnode->signed_timer);
+	fnode->signed_timer = 0;
+
+	if (!purple_account_is_connected(buddy->account) ||
+			(!PURPLE_BUDDY_IS_ONLINE(buddy) && !purple_prefs_get_bool(PREF_ROOT "/showoffline"))) {
+		node_remove(purple_get_blist(), node);
+	} else {
+		update_node_display(node, ggblist);
+		if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent))
+			update_node_display(node->parent, ggblist);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+buddy_signed_on_off_cb(gpointer data)
+{
+	PurpleBlistNode *node = data;
+	FinchBlistNode *fnode = node->ui_data;
+	if (!ggblist || !fnode)
+		return FALSE;
+
+	if (fnode->signed_timer)
+		purple_timeout_remove(fnode->signed_timer);
+	fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data);
+	update_node_display(node, ggblist);
+	if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent))
+		update_node_display(node->parent, ggblist);
+	return FALSE;
+}
+
+static void
+buddy_signed_on_off(PurpleBuddy* buddy, gpointer null)
+{
+	g_idle_add(buddy_signed_on_off_cb, buddy);
+}
+
 static void
 reconstruct_plugins_menu(void)
 {
@@ -2513,12 +2597,12 @@
 	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(),
 				PURPLE_CALLBACK(reconstruct_plugins_menu), NULL);
 
+	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
+				PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
+	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
+				PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
+
 #if 0
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
-				PURPLE_CALLBACK(buddy_signed_on), ggblist);
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
-				PURPLE_CALLBACK(buddy_signed_off), ggblist);
-
 	/* These I plan to use to indicate unread-messages etc. */
 	purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", finch_blist_get_handle(),
 				PURPLE_CALLBACK(received_im_msg), list);
--- a/finch/libgnt/gntwm.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/finch/libgnt/gntwm.c	Sat Jan 05 18:07:16 2008 +0000
@@ -388,10 +388,10 @@
 }
 
 static void
-switch_window(GntWM *wm, int direction)
+switch_window(GntWM *wm, int direction, gboolean urgent)
 {
 	GntWidget *w = NULL, *wid = NULL;
-	int pos;
+	int pos, orgpos;
 
 	if (wm->_list.window || wm->menu)
 		return;
@@ -404,15 +404,20 @@
 	}
 
 	w = wm->cws->ordered->data;
-	pos = g_list_index(wm->cws->list, w);
-	pos += direction;
+	orgpos = pos = g_list_index(wm->cws->list, w);
+
+	do {
+		pos += direction;
 
-	if (pos < 0)
-		wid = g_list_last(wm->cws->list)->data;
-	else if (pos >= g_list_length(wm->cws->list))
-		wid = wm->cws->list->data;
-	else if (pos >= 0)
-		wid = g_list_nth_data(wm->cws->list, pos);
+		if (pos < 0) {
+			wid = g_list_last(wm->cws->list)->data;
+			pos = g_list_length(wm->cws->list) - 1;
+		} else if (pos >= g_list_length(wm->cws->list)) {
+			wid = wm->cws->list->data;
+			pos = 0;
+		} else
+			wid = g_list_nth_data(wm->cws->list, pos);
+	} while (urgent && !GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT) && pos != orgpos);
 
 	gnt_wm_raise_window(wm, wid);
 }
@@ -421,7 +426,7 @@
 window_next(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	switch_window(wm, 1);
+	switch_window(wm, 1, FALSE);
 	return TRUE;
 }
 
@@ -429,7 +434,7 @@
 window_prev(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	switch_window(wm, -1);
+	switch_window(wm, -1, FALSE);
 	return TRUE;
 }
 
@@ -1202,6 +1207,22 @@
 	return ignore_keys ? !(ignore_keys = FALSE) : FALSE;
 }
 
+static gboolean
+window_next_urgent(GntBindable *bindable, GList *n)
+{
+	GntWM *wm = GNT_WM(bindable);
+	switch_window(wm, 1, TRUE);
+	return TRUE;
+}
+
+static gboolean
+window_prev_urgent(GntBindable *bindable, GList *n)
+{
+	GntWM *wm = GNT_WM(bindable);
+	switch_window(wm, -1, TRUE);
+	return TRUE;
+}
+
 #ifdef USE_PYTHON
 static void
 python_script_selected(GntFileSel *fs, const char *path, const char *f, gpointer n)
@@ -1323,6 +1344,7 @@
 {
 	int i;
 	GObjectClass *gclass = G_OBJECT_CLASS(klass);
+	char key[32];
 
 	gclass->dispose = gnt_wm_destroy;
 
@@ -1482,10 +1504,15 @@
 				"\033" "\\", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window,
 				"\033" "|", NULL);
-	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start, 
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start,
 				GNT_KEY_CTRL_G, NULL);
-	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end, 
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end,
 				"\033" GNT_KEY_CTRL_G, NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next-urgent", window_next_urgent,
+				"\033" "\t", NULL);
+	snprintf(key, sizeof(key), "\033%s", GNT_KEY_BACK_TAB);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev-urgent", window_prev_urgent,
+				key[1] ? key : NULL, NULL);
 #ifdef USE_PYTHON
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "run-python", run_python,
 				GNT_KEY_F3, NULL);
--- a/libpurple/buddyicon.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/buddyicon.c	Sat Jan 05 18:07:16 2008 +0000
@@ -124,7 +124,7 @@
 							purple_imgstore_get_size(img));	
 	} else 	{
 		purple_debug_error("buddyicon", "Unable to create file %s: %s\n",
-		                   path, g_strerror(errno));
+		                   path, "File already exists.");
 	}
 	g_free(path);
 }
--- a/libpurple/protocols/bonjour/mdns_win32.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Sat Jan 05 18:07:16 2008 +0000
@@ -30,19 +30,21 @@
 
 static GSList *pending_buddies = NULL;
 
+typedef struct _dnssd_service_ref_handler {
+	DNSServiceRef sdRef;
+	PurpleAccount *account;
+	guint input_handler;
+} DnsSDServiceRefHandlerData;
+
 /* data used by win32 bonjour implementation */
 typedef struct _win32_session_impl_data {
-	DNSServiceRef presence_svc;
-	DNSServiceRef browser_svc;
+	DnsSDServiceRefHandlerData *presence_query;
+	DnsSDServiceRefHandlerData *browser_query;
 	DNSRecordRef buddy_icon_rec;
-
-	guint presence_handler;
-	guint browser_handler;
 } Win32SessionImplData;
 
 typedef struct _win32_buddy_service_resolver_data {
-	DNSServiceRef txt_query;
-	guint txt_query_handler;
+	DnsSDServiceRefHandlerData *txt_query;
 	uint32_t if_idx;
 	gchar *name;
 	gchar *type;
@@ -53,21 +55,20 @@
 
 typedef struct _win32_buddy_impl_data {
 	GSList *resolvers;
-	DNSServiceRef null_query;
-	guint null_query_handler;
+	DnsSDServiceRefHandlerData *null_query;
 } Win32BuddyImplData;
 
 /* data structure for the resolve callback */
 typedef struct _ResolveCallbackArgs {
-	DNSServiceRef resolver;
-	guint resolver_handler;
+	DnsSDServiceRefHandlerData *resolver_query;
 	PurpleAccount *account;
 	BonjourBuddy *bb;
 	Win32SvcResolverData *res_data;
 	gchar *full_service_name;
-	PurpleDnsQueryData *query;
+	PurpleDnsQueryData *dns_query;
 } ResolveCallbackArgs;
 
+
 static gint
 _find_resolver_data(gconstpointer a, gconstpointer b) {
 	const Win32SvcResolverData *rd_a = a;
@@ -87,8 +88,9 @@
 static void
 _cleanup_resolver_data(Win32SvcResolverData *rd) {
 	if (rd->txt_query != NULL) {
-		purple_input_remove(rd->txt_query_handler);
-		DNSServiceRefDeallocate(rd->txt_query);
+		purple_input_remove(rd->txt_query->input_handler);
+		DNSServiceRefDeallocate(rd->txt_query->sdRef);
+		g_free(rd->txt_query);
 	}
 	g_free(rd->name);
 	g_free(rd->type);
@@ -98,7 +100,16 @@
 
 static void
 _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
-	DNSServiceProcessResult((DNSServiceRef) data);
+	DnsSDServiceRefHandlerData *srh = data;
+	DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef);
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
+		/* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
+		if (errorCode == kDNSServiceErr_Unknown) {
+			purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Error communicating with local mDNSResponder."));
+		}
+	}
 }
 
 static void
@@ -123,7 +134,7 @@
 	uint32_t ttl, void *context)
 {
 
-	if (kDNSServiceErr_NoError != errorCode) {
+	if (errorCode != kDNSServiceErr_NoError) {
 		purple_debug_error("bonjour", "record query - callback error.\n");
 		/* TODO: Probably should remove the buddy when this happens */
 	} else if (flags & kDNSServiceFlagsAdd) {
@@ -142,9 +153,9 @@
 			bonjour_buddy_got_buddy_icon(bb, rdata, rdlen);
 
 			/* We've got what we need; stop listening */
-			purple_input_remove(idata->null_query_handler);
-			idata->null_query_handler = 0;
-			DNSServiceRefDeallocate(idata->null_query);
+			purple_input_remove(idata->null_query->input_handler);
+			DNSServiceRefDeallocate(idata->null_query->sdRef);
+			g_free(idata->null_query);
 			idata->null_query = NULL;
 		}
 	}
@@ -153,7 +164,7 @@
 static void
 _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
 {
-	ResolveCallbackArgs* args = (ResolveCallbackArgs*) data;
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*) data;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 	gboolean delete_buddy = FALSE;
 	PurpleBuddy *pb;
@@ -168,27 +179,31 @@
 		delete_buddy = TRUE;
 	} else {
 		struct sockaddr_in *addr = g_slist_nth_data(hosts, 1);
+		DNSServiceErrorType errorCode;
+		DNSServiceRef txt_query_sr;
 
 		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
-
-		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery,
+		errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery,
 				kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
-				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb)) {
-
+				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb);
+		if (errorCode == kDNSServiceErr_NoError) {
 			const char *ip = inet_ntoa(addr->sin_addr);
 
 			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
 
-
 			args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip));
 			args->res_data->ip = args->bb->ips->data;
 
-			args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query),
+			args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1);
+			args->res_data->txt_query->sdRef = txt_query_sr;
+			args->res_data->txt_query->account = args->account;
+
+			args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr),
 				PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
 
 			bonjour_buddy_add_to_purple(args->bb, NULL);
 		} else {
-			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s\n", args->bb->name);
+			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s (%d)\n", args->bb->name, errorCode);
 			delete_buddy = TRUE;
 		}
 
@@ -230,21 +245,21 @@
 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
     const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
 {
-	ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 
 	/* remove the input fd and destroy the service ref */
-	purple_input_remove(args->resolver_handler);
-	args->resolver_handler = 0;
-	DNSServiceRefDeallocate(args->resolver);
-	args->resolver = NULL;
+	purple_input_remove(args->resolver_query->input_handler);
+	DNSServiceRefDeallocate(args->resolver_query->sdRef);
+	g_free(args->resolver_query);
+	args->resolver_query = NULL;
 
-	if (kDNSServiceErr_NoError != errorCode)
+	if (errorCode != kDNSServiceErr_NoError)
 		purple_debug_error("bonjour", "service resolver - callback error.\n");
 	else {
 		/* set more arguments, and start the host resolver */
 
-		if ((args->query =
+		if ((args->dns_query =
 				purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) {
 
 			args->full_service_name = g_strdup(fullname);
@@ -286,7 +301,7 @@
 				const char *name, const char *regtype, const char *domain, void *context) {
 
 	/* TODO: deal with collision */
-	if (kDNSServiceErr_NoError != errorCode)
+	if (errorCode != kDNSServiceErr_NoError)
 		purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode);
 	else
 		purple_debug_info("bonjour", "service advertisement - callback.\n");
@@ -298,26 +313,28 @@
 {
 	PurpleAccount *account = (PurpleAccount*)context;
 
-	if (kDNSServiceErr_NoError != errorCode)
-		purple_debug_error("bonjour", "service browser - callback error\n");
+	if (errorCode != kDNSServiceErr_NoError)
+		purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode);
 	else if (flags & kDNSServiceFlagsAdd) {
 		/* A presence service instance has been discovered... check it isn't us! */
 		if (purple_utf8_strcasecmp(serviceName, account->username) != 0) {
+			DNSServiceErrorType resErrorCode;
 			/* OK, lets go ahead and resolve it to add to the buddy list */
 			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
+			DNSServiceRef resolver_sr;
 
 			purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
 							  serviceName, interfaceIndex, regtype ? regtype : "",
 							  replyDomain ? replyDomain : "");
 
-			if (kDNSServiceErr_NoError == DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
-					replyDomain, _mdns_service_resolve_callback, args)) {
+			resErrorCode = DNSServiceResolve(&resolver_sr, 0, 0, serviceName, regtype,
+					replyDomain, _mdns_service_resolve_callback, args);
+			if (resErrorCode == kDNSServiceErr_NoError) {
 				GSList *tmp = pending_buddies;
 				PurpleBuddy *pb;
 				BonjourBuddy* bb = NULL;
 				Win32SvcResolverData *rd;
 				Win32BuddyImplData *idata;
-				gint fd;
 
 				/* Is there an existing buddy? */
 				if ((pb = purple_find_buddy(account, serviceName)))
@@ -344,7 +361,6 @@
 						pb->proto_data = bb;
 				}
 
-
 				rd = g_new0(Win32SvcResolverData, 1);
 				rd->if_idx = interfaceIndex;
 				rd->name = g_strdup(serviceName);
@@ -358,11 +374,14 @@
 				args->res_data = rd;
 				args->account = account;
 
+				args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1);
+				args->resolver_query->sdRef = resolver_sr;
+				args->resolver_query->account = account;
 				/* get a file descriptor for this service ref, and add it to the input list */
-				fd = DNSServiceRefSockFD(args->resolver);
-				args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
+				args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr),
+					PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
 			} else {
-				purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
+				purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode);
 				g_free(args);
 			}
 		}
@@ -432,7 +451,7 @@
 gboolean _mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records) {
 	TXTRecordRef dns_data;
 	gboolean ret = TRUE;
-	DNSServiceErrorType set_ret = kDNSServiceErr_NoError;
+	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
 	Win32SessionImplData *idata = data->mdns_impl_data;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
@@ -441,44 +460,46 @@
 
 	while (records) {
 		PurpleKeyValuePair *kvp = records->data;
-		set_ret = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
-		if (set_ret != kDNSServiceErr_NoError)
+		errorCode = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
+		if (errorCode != kDNSServiceErr_NoError)
 			break;
 		records = records->next;
 	}
 
-	if (set_ret != kDNSServiceErr_NoError) {
-		purple_debug_error("bonjour", "Unable to allocate memory for text record.\n");
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Unable to allocate memory for text record.(%d)\n", errorCode);
 		ret = FALSE;
 	} else {
-		DNSServiceErrorType err = kDNSServiceErr_NoError;
-
 		/* OK, we're done constructing the text record, (re)publish the service */
+		DNSServiceRef presence_sr;
 
 		switch (type) {
 			case PUBLISH_START:
 				purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj);
-				err = DNSServiceRegister(&idata->presence_svc, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
+				errorCode = DNSServiceRegister(&presence_sr, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
 					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
 					_mdns_service_register_callback, NULL);
 				break;
 
 			case PUBLISH_UPDATE:
 				purple_debug_info("bonjour", "Updating presence.\n");
-				err = DNSServiceUpdateRecord(idata->presence_svc, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
+				errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
 				break;
 		}
 
-		if (err != kDNSServiceErr_NoError) {
-			purple_debug_error("bonjour", "Failed to publish presence service.\n");
+		if (errorCode != kDNSServiceErr_NoError) {
+			purple_debug_error("bonjour", "Failed to publish presence service.(%d)\n", errorCode);
 			ret = FALSE;
 		} else if (type == PUBLISH_START) {
 			/* We need to do this because according to the Apple docs:
 			 * "the client is responsible for ensuring that DNSServiceProcessResult() is called
 			 * whenever there is a reply from the daemon - the daemon may terminate its connection
 			 * with a client that does not process the daemon's responses */
-			idata->presence_handler = purple_input_add(DNSServiceRefSockFD(idata->presence_svc),
-				PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_svc);
+			idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1);
+			idata->presence_query->sdRef = presence_sr;
+			idata->presence_query->account = data->account;
+			idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query);
 		}
 	}
 
@@ -488,17 +509,24 @@
 }
 
 gboolean _mdns_browse(BonjourDnsSd *data) {
+	DNSServiceErrorType errorCode;
 	Win32SessionImplData *idata = data->mdns_impl_data;
+	DNSServiceRef browser_sr;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	if (DNSServiceBrowse(&idata->browser_svc, 0, 0, ICHAT_SERVICE, NULL,
-				 _mdns_service_browse_callback, data->account)
-			== kDNSServiceErr_NoError) {
-		idata->browser_handler = purple_input_add(DNSServiceRefSockFD(idata->browser_svc),
-			PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_svc);
+	errorCode = DNSServiceBrowse(&browser_sr, 0, 0, ICHAT_SERVICE, NULL,
+		_mdns_service_browse_callback, data->account);
+	if (errorCode == kDNSServiceErr_NoError) {
+		idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1);
+		idata->browser_query->sdRef = browser_sr;
+		idata->browser_query->account = data->account;
+		idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr),
+			PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query);
 		return TRUE;
-	}
+	} else
+		purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode);
+
 
 	return FALSE;
 }
@@ -509,14 +537,16 @@
 	if (idata == NULL)
 		return;
 
-	if (idata->presence_svc != NULL) {
-		purple_input_remove(idata->presence_handler);
-		DNSServiceRefDeallocate(idata->presence_svc);
+	if (idata->presence_query != NULL) {
+		purple_input_remove(idata->presence_query->input_handler);
+		DNSServiceRefDeallocate(idata->presence_query->sdRef);
+		g_free(idata->presence_query);
 	}
 
-	if (idata->browser_svc != NULL) {
-		purple_input_remove(idata->browser_handler);
-		DNSServiceRefDeallocate(idata->browser_svc);
+	if (idata->browser_query != NULL) {
+		purple_input_remove(idata->browser_query->input_handler);
+		DNSServiceRefDeallocate(idata->browser_query->sdRef);
+		g_free(idata->browser_query);
 	}
 
 	g_free(idata);
@@ -526,28 +556,30 @@
 
 gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) {
 	Win32SessionImplData *idata = data->mdns_impl_data;
-	DNSServiceErrorType err = kDNSServiceErr_NoError;
+	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
 	if (avatar_data != NULL && idata->buddy_icon_rec == NULL) {
 		purple_debug_info("bonjour", "Setting new buddy icon.\n");
-		err = DNSServiceAddRecord(idata->presence_svc, &idata->buddy_icon_rec,
+		errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec,
 			0, kDNSServiceType_NULL, avatar_len, avatar_data, 0);
 	} else if (avatar_data != NULL) {
 		purple_debug_info("bonjour", "Updating existing buddy icon.\n");
-		err = DNSServiceUpdateRecord(idata->presence_svc, idata->buddy_icon_rec,
+		errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec,
 			0, avatar_len, avatar_data, 0);
 	} else if (idata->buddy_icon_rec != NULL) {
 		purple_debug_info("bonjour", "Removing existing buddy icon.\n");
-		DNSServiceRemoveRecord(idata->presence_svc, idata->buddy_icon_rec, 0);
+		errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0);
 		idata->buddy_icon_rec = NULL;
 	}
 
-	if (err != kDNSServiceErr_NoError)
-		purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", err);
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", errorCode);
+		return FALSE;
+	}
 
-	return (err == kDNSServiceErr_NoError);
+	return TRUE;
 }
 
 void _mdns_init_buddy(BonjourBuddy *buddy) {
@@ -566,8 +598,9 @@
 	}
 
 	if (idata->null_query != NULL) {
-		purple_input_remove(idata->null_query_handler);
-		DNSServiceRefDeallocate(idata->null_query);
+		purple_input_remove(idata->null_query->input_handler);
+		DNSServiceRefDeallocate(idata->null_query->sdRef);
+		g_free(idata->null_query);
 	}
 
 	g_free(idata);
@@ -583,17 +616,30 @@
 
 	/* Cancel any existing query */
 	if (idata->null_query != NULL) {
-		purple_input_remove(idata->null_query_handler);
-		idata->null_query_handler = 0;
-		DNSServiceRefDeallocate(idata->null_query);
+		purple_input_remove(idata->null_query->input_handler);
+		DNSServiceRefDeallocate(idata->null_query->sdRef);
+		g_free(idata->null_query);
 		idata->null_query = NULL;
 	}
 
-	DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local");
-	if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, kDNSServiceInterfaceIndexAny, svc_name,
-			kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy)) {
-		idata->null_query_handler = purple_input_add(DNSServiceRefSockFD(idata->null_query),
-			PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
+	if (DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local") != 0)
+		purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name);
+	else {
+		DNSServiceRef null_query_sr;
+
+		DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny,
+			svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy);
+
+		if (errorCode == kDNSServiceErr_NoError) {
+			idata->null_query = g_new(DnsSDServiceRefHandlerData, 1);
+
+			idata->null_query->sdRef = null_query_sr;
+			idata->null_query->account = buddy->account;
+
+			idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
+		} else
+			purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode);
 	}
 
 }
--- a/libpurple/protocols/jabber/jabber.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Jan 05 18:07:16 2008 +0000
@@ -614,7 +614,7 @@
 	js->user = jabber_id_new(purple_account_get_username(account));
 	js->next_id = g_random_int();
 	js->write_buffer = purple_circ_buffer_new(512);
-	js->old_length = -1;
+	js->old_length = 0;
 	js->keepalive_timeout = -1;
 
 	if(!js->user) {
@@ -1100,7 +1100,7 @@
 			g_free, g_free);
 	js->user = jabber_id_new(purple_account_get_username(account));
 	js->next_id = g_random_int();
-	js->old_length = -1;
+	js->old_length = 0;
 
 	if(!js->user) {
 		purple_connection_error_reason (gc,
@@ -1523,10 +1523,16 @@
 				} else
 					purple_notify_user_info_add_pair(user_info, _("Mood"), mood);
 			}
-			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {	
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 				PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 				const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
-				purple_notify_user_info_add_pair(user_info, _("Current media"), title);
+				const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+				const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+				char *playing = purple_util_format_song_info(title, artist, album, NULL);
+				if (playing) {
+					purple_notify_user_info_add_pair(user_info, _("Now Listening"), playing);
+					g_free(playing);
+				}
 			}
 		}
 
--- a/libpurple/protocols/jabber/usertune.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/jabber/usertune.c	Sat Jan 05 18:07:16 2008 +0000
@@ -35,11 +35,12 @@
 	xmlnode *tuneinfo, *tune;
 	PurpleJabberTuneInfo tuneinfodata;
 	JabberBuddyResource *resource;
-	
+	gboolean valid = FALSE;
+
 	/* ignore the tune of people not on our buddy list */
 	if (!buddy || !item)
 		return;
-	
+
 	tuneinfodata.artist = NULL;
 	tuneinfodata.title = NULL;
 	tuneinfodata.album = NULL;
@@ -58,36 +59,47 @@
 			if (!strcmp(tuneinfo->name, "artist")) {
 				if (tuneinfodata.artist == NULL) /* only pick the first one */
 					tuneinfodata.artist = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "length")) {
 				if (tuneinfodata.time == -1) {
 					char *length = xmlnode_get_data(tuneinfo);
 					if (length)
 						tuneinfodata.time = strtol(length, NULL, 10);
 					g_free(length);
+					if (tuneinfodata.time > 0)
+						valid = TRUE;
 				}
 			} else if (!strcmp(tuneinfo->name, "source")) {
 				if (tuneinfodata.album == NULL) /* only pick the first one */
 					tuneinfodata.album = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "title")) {
 				if (tuneinfodata.title == NULL) /* only pick the first one */
 					tuneinfodata.title = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "track")) {
 				if (tuneinfodata.track == NULL) /* only pick the first one */
 					tuneinfodata.track = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "uri")) {
 				if (tuneinfodata.url == NULL) /* only pick the first one */
 					tuneinfodata.url = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			}
 		}
 	}
 
-	purple_prpl_got_user_status(js->gc->account, from, "tune",
-			PURPLE_TUNE_ARTIST, tuneinfodata.artist,
-			PURPLE_TUNE_TITLE, tuneinfodata.title,
-			PURPLE_TUNE_ALBUM, tuneinfodata.album,
-			PURPLE_TUNE_TRACK, tuneinfodata.track,
-			PURPLE_TUNE_TIME, tuneinfodata.time,
-			PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+	if (valid) {
+		purple_prpl_got_user_status(js->gc->account, from, "tune",
+				PURPLE_TUNE_ARTIST, tuneinfodata.artist,
+				PURPLE_TUNE_TITLE, tuneinfodata.title,
+				PURPLE_TUNE_ALBUM, tuneinfodata.album,
+				PURPLE_TUNE_TRACK, tuneinfodata.track,
+				PURPLE_TUNE_TIME, tuneinfodata.time,
+				PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+	} else {
+		purple_prpl_got_user_status_deactive(js->gc->account, from, "tune");
+	}
 
 	g_free(tuneinfodata.artist);
 	g_free(tuneinfodata.title);
@@ -119,7 +131,7 @@
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "source"),tuneinfo->album,-1);
 		if(tuneinfo->url && tuneinfo->url[0] != '\0')
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "uri"),tuneinfo->url,-1);
-		if(tuneinfo->time >= 0) {
+		if(tuneinfo->time > 0) {
 			char *length = g_strdup_printf("%d", tuneinfo->time);
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "length"),length,-1);
 			g_free(length);
--- a/libpurple/protocols/msn/msn.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Jan 05 18:07:16 2008 +0000
@@ -593,8 +593,8 @@
 			PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 			const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
 			const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
-			currentmedia = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
-					artist ? artist : "");
+			const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+			currentmedia = purple_util_format_song_info(title, artist, album, NULL);
 			/* We could probably just use user->media.title etc. here */
 		}
 
@@ -643,9 +643,7 @@
 		}
 
 		if (currentmedia) {
-			tmp = g_markup_escape_text(currentmedia, -1);
-			purple_notify_user_info_add_pair(user_info, _("Current media"), tmp);
-			g_free(tmp);
+			purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia);
 			g_free(currentmedia);
 		}
 	}
--- a/libpurple/protocols/oscar/family_auth.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Sat Jan 05 18:07:16 2008 +0000
@@ -196,6 +196,10 @@
  *   unknown= 0x0000008b
  *   serverstore = 0x01
  *
+ * @param truncate_pass Truncate the password to 8 characters.  This
+ *        usually happens for AOL accounts.  We are told that we
+ *        should truncate it if the 0x0017/0x0007 SNAC contains
+ *        a TLV of type 0x0026 with data 0x0000.
  */
 int
 aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key)
@@ -522,8 +526,8 @@
 
 	/*
 	 * If the truncate_pass TLV exists then we should truncate the
-	 * user's password to 8 characters.  This flag is sent when you
-	 * try to log in with an AOL user's screen name.
+	 * user's password to 8 characters.  This flag is sent to us
+	 * when logging in with an AOL user's screen name.
 	 */
 	truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL;
 
--- a/libpurple/protocols/oscar/family_icbm.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Sat Jan 05 18:07:16 2008 +0000
@@ -51,6 +51,9 @@
 #include "win32dep.h"
 #endif
 
+#include "util.h"
+
+
 /**
  * Add a standard ICBM header to the given bstream with the given
  * information.
@@ -2335,11 +2338,166 @@
 	sn = byte_stream_getstr(bs, snlen);
 	reason = byte_stream_get16(bs);
 
-	if (channel == 0x0002) { /* File transfer declined */
+	if (channel == 0x0002)
+	{
+		/* parse status note text */
+
+		struct aim_icq_info *info = NULL;
+		struct aim_icq_info *prev_info = NULL;
+		char *response = NULL;
+		char *encoding = NULL;
+		char *stripped_encoding = NULL;
+		char *status_note_text = NULL;
+		char *stripped_status_note_text = NULL;
+		char *status_note = NULL;
+
+		/*
+		 * TODO: Using a while statement here is kind of an ugly hack
+		 *       to be able to use 'break'.  We might as well be using
+		 *       'goto'.  Should probably get rid of this.
+		 */
+		while (reason == 0x0003) /* channel-specific */
+		{
+			guint32 length;
+			guint16 version;
+			guint32 capability;
+			guint8 message_type;
+			guint16 status_code;
+			guint16 text_length;
+			guint32 request_length;
+			guint32 response_length;
+			guint32 encoding_length;
+			PurpleAccount *account;
+			PurpleBuddy *buddy;
+			PurplePresence *presence;
+			PurpleStatus *status;
+
+			for (info = od->icq_info; info != NULL; info = info->next)
+			{
+				if (memcmp(&info->icbm_cookie, cookie, 8) == 0)
+				{
+					if (prev_info == NULL)
+						od->icq_info = info->next;
+					else
+						prev_info->next = info->next;
+
+					break;
+				}
+
+				prev_info = info;
+			}
+
+			if (info == NULL)
+				break;
+
+			if ((length = byte_stream_getle16(bs)) != 27)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 27, received %u.\n", length);
+				break;
+			}
+			if ((version = byte_stream_getle16(bs)) != 9)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect version; expected 9, received %u.\n", version);
+				break;
+			}
+			capability = aim_locate_getcaps(od, bs, 0x10);
+			if (capability != OSCAR_CAPABILITY_EMPTY)
+			{
+				purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n");
+				break;
+			}
+			byte_stream_advance(bs, 2); /* unknown */
+			byte_stream_advance(bs, 4); /* client capabilities flags */
+			byte_stream_advance(bs, 1); /* unknown */
+			byte_stream_advance(bs, 2); /* downcouner? */
+
+			if ((length = byte_stream_getle16(bs)) != 14)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 14, received %u.\n", length);
+				break;
+			}
+			byte_stream_advance(bs, 2); /* downcounter? */
+			byte_stream_advance(bs, 12); /* unknown */
+
+			if ((message_type = byte_stream_get8(bs)) != 0x1a)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect message type; expected 0x1a, received 0x%x.\n", message_type);
+				break;
+			}
+			byte_stream_advance(bs, 1); /* message flags */
+			if ((status_code = byte_stream_getle16(bs)) != 0)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect status code; expected 0, received %u.\n", status_code);
+				break;
+			}
+			byte_stream_advance(bs, 2); /* priority code */
+
+			text_length = byte_stream_getle16(bs);
+			byte_stream_advance(bs, text_length); /* text */
+
+			length = byte_stream_getle16(bs);
+			byte_stream_advance(bs, 18); /* unknown */
+			if (length != 18 + 4 + (request_length = byte_stream_getle32(bs)) + 17)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 18 + 4 + request_length + 17, length);
+				break;
+			}
+			byte_stream_advance(bs, request_length); /* x request */
+			byte_stream_advance(bs, 17); /* unknown */
+
+			length = byte_stream_getle32(bs);
+			response_length = byte_stream_getle32(bs);
+			response = byte_stream_getstr(bs, response_length);
+			if (length != 4 + response_length + 4 + (encoding_length = byte_stream_getle32(bs)))
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 4 + response_length + 4 + encoding_length, length);
+				break;
+			}
+			encoding = byte_stream_getstr(bs, encoding_length);
+
+			account = purple_connection_get_account(od->gc);
+			stripped_encoding = oscar_encoding_extract(encoding);
+			status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length);
+			stripped_status_note_text = purple_markup_strip_html(status_note_text);
+
+			if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0)
+				status_note = g_strdup_printf("%s: %s", info->status_note_title, stripped_status_note_text);
+			else
+				status_note = g_strdup(info->status_note_title);
+
+			buddy = purple_find_buddy(account, sn);
+			if (buddy == NULL)
+			{
+				purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", sn);
+				break;
+			}
+
+			purple_debug_misc("oscar", "clientautoresp: setting status message to \"%s\".\n", status_note);
+
+			presence = purple_buddy_get_presence(buddy);
+			status = purple_presence_get_active_status(presence);
+
+			purple_prpl_got_user_status(account, sn,
+					purple_status_get_id(status),
+					"message", status_note, NULL);
+
+			break;
+		}
+
+		g_free(status_note);
+		g_free(stripped_status_note_text);
+		g_free(status_note_text);
+		g_free(stripped_encoding);
+		g_free(encoding);
+		g_free(response);
+		g_free(info->status_note_title);
+		g_free(info);
+
 		byte_stream_get16(bs); /* Unknown */
 		byte_stream_get16(bs); /* Unknown */
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 			ret = userfunc(od, conn, frame, channel, sn, reason, cookie);
+
 	} else if (channel == 0x0004) { /* ICQ message */
 		switch (reason) {
 			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
--- a/libpurple/protocols/oscar/family_icq.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Sat Jan 05 18:07:16 2008 +0000
@@ -435,6 +435,65 @@
 	return 0;
 }
 
+/*
+ * getstatusnote may be a misleading name because the response
+ * contains a lot of different information but currently it's only
+ * used to get that.
+ */
+int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len)
+{
+	FlapConnection *conn;
+	FlapFrame *frame;
+	aim_snacid_t snacid;
+	int bslen;
+
+	purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin);
+
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	{
+		purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n");
+		return -EINVAL;
+	}
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin);
+
+	frame = flap_frame_new(od, 0x02, 10 + 4 + bslen);
+
+	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&frame->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	byte_stream_put16(&frame->data, 0x0001);
+	byte_stream_put16(&frame->data, bslen);
+
+	byte_stream_putle16(&frame->data, bslen - 2);
+	byte_stream_putle32(&frame->data, atoi(od->sn));
+	byte_stream_putle16(&frame->data, 0x07d0); /* I command thee. */
+	byte_stream_putle16(&frame->data, snacid); /* eh. */
+	byte_stream_putle16(&frame->data, 0x0fa0); /* shrug. */
+	byte_stream_putle16(&frame->data, 58 + strlen(uin));
+
+	byte_stream_put32(&frame->data, 0x05b90002);    /* don't ask */
+	byte_stream_put32(&frame->data, 0x80000000);
+	byte_stream_put32(&frame->data, 0x00000006);
+	byte_stream_put32(&frame->data, 0x00010002);
+	byte_stream_put32(&frame->data, 0x00020000);
+	byte_stream_put32(&frame->data, 0x04e30000);
+	byte_stream_put32(&frame->data, 0x00020002);
+	byte_stream_put32(&frame->data, 0x00000001);
+
+	byte_stream_put16(&frame->data, 24 + strlen(uin));
+	byte_stream_put32(&frame->data, 0x003c0010);
+	byte_stream_putraw(&frame->data, note_hash, 16); /* status note hash */
+	byte_stream_put16(&frame->data, 0x0032);        /* buddy uin */
+	byte_stream_put16(&frame->data, strlen(uin));
+	byte_stream_putstr(&frame->data, uin);
+
+	flap_connection_send(conn, frame);
+
+	return 0;
+}
+
 static void aim_icq_freeinfo(struct aim_icq_info *info) {
 	int i;
 
@@ -467,6 +526,7 @@
 	g_free(info->workposition);
 	g_free(info->workwebpage);
 	g_free(info->info);
+	g_free(info->status_note_title);
 	g_free(info);
 }
 
@@ -641,6 +701,178 @@
 			info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
 			/* Then 0x00 02 00 00 00 00 00 */
 		} break;
+
+		/* status note title and send request for status note text */
+		case 0x0fb4: {
+			GSList *tlvlist;
+			aim_tlv_t *tlv;
+			FlapConnection *conn;
+			char *uin = NULL;
+			char *status_note_title = NULL;
+
+			conn = flap_connection_findbygroup(od, 0x0004);
+			if (conn == NULL)
+			{
+				purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n");
+				break;
+			}
+
+			byte_stream_advance(&qbs, 0x02); /* length */
+			byte_stream_advance(&qbs, 0x2f); /* unknown stuff */
+
+			tlvlist = aim_tlvlist_read(&qbs);
+
+			tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1);
+			if (tlv != NULL)
+				/* Get user number */
+				uin = aim_tlv_getvalue_as_string(tlv);
+
+			tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1);
+			if (tlv != NULL)
+				/* Get status note title */
+				status_note_title = aim_tlv_getvalue_as_string(tlv);
+
+			aim_tlvlist_free(tlvlist);
+
+			if (uin == NULL || status_note_title == NULL)
+			{
+				purple_debug_misc("oscar", "icq/0x0fb4: uin or "
+						"status_note_title was not found\n");
+				g_free(uin);
+				g_free(status_note_title);
+				break;
+			}
+
+			if (status_note_title[0] == '\0')
+			{
+				PurpleAccount *account;
+				PurpleBuddy *buddy;
+				PurplePresence *presence;
+				PurpleStatus *status;
+
+				account = purple_connection_get_account(od->gc);
+				buddy = purple_find_buddy(account, uin);
+				presence = purple_buddy_get_presence(buddy);
+				status = purple_presence_get_active_status(presence);
+
+				purple_prpl_got_user_status(account, uin,
+						purple_status_get_id(status),
+						"message", NULL, NULL);
+
+				g_free(status_note_title);
+			}
+			else
+			{
+				struct aim_icq_info *info;
+				guint32 data_len;
+				FlapFrame *frame;
+				aim_snacid_t snacid;
+				guchar cookie[8];
+
+				info = g_new0(struct aim_icq_info, 1);
+
+				if (info == NULL)
+				{
+					g_free(uin);
+					g_free(status_note_title);
+
+					break;
+				}
+
+				data_len = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4;
+				frame = flap_frame_new(od, 0x0002, 10 + 4 + data_len);
+				snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+
+				aim_putsnac(&frame->data, 0x0004, 0x0006, 0x0000, snacid);
+
+				aim_icbm_makecookie(cookie);
+
+				byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */
+				byte_stream_put16(&frame->data, 0x0002); /* message channel */
+				byte_stream_put8(&frame->data, strlen(uin)); /* uin */
+				byte_stream_putstr(&frame->data, uin);
+
+				byte_stream_put16(&frame->data, 0x0005); /* rendez vous data */
+				byte_stream_put16(&frame->data, 0x00b2);
+				byte_stream_put16(&frame->data, 0x0000); /* request */
+				byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */
+				byte_stream_put32(&frame->data, 0x09461349); /* ICQ server relaying */
+				byte_stream_put16(&frame->data, 0x4c7f);
+				byte_stream_put16(&frame->data, 0x11d1);
+				byte_stream_put32(&frame->data, 0x82224445);
+				byte_stream_put32(&frame->data, 0x53540000);
+
+				byte_stream_put16(&frame->data, 0x000a); /* unknown TLV */
+				byte_stream_put16(&frame->data, 0x0002);
+				byte_stream_put16(&frame->data, 0x0001);
+
+				byte_stream_put16(&frame->data, 0x000f); /* unknown TLV */
+				byte_stream_put16(&frame->data, 0x0000);
+
+				byte_stream_put16(&frame->data, 0x2711); /* extended data */
+				byte_stream_put16(&frame->data, 0x008a);
+				byte_stream_putle16(&frame->data, 0x001b); /* length */
+				byte_stream_putle16(&frame->data, 0x0009); /* version */
+				byte_stream_putle32(&frame->data, 0x00000000); /* plugin: none */
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle16(&frame->data, 0x0000); /* unknown */
+				byte_stream_putle32(&frame->data, 0x00000000); /* client capabilities flags */
+				byte_stream_put8(&frame->data, 0x00); /* unknown */
+				byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */
+				byte_stream_putle16(&frame->data, 0x000e); /* length */
+				byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */
+				byte_stream_putle32(&frame->data, 0x00000000); /* unknown */
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_put8(&frame->data, 0x1a); /* message type: plugin message descibed by text string */
+				byte_stream_put8(&frame->data, 0x00); /* message flags */
+				byte_stream_putle16(&frame->data, 0x0000); /* status code */
+				byte_stream_putle16(&frame->data, 0x0001); /* priority code */
+				byte_stream_putle16(&frame->data, 0x0000); /* text length */
+
+				byte_stream_put8(&frame->data, 0x3a); /* message dump */
+				byte_stream_put32(&frame->data, 0x00811a18);
+				byte_stream_put32(&frame->data, 0xbc0e6c18);
+				byte_stream_put32(&frame->data, 0x47a5916f);
+				byte_stream_put32(&frame->data, 0x18dcc76f);
+				byte_stream_put32(&frame->data, 0x1a010013);
+				byte_stream_put32(&frame->data, 0x00000041);
+				byte_stream_put32(&frame->data, 0x77617920);
+				byte_stream_put32(&frame->data, 0x53746174);
+				byte_stream_put32(&frame->data, 0x7573204d);
+				byte_stream_put32(&frame->data, 0x65737361);
+				byte_stream_put32(&frame->data, 0x67650100);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000015);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x0000000d);
+				byte_stream_put32(&frame->data, 0x00000074);
+				byte_stream_put32(&frame->data, 0x6578742f);
+				byte_stream_put32(&frame->data, 0x782d616f);
+				byte_stream_put32(&frame->data, 0x6c727466);
+
+				byte_stream_put16(&frame->data, 0x0003); /* server ACK requested */
+				byte_stream_put16(&frame->data, 0x0000);
+
+				info->uin = atoi(uin);
+				info->status_note_title = status_note_title;
+
+				memcpy(&info->icbm_cookie, cookie, 8);
+
+				info->next = od->icq_info;
+				od->icq_info = info;
+
+				flap_connection_send(conn, frame);
+			}
+
+			g_free(uin);
+
+		} break;
+
 		} /* End switch statement */
 
 		if (!(snac->flags & 0x0001)) {
--- a/libpurple/protocols/oscar/family_locate.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Sat Jan 05 18:07:16 2008 +0000
@@ -320,10 +320,10 @@
 		cur->away_encoding = g_strdup(userinfo->away_encoding);
 		cur->away_len = userinfo->away_len;
 
-	} else if (!(userinfo->flags & AIM_FLAG_AWAY)) {
+	} else {
 		/*
-		 * We don't have an away message specified in this user_info block.
-		 * If the user is not away, clear any cached away message now.
+		 * We don't have an away message specified in this user_info
+		 * block, so clear any cached away message now.
 		 */
 		if (cur->away) {
 			g_free(cur->away);
@@ -347,41 +347,6 @@
 		userfunc(od, conn, NULL, cur);
 }
 
-void
-aim_locate_dorequest(OscarData *od)
-{
-	struct userinfo_node *cur = od->locate.torequest;
-
-	if (od->locate.waiting_for_response == TRUE)
-		return;
-
-	od->locate.waiting_for_response = TRUE;
-	aim_locate_getinfoshort(od, cur->sn, 0x00000003);
-
-	/* Move this node to the "requested" queue */
-	od->locate.torequest = cur->next;
-	cur->next = od->locate.requested;
-	od->locate.requested = cur;
-}
-
-static gboolean
-purple_reqinfo_timeout_cb(void *data)
-{
-	OscarData *od;
-
-	od = data;
-
-	if (od->locate.torequest == NULL)
-	{
-		od->getinfotimer = 0;
-		return FALSE;
-	}
-
-	aim_locate_dorequest(od);
-
-	return TRUE;
-}
-
 /**
  * Remove this screen name from our queue.  If this info was requested
  * by our info request queue, then pop the next element off of the queue.
@@ -417,19 +382,6 @@
 			cur = cur->next;
 	}
 
-	if (!was_explicit) {
-		od->locate.waiting_for_response = FALSE;
-
-		/*
-		 * Wait a little while then call aim_locate_dorequest(od).
-		 * This keeps us from hitting the rate limit due to
-		 * requesting away messages and info too quickly.
-		 */
-		if (od->getinfotimer == 0)
-			od->getinfotimer = purple_timeout_add(500,
-					purple_reqinfo_timeout_cb, od);
-	}
-
 	return was_explicit;
 }
 
@@ -438,22 +390,18 @@
 {
 	struct userinfo_node *cur;
 
-	/* Make sure we aren't already requesting info for this buddy */
-	cur = od->locate.torequest;
-	while (cur != NULL) {
+	/* Make sure we haven't already requested info for this buddy */
+	for (cur = od->locate.requested; cur != NULL; cur = cur->next)
 		if (aim_sncmp(sn, cur->sn) == 0)
 			return;
-		cur = cur->next;
-	}
 
 	/* Add a new node to our request queue */
 	cur = (struct userinfo_node *)g_malloc(sizeof(struct userinfo_node));
 	cur->sn = g_strdup(sn);
-	cur->next = od->locate.torequest;
-	od->locate.torequest = cur;
+	cur->next = od->locate.requested;
+	od->locate.requested = cur;
 
-	/* Actually request some info up in this piece */
-	aim_locate_dorequest(od);
+	aim_locate_getinfoshort(od, cur->sn, 0x00000003);
 }
 
 aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn) {
--- a/libpurple/protocols/oscar/oscar.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sat Jan 05 18:07:16 2008 +0000
@@ -1722,8 +1722,7 @@
 	int type = 0;
 	gboolean buddy_is_away = FALSE;
 	const char *status_id;
-	gboolean have_status_message = FALSE;
-	char *message = NULL;
+	char *itmsurl = NULL;
 	va_list ap;
 	aim_userinfo_t *info;
 
@@ -1771,20 +1770,10 @@
 			status_id = OSCAR_STATUS_ID_AVAILABLE;
 	}
 
-	/*
-	 * Handle the available message.  If info->status is NULL then the user
-	 * may or may not have an available message, so don't do anything.  If
-	 * info->status is set to the empty string, then the user's client DOES
-	 * support available messages and the user DOES NOT have one set.
-	 * Otherwise info->status contains the available message.
-	 */
-	if (info->status != NULL)
-	{
-		have_status_message = TRUE;
-		if (info->status[0] != '\0')
-			message = oscar_encoding_to_utf8(account, info->status_encoding,
-											 info->status, info->status_len);
-	}
+	if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len)
+		/* Grab the iTunes Music Store URL */
+		itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
+				info->itmsurl, info->itmsurl_len);
 
 	if (info->flags & AIM_FLAG_WIRELESS)
 	{
@@ -1793,38 +1782,27 @@
 		purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE);
 	}
 
-	if (have_status_message)
+	if (status_id == OSCAR_STATUS_ID_AVAILABLE)
 	{
-		if ((!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE)) && (info->itmsurl != NULL))
-		{
-			char *itmsurl;
-			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
-					info->itmsurl, info->itmsurl_len);
-			purple_prpl_got_user_status(account, info->sn, status_id,
-					"message", message, "itmsurl", itmsurl, NULL);
-			g_free(itmsurl);
-		}
-		else
-		{
-			purple_prpl_got_user_status(account, info->sn, status_id,
-					"message", message, NULL);
-		}
+		char *message = NULL;
+
+		if (info->status != NULL && info->status[0] != '\0')
+			/* Grab the available message */
+			message = oscar_encoding_to_utf8(account, info->status_encoding,
+					info->status, info->status_len);
+
+		purple_prpl_got_user_status(account, info->sn, status_id,
+				"message", message, "itmsurl", itmsurl, NULL);
+
 		g_free(message);
 	}
 	else
 	{
-		PurpleBuddy *b = purple_find_buddy(account, info->sn);
-		PurpleStatus *status = NULL;
-		const char *active_status_id = NULL;
-
-		if (b != NULL) {
-			status = purple_presence_get_active_status(purple_buddy_get_presence(b));
-			active_status_id = purple_status_get_id(status);
-		}
-
-		if (!active_status_id || strcmp(active_status_id, status_id))
-			purple_prpl_got_user_status(account, info->sn, status_id, NULL);
-	}
+		purple_prpl_got_user_status(account, info->sn, status_id,
+				"itmsurl", itmsurl, NULL);
+	}
+
+	g_free(itmsurl);
 
 	/* Login time stuff */
 	if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
@@ -1879,6 +1857,31 @@
 		g_free(b16);
 	}
 
+	/*
+	 * If we didn't receive a status message with the status change,
+	 * or if the message is empty, and we have a note hash, then
+	 * query the ICQ6 status note.
+	 *
+	 * TODO: We should probably always query the status note regardless
+	 *       of whether they have a status message set, and we should
+	 *       figure out a way to display both the status note and the
+	 *       status message at the same time.
+	 */
+	if (info->status == NULL || info->status[0] == '\0')
+	{
+		struct aim_ssi_item *ssi_item;
+		aim_tlv_t *note_hash;
+
+		ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
+				NULL, info->sn, AIM_SSI_TYPE_BUDDY);
+		if (ssi_item != NULL)
+		{
+			note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
+			if (note_hash != NULL)
+				aim_icq_getstatusnote(od, info->sn, note_hash->value, note_hash->length);
+		}
+	}
+
 	return 1;
 }
 
@@ -3000,7 +3003,7 @@
 
 	if (!aim_snvalid_icq(userinfo->sn))
 	{
-		if (strcmp(purple_buddy_get_name(b), userinfo->sn))
+		if (strcmp(purple_buddy_get_name(b), userinfo->sn) != 0)
 			serv_got_alias(gc, purple_buddy_get_name(b), userinfo->sn);
 		else
 			serv_got_alias(gc, purple_buddy_get_name(b), NULL);
@@ -3009,23 +3012,19 @@
 	presence = purple_buddy_get_presence(b);
 	status = purple_presence_get_active_status(presence);
 
-	if (!purple_status_is_available(status) && purple_status_is_online(status))
+	if (purple_status_is_online(status) && !purple_status_is_available(status) &&
+			userinfo->flags & AIM_FLAG_AWAY && userinfo->away_len > 0 &&
+			userinfo->away != NULL && userinfo->away_encoding != NULL)
 	{
-		if ((userinfo->flags & AIM_FLAG_AWAY) &&
-			(userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
-			gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
-			message = oscar_encoding_to_utf8(account, charset,
-			                                 userinfo->away,
-			                                 userinfo->away_len);
-			g_free(charset);
-			purple_status_set_attr_string(status, "message", message);
-			g_free(message);
-		}
-		else
-			/* Set an empty message so that we know not to show "pending" */
-			purple_status_set_attr_string(status, "message", "");
-
-		purple_blist_update_buddy_status(b, status);
+		gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
+		message = oscar_encoding_to_utf8(account, charset,
+		                                 userinfo->away,
+		                                 userinfo->away_len);
+		g_free(charset);
+		purple_prpl_got_user_status(account, userinfo->sn,
+				purple_status_get_id(status),
+				"message", message, NULL);
+		g_free(message);
 	}
 
 	return 1;
@@ -4520,12 +4519,11 @@
 		/* This is needed for us to un-set any previous away message. */
 		away = g_strdup("");
 	}
-	else if ((primitive == PURPLE_STATUS_AWAY) ||
-			 (primitive == PURPLE_STATUS_EXTENDED_AWAY))
+	else
 	{
 		htmlaway = purple_status_get_attr_string(status, "message");
 		if ((htmlaway == NULL) || (*htmlaway == '\0'))
-			htmlaway = _("Away");
+			htmlaway = purple_status_type_get_name(status_type);
 		away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
 
 		if (awaylen > od->rights.maxawaymsglen)
@@ -5099,6 +5097,8 @@
 	char *gname, *gname_utf8, *alias, *alias_utf8;
 	PurpleBuddy *b;
 	PurpleGroup *g;
+	struct aim_ssi_item *ssi_item;
+	aim_tlv_t *note_hash;
 	va_list ap;
 	guint16 snac_subtype, type;
 	const char *name;
@@ -5166,6 +5166,21 @@
 
 	}
 
+	ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
+			gname, name, AIM_SSI_TYPE_BUDDY);
+	if (ssi_item != NULL)
+	{
+		note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
+		if (note_hash != NULL)
+			aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length);
+	}
+	else
+	{
+		purple_debug_error("oscar", "purple_ssi_parseaddmod: "
+				"Could not find ssi item for oncoming buddy %s, "
+				"group %s\n", name, gname);
+	}
+
 	g_free(gname_utf8);
 	g_free(alias_utf8);
 
--- a/libpurple/protocols/oscar/oscar.h	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sat Jan 05 18:07:16 2008 +0000
@@ -3,8 +3,6 @@
  * This file is the legal property of its developers.
  * Please see the AUTHORS file distributed alongside this file.
  *
- * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
- *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -311,7 +309,7 @@
 }
 
 #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
-#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777
+#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000
 
 typedef enum
 {
@@ -468,7 +466,6 @@
 
 	gboolean icq;
 	guint getblisttimer;
-	guint getinfotimer;
 
 	struct {
 		guint maxwatchers; /* max users who can watch you */
@@ -513,9 +510,7 @@
 
 	struct {
 		struct aim_userinfo_s *userinfo;
-		struct userinfo_node *torequest;
 		struct userinfo_node *requested;
-		gboolean waiting_for_response;
 	} locate;
 
 	/* Server-stored information (ssi) */
@@ -1329,6 +1324,10 @@
 
 	/* we keep track of these in a linked list because we're 1337 */
 	struct aim_icq_info *next;
+
+	/* status note info */
+	guint8 icbm_cookie[8];
+	char *status_note_title;
 };
 
 int aim_icq_reqofflinemsgs(OscarData *od);
@@ -1339,7 +1338,7 @@
 int aim_icq_getalias(OscarData *od, const char *uin);
 int aim_icq_getallinfo(OscarData *od, const char *uin);
 int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
-
+int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len);
 
 
 /* 0x0017 - family_auth.c */
--- a/libpurple/protocols/oscar/oscar_data.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/protocols/oscar/oscar_data.c	Sat Jan 05 18:07:16 2008 +0000
@@ -97,8 +97,6 @@
 	g_free(od->oldp);
 	if (od->getblisttimer > 0)
 		purple_timeout_remove(od->getblisttimer);
-	if (od->getinfotimer > 0)
-		purple_timeout_remove(od->getinfotimer);
 	while (od->oscar_connections != NULL)
 		flap_connection_destroy(od->oscar_connections->data,
 				OSCAR_DISCONNECT_DONE, NULL);
--- a/libpurple/util.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/util.c	Sat Jan 05 18:07:16 2008 +0000
@@ -4628,3 +4628,57 @@
 #endif /* HAVE_SIGNAL_H */
 #endif /* !_WIN32 */
 }
+
+void purple_util_set_current_song(const char *title, const char *artist, const char *album)
+{
+	GList *list = purple_accounts_get_all();
+	for (; list; list = list->next) {
+		PurplePresence *presence;
+		PurpleStatus *tune;
+		PurpleAccount *account = list->data;
+		if (!purple_account_get_enabled(account, purple_core_get_ui()))
+			continue;
+
+		presence = purple_account_get_presence(account);
+		tune = purple_presence_get_status(presence, "tune");
+		if (!tune)
+			continue;
+		if (title) {
+			purple_status_set_active(tune, TRUE);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_TITLE, title);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_ARTIST, artist);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_ALBUM, album);
+		} else {
+			purple_status_set_active(tune, FALSE);
+		}
+	}
+}
+
+char * purple_util_format_song_info(const char *title, const char *artist, const char *album, gpointer unused)
+{
+	GString *string;
+	char *esc;
+
+	if (!title)
+		return NULL;
+
+	esc = g_markup_escape_text(title, -1);
+	string = g_string_new("");
+	g_string_append_printf(string, "%s", esc);
+	g_free(esc);
+
+	if (artist) {
+		esc = g_markup_escape_text(artist, -1);
+		g_string_append_printf(string, _(" - %s"), esc);
+		g_free(esc);
+	}
+
+	if (album) {
+		esc = g_markup_escape_text(album, -1);
+		g_string_append_printf(string, _(" (%s)"), esc);
+		g_free(esc);
+	}
+
+	return g_string_free(string, FALSE);
+}
+
--- a/libpurple/util.h	Sat Jan 05 18:01:12 2008 +0000
+++ b/libpurple/util.h	Sat Jan 05 18:07:16 2008 +0000
@@ -85,6 +85,31 @@
  */
 void purple_menu_action_free(PurpleMenuAction *act);
 
+/**
+ * Set the appropriate presence values for the currently playing song.
+ *
+ * @param title     The title of the song, @c NULL to unset the value.
+ * @param artist    The artist of the song, can be @c NULL.
+ * @param album     The album of the song, can be @c NULL.
+ * @since 2.4.0
+ */
+void purple_util_set_current_song(const char *title, const char *artist,
+		const char *album);
+
+/**
+ * Format song information.
+ *
+ * @param title     The title of the song, @c NULL to unset the value.
+ * @param artist    The artist of the song, can be @c NULL.
+ * @param album     The album of the song, can be @c NULL.
+ * @param unused    Currently unused, must be @c NULL.
+ *
+ * @return   The formatted string. The caller must #g_free the returned string.
+ * @since 2.4.0
+ */
+char * purple_util_format_song_info(const char *title, const char *artist,
+		const char *album, gpointer unused);
+
 /**************************************************************************/
 /** @name Utility Subsystem                                               */
 /**************************************************************************/
--- a/pidgin/gtkaccount.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkaccount.c	Sat Jan 05 18:07:16 2008 +0000
@@ -158,25 +158,7 @@
 add_pref_box(AccountPrefsDialog *dialog, GtkWidget *parent,
 			 const char *text, GtkWidget *widget)
 {
-	GtkWidget *hbox;
-	GtkWidget *label;
-
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(text);
-	gtk_size_group_add_widget(dialog->sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
-	gtk_widget_show(label);
-
-	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER);
-	gtk_widget_show(widget);
-	pidgin_set_accessible_label (widget, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(parent), text, dialog->sg, widget, TRUE, NULL);
 }
 
 static void
--- a/pidgin/gtkblist.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkblist.c	Sat Jan 05 18:07:16 2008 +0000
@@ -843,20 +843,10 @@
 
 	for (tmp = list; tmp; tmp = tmp->next)
 	{
-		GtkWidget *label;
-		GtkWidget *rowbox;
 		GtkWidget *input;
 
 		pce = tmp->data;
 
-		rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-		gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0);
-
-		label = gtk_label_new_with_mnemonic(pce->label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-		gtk_size_group_add_widget(data->sg, label);
-		gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 		if (pce->is_int)
 		{
 			GtkObject *adjust;
@@ -864,7 +854,7 @@
 										1, 10, 10);
 			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 			gtk_widget_set_size_request(input, 50, -1);
-			gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
 		}
 		else
 		{
@@ -880,7 +870,7 @@
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
 			}
-			gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
 							 G_CALLBACK(joinchat_set_sensitive_if_input_cb), data);
 		}
@@ -891,8 +881,6 @@
 			gtk_widget_grab_focus(input);
 			focus = FALSE;
 		}
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), input);
-		pidgin_set_accessible_label(input, label);
 		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
 		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
 		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
@@ -988,23 +976,14 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
-	rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, TRUE, TRUE, 0);
-
 	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	label = gtk_label_new_with_mnemonic(_("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(data->sg, label);
-
 	data->account_menu = pidgin_account_option_menu_new(NULL, FALSE,
 			G_CALLBACK(joinchat_select_account_cb),
 			chat_account_filter_func, data);
 	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label),
-								  GTK_WIDGET(data->account_menu));
-	pidgin_set_accessible_label (data->account_menu, label);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
 	gtk_container_add(GTK_CONTAINER(vbox), data->entries_box);
@@ -2576,6 +2555,35 @@
 	int height;
 };
 
+static PangoLayout * create_pango_layout(const char *markup, int *width, int *height)
+{
+	PangoLayout *layout;
+	int w, h;
+
+	layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
+	pango_layout_set_markup(layout, markup, -1);
+	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+	pango_layout_set_width(layout, 300000);
+
+	pango_layout_get_size (layout, &w, &h);
+	if (width)
+		*width = PANGO_PIXELS(w);
+	if (height)
+		*height = PANGO_PIXELS(h);
+	return layout;
+}
+
+static struct tooltip_data * create_tip_for_account(PurpleAccount *account)
+{
+	struct tooltip_data *td = g_new0(struct tooltip_data, 1);
+	td->status_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+		/* Yes, status_icon, not prpl_icon */
+	if (purple_account_is_disconnected(account))
+		gdk_pixbuf_saturate_and_pixelate(td->status_icon, td->status_icon, 0.0, FALSE);
+	td->layout = create_pango_layout(purple_account_get_username(account), &td->width, &td->height);
+	return td;
+}
+
 static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean full)
 {
 	struct tooltip_data *td = g_new0(struct tooltip_data, 1);
@@ -2594,8 +2602,9 @@
 		td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
 	}
 	tooltip_text = pidgin_get_tooltip_text(node, full);
-	td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
-	td->name_layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
+	if (tooltip_text && *tooltip_text) {
+		td->layout = create_pango_layout(tooltip_text, &td->width, &td->height);
+	}
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		tmp = g_markup_escape_text(purple_buddy_get_name((PurpleBuddy*)node), -1);
@@ -2612,21 +2621,9 @@
 	node_name = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", tmp);
 	g_free(tmp);
 
-	pango_layout_set_markup(td->layout, tooltip_text, -1);
-	pango_layout_set_wrap(td->layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(td->layout, 300000);
-
-	pango_layout_get_size (td->layout, &td->width, &td->height);
-	td->width = PANGO_PIXELS(td->width);
-	td->height = PANGO_PIXELS(td->height);
-
-	pango_layout_set_markup(td->name_layout, node_name, -1);
-	pango_layout_set_wrap(td->name_layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(td->name_layout, 300000);
-
-	pango_layout_get_size (td->name_layout, &td->name_width, &td->name_height);
-	td->name_width = PANGO_PIXELS(td->name_width) + SMALL_SPACE + PRPL_SIZE;
-	td->name_height = MAX(PANGO_PIXELS(td->name_height), PRPL_SIZE + SMALL_SPACE);
+	td->name_layout = create_pango_layout(node_name, &td->name_width, &td->name_height);
+	td->name_width += SMALL_SPACE + PRPL_SIZE;
+	td->name_height = MAX(td->name_height, PRPL_SIZE + SMALL_SPACE);
 #if 0  /* PRPL Icon as avatar */
 	if(!td->avatar && full) {
 		td->avatar = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE);
@@ -2654,6 +2651,7 @@
 	GList *l;
 	int prpl_col = 0;
 	GtkTextDirection dir = gtk_widget_get_direction(widget);
+	int status_size = 0;
 
 	if(gtkblist->tooltipdata == NULL)
 		return FALSE;
@@ -2670,13 +2668,15 @@
 		max_text_width = MAX(max_text_width,
 				MAX(td->width, td->name_width));
 		max_avatar_width = MAX(max_avatar_width, td->avatar_width);
-	}
-
-	max_width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
+		if (td->status_icon)
+			status_size = STATUS_SIZE;
+	}
+
+	max_width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
 	if (dir == GTK_TEXT_DIR_RTL)
 		prpl_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE;
 	else
-		prpl_col = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width - PRPL_SIZE;
+		prpl_col = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width - PRPL_SIZE;
 
 	current_height = 12;
 	for(l = gtkblist->tooltipdata; l; l = l->next)
@@ -2700,7 +2700,7 @@
 		if (td->status_icon) {
 			if (dir == GTK_TEXT_DIR_RTL)
 				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
-				                0, 0, max_width - TOOLTIP_BORDER - STATUS_SIZE, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+				                0, 0, max_width - TOOLTIP_BORDER - status_size, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
 			else
 				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
 				                0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
@@ -2733,26 +2733,31 @@
 					max_width - (td->avatar_width + TOOLTIP_BORDER),
 					current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
 #endif
-		if (dir == GTK_TEXT_DIR_RTL) {
-			gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					max_width  -(TOOLTIP_BORDER + STATUS_SIZE +SMALL_SPACE) - PANGO_PIXELS(300000),
-					current_height, td->name_layout);
-		} else {
-			gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height, td->name_layout);
+		if (td->name_layout) {
+			if (dir == GTK_TEXT_DIR_RTL) {
+				gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						max_width  -(TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+						current_height, td->name_layout);
+			} else {
+				gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height, td->name_layout);
+			}
 		}
-		if (dir != GTK_TEXT_DIR_RTL) {
-			gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height + td->name_height, td->layout);
-		} else {
-			gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					max_width - (TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE) - PANGO_PIXELS(300000),
-					current_height + td->name_height,
-					td->layout);
+
+		if (td->layout) {
+			if (dir != GTK_TEXT_DIR_RTL) {
+				gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height + td->name_height, td->layout);
+			} else {
+				gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+						current_height + td->name_height,
+						td->layout);
+			}
 		}
 
 		current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER;
@@ -2772,8 +2777,10 @@
 			g_object_unref(td->status_icon);
 		if(td->prpl_icon)
 			g_object_unref(td->prpl_icon);
-		g_object_unref(td->layout);
-		g_object_unref(td->name_layout);
+		if (td->layout)
+			g_object_unref(td->layout);
+		if (td->name_layout)
+			g_object_unref(td->name_layout);
 		g_free(td);
 		gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata);
 	}
@@ -2790,6 +2797,10 @@
 {
 	PurpleBlistNode *node = data;
 	int width, height;
+	GList *list;
+	int max_text_width = 0;
+	int max_avatar_width = 0;
+	int status_size = 0;
 
 	if (gtkblist->tooltipdata) {
 		gtkblist->tipwindow = NULL;
@@ -2797,20 +2808,27 @@
 	}
 
 	gtkblist->tipwindow = widget;
-	if(PURPLE_BLIST_NODE_IS_CHAT(node) ||
-	   PURPLE_BLIST_NODE_IS_BUDDY(node) ||
-	   PURPLE_BLIST_NODE_IS_GROUP(node)) {
+	if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
+	   PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		struct tooltip_data *td = create_tip_for_node(node, TRUE);
+		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		PurpleGroup *group = (PurpleGroup*)node;
+		GSList *accounts;
 		struct tooltip_data *td = create_tip_for_node(node, TRUE);
 		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
-		width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE +
-			MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER;
-		height = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height))
-			+ TOOLTIP_BORDER;
-	} else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+
+		/* Accounts with buddies in group */
+		accounts = purple_group_get_accounts(group);
+		for (; accounts != NULL;
+		     accounts = g_slist_delete_link(accounts, accounts)) {
+			PurpleAccount *account = accounts->data;
+			td = create_tip_for_account(account);
+			gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+		}
+	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		PurpleBlistNode *child;
 		PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node);
-		int max_text_width = 0;
-		int max_avatar_width = 0;
 		width = height = 0;
 
 		for(child = node->child; child; child = child->next)
@@ -2822,18 +2840,25 @@
 				} else {
 					gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
 				}
-				max_text_width = MAX(max_text_width, MAX(td->width, td->name_width));
-				max_avatar_width = MAX(max_avatar_width, td->avatar_width);
-				height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height),
-						TOOLTIP_BORDER + td->height + td->name_height);
 			}
 		}
-		height += TOOLTIP_BORDER;
-		width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
 	} else {
 		return FALSE;
 	}
 
+	height = width = 0;
+	for (list = gtkblist->tooltipdata; list; list = list->next) {
+		struct tooltip_data *td = list->data;
+		max_text_width = MAX(max_text_width, MAX(td->width, td->name_width));
+		max_avatar_width = MAX(max_avatar_width, td->avatar_width);
+		height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE, td->avatar_height),
+				TOOLTIP_BORDER + td->height + td->name_height);
+		if (td->status_icon)
+			status_size = MAX(status_size, STATUS_SIZE);
+	}
+	height += TOOLTIP_BORDER;
+	width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
+
 	if (w)
 		*w = width;
 	if (h)
@@ -3100,7 +3125,7 @@
 		if (g_list_length(purple_connections_get_all()) > 1)
 		{
 			tmp = g_markup_escape_text(chat->account->username, -1);
-			g_string_append_printf(str, _("\n<b>Account:</b> %s"), tmp);
+			g_string_append_printf(str, _("<b>Account:</b> %s"), tmp);
 			g_free(tmp);
 		}
 
@@ -3293,7 +3318,6 @@
 
 		purple_notify_user_info_destroy(user_info);
 	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
-		GSList *accounts;
 		PurpleGroup *group = (PurpleGroup*)node;
 		PurpleNotifyUserInfo *user_info;
 
@@ -3313,14 +3337,6 @@
 		                                 tmp);
 		g_free(tmp);
 
-		/* Accounts with buddies in group */
-		accounts = purple_group_get_accounts(group);
-		for (; accounts != NULL;
-		     accounts = g_slist_delete_link(accounts, accounts)) {
-			PurpleAccount *account = accounts->data;
-			purple_notify_user_info_add_pair(user_info, _("Account"), purple_account_get_username(account));
-		}
-
 		tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
 		g_string_append(str, tmp);
 		g_free(tmp);
@@ -6480,20 +6496,10 @@
 
 	for (tmp = list; tmp; tmp = tmp->next)
 	{
-		GtkWidget *label;
-		GtkWidget *rowbox;
 		GtkWidget *input;
 
 		pce = tmp->data;
 
-		rowbox = gtk_hbox_new(FALSE, 5);
-		gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0);
-
-		label = gtk_label_new_with_mnemonic(pce->label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-		gtk_size_group_add_widget(data->sg, label);
-		gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 		if (pce->is_int)
 		{
 			GtkObject *adjust;
@@ -6501,7 +6507,7 @@
 										1, 10, 10);
 			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 			gtk_widget_set_size_request(input, 50, -1);
-			gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
 		}
 		else
 		{
@@ -6517,7 +6523,7 @@
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
 			}
-			gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
 							 G_CALLBACK(addchat_set_sensitive_if_input_cb), data);
 		}
@@ -6528,8 +6534,6 @@
 			gtk_widget_grab_focus(input);
 			focus = FALSE;
 		}
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), input);
-		pidgin_set_accessible_label(input, label);
 		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
 		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
 		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
@@ -6572,7 +6576,6 @@
 	GList *l;
 	PurpleConnection *gc;
 	GtkWidget *label;
-	GtkWidget *rowbox;
 	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *img;
@@ -6648,20 +6651,10 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->account_menu = pidgin_account_option_menu_new(account, FALSE,
 			G_CALLBACK(addchat_select_account_cb),
 			chat_account_filter_func, data);
-	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu);
-	pidgin_set_accessible_label (data->account_menu, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
 	gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0);
@@ -6669,36 +6662,17 @@
 
 	rebuild_addchat_entries(data);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("A_lias:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->alias_entry = gtk_entry_new();
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias);
-	gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0);
 	gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry);
-	pidgin_set_accessible_label (data->alias_entry, label);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_lias:"), data->sg, data->alias_entry, TRUE, NULL);
 	if (name != NULL)
 		gtk_widget_grab_focus(data->alias_entry);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Group:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree());
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->group_combo)->child);
-	pidgin_set_accessible_label (data->group_combo, label);
-	gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"), data->sg, data->group_combo, TRUE, NULL);
 	
 	data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online."));
 	data->persistent = gtk_check_button_new_with_mnemonic(_("_Hide chat when the window is closed."));
@@ -6972,7 +6946,8 @@
 	pidgin_blist_update_sort_methods();
 }
 
-void pidgin_blist_sort_method_unreg(const char *id){
+void pidgin_blist_sort_method_unreg(const char *id)
+{
 	GList *l = pidgin_blist_sort_methods;
 
 	while(l) {
@@ -6984,6 +6959,7 @@
 			g_free(method);
 			break;
 		}
+		l = l->next;
 	}
 	pidgin_blist_update_sort_methods();
 }
--- a/pidgin/gtkconv.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkconv.c	Sat Jan 05 18:07:16 2008 +0000
@@ -2444,7 +2444,6 @@
 {
 	PidginConversation *gtkconv;
 	PidginWindow *win;
-	PurpleBuddy *b;
 	GList *l;
 	GdkPixbuf *status = NULL;
 	GdkPixbuf *infopane_status = NULL;
@@ -2457,13 +2456,18 @@
 	if (conv != gtkconv->active_conv)
 		return;
 
-
 	status = pidgin_conv_get_tab_icon(conv, TRUE);
 	infopane_status = pidgin_conv_get_tab_icon(conv, FALSE);
 
-	b = purple_find_buddy(conv->account, conv->name);
-	if (b)
-		emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+		PurpleBuddy *b = purple_find_buddy(conv->account, conv->name);
+		if (b)
+			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
+	} else {
+		PurpleChat *c = purple_blist_find_chat(conv->account, conv->name);
+		if (c)
+			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)c);
+	}
 
 	g_return_if_fail(status != NULL);
 
--- a/pidgin/gtkpluginpref.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkpluginpref.c	Sat Jan 05 18:07:16 2008 +0000
@@ -93,22 +93,6 @@
 		case PURPLE_PLUGIN_PREF_NONE:
 		default:
 			if (format == PURPLE_STRING_FORMAT_TYPE_NONE)
-				box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-			else
-				box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-
-			gtk_widget_show(box);
-			gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0);
-
-			gtk_label = gtk_label_new_with_mnemonic(pref_label);
-			gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5);
-			gtk_widget_show(gtk_label);
-			gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0);
-
-			if(sg)
-				gtk_size_group_add_widget(sg, gtk_label);
-
-			if (format == PURPLE_STRING_FORMAT_TYPE_NONE)
 			{				
 				entry = gtk_entry_new();
 				gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(pref_name));
@@ -123,9 +107,7 @@
 				g_signal_connect(G_OBJECT(entry), "changed",
 								 G_CALLBACK(entry_cb),
 								 (gpointer)pref_name);
-				gtk_label_set_mnemonic_widget(GTK_LABEL(gtk_label), entry);
-				gtk_widget_show(entry);
-				gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
+				pidgin_add_widget_to_vbox(GTK_BOX(parent), pref_label, sg, entry, TRUE, NULL);
 			}
 			else
 			{
@@ -135,6 +117,19 @@
 				GtkWidget *toolbar;
 				GtkWidget *frame;
 
+				box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+
+				gtk_widget_show(box);
+				gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0);
+
+				gtk_label = gtk_label_new_with_mnemonic(pref_label);
+				gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5);
+				gtk_widget_show(gtk_label);
+				gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0);
+
+				if(sg)
+					gtk_size_group_add_widget(sg, gtk_label);
+
 				hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 				gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 				gtk_widget_show(hbox);
--- a/pidgin/gtkprefs.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkprefs.c	Sat Jan 05 18:07:16 2008 +0000
@@ -89,23 +89,12 @@
 pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title,
 		const char *key, int min, int max, GtkSizeGroup *sg)
 {
-	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *spin;
 	GtkObject *adjust;
 	int val;
 
 	val = purple_prefs_get_int(key);
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(title);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	adjust = gtk_adjustment_new(val, min, max, 1, 1, 1);
 	spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 	g_object_set_data(G_OBJECT(spin), "val", (char *)key);
@@ -113,21 +102,11 @@
 		gtk_widget_set_size_request(spin, 50, -1);
 	else
 		gtk_widget_set_size_request(spin, 60, -1);
-	gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(adjust), "value-changed",
 					 G_CALLBACK(update_spin_value), GTK_WIDGET(spin));
 	gtk_widget_show(spin);
 
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin);
-
-	if (sg) {
-		gtk_size_group_add_widget(sg, label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	}
-
-	pidgin_set_accessible_label (spin, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL);
 }
 
 static void
@@ -141,37 +120,18 @@
 pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title,
 							 const char *key, GtkSizeGroup *sg)
 {
-	GtkWidget *hbox, *label, *entry;
+	GtkWidget *entry;
 	const gchar *value;
 
 	value = purple_prefs_get_string(key);
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(page), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(title);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	entry = gtk_entry_new();
 	gtk_entry_set_text(GTK_ENTRY(entry), value);
-	gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(entry_set), (char*)key);
 	gtk_widget_show(entry);
 
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
-	if(sg) {
-		gtk_size_group_add_widget(sg, label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	}
-
-	pidgin_set_accessible_label(entry, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL);
 }
 
 static void
@@ -205,7 +165,6 @@
 {
 	GtkWidget  *dropdown, *opt, *menu;
 	GtkWidget  *label = NULL;
-	GtkWidget  *hbox;
 	gchar      *text;
 	const char *stored_str = NULL;
 	int         stored_int = 0;
@@ -215,19 +174,6 @@
 
 	g_return_val_if_fail(menuitems != NULL, NULL);
 
-	if (title != NULL) {
-		hbox = gtk_hbox_new(FALSE, 5);
-		/*gtk_container_add (GTK_CONTAINER (box), hbox);*/
-		gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-		gtk_widget_show(hbox);
-
-		label = gtk_label_new_with_mnemonic(title);
-		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-		gtk_widget_show(label);
-	} else {
-		hbox = box;
-	}
-
 #if 0 /* GTK_CHECK_VERSION(2,4,0) */
 	if(type == PURPLE_PREF_INT)
 		model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
@@ -239,11 +185,6 @@
 	menu = gtk_menu_new();
 #endif
 
-	if (label != NULL) {
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown);
-		pidgin_set_accessible_relations (dropdown, label);
-	}
-
 	if (type == PURPLE_PREF_INT)
 		stored_int = purple_prefs_get_int(key);
 	else if (type == PURPLE_PREF_STRING)
@@ -293,8 +234,8 @@
 	}
 
 	gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0);
-	gtk_widget_show(dropdown);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label);
 
 	return label;
 }
@@ -983,7 +924,6 @@
 
 #if GTK_CHECK_VERSION(2,4,0)
 	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *font_button;
 	const char *font_name;
 #endif
@@ -1026,19 +966,15 @@
 		fontpref = pidgin_prefs_checkbox(_("Use document font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
 	else
 		fontpref = pidgin_prefs_checkbox(_("Use font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
-	hbox = gtk_hbox_new(FALSE, 3);
-	label = gtk_label_new_with_mnemonic(_("Conversation _font:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
 	font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font");
 	font_button = gtk_font_button_new_with_font(font_name ? font_name : NULL);
 	gtk_font_button_set_show_style(GTK_FONT_BUTTON(font_button), TRUE);
-	gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Conversation _font:"), NULL, font_button, FALSE, NULL);
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font"))
 		gtk_widget_set_sensitive(hbox, FALSE);
 	g_signal_connect(G_OBJECT(fontpref), "clicked", G_CALLBACK(pidgin_toggle_sensitive), hbox);
 	g_signal_connect(G_OBJECT(font_button), "font-set", G_CALLBACK(pidgin_custom_font_set), NULL);
-	gtk_widget_show_all(hbox);
 #endif
 
 	vbox = pidgin_make_frame(ret, _("Default Formatting"));
@@ -1520,28 +1456,16 @@
 									browser_changed1_cb, hbox);
 	}
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Manual:\n(%s for URL)"));
-	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	entry = gtk_entry_new();
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
 	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom"))
 		gtk_widget_set_sensitive(hbox, FALSE);
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser",
 								browser_changed2_cb, hbox);
-
-	gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
-
 	gtk_entry_set_text(GTK_ENTRY(entry),
 					   purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command"));
 	g_signal_connect(G_OBJECT(entry), "focus-out-event",
 					 G_CALLBACK(manual_browser_set), NULL);
-	pidgin_set_accessible_label (entry, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL);
 
 	gtk_widget_show_all(ret);
 	g_object_unref(sg);
@@ -1824,33 +1748,20 @@
 	gtk_size_group_add_widget(sg, dd);
 	gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
 
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("Sound c_ommand:\n(%s for filename)"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
 	entry = gtk_entry_new();
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
 	gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
 	cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command");
 	if(cmd)
 		gtk_entry_set_text(GTK_ENTRY(entry), cmd);
-
-	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(sound_cmd_yeah), NULL);
 
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Sound c_ommand:\n(%s for filename)"), sg, entry, TRUE, NULL);
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed1_cb, hbox);
 	gtk_widget_set_sensitive(hbox,
 			!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"),
 					"custom"));
-
-	pidgin_set_accessible_label (entry, label);
 #endif /* _WIN32 */
 
 	vbox = pidgin_make_frame (ret, _("Sound Options"));
@@ -1864,13 +1775,6 @@
 				NULL);
 
 #ifdef USE_GSTREAMER
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("Volume:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
 	sw = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
 	gtk_range_set_increments(GTK_RANGE(sw), 5.0, 25.0);
 	gtk_range_set_value(GTK_RANGE(sw), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"));
@@ -1880,7 +1784,7 @@
 	g_signal_connect (G_OBJECT (sw), "value-changed",
 			  G_CALLBACK (prefs_sound_volume_changed),
 			  NULL);
-	gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Volume:"), NULL, sw, TRUE, NULL);
 
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed3_cb, hbox);
@@ -2009,7 +1913,6 @@
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
-	GtkWidget *hbox;
 	GtkWidget *dd;
 	GtkWidget *label;
 	GtkWidget *button;
@@ -2060,22 +1963,13 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), select);
 
-	hbox = gtk_hbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new_with_mnemonic(_("Change _status to:"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	/* TODO: Show something useful if we don't have any saved statuses. */
+	menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away));
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Change _status to:"), sg, menu, TRUE, &label);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(pidgin_toggle_sensitive), menu);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), label);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	/* TODO: Show something useful if we don't have any saved statuses. */
-	menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away));
-	gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(pidgin_toggle_sensitive), menu);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu);
 
 	if (!purple_prefs_get_bool("/purple/away/away_when_idle")) {
 		gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
@@ -2089,22 +1983,13 @@
 	button = pidgin_prefs_checkbox(_("Use status from last _exit at startup"),
 		"/purple/savedstatus/startup_current_status", vbox);
 
-	hbox = gtk_hbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new_with_mnemonic(_("Status to a_pply at startup:"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	/* TODO: Show something useful if we don't have any saved statuses. */
+	menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus));
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(pidgin_toggle_sensitive), menu);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Status to a_pply at startup:"), sg, menu, TRUE, &label);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), label);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	/* TODO: Show something useful if we don't have any saved statuses. */
-	menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus));
-	gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(pidgin_toggle_sensitive), menu);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu);
 
 	if (purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) {
 		gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
--- a/pidgin/gtkprivacy.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkprivacy.c	Sat Jan 05 18:07:16 2008 +0000
@@ -359,7 +359,6 @@
 privacy_dialog_new(void)
 {
 	PidginPrivacyDialog *dialog;
-	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *button;
 	GtkWidget *dropdown;
@@ -386,22 +385,10 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_widget_show(label);
 
-	/* Hbox for the accounts drop-down and label. */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	/* "Set privacy for:" label */
-	label = gtk_label_new(_("Set privacy for:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	/* Accounts drop-down */
 	dropdown = pidgin_account_option_menu_new(NULL, FALSE,
 												G_CALLBACK(select_account_cb), NULL, dialog);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0);
-	gtk_widget_show(dropdown);
-	pidgin_set_accessible_label (dropdown, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Set privacy for:"), NULL, dropdown, TRUE, NULL);
 	dialog->account = pidgin_account_option_menu_get_selected(dropdown);
 
 	/* Add the drop-down list with the allow/block types. */
--- a/pidgin/gtkroomlist.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkroomlist.c	Sat Jan 05 18:07:16 2008 +0000
@@ -521,9 +521,7 @@
 	GtkWidget *window;
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
-	GtkWidget *account_hbox;
 	GtkWidget *bbox;
-	GtkWidget *label;
 
 	dialog = g_new0(PidginRoomlistDialog, 1);
 	dialog->account = account;
@@ -542,25 +540,11 @@
 	gtk_widget_show(vbox2);
 
 	/* accounts dropdown list */
-	account_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox2), account_hbox, FALSE, FALSE, 0);
-	gtk_widget_show(account_hbox);
-
-	label = gtk_label_new(NULL);
-	gtk_box_pack_start(GTK_BOX(account_hbox), label, FALSE, FALSE, 0);
-	gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_widget_show(label);
-
 	dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE,
 	                         G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog);
-
 	if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */
 		dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget);
-
-	gtk_box_pack_start(GTK_BOX(account_hbox), dialog->account_widget, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(dialog->account_widget));
-	gtk_widget_show(dialog->account_widget);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
 
 	/* scrolled window */
 	dialog->sw = gtk_scrolled_window_new(NULL, NULL);
--- a/pidgin/gtksavedstatuses.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtksavedstatuses.c	Sat Jan 05 18:07:16 2008 +0000
@@ -1092,7 +1092,6 @@
 	GtkWidget *entry;
 	GtkWidget *frame;
 	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *sw;
 	GtkWidget *text;
 	GtkWidget *toolbar;
@@ -1141,52 +1140,29 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	/* Title */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Title:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	entry = gtk_entry_new();
 	dialog->title = GTK_ENTRY(entry);
 	if ((saved_status != NULL)
 			&& !purple_savedstatus_is_transient(saved_status)
 			&& (purple_savedstatus_get_title(saved_status) != NULL))
 		gtk_entry_set_text(GTK_ENTRY(entry), purple_savedstatus_get_title(saved_status));
-	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(editor_title_changed_cb), dialog);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Title:"), sg, entry, TRUE, NULL);
 
 	/* Status type */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Status:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	if (saved_status != NULL)
 		dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status));
 	else
 		dropdown = create_status_type_menu(PURPLE_STATUS_AWAY);
 	dialog->type = GTK_OPTION_MENU(dropdown);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL);
 
 	/* Status message */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Message:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL);
 	dialog->message = GTK_IMHTML(text);
-	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Message:"), sg, frame, TRUE, NULL);
+	gtk_container_child_set(GTK_CONTAINER(vbox), hbox, "expand", TRUE, "fill", TRUE, NULL);
 	focus_chain = g_list_prepend(focus_chain, dialog->message);
 	gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain);
 	g_list_free(focus_chain);
--- a/pidgin/gtkutils.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkutils.c	Sat Jan 05 18:07:16 2008 +0000
@@ -1701,23 +1701,23 @@
 	GdkPixbuf *pixbuf = NULL;
 
 	if (prim == PURPLE_STATUS_UNAVAILABLE)
-        	pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_AWAY)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_EXTENDED_AWAY)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_INVISIBLE)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_OFFLINE)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE,
-                                                 icon_size, "GtkWidget");
-        else
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE,
-                                                 icon_size, "GtkWidget");
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_AWAY)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_EXTENDED_AWAY)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_INVISIBLE)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_OFFLINE)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE,
+				icon_size, "GtkWidget");
+	else
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE,
+				icon_size, "GtkWidget");
 	return pixbuf;
 
 }
@@ -3321,6 +3321,40 @@
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
 }
 
+GtkWidget *
+pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label)
+{
+	GtkWidget *hbox;
+	GtkWidget *label = NULL;
+
+	if (widget_label) {
+		hbox = gtk_hbox_new(FALSE, 5);
+		gtk_widget_show(hbox);
+		gtk_box_pack_start(vbox, hbox, FALSE, FALSE, 0);
+
+		label = gtk_label_new_with_mnemonic(widget_label);
+		gtk_widget_show(label);
+		if (sg) {
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+			gtk_size_group_add_widget(sg, label);
+		}
+		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	} else {
+		hbox = GTK_WIDGET(vbox);
+	}
+
+	gtk_widget_show(widget);
+	gtk_box_pack_start(GTK_BOX(hbox), widget, expand, TRUE, 0);
+	if (label) {
+		gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
+		pidgin_set_accessible_label (widget, label);
+	}
+
+	if (p_label)
+		(*p_label) = label;
+	return hbox;
+}
+
 gboolean pidgin_auto_parent_window(GtkWidget *widget)
 {
 #if 0
--- a/pidgin/gtkutils.h	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/gtkutils.h	Sat Jan 05 18:07:16 2008 +0000
@@ -794,5 +794,20 @@
  */
 gboolean pidgin_auto_parent_window(GtkWidget *window);
 
+/**
+ * Add a labelled widget to a GtkVBox
+ *
+ * @param vbox         The GtkVBox to add the widget to.
+ * @param widget_label The label to give the widget.
+ * @param sg           The GtkSizeGroup to add the label to.
+ * @param widget       The GtkWidget to add
+ * @param expand       Whether to expand the widget horizontally.
+ * @param p_label      Place to store a pointer to the GtkLabel, or NULL if you don't care.
+ *
+ * @return  A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget.
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
+
 #endif /* _PIDGINUTILS_H_ */
 
--- a/pidgin/plugins/spellchk.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/plugins/spellchk.c	Sat Jan 05 18:07:16 2008 +0000
@@ -2161,14 +2161,13 @@
 get_config_frame(PurplePlugin *plugin)
 {
 	GtkWidget *ret, *vbox, *win;
-	GtkWidget *hbox, *label;
+	GtkWidget *hbox;
 	GtkWidget *button;
 	GtkSizeGroup *sg;
 	GtkSizeGroup *sg2;
 	GtkCellRenderer *renderer;
 	GtkTreeViewColumn *column;
 	GtkWidget *vbox2;
-	GtkWidget *hbox2;
 	GtkWidget *vbox3;
 
 	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
@@ -2275,37 +2274,15 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 	sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	hbox2 = gtk_hbox_new(FALSE, 2);
-	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
-	gtk_widget_show(hbox2);
-
-	label = gtk_label_new_with_mnemonic(_("You _type:"));
-	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-
 	bad_entry = gtk_entry_new();
 	/* Set a minimum size. Since they're in a size group, the other entry will match up. */
 	gtk_widget_set_size_request(bad_entry, 350, -1);
-	gtk_box_pack_start(GTK_BOX(hbox2), bad_entry, TRUE, TRUE, 0);
 	gtk_size_group_add_widget(sg2, bad_entry);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry);
-	gtk_widget_show(bad_entry);
-
-	hbox2 = gtk_hbox_new(FALSE, 2);
-	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
-	gtk_widget_show(hbox2);
-
-	label = gtk_label_new_with_mnemonic(_("You _send:"));
-	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _type:"), sg, bad_entry, FALSE, NULL);
 
 	good_entry = gtk_entry_new();
-	gtk_box_pack_start(GTK_BOX(hbox2), good_entry, TRUE, TRUE, 0);
 	gtk_size_group_add_widget(sg2, good_entry);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry);
-	gtk_widget_show(good_entry);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _send:"), sg, good_entry, FALSE, NULL);
 
 	/* Created here so it can be passed to whole_words_button_toggled. */
 	case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)"));
--- a/pidgin/win32/winpidgin.c	Sat Jan 05 18:01:12 2008 +0000
+++ b/pidgin/win32/winpidgin.c	Sat Jan 05 18:07:16 2008 +0000
@@ -450,23 +450,25 @@
 
 	if ((h = CreateMutex(NULL, FALSE, "pidgin_is_running"))) {
 		DWORD err = GetLastError();
-		if (err == ERROR_ALREADY_EXISTS && fail_if_running) {
-			HWND msg_win;
+		if (err == ERROR_ALREADY_EXISTS) {
+			if (fail_if_running) {
+				HWND msg_win;
 
-			printf("An instance of Pidgin is already running.\n");
+				printf("An instance of Pidgin is already running.\n");
 
-			if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL)))
-				if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
-					return FALSE;
+				if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL)))
+					if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
+						return FALSE;
 
-			/* If we get here, the focus request wasn't successful */
+				/* If we get here, the focus request wasn't successful */
 
-			MessageBox(NULL,
-				"An instance of Pidgin is already running",
-				NULL, MB_OK | MB_TOPMOST);
+				MessageBox(NULL,
+					"An instance of Pidgin is already running",
+					NULL, MB_OK | MB_TOPMOST);
 
-			return FALSE;
-		} else
+				return FALSE;
+			}
+		} else if (err != ERROR_SUCCESS)
 			printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err);
 	}
 	return TRUE;
--- a/po/de.po	Sat Jan 05 18:01:12 2008 +0000
+++ b/po/de.po	Sat Jan 05 18:07:16 2008 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-12-19 10:17+0100\n"
-"PO-Revision-Date: 2007-12-19 10:17+0100\n"
+"POT-Creation-Date: 2008-01-05 13:31+0100\n"
+"PO-Revision-Date: 2008-01-05 13:31+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1436,19 +1436,6 @@
 msgstr "Ze_rtifikat ansehen..."
 
 #. Prompt the user to authenticate the certificate
-#. TODO: Provide the user with more guidance about why he is
-#. being prompted
-#. vrq will be completed by user_auth
-#, c-format
-msgid ""
-"The certificate presented by \"%s\" claims to be from \"%s\" instead.  This "
-"could mean that you are not connecting to the service you believe you are."
-msgstr ""
-"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen "
-"von „%s“ zu kommen.  Das kann bedeuten, dass Sie tatsächlich nicht mit dem "
-"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein."
-
-#. Prompt the user to authenticate the certificate
 #. vrq will be completed by user_auth
 #, c-format
 msgid ""
@@ -1498,6 +1485,19 @@
 msgid "Invalid certificate authority signature"
 msgstr "Unbekannte Zertifizierungsstellensignatur"
 
+#. Prompt the user to authenticate the certificate
+#. TODO: Provide the user with more guidance about why he is
+#. being prompted
+#. vrq will be completed by user_auth
+#, c-format
+msgid ""
+"The certificate presented by \"%s\" claims to be from \"%s\" instead.  This "
+"could mean that you are not connecting to the service you believe you are."
+msgstr ""
+"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen "
+"von „%s“ zu kommen.  Das kann bedeuten, dass Sie tatsächlich nicht mit dem "
+"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein."
+
 #. Make messages
 #, c-format
 msgid ""
@@ -3285,6 +3285,10 @@
 msgid "nickserv: Send a command to nickserv"
 msgstr "nickserv: Sendet ein Kommando zum Nickserv"
 
+msgid "notice &lt;target&lt;:  Send a notice to a user or channel."
+msgstr ""
+"notice &lt;Ziel&gt;:  Sende eine Notiz an einen Benutzer oder an einen Kanal."
+
 msgid ""
 "op &lt;nick1&gt; [nick2] ...:  Grant channel operator status to someone. You "
 "must be a channel operator to do this."
@@ -3971,8 +3975,8 @@
 msgid "Mood"
 msgstr "Stimmung"
 
-msgid "Current media"
-msgstr "Aktuelles Medium"
+msgid "Now Listening"
+msgstr ""
 
 msgid "Mood Text"
 msgstr "Stimmungstext"
@@ -9632,6 +9636,14 @@
 msgid "Unable to connect to %s: %s"
 msgstr "Verbindung zu %s nicht möglich: %s"
 
+#, c-format
+msgid " - %s"
+msgstr " - %s"
+
+#, c-format
+msgid " (%s)"
+msgstr " (%s)"
+
 #. 10053
 #, c-format
 msgid "Connection interrupted by other software on your computer."
@@ -9640,7 +9652,7 @@
 "unterbrochen."
 
 #. 10054
-#, fuzzy, c-format
+#, c-format
 msgid "Remote host closed connection."
 msgstr "Der entfernte Host hat die Verbindung beendet."
 
@@ -9908,6 +9920,12 @@
 "Sie sind im Moment nicht mit einem Konto angemeldet, welches benutzt werden "
 "kann, um diesen Buddy hinzuzufügen."
 
+#. I don't believe this can happen currently, I think
+#. * everything that calls this function checks for one of the
+#. * above node types first.
+msgid "Unknown node type"
+msgstr "Unbekannter Knotentyp"
+
 #. Buddies menu
 msgid "/_Buddies"
 msgstr "/_Buddys"
@@ -10009,12 +10027,8 @@
 msgstr "/Hilfe/Ü_ber"
 
 #, c-format
-msgid ""
-"\n"
-"<b>Account:</b> %s"
-msgstr ""
-"\n"
-"<b>Konto:</b> %s"
+msgid "<b>Account:</b> %s"
+msgstr "<b>Konto:</b> %s"
 
 #, c-format
 msgid ""
@@ -10045,6 +10059,12 @@
 msgid "Rockin'"
 msgstr "Abgefahren"
 
+msgid "Total Buddies"
+msgstr "Buddy-Anzahl"
+
+msgid "Online Buddies"
+msgstr "Online-Buddys"
+
 #, c-format
 msgid "Idle %dd %dh %02dm"
 msgstr "Untätig %dd %dh %02dm"
@@ -11108,31 +11128,33 @@
 msgstr ""
 "Farbe zum Darstellen von Hyperlinks, wenn sich die Maus darüber befindet."
 
-#, fuzzy
 msgid "Sent Message Name Color"
-msgstr "Gesendete Nachrichten"
+msgstr "Farbe des Absendernamens für gesendete Nachrichten"
 
 msgid "Color to draw the name of a message you sent."
 msgstr ""
-
-#, fuzzy
+"Farbe, mit der der Name in einer gesendeten Nachricht dargestellt wird."
+
 msgid "Received Message Name Color"
-msgstr "Empfangene Nachrichten"
+msgstr "Farbe des Absendernamens für empfangene Nachrichten"
 
 msgid "Color to draw the name of a message you received."
 msgstr ""
+"Farbe, mit der der Name in einer empfangenen Nachricht dargestellt wird."
 
 msgid "\"Attention\" Name Color"
-msgstr ""
+msgstr "Farbe des Absendernamens für \"Achtung\"-Nachrichten"
 
 msgid "Color to draw the name of a message you received containing your name."
 msgstr ""
+"Farbe, mit der der Name in einer Nachricht dargestellt wird, die Ihren Namen "
+"enthält."
 
 msgid "Action Message Name Color"
-msgstr ""
+msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
 
 msgid "Color to draw the name of an action message."
-msgstr ""
+msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird."
 
 msgid "_Copy E-Mail Address"
 msgstr "Kopiere _E-Mail-Adresse"
@@ -11187,15 +11209,6 @@
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-msgid "_Font"
-msgstr "_Schrift"
-
-msgid "_Insert"
-msgstr "_Einfügen"
-
-msgid "S_mile!"
-msgstr "_Lächeln!"
-
 msgid "Select Font"
 msgstr "Schriftart wählen"
 
@@ -11224,6 +11237,9 @@
 msgid "Insert Link"
 msgstr "Link einfügen"
 
+msgid "_Insert"
+msgstr "_Einfügen"
+
 #, c-format
 msgid "Failed to store image: %s\n"
 msgstr "Speichern des Bildes fehlgeschlagen: %s\n"
@@ -11231,12 +11247,14 @@
 msgid "Insert Image"
 msgstr "Bild einfügen"
 
+msgid "Smile!"
+msgstr "Lächeln!"
+
 msgid "This theme has no available smileys."
 msgstr "Dieses Thema verfügt über keine Smileys."
 
-#. show everything
-msgid "Smile!"
-msgstr "Lächeln!"
+msgid "_Font"
+msgstr "_Schrift"
 
 msgid "Group Items"
 msgstr "Elemente gruppieren"
@@ -12029,7 +12047,6 @@
 msgid "Changes to privacy settings take effect immediately."
 msgstr "Einstellungen bzgl. der Privatsphäre werden sofort wirksam."
 
-#. "Set privacy for:" label
 msgid "Set privacy for:"
 msgstr "Setze Privatsphäre für:"
 
@@ -12850,9 +12867,8 @@
 msgid "Hyperlink Color"
 msgstr "Hyperlink-Farbe"
 
-#, fuzzy
 msgid "Highlighted Message Name Color"
-msgstr "Hervorgehobene Nachrichten"
+msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten"
 
 msgid "GtkTreeView Horizontal Separation"
 msgstr "GtkTreeview horizontaler Abstand"
@@ -12861,7 +12877,7 @@
 msgstr "Unterhaltungseintrag"
 
 msgid "Request Dialog"
-msgstr "Dialog anfordern"
+msgstr "Anfrage-Dialog"
 
 msgid "Notify Dialog"
 msgstr "Benachrichtigungsdialog"