changeset 17178:c5ca1d9b67fa

merge of '745cf14dbfb86060ab8826900f365a2f0592747e' and 'a2283b64b34e89187d1eb3015219bafedbdfdae3'
author Eric Polino <aluink@pidgin.im>
date Fri, 18 May 2007 14:00:33 +0000
parents d761647bc7c4 (current diff) 7f00fb7f18b5 (diff)
children 0598803f9b64 d8b9bea550bc
files
diffstat 24 files changed, 583 insertions(+), 243 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun May 13 21:27:13 2007 +0000
+++ b/COPYRIGHT	Fri May 18 14:00:33 2007 +0000
@@ -323,6 +323,7 @@
 Phil Snowberger
 Eddie Sohn (tr1sk)
 Sony Computer Entertainment America, Inc.
+Andy Spencer
 Mark Spencer
 Lex Spoon
 Chris Stafford
@@ -360,6 +361,7 @@
 James Vega
 David Vermeille
 Sid Vicious
+Jorge VillaseƱor (Masca)
 Bjoern Voigt
 Wan Hing Wah
 Philip Walford
--- a/ChangeLog	Sun May 13 21:27:13 2007 +0000
+++ b/ChangeLog	Fri May 18 14:00:33 2007 +0000
@@ -24,9 +24,14 @@
 	* Identify the account when warning about plaintext auth over an
 	  unencrypted channel
 	* Fix XMPP SASL authentication error when using Cyrus and a connect server
+	* Fix changing tab locations to update properly
 	* Turning off "Show formatting on incoming messages" now ignores
 	  formatting in <span> tags too
 	* File transfer progress for transfers on MSN is now correctly displayed
+	* You can set/change alias of buddies/chats by double-clicking on the
+	  conversation tabs (Ma Xuan)
+	* Fix IRC connection bug with dircproxy (xjoe)
+	* Ctrl+[shift]+tab focuses the next most active tab (William Thompson)
 
 	Finch:
 	* Userlist in chat windows, which can be turned on or off using
@@ -39,6 +44,7 @@
 	* Work around an ncurses bug which appears when half of a multi-cell
 	  character is covered by an upper-level window
 	* New plugins are shown in bold text in the plugin dialog
+	* Nicer HTML screendumps
 
 version 2.0.0 (5/3/2007):
 	* The project has new names - libpurple for the core, Pidgin for the
--- a/finch/libgnt/gntwm.c	Sun May 13 21:27:13 2007 +0000
+++ b/finch/libgnt/gntwm.c	Fri May 18 14:00:33 2007 +0000
@@ -628,14 +628,40 @@
 	int x, y;
 	chtype old = 0, now = 0;
 	FILE *file = fopen("dump.html", "w");
+	struct {
+		char ascii;
+		char *unicode;
+	} unis[] = {
+		{'q', "&#x2500;"},
+		{'t', "&#x251c;"},
+		{'u', "&#x2524;"},
+		{'x', "&#x2502;"},
+		{'-', "&#x2191;"},
+		{'.', "&#x2193;"},
+		{'l', "&#x250c;"},
+		{'k', "&#x2510;"},
+		{'m', "&#x2514;"},
+		{'j', "&#x2518;"},
+		{'a', "&#x2592;"},
+		{'\0', NULL}
+	};
 
+	fprintf(file, "<head>\n  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n</head>\n<body>\n");
 	fprintf(file, "<pre>");
 	for (y = 0; y < getmaxy(stdscr); y++) {
 		for (x = 0; x < getmaxx(stdscr); x++) {
-			char ch;
+			char ch[2] = {0, 0}, *print;
+#ifdef NO_WIDECHAR
 			now = mvwinch(curscr, y, x);
-			ch = now & A_CHARTEXT;
-			now ^= ch;
+			ch[0] = now & A_CHARTEXT;
+			now ^= ch[0];
+#else
+			cchar_t wch;
+			char unicode[12];
+			mvwin_wch(curscr, y, x, &wch);
+			now = wch.attr;
+			ch[0] = (char)(wch.chars[0] & 0xff);
+#endif
 
 #define CHECK(attr, start, end) \
 			do \
@@ -688,48 +714,39 @@
 				fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">",
 						bg.r, bg.g, bg.b, fg.r, fg.g, fg.b);
 			}
+			print = ch;
+#ifndef NO_WIDECHAR
+			if (wch.chars[0] > 255) {
+				snprintf(unicode, sizeof(unicode), "&#x%x;", wch.chars[0]);
+				print = unicode;
+			}
+#endif
 			if (now & A_ALTCHARSET)
 			{
-				switch (ch)
-				{
-					case 'q':
-						ch = '-'; break;
-					case 't':
-					case 'u':
-					case 'x':
-						ch = '|'; break;
-					case 'v':
-					case 'w':
-					case 'l':
-					case 'm':
-					case 'k':
-					case 'j':
-					case 'n':
-						ch = '+'; break;
-					case '-':
-						ch = '^'; break;
-					case '.':
-						ch = 'v'; break;
-					case 'a':
-						ch = '#'; break;
-					default:
-						ch = ' '; break;
+				int u;
+				for (u = 0; unis[u].ascii; u++) {
+					if (ch[0] == unis[u].ascii) {
+						print = unis[u].unicode;
+						break;
+					}
 				}
+				if (!unis[u].ascii)
+					print = " ";
 			}
-			if (ch == '&')
+			if (ch[0] == '&')
 				fprintf(file, "&amp;");
-			else if (ch == '<')
+			else if (ch[0] == '<')
 				fprintf(file, "&lt;");
-			else if (ch == '>')
+			else if (ch[0] == '>')
 				fprintf(file, "&gt;");
 			else
-				fprintf(file, "%c", ch);
+				fprintf(file, "%s", print);
 			old = now;
 		}
 		fprintf(file, "</span>\n");
 		old = 0;
 	}
-	fprintf(file, "</pre>");
+	fprintf(file, "</pre>\n</body>");
 	fclose(file);
 	return TRUE;
 }
--- a/finch/plugins/gntgf.c	Sun May 13 21:27:13 2007 +0000
+++ b/finch/plugins/gntgf.c	Fri May 18 14:00:33 2007 +0000
@@ -56,7 +56,8 @@
 #include <gntlabel.h>
 #include <gnttree.h>
 
