changeset 17138:ec6a029c865d

merge of '0397391b6a278804196e57830cc216b8e236442a' and 'f858c38e5d9f9c2d048dbe13db10776cdf7fafc9'
author Evan Schoenberg <evan.s@dreskin.net>
date Thu, 17 May 2007 14:38:35 +0000
parents 8ed7b2d32ad1 (current diff) 3aee35e0ecd8 (diff)
children 17117b8cfbb7 c8ed8e351541
files
diffstat 16 files changed, 458 insertions(+), 174 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu May 17 14:32:24 2007 +0000
+++ b/COPYRIGHT	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/ChangeLog	Thu May 17 14:38:35 2007 +0000
@@ -24,9 +24,13 @@
 	* 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)
 
 	Finch:
 	* Userlist in chat windows, which can be turned on or off using
--- a/libpurple/idle.c	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/idle.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/plugins/joinpart.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/plugins/perl/Makefile.am	Thu May 17 14:38:35 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	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/protocols/irc/msgs.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Thu May 17 14:38:35 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/util.c	Thu May 17 14:32:24 2007 +0000
+++ b/libpurple/util.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/pidgin/gtkblist.c	Thu May 17 14:38:35 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,
@@ -3957,6 +3954,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 +3977,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 +3987,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 +3998,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 +4009,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 +4475,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 +4573,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	Thu May 17 14:32:24 2007 +0000
+++ b/pidgin/gtkconv.c	Thu May 17 14:38:35 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;
@@ -3976,6 +3977,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 +4597,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 +4682,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 +6533,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 +7408,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 +7424,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 +7817,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 +8160,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 +8214,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 +8253,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	Thu May 17 14:32:24 2007 +0000
+++ b/pidgin/gtkdnd-hints.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/pidgin/gtkidle.c	Thu May 17 14:38:35 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	Thu May 17 14:32:24 2007 +0000
+++ b/pidgin/gtkimhtml.c	Thu May 17 14:38:35 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));