-#include <gntplugin.h>
+#include "gntplugin.h"
+#include "gntconv.h"
 
 typedef struct
 {
@@ -154,7 +155,7 @@
 #endif
 
 static void
-notify(const char *fmt, ...)
+notify(PurpleConversation *conv, const char *fmt, ...)
 {
 	GntWidget *window;
 	GntToast *toast;
@@ -164,6 +165,13 @@
 
 	if (purple_prefs_get_bool(PREFS_BEEP))
 		beep();
+
+	if (conv != NULL) {
+		FinchConv *fc = conv->ui_data;
+		if (gnt_widget_has_focus(fc->window))
+			return;
+	}
+
 #ifdef HAVE_X11
 	if (purple_prefs_get_bool(PREFS_URGENT))
 		urgent();
@@ -220,14 +228,14 @@
 buddy_signed_on(PurpleBuddy *buddy, gpointer null)
 {
 	if (purple_prefs_get_bool(PREFS_EVENT_SIGNONF))
-		notify(_("%s just signed on"), purple_buddy_get_alias(buddy));
+		notify(NULL, _("%s just signed on"), purple_buddy_get_alias(buddy));
 }
 
 static void
 buddy_signed_off(PurpleBuddy *buddy, gpointer null)
 {
 	if (purple_prefs_get_bool(PREFS_EVENT_SIGNONF))
-		notify(_("%s just signed off"), purple_buddy_get_alias(buddy));
+		notify(NULL, _("%s just signed off"), purple_buddy_get_alias(buddy));
 }
 
 static void
@@ -235,7 +243,7 @@
 		PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
 {
 	if (purple_prefs_get_bool(PREFS_EVENT_IM_MSG))
-		notify(_("%s sent you a message"), sender);
+		notify(conv, _("%s sent you a message"), sender);
 }
 
 static void
@@ -254,9 +262,9 @@
 
 	if (purple_prefs_get_bool(PREFS_EVENT_CHAT_NICK) &&
 			(purple_utf8_has_word(msg, nick)))
-		notify(_("%s said your nick in %s"), sender, purple_conversation_get_name(conv));
+		notify(conv, _("%s said your nick in %s"), sender, purple_conversation_get_name(conv));
 	else if (purple_prefs_get_bool(PREFS_EVENT_CHAT_MSG))
-		notify(_("%s sent a message in %s"), sender, purple_conversation_get_name(conv));
+		notify(conv, _("%s sent a message in %s"), sender, purple_conversation_get_name(conv));
 }
 
 static gboolean
--- a/libpurple/idle.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/idle.c	Fri May 18 14:00:33 2007 +0000
@@ -31,7 +31,6 @@
 #include "signals.h"
 
 #define IDLEMARK 600 /* 10 minutes! */
-#define IDLE_CHECK_INTERVAL 5 /* 5 seconds */
 
 typedef enum
 {
@@ -92,6 +91,8 @@
 	purple_presence_set_idle(presence, FALSE, 0);
 }
 
+
+static gint time_until_next_idle_event;
 /*
  * This function should be called when you think your idle state
  * may have changed.  Maybe you're over the 10-minute mark and
@@ -110,14 +111,17 @@
  * 2. Set or unset your auto-away message.
  * 3. Report your current idle time to the IM server.
  */
-static gint
-check_idleness()
+
+static void
+check_idleness(void)
 {
 	time_t time_idle;
 	gboolean auto_away;
 	const gchar *idle_reporting;
 	gboolean report_idle;
 	GList *l;
+	gint away_seconds = 0;
+	static int no_away = 0;
 
 	purple_signal_emit(purple_blist_get_handle(), "update-idle");
 
@@ -153,14 +157,24 @@
 			time_idle = time(NULL) - last_active_time;
 	}
 
-	if (auto_away &&
-		(time_idle > (60 * purple_prefs_get_int("/purple/away/mins_before_away"))))
+	time_until_next_idle_event = IDLEMARK - time_idle; /* reasonable start upperbound */
+
+	if (auto_away || !no_away)
+		away_seconds = 60 * purple_prefs_get_int("/purple/away/mins_before_away");
+
+	if (auto_away && time_idle > away_seconds)
 	{
 		purple_savedstatus_set_idleaway(TRUE);
+		no_away = 0;
+		if (time_idle < away_seconds && (away_seconds - time_idle) < time_until_next_idle_event)
+			time_until_next_idle_event = away_seconds - time_idle;
 	}
-	else if (time_idle < 60 * purple_prefs_get_int("/purple/away/mins_before_away"))
+	else if (!no_away && time_idle < away_seconds)
 	{
 		purple_savedstatus_set_idleaway(FALSE);
+		no_away = 1;
+		if (time_idle < away_seconds && (away_seconds - time_idle) < time_until_next_idle_event)
+			time_until_next_idle_event = away_seconds - time_idle;
 	}
 
 	/* Idle reporting stuff */
@@ -177,8 +191,21 @@
 		while (idled_accts != NULL)
 			set_account_unidle(idled_accts->data);
 	}
+	
+	if (time_until_next_idle_event < 0)
+		time_until_next_idle_event = IDLEMARK;
+}
 
-	return TRUE;
+
+/*
+ * Check idle and set the timer to fire at the next idle-worth event 
+ */
+static gint
+check_idleness_timer()
+{
+	check_idleness();
+	idle_timer = purple_timeout_add(1000 * (time_until_next_idle_event + 1), check_idleness_timer, NULL);
+	return FALSE;
 }
 
 static void
@@ -241,7 +268,7 @@
 purple_idle_init()
 {
 	/* Add the timer to check if we're idle */
-	idle_timer = purple_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idleness, NULL);
+	idle_timer = purple_timeout_add(1000 * (IDLEMARK + 1), check_idleness_timer, NULL);
 
 	purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg",
 						purple_idle_get_handle(),
--- a/libpurple/plugins/joinpart.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/plugins/joinpart.c	Fri May 18 14:00:33 2007 +0000
@@ -156,7 +156,7 @@
 static gboolean check_expire_time(struct joinpart_key *key,
                                   time_t *last_said, time_t *limit)
 {
-	purple_debug_info("joinpart", "Removing key for %s/%s\n", key->conv->name, key->user);
+	purple_debug_info("joinpart", "Removing key for %s\n", key->user);
 	return (*last_said < *limit);
 }
 
--- a/libpurple/plugins/perl/Makefile.am	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/plugins/perl/Makefile.am	Fri May 18 14:00:33 2007 +0000
@@ -78,7 +78,8 @@
 #	common/fallback/const-xs.inc
 
 perl_scripts = \
-	scripts/function_list.pl
+	scripts/function_list.pl \
+	scripts/signals-test.pl
 
 EXTRA_DIST = \
 	Makefile.mingw \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/perl/scripts/signals-test.pl	Fri May 18 14:00:33 2007 +0000
@@ -0,0 +1,80 @@
+$MODULE_NAME = "Signals Test Script in Perl";
+
+use Purple;
+
+%PLUGIN_INFO = (
+	perl_api_version => 2,
+	name => "Perl: $MODULE_NAME",
+	version => "0.1",
+	summary => "Signals Test plugin for the Perl interpreter.",
+	description => "Demonstrate the user of purple signals from " .
+		       "a perl plugin.",
+	author => "Sadrul Habib Chowdhury <sadrul\@pidgin.im>",
+	url => "http://developer.pidgin.im/wiki/sadrul/",
+
+	load => "plugin_load",
+	unload => "plugin_unload"
+);
+
+# Accounts
+sub account_connecting_cb
+{
+	my $account = shift;
+	Purple::Debug::misc("signals test in perl", "account-connecting (" . $account->get_username() . ")\n");
+}
+
+# Buddylist
+sub buddy_signed_on
+{
+	my $buddy = shift;
+	Purple::Debug::misc("signals test in perl", "buddy-signed-on (" . $buddy->get_name() . ")\n");
+}
+
+# Connections
+sub signed_on
+{
+	my $conn = shift;
+	Purple::Debug::misc("signals test in perl", "signed-on (" . $conn->get_account()->get_username() . ")\n");
+}
+
+# Conversations
+sub conv_received_msg
+{
+	my ($account, $sender, $message, $conv, $flags, $data) = @_;
+	Purple::Debug::misc("signals test in perl", "$data (" . $account->get_username() . ", $sender, $message, $flags)\n");
+}
+
+sub plugin_load
+{
+	my $plugin = shift;
+
+	# Hook to the signals
+
+	# Accounts
+	$act_handle = Purple::Accounts::get_handle();
+	Purple::Signal::connect($act_handle, "account-connecting", $plugin,
+					\&account_connecting_cb, 0);
+
+	# Buddy List
+	$blist = Purple::BuddyList::get_handle();
+	Purple::Signal::connect($blist, "buddy-signed-on", $plugin,
+					\&buddy_signed_on, 0);
+
+	# Connections
+	$conn = Purple::Connections::get_handle();
+	Purple::Signal::connect($conn, "signed-on", $plugin,
+					\&signed_on, 0);
+
+	# Conversations
+	$conv = Purple::Conversations::get_handle();
+	Purple::Signal::connect($conv, "received-im-msg", $plugin,
+					\&conv_received_msg, "received im message");
+	Purple::Signal::connect($conv, "received-chat-msg", $plugin,
+					\&conv_received_msg, "received chat message");
+}
+
+sub plugin_unload
+{
+	# Nothing to do here for this plugin.
+}
+
--- a/libpurple/protocols/irc/msgs.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/irc/msgs.c	Fri May 18 14:00:33 2007 +0000
@@ -35,6 +35,7 @@
 static char *irc_mask_userhost(const char *mask);
 static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]);
 static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc);
+static void irc_connected(struct irc_conn *irc, const char *nick);
 
 static void irc_msg_handle_privmsg(struct irc_conn *irc, const char *name,
                                    const char *from, const char *to,
@@ -70,6 +71,52 @@
 	g_free(message);
 }
 
+static void irc_connected(struct irc_conn *irc, const char *nick)
+{
+	PurpleConnection *gc;
+	PurpleStatus *status;
+	PurpleBlistNode *gnode, *cnode, *bnode;
+
+	if ((gc = purple_account_get_connection(irc->account)) == NULL
+	    || PURPLE_CONNECTION_IS_CONNECTED(gc))
+		return;
+
+	purple_connection_set_display_name(gc, nick);
+	purple_connection_set_state(gc, PURPLE_CONNECTED);
+
+	/* If we're away then set our away message */
+	status = purple_account_get_active_status(irc->account);
+	if (!purple_status_get_type(status) != PURPLE_STATUS_AVAILABLE) {
+		PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		prpl_info->set_status(irc->account, status);
+	}
+
+	/* this used to be in the core, but it's not now */
+	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+				continue;
+			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+				PurpleBuddy *b;
+				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+					continue;
+				b = (PurpleBuddy *)bnode;
+				if(b->account == gc->account) {
+					struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
+					ib->name = g_strdup(b->name);
+					g_hash_table_insert(irc->buddies, ib->name, ib);
+				}
+			}
+		}
+	}
+
+	irc_blist_timeout(irc);
+	if (!irc->timer)
+		irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc);
+}
+
 void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", args[0]);
@@ -95,56 +142,16 @@
 
 void irc_msg_luser(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	PurpleConnection *gc;
-	PurpleStatus *status;
-	PurpleBlistNode *gnode, *cnode, *bnode;
-
-	if (!args || !args[0] || !args[1])
-		return;
-
-	gc = purple_account_get_connection(irc->account);
-	if (!gc)
+	if (!args || !args[0])
 		return;
 
 	if (!strcmp(name, "251")) {
-		/* 251 is required, so we pluck our nick from here */
-		purple_connection_set_display_name(gc, args[0]);
+		/* 251 is required, so we pluck our nick from here and
+		 * finalize connection */
+		irc_connected(irc, args[0]);
 		/* Some IRC servers seem to not send a 255 numeric, so
 		 * I guess we can't require it; 251 will do. */
 	/* } else if (!strcmp(name, "255")) { */
-		purple_connection_set_state(gc, PURPLE_CONNECTED);
-
-		/* If we're away then set our away message */
-		status = purple_account_get_active_status(irc->account);
-		if (!purple_status_get_type(status) != PURPLE_STATUS_AVAILABLE) {
-			PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-			prpl_info->set_status(irc->account, status);
-		}
-
-		/* this used to be in the core, but it's not now */
-		for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
-			if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
-				continue;
-			for(cnode = gnode->child; cnode; cnode = cnode->next) {
-				if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-					continue;
-				for(bnode = cnode->child; bnode; bnode = bnode->next) {
-					PurpleBuddy *b;
-					if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-						continue;
-					b = (PurpleBuddy *)bnode;
-					if(b->account == gc->account) {
-						struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
-						ib->name = g_strdup(b->name);
-						g_hash_table_insert(irc->buddies, ib->name, ib);
-					}
-				}
-			}
-		}
-
-		irc_blist_timeout(irc);
-		if (!irc->timer)
-			irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc);
 	}
 }
 
@@ -523,6 +530,9 @@
 {
 	char *escaped;
 
+	if (!args || !args[0])
+		return;
+
 	if (!irc->motd)
 		irc->motd = g_string_new("");
 
@@ -532,7 +542,9 @@
 		irc->motd = g_string_new("");
 		return;
 	} else if (!strcmp(name, "376")) {
-		/* We no longer have to do anything for ENDMOTD */
+		/* dircproxy 1.0.5 does not send 251 on reconnection, so
+		 * finalize the connection here if it is not already done. */
+		irc_connected(irc, args[0]);
 		return;
 	}
 
@@ -541,6 +553,9 @@
 		return;
 	}
 
+	if (!args[1])
+		return;
+
 	escaped = g_markup_escape_text(args[1], -1);
 	g_string_append_printf(irc->motd, "%s<br>", escaped);
 	g_free(escaped);
--- a/libpurple/protocols/irc/parse.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Fri May 18 14:00:33 2007 +0000
@@ -227,7 +227,7 @@
 	enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET);
 	encodings = g_strsplit(enclist, ",", 2);
 
-	if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) {
+	if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) {
 		g_strfreev(encodings);
 		return g_strdup(string);
 	}
--- a/libpurple/protocols/msn/msn.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Fri May 18 14:00:33 2007 +0000
@@ -619,7 +619,7 @@
 	account = purple_connection_get_account(gc);
 	user = msn_normalize(account, purple_account_get_username(account));
 
-	if (strstr(user, "@hotmail.com") != NULL)
+	if (strstr(user, "@hotmail.") != NULL)
 	{
 		m = g_list_append(m, NULL);
 		act = purple_plugin_action_new(_("Open Hotmail Inbox"),
--- a/libpurple/protocols/msn/notification.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Fri May 18 14:00:33 2007 +0000
@@ -982,7 +982,8 @@
 	}
 	else
 	{
-		fputs("<html>\n"
+		fputs("<!-- saved from url=(0013)about:internet -->\n"
+			  "<html>\n"
 			  "<head>\n"
 			  "<noscript>\n"
 			  "<meta http-equiv=\"Refresh\" content=\"0; "
--- a/libpurple/protocols/msn/slp.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Fri May 18 14:00:33 2007 +0000
@@ -363,6 +363,8 @@
 			purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
 
 			slpcall->xfer = xfer;
+			purple_xfer_ref(slpcall->xfer);
+
 			xfer->data = slpcall;
 
 			purple_xfer_request(xfer);
--- a/libpurple/protocols/msn/slpcall.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Fri May 18 14:00:33 2007 +0000
@@ -120,6 +120,9 @@
 	if (slpcall->end_cb != NULL)
 		slpcall->end_cb(slpcall, session);
 
+	if (slpcall->xfer != NULL)
+		purple_xfer_unref(slpcall->xfer);
+
 	g_free(slpcall);
 }
 
--- a/libpurple/protocols/msn/slplink.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Fri May 18 14:00:33 2007 +0000
@@ -763,6 +763,7 @@
 	slpcall->progress_cb = msn_xfer_progress_cb;
 	slpcall->cb = msn_xfer_completed_cb;
 	slpcall->xfer = xfer;
+	purple_xfer_ref(slpcall->xfer);
 
 	slpcall->pending = TRUE;
 
--- a/libpurple/protocols/simple/simple.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Fri May 18 14:00:33 2007 +0000
@@ -277,18 +277,18 @@
 							auth->nonce, noncecount, NULL, auth->digest_session_key);
 		purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response);
 
-		ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response);
+		ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser, auth->realm, auth->nonce, target, noncecount, response);
 		g_free(response);
 		return ret;
 	} else if(auth->type == 2) { /* NTLM */
 		if(auth->nc == 3 && auth->nonce) {
 			/* TODO: Don't hardcode "purple" as the hostname */
 			ret = purple_ntlm_gen_type3(authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
-			tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"\r\n", auth->opaque, auth->realm, auth->target, ret);
+			tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, ret);
 			g_free(ret);
 			return tmp;
 		}
-		tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", auth->realm, auth->target);
+		tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
 		return tmp;
 	}
 
@@ -298,7 +298,7 @@
 						auth->nonce, noncecount, NULL, auth->digest_session_key);
 	purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response);
 
-	ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response);
+	ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser, auth->realm, auth->nonce, target, noncecount, response);
 	g_free(response);
 	return ret;
 }
@@ -630,14 +630,12 @@
 	if(addheaders) addh = addheaders;
 	if(sip->registrar.type && !strcmp(method, "REGISTER")) {
 		buf = auth_header(sip, &sip->registrar, method, url);
-		auth = g_strdup_printf("Authorization: %s", buf);
+		auth = g_strdup_printf("Authorization: %s\r\n", buf);
 		g_free(buf);
 		purple_debug(PURPLE_DEBUG_MISC, "simple", "header %s", auth);
-	}
-
-	if(sip->proxy.type && strcmp(method, "REGISTER")) {
+	} else if(sip->proxy.type && strcmp(method, "REGISTER")) {
 		buf = auth_header(sip, &sip->proxy, method, url);
-		auth = g_strdup_printf("Proxy-Authorization: %s", buf);
+		auth = g_strdup_printf("Proxy-Authorization: %s\r\n", buf);
 		g_free(buf);
 		purple_debug(PURPLE_DEBUG_MISC, "simple", "header %s", auth);
 	}
@@ -984,8 +982,8 @@
 		state = xmlnode_get_child(isc, "state");
 
 		if(!state) {
-			purple_debug_info("simple", "process_incoming_message: no state found\n");
-			xmlnode_free(isc);
+				purple_debug_info("simple", "process_incoming_message: no state found\n");
+				xmlnode_free(isc);
 			return;
 		}
 
--- a/libpurple/util.c	Sun May 13 21:27:13 2007 +0000
+++ b/libpurple/util.c	Fri May 18 14:00:33 2007 +0000
@@ -3285,6 +3285,9 @@
 		gfud->inpa = 0;
 		close(gfud->fd);
 		gfud->fd = -1;
+		gfud->request_written = 0;
+		gfud->len = 0;
+		gfud->data_len = 0;
 
 		g_free(gfud->website.user);
 		g_free(gfud->website.passwd);
@@ -3497,7 +3500,7 @@
 	}
 	gfud->request_written += len;
 
-	if (gfud->request_written != total_len)
+	if (gfud->request_written < total_len)
 		return;
 
 	/* We're done writing our request, now start reading the response */
--- a/pidgin/gtkblist.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkblist.c	Fri May 18 14:00:33 2007 +0000
@@ -327,7 +327,6 @@
 	GValue val;
 	PurpleBlistNode *node;
 	const char *text = NULL;
-	char *esc;
 
 	path = gtk_tree_path_new_from_string (path_str);
 	gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
@@ -350,12 +349,10 @@
 		g_return_if_reached();
 	}
 
-	esc = g_markup_escape_text(text, -1);
 	if (GTK_IS_ENTRY (editable)) {
 		GtkEntry *entry = GTK_ENTRY (editable);
-		gtk_entry_set_text(entry, esc);
-	}
-	g_free(esc);
+		gtk_entry_set_text(entry, text);
+	}
 }
 
 static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1,
@@ -785,7 +782,8 @@
 	gtk_widget_show_all(data->window);
 }
 
-static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
+static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
+{
 	PurpleBlistNode *node;
 	GValue val;
 
@@ -809,7 +807,8 @@
 	}
 }
 
-static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) {
+static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
+{
 	PurpleBlistNode *node;
 	GValue val;
 
@@ -820,6 +819,8 @@
 
 	if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
 		char *title;
+		struct _pidgin_blist_node *gtknode;
+		PurpleBlistNode *cnode;
 
 		title = pidgin_get_group_title(node, FALSE);
 
@@ -830,6 +831,17 @@
 		g_free(title);
 
 		purple_blist_node_set_bool(node, "collapsed", TRUE);
+
+		for(cnode = node->child; cnode; cnode = cnode->next) {
+			if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+				gtknode = cnode->ui_data;
+				if (!gtknode->contact_expanded)
+					continue;
+				gtknode->contact_expanded = FALSE;
+				pidgin_blist_update_contact(NULL, cnode);
+			}
+		}
+
 	} else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		pidgin_blist_collapse_contact_cb(NULL, node);
 	}
@@ -3957,6 +3969,9 @@
 	g_free(primary);
 	gtk_widget_destroy(GTK_WIDGET(widget));
 	g_hash_table_remove(gtkblist->connection_errors, account);
+	if (gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons)) == NULL) {
+		gtk_widget_hide(gtkblist->error_buttons);
+	}
 }
 
 /* Add some buttons that show connection errors */
@@ -3977,7 +3992,7 @@
 	                       escaped);
 	g_free(escaped);
 
-	hbox = gtk_hbox_new(FALSE, 0);
+	hbox = gtk_hbox_new(FALSE, 6);
 
 	/* Create the icon */
 	if ((status_type = purple_account_get_status_type_with_primitive(account,
@@ -3987,8 +4002,7 @@
 			image = gtk_image_new_from_pixbuf(pixbuf);
 			g_object_unref(pixbuf);
 
-			gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE,
-			                   PIDGIN_HIG_BOX_SPACE);
+			gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
 		}
 	}
 
@@ -3999,8 +4013,7 @@
 #if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(label, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 #endif
-	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE,
-	                   PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 
 	/* Create the actual button and put the icon and text on it */
 	button = gtk_button_new();
@@ -4011,6 +4024,7 @@
 	gtk_widget_show_all(button);
 	gtk_box_pack_end(GTK_BOX(gtkblist->error_buttons), button,
 	                 FALSE, FALSE, 0);
+	gtk_widget_show_all(gtkblist->error_buttons);
 }
 
 void
@@ -4476,7 +4490,8 @@
 	/* Create an empty vbox used for showing connection errors */
 	gtkblist->error_buttons = gtk_vbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->error_buttons, FALSE, FALSE, 0);
-
+        gtk_container_set_border_width(GTK_CONTAINER(gtkblist->error_buttons), 3);
+	
 	/* Add the statusbox */
 	gtkblist->statusbox = pidgin_status_box_new();
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0);
@@ -4573,6 +4588,7 @@
 						gtkblist);
 
 	gtk_widget_hide(gtkblist->headline_hbox);
+	gtk_widget_hide(gtkblist->error_buttons);
 
 	/* emit our created signal */
 	purple_signal_emit(handle, "gtkblist-created", list);
--- a/pidgin/gtkconv.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkconv.c	Fri May 18 14:00:33 2007 +0000
@@ -176,6 +176,7 @@
 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
+static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
 
 static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) {
 	static GdkColor col;
@@ -1754,7 +1755,8 @@
 static void
 move_to_next_unread_tab(PidginConversation *gtkconv, gboolean forward)
 {
-	PidginConversation *next_gtkconv = NULL;
+	PidginConversation *next_gtkconv = NULL, *most_active = NULL;
+	PidginUnseenState unseen_state = PIDGIN_UNSEEN_NONE;
 	PidginWindow *win;
 	int initial, i, total, diff;
 
@@ -1770,17 +1772,21 @@
 
 	for (i = (initial + diff) % total; i != initial; i = (i + diff) % total) {
 		next_gtkconv = pidgin_conv_window_get_gtkconv_at_index(win, i);
-		if (next_gtkconv->unseen_state > 0)
-			break;
-	}
-
-	if (i == initial) { /* no new messages */
+		if (next_gtkconv->unseen_state > unseen_state) {
+			most_active = next_gtkconv;
+			unseen_state = most_active->unseen_state;
+			if(PIDGIN_UNSEEN_NICK == unseen_state) /* highest possible state */
+				break;
+		}
+	}
+
+	if (most_active == NULL) { /* no new messages */
 		i = (i + diff) % total;
-		next_gtkconv = pidgin_conv_window_get_gtkconv_at_index(win, i);
-	}
-
-	if (next_gtkconv != NULL && next_gtkconv != gtkconv)
-		pidgin_conv_window_switch_gtkconv(win, next_gtkconv);
+		most_active = pidgin_conv_window_get_gtkconv_at_index(win, i);
+	}
+
+	if (most_active != NULL && most_active != gtkconv)
+		pidgin_conv_window_switch_gtkconv(win, most_active);
 }
 
 static gboolean
@@ -3976,6 +3982,12 @@
 	}
 	else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
 		update_chat_alias((PurpleBuddy *)node, conv, gc, prpl_info);
+	else if (PURPLE_BLIST_NODE_IS_CHAT(node) &&
+			purple_conversation_get_account(conv) == ((PurpleChat*)node)->account)
+	{
+		if (old_alias == NULL || g_utf8_collate(old_alias, purple_conversation_get_title(conv)) == 0)
+			pidgin_conv_update_fields(conv, PIDGIN_CONV_SET_TITLE);
+	}
 }
 
 static void
@@ -4590,6 +4602,18 @@
 	}
 }
 
+static gboolean
+ignore_middle_click(GtkWidget *widget, GdkEventButton *e, gpointer null)
+{
+	/* A click on the pane is propagated to the notebook containing the pane.
+	 * So if Stu accidentally aims high and middle clicks on the pane-handle,
+	 * it causes a conversation tab to close. Let's stop that from happening.
+	 */
+	if (e->button == 2 && e->type == GDK_BUTTON_PRESS)
+		return TRUE;
+	return FALSE;
+}
+
 /**************************************************************************
  * Conversation UI operations
  **************************************************************************/
@@ -4663,6 +4687,8 @@
 	                  te, sizeof(te) / sizeof(GtkTargetEntry),
 	                  GDK_ACTION_COPY);
 
+	g_signal_connect(G_OBJECT(pane), "button_press_event",
+	                 G_CALLBACK(ignore_middle_click), NULL);
 	g_signal_connect(G_OBJECT(pane), "drag_data_received",
 	                 G_CALLBACK(conv_dnd_recv), gtkconv);
 	g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received",
@@ -6512,16 +6538,18 @@
 tab_side_pref_cb(const char *name, PurplePrefType type,
 				 gconstpointer value, gpointer data)
 {
-	GList *l;
+	GList *gtkwins, *gtkconvs;
 	GtkPositionType pos;
-	PidginWindow *win;
+	PidginWindow *gtkwin;
 
 	pos = GPOINTER_TO_INT(value);
 
-	for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
-		win = l->data;
-
-		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(win->notebook), pos&~8);
+	for (gtkwins = pidgin_conv_windows_get_list(); gtkwins != NULL; gtkwins = gtkwins->next) {
+		gtkwin = gtkwins->data;
+		gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkwin->notebook), pos&~8);
+		for (gtkconvs = gtkwin->gtkconvs; gtkconvs != NULL; gtkconvs = gtkconvs->next) {
+			pidgin_conv_tab_pack(gtkwin, gtkconvs->data);
+		}
 	}
 }
 
@@ -7385,8 +7413,7 @@
 		PidginWindow *dest_win;
 		GtkNotebook *dest_notebook;
 		GtkWidget *tab;
-		gint nb_x, nb_y, page_num;
-		gint arrow1_x, arrow1_y, arrow2_x, arrow2_y;
+		gint page_num;
 		gboolean horiz_tabs = FALSE;
 		PidginConversation *gtkconv;
 		gboolean to_right = FALSE;
@@ -7402,52 +7429,35 @@
 
 		dest_notebook = GTK_NOTEBOOK(dest_win->notebook);
 
-		gdk_window_get_origin(GTK_WIDGET(dest_notebook)->window, &nb_x, &nb_y);
-
-		arrow1_x = arrow2_x = nb_x;
-		arrow1_y = arrow2_y = nb_y;
-
 		page_num = pidgin_conv_get_tab_at_xy(dest_win,
 		                                      e->x_root, e->y_root, &to_right);
 		to_right = to_right && (win != dest_win);
 
 		if (gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_TOP ||
-		    gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_BOTTOM) {
-
-			    horiz_tabs = TRUE;
-		    }
+				gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_BOTTOM) {
+			horiz_tabs = TRUE;
+		}
 
 		gtkconv = pidgin_conv_window_get_gtkconv_at_index(dest_win, page_num);
 		tab = gtkconv->tabby;
 
 		if (horiz_tabs) {
-			arrow1_x = arrow2_x = nb_x + tab->allocation.x;
-
 			if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) {
-				arrow1_x += tab->allocation.width;
-				arrow2_x += tab->allocation.width;
+				dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
+				dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
+			} else {
+				dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
+				dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
 			}
-
-			arrow1_y = nb_y + tab->allocation.y;
-			arrow2_y = nb_y + tab->allocation.y + tab->allocation.height;
 		} else {
-			arrow1_x = nb_x + tab->allocation.x;
-			arrow2_x = nb_x + tab->allocation.x + tab->allocation.width;
-			arrow1_y = arrow2_y = nb_y + tab->allocation.y;
-
 			if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) {
-				arrow1_y += tab->allocation.height;
-				arrow2_y += tab->allocation.height;
+				dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
+				dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
+			} else {
+				dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
+				dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
 			}
 		}
-
-		if (horiz_tabs) {
-			dnd_hints_show(HINT_ARROW_DOWN, arrow1_x, arrow1_y);
-			dnd_hints_show(HINT_ARROW_UP,   arrow2_x, arrow2_y);
-		} else {
-			dnd_hints_show(HINT_ARROW_RIGHT, arrow1_x, arrow1_y);
-			dnd_hints_show(HINT_ARROW_LEFT,  arrow2_x, arrow2_y);
-		}
 	}
 
 	return TRUE;
@@ -7812,6 +7822,105 @@
 }
 
 static void
+remove_edit_entry(PidginConversation *gtkconv, GtkWidget *entry)
+{
+	g_signal_handlers_disconnect_matched(G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
+				0, 0, NULL, NULL, gtkconv);
+	gtk_widget_show(gtkconv->tab_label);
+	gtk_widget_grab_focus(gtkconv->entry);
+	gtk_widget_destroy(entry);
+}
+
+static gboolean
+alias_focus_cb(GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
+{
+	remove_edit_entry(user_data, widget);
+	return FALSE;
+}
+
+static gboolean
+alias_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+	if (event->keyval == GDK_Escape) {
+		remove_edit_entry(user_data, widget);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+alias_cb(GtkEntry *entry, gpointer user_data)
+{
+	PidginConversation *gtkconv;
+	PurpleConversation *conv;
+	PurpleAccount *account;
+	const char *name;
+
+	gtkconv = (PidginConversation *)user_data;
+	if (gtkconv == NULL) {
+		return;
+	}
+	conv    = gtkconv->active_conv;
+	account = purple_conversation_get_account(conv);
+	name    = purple_conversation_get_name(conv);
+
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+		PurpleBuddy *buddy;
+		buddy = purple_find_buddy(account, name);
+		if (buddy != NULL) {
+			purple_blist_alias_buddy(buddy,
+                                                 gtk_entry_get_text(entry));
+		}
+		serv_alias_buddy(buddy);
+	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {	        
+		PurpleChat *chat;
+
+		chat = purple_blist_find_chat(account, name);
+		if (chat != NULL) {
+			purple_blist_alias_chat(chat,
+			                        gtk_entry_get_text(entry));
+		}
+	}
+	remove_edit_entry(user_data, GTK_WIDGET(entry));
+}
+
+static gboolean
+alias_double_click_cb(GtkNotebook *notebook, GdkEventButton *event, PidginConversation *gtkconv)
+{
+	GtkWidget *entry = NULL;
+
+	if (event->button != 1 || event->type != GDK_2BUTTON_PRESS) {
+		return FALSE;
+	}
+
+	if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
+		/* There's already an entry for alias. Let's not create another one. */
+		return FALSE;
+	}
+
+	/* alias label */
+	entry = gtk_entry_new();
+	gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
+	gtk_entry_set_width_chars(GTK_ENTRY(entry), 10);
+	gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5);
+
+	gtk_box_pack_start(GTK_BOX(gtkconv->tabby), entry, TRUE, TRUE, 0);
+	/* after the tab label */
+	gtk_box_reorder_child(GTK_BOX(gtkconv->tabby), entry, 2);
+
+	g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(alias_cb), gtkconv);
+	g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(alias_focus_cb), gtkconv);
+	g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(alias_key_press_cb), gtkconv);
+	gtk_entry_set_text(GTK_ENTRY(entry),
+			gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
+	gtk_widget_show(entry);
+	gtk_widget_hide(gtkconv->tab_label);
+	gtk_widget_grab_focus(entry);
+
+	return FALSE;
+}
+
+static void
 switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
                gpointer user_data)
 {
@@ -8056,35 +8165,17 @@
 {
 	PurpleConversation *conv = gtkconv->active_conv;
 	PidginConversation *focus_gtkconv;
-	GtkWidget *tabby, *menu_tabby;
 	GtkWidget *tab_cont = gtkconv->tab_cont;
 	GtkWidget *close_image;
 	PurpleConversationType conv_type;
 	const gchar *tmp_lab;
 	gint close_button_width, close_button_height, focus_width, focus_pad;
-	gboolean tabs_side = FALSE;
-	gint angle = 0;
 
 	conv_type = purple_conversation_get_type(conv);
 
-
 	win->gtkconvs = g_list_append(win->gtkconvs, gtkconv);
 	gtkconv->win = win;
 
-	if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT ||
-	    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT)
-		tabs_side = TRUE;
-	else if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_LEFT|8))
-		angle = 90;
-	else if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_RIGHT|8))
-		angle = 270;
-
-	if (angle)
-		gtkconv->tabby = tabby = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	else
-		gtkconv->tabby = tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtkconv->menu_tabby = menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-
 	/* Close button. */
 	gtkconv->close = gtk_button_new();
 	gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &close_button_width, &close_button_height);
@@ -8128,56 +8219,25 @@
 	/* Tab label. */
 	gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv));
 
-#if GTK_CHECK_VERSION(2,6,0)
-	if (!angle)
-		g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-	gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 6);
-	if (tabs_side) {
-		gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), MIN(g_utf8_strlen(tmp_lab, -1), 12));
-	}
-	if (angle)
-		gtk_label_set_angle(GTK_LABEL(gtkconv->tab_label), angle);
-#endif
-	gtkconv->menu_label = gtk_label_new(purple_conversation_get_title(conv));
-#if 0
-	gtk_misc_set_alignment(GTK_MISC(gtkconv->tab_label), 0.00, 0.5);
-	gtk_misc_set_padding(GTK_MISC(gtkconv->tab_label), 4, 0);
-#endif
-
-	/* Pack it all together. */
-	if (angle == 90)
-		gtk_box_pack_start(GTK_BOX(tabby), gtkconv->close, FALSE, FALSE, 0);
-	else
-		gtk_box_pack_start(GTK_BOX(tabby), gtkconv->icon, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(menu_tabby), gtkconv->menu_icon,
-	                   FALSE, FALSE, 0);
-
-	gtk_widget_show_all(gtkconv->icon);
+	gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtkconv->menu_label = gtk_label_new(purple_conversation_get_title(gtkconv->active_conv));
+	gtk_box_pack_start(GTK_BOX(gtkconv->menu_tabby), gtkconv->menu_icon, FALSE, FALSE, 0);
+
 	gtk_widget_show_all(gtkconv->menu_icon);
 
-	gtk_box_pack_start(GTK_BOX(tabby), gtkconv->tab_label, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(menu_tabby), gtkconv->menu_label, TRUE, TRUE, 0);
-	gtk_widget_show(gtkconv->tab_label);
+	gtk_box_pack_start(GTK_BOX(gtkconv->menu_tabby), gtkconv->menu_label, TRUE, TRUE, 0);
 	gtk_widget_show(gtkconv->menu_label);
 	gtk_misc_set_alignment(GTK_MISC(gtkconv->menu_label), 0, 0);
 
-	if (angle == 90)
-		gtk_box_pack_start(GTK_BOX(tabby), gtkconv->icon, FALSE, FALSE, 0);
-	else
-		gtk_box_pack_start(GTK_BOX(tabby), gtkconv->close, FALSE, FALSE, 0);
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs"))
-		gtk_widget_show(gtkconv->close);
-
-	gtk_widget_show(tabby);
-	gtk_widget_show(menu_tabby);
+	gtk_widget_show(gtkconv->menu_tabby);
 
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 		pidgin_conv_update_buddy_icon(conv);
 
-	/* Add this pane to the conversation's notebook. */
-	gtk_notebook_append_page_menu(GTK_NOTEBOOK(win->notebook), tab_cont, tabby, menu_tabby);
-	gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START);
-
+	/* Build and set conversations tab */
+	pidgin_conv_tab_pack(win, gtkconv);
+
+	gtk_notebook_set_menu_label(GTK_NOTEBOOK(win->notebook), tab_cont, gtkconv->menu_tabby);
 
 	gtk_widget_show(tab_cont);
 
@@ -8198,6 +8258,94 @@
 		update_send_to_selection(win);
 }
 
+static void
+pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
+{
+	gboolean tabs_side = FALSE;
+	gint angle = 0;
+	GtkWidget *first, *third, *ebox;
+
+	if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT ||
+	    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT)
+		tabs_side = TRUE;
+	else if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_LEFT|8))
+		angle = 90;
+	else if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_RIGHT|8))
+		angle = 270;
+
+#if GTK_CHECK_VERSION(2,6,0)
+	if (!angle)
+		g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END,  NULL);
+	else
+		g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
+	gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 6);
+	if (tabs_side) {
+		gtk_label_set_width_chars(
+			GTK_LABEL(gtkconv->tab_label),
+			MIN(g_utf8_strlen(gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), -1), 12)
+		);
+	}
+	if (angle)
+		gtk_label_set_angle(GTK_LABEL(gtkconv->tab_label), angle);
+#endif
+
+#if 0
+	gtk_misc_set_alignment(GTK_MISC(gtkconv->tab_label), 0.00, 0.5);
+	gtk_misc_set_padding(GTK_MISC(gtkconv->tab_label), 4, 0);
+#endif
+
+	if (angle)
+		gtkconv->tabby = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	else
+		gtkconv->tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+
+	/* select the correct ordering for verticle tabs */
+	if (angle == 90) {
+		first = gtkconv->close;
+		third = gtkconv->icon;
+	} else {
+		first = gtkconv->icon;
+		third = gtkconv->close;
+	}
+
+	ebox = gtk_event_box_new();
+	gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
+	gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby);
+	g_signal_connect(G_OBJECT(ebox), "button-press-event",
+					G_CALLBACK(alias_double_click_cb), gtkconv);
+
+	if (gtkconv->tab_label->parent == NULL) {
+		/* Pack if it's a new widget */
+		gtk_box_pack_start(GTK_BOX(gtkconv->tabby), first,              FALSE, FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(gtkconv->tabby), gtkconv->tab_label, TRUE,  TRUE,  0);
+		gtk_box_pack_start(GTK_BOX(gtkconv->tabby), third,              FALSE, FALSE, 0);
+
+		/* Add this pane to the conversation's notebook. */
+		gtk_notebook_append_page(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, ebox);
+	} else {
+		/* reparent old widgets on preference changes */
+		gtk_widget_reparent(first,              gtkconv->tabby);
+		gtk_widget_reparent(gtkconv->tab_label, gtkconv->tabby);
+		gtk_widget_reparent(third,              gtkconv->tabby);
+		gtk_box_set_child_packing(GTK_BOX(gtkconv->tabby), first,              FALSE, FALSE, 0, GTK_PACK_START);
+		gtk_box_set_child_packing(GTK_BOX(gtkconv->tabby), gtkconv->tab_label, TRUE,  TRUE,  0, GTK_PACK_START);
+		gtk_box_set_child_packing(GTK_BOX(gtkconv->tabby), third,              FALSE, FALSE, 0, GTK_PACK_START);
+
+		/* Reset the tabs label to the new version */
+		gtk_notebook_set_tab_label(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, ebox);
+	}
+
+	gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START);
+
+	/* show the widgets */
+	gtk_widget_show(gtkconv->icon);
+	gtk_widget_show(gtkconv->tab_label);
+	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs"))
+		gtk_widget_show(gtkconv->close);
+	gtk_widget_show(gtkconv->tabby);
+	gtk_widget_show(ebox);
+}
+
 void
 pidgin_conv_window_remove_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
 {
--- a/pidgin/gtkdnd-hints.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkdnd-hints.c	Fri May 18 14:00:33 2007 +0000
@@ -91,8 +91,6 @@
 	if (w->parent && w->parent->window == w->window)
 	{
 		get_widget_coords(w->parent, &ox, &oy, NULL, NULL);
-		ox += w->allocation.x;
-		oy += w->allocation.y;
 		height = w->allocation.height;
 		width = w->allocation.width;
 	}
@@ -174,6 +172,8 @@
 	gint x = 0, y = 0;
 
 	get_widget_coords(widget, &x1, &y1, &x2, &y2);
+	x1 += widget->allocation.x;	x2 += widget->allocation.x;
+	y1 += widget->allocation.y;	y2 += widget->allocation.y;
 
 	switch (horiz)
 	{
--- a/pidgin/gtkidle.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkidle.c	Fri May 18 14:00:33 2007 +0000
@@ -103,14 +103,21 @@
 
 	/* Query xscreensaver */
 	static XScreenSaverInfo *mit_info = NULL;
+	static int has_extension = -1;
 	int event_base, error_base;
-	if (XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, &error_base)) {
-		if (mit_info == NULL) {
+
+	if (has_extension == -1)
+		has_extension = XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, &error_base);
+
+	if (has_extension)
+	{
+		if (mit_info == NULL)
 			mit_info = XScreenSaverAllocInfo();
-		}
+
 		XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), mit_info);
 		return (mit_info->idle) / 1000;
-	} else
+	}
+	else
 		return 0;
 #  endif /* !_WIN32 */
 # endif /* !HAVE_IOKIT */
--- a/pidgin/gtkimhtml.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkimhtml.c	Fri May 18 14:00:33 2007 +0000
@@ -3388,7 +3388,7 @@
 		return FALSE;
 
 	pix = gdk_pixbuf_animation_get_static_image(anim);
-	image = gtk_imhtml_image_new(pix, NULL, 0);
+	image = gtk_imhtml_image_new(pix, smiley->smile, 0);
 	ret = gtk_imhtml_image_clicked(w, event, (GtkIMHtmlImage*)image);
 	g_object_set_data_full(G_OBJECT(w), "image-data", image, (GDestroyNotify)gtk_imhtml_image_free);
 	g_object_unref(G_OBJECT(pix));
@@ -4441,7 +4441,7 @@
 		}
 	}
 
-	if (imhtml_smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) {
+	if (imhtml_smiley && imhtml_smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) {
 		ebox = gtk_event_box_new();
 		gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
 		gtk_widget_show(ebox);
--- a/pidgin/gtkstatusbox.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Fri May 18 14:00:33 2007 +0000
@@ -2334,11 +2334,16 @@
 
 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
 
+	height = 0;
 	wrapped_lines = 1;
 	gtk_text_buffer_get_start_iter(buffer, &iter);
-	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
-	while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter))
+	do {
+		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+		height += oneline.height;
 		wrapped_lines++;
+		if (wrapped_lines > 4)
+			break;
+	} while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter));
 
 	lines = gtk_text_buffer_get_line_count(buffer);
 
@@ -2350,8 +2355,8 @@
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->imhtml));
 	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->imhtml));
 
-	height = (oneline.height + pad_top + pad_bottom) * lines;
-	height += (oneline.height + pad_inside) * (wrapped_lines - lines);
+	height += (pad_top + pad_bottom) * lines;
+	height += (pad_inside) * (wrapped_lines - lines);
 
 	gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE);
 }
--- a/pidgin/win32/winpidgin.c	Sun May 13 21:27:13 2007 +0000
+++ b/pidgin/win32/winpidgin.c	Fri May 18 14:00:33 2007 +0000
@@ -145,7 +145,7 @@
 		putenv(settingsdir);
 
 		snprintf(aspelldir, sizeof(aspelldir), "PIDGIN_ASPELL_DIR=%s\\Aspell\\bin", path);
-		printf("%s", aspelldir);
+		printf("%s\n", aspelldir);
 		putenv(aspelldir);
 
 		/* set the GTK+ path to be \\path\to\GTK\bin */