propagate from branch 'im.pidgin.pidgin' (head 75a15398655acecce933518015eac8b6a7340fd2) soc.2007.xmpp

Mon, 30 Jul 2007 00:05:02 +0000

author
Andreas Monitzer <am@adiumx.com>
date
Mon, 30 Jul 2007 00:05:02 +0000
branch
soc.2007.xmpp
changeset 18993
bb6697a6a9c9
parent 18992
75a15398655a (diff)
parent 18912
606597744812 (current diff)
child 18994
310e9d853e2b

propagate from branch 'im.pidgin.pidgin' (head 75a15398655acecce933518015eac8b6a7340fd2)
to branch 'im.pidgin.soc.2007.xmpp' (head 6065977448124f2e46cdfa43291659676625bc65)

libpurple/protocols/jabber/jabber.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/presence.c file | annotate | diff | comparison | revisions
--- a/AUTHORS	Mon Jul 30 00:01:08 2007 +0000
+++ b/AUTHORS	Mon Jul 30 00:05:02 2007 +0000
@@ -19,6 +19,7 @@
 Daniel 'datallah' Atallah - Developer
 Ethan 'Paco-Paco' Blanton - Developer
 Thomas Butter - Developer
+Ka-Hing Cheung - Developer
 Sadrul Habib Chowdhury - Developer
 Mark 'KingAnt' Doliner - Developer
 Christian 'ChipX86' Hammond - Developer & Webmaster
@@ -38,7 +39,6 @@
 Crazy Patch Writers:
 -------------------
 John 'rekkanoryo' Bailey
-Ka-Hing 'javabsp' Cheung
 Felipe 'shx' Contreras
 Decklin Foster
 Casey Harkins
--- a/ChangeLog	Mon Jul 30 00:01:08 2007 +0000
+++ b/ChangeLog	Mon Jul 30 00:05:02 2007 +0000
@@ -1,6 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.1.0 (??/??/????):
+version 2.1.0 (07/28/2007):
 	libpurple:
 	* Core changes to allow UIs to use second-granularity for scheduling.
 	  Pidgin and Finch, which use the glib event loop, were changed to use
--- a/ChangeLog.API	Mon Jul 30 00:01:08 2007 +0000
+++ b/ChangeLog.API	Mon Jul 30 00:05:02 2007 +0000
@@ -1,6 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.1.0 (??/??/????):
+version 2.1.0 (7/28/2007):
 	libpurple:
 		Added:
 		* purple-remote: added getstatus command
@@ -29,6 +29,8 @@
 		* purple_xfer_get_remote_user
 		* purple_pounces_get_all_for_ui
 		* purple_prefs_get_children_names
+		* added displaying-email-notification and 
+ 	          displaying-emails-notification signals
 
 		Changed:
 		* The documentation of the following functions now properly
@@ -74,6 +76,8 @@
 		* pidgin_menu_position_func_helper
 		* pidgin_blist_get_name_markup, returns the buddy list markup
 		  text for a given buddy.
+		* pidgin_blist_draw_tooltip and pidgin_blist_tooltip_destroy
+		  for creating blist tooltips from outside of buddy list code
 		* pidgin_themes_remove_smiley_theme
 
 		Changed:
--- a/ChangeLog.win32	Mon Jul 30 00:01:08 2007 +0000
+++ b/ChangeLog.win32	Mon Jul 30 00:05:02 2007 +0000
@@ -1,4 +1,4 @@
-version 2.1.0 (??/??/????):
+version 2.1.0 (7/28/2007):
 	* Updated launcher application (pidgin.exe) to support portable mode
 	  via `pidgin.exe --portable-mode` or by renaming the binary to
 	  `pidgin-portable.exe`
--- a/NEWS	Mon Jul 30 00:01:08 2007 +0000
+++ b/NEWS	Mon Jul 30 00:05:02 2007 +0000
@@ -1,5 +1,14 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+2.1.0 (7/28/2007):
+	Sean: This release took a bit longer than 3 weeks, but boy is it 
+	worth it! We're beginning to experiment with new UI concepts and
+	this release features a largely re-designed conversation window.
+	We've closed 150 tickets for this release; much thanks go to all
+	the developers, translators, and testers who made this possible.
+
+	Ka-Hing: Sean said no one else NEWS'ed, so I figure I should.
+
 2.0.2 (6/14/2007):
 	Sean: Another big maintenance release. Again, about 100 tickets were
 	resolved in this release, and they keep coming in. Lots of bug fixes,
--- a/configure.ac	Mon Jul 30 00:01:08 2007 +0000
+++ b/configure.ac	Mon Jul 30 00:05:02 2007 +0000
@@ -47,7 +47,7 @@
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [1])
 m4_define([purple_micro_version], [0])
-m4_define([purple_version_suffix], [devel])
+m4_define([purple_version_suffix], [])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
@@ -56,7 +56,7 @@
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [0])
 m4_define([gnt_micro_version], [0])
-m4_define([gnt_version_suffix], [devel])
+m4_define([gnt_version_suffix], [])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
 m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix]))
--- a/doc/notify-signals.dox	Mon Jul 30 00:01:08 2007 +0000
+++ b/doc/notify-signals.dox	Mon Jul 30 00:05:02 2007 +0000
@@ -2,6 +2,8 @@
 
  @signals
   @signal displaying-userinfo
+  @signal displaying-email-notification
+  @signal displaying-emails-notification
  @endsignals
 
  @signaldef displaying-userinfo
@@ -18,5 +20,37 @@
   @param user_info The information to be displayed, as PurpleNotifyUserInfoEntry objects
  @endsignaldef
 
+ @signaldef displaying-email-notification
+  @signalproto
+void (*displaying_email_notification)(const char *subject,
+                                      const char *from,
+                                      const char *to,
+                                      const char *url);
+  @endsignalproto
+  @signaldesc
+   Emitted before email notification is handed to the UI to display.
+  @param subject   Subject of email being notified of.
+  @param from      Who the email is from.
+  @param to        Who the email is to.
+  @param url       A url to view the email.
+ @endsignaldef
+
+ @signaldef displaying-emails-notification
+  @signalproto
+void (*displaying_emails_notification)(const char **subjects,
+                                       const char **froms,
+                                       const char **tos,
+                                       const char **urls,
+                                       guint count);
+  @endsignalproto
+  @signaldesc
+   Emitted before notification of multiple emails is handed to the UI to display.
+  @param subjects   Subjects of emails being notified of.
+  @param froms      Who the emails are from.
+  @param tos        Who the emails are to.
+  @param urls       The urls to view the emails.
+  @param count      Number of emails being notified of.
+ @endsignaldef
+
 */
 // vim: syntax=c tw=75 et
--- a/doc/pidgin.1.in	Mon Jul 30 00:01:08 2007 +0000
+++ b/doc/pidgin.1.in	Mon Jul 30 00:05:02 2007 +0000
@@ -539,6 +539,8 @@
 .br
  Thomas Butter (developer)
 .br
+ Ka-Hing Cheung (developer)
+.br
  Sadrul Habib Chowdhury (developer)
 .br
  Mark 'KingAnt' Doliner (developer) <\fIthekingant@users.sourceforge.net\fR>
@@ -575,8 +577,6 @@
 
 John 'rekkanoryo' Bailey
 .br
-Ka-Hing 'javabsp' Cheung
-.br
 Felipe 'shx' Contreras
 .br
 Decklin Foster
--- a/finch/gntblist.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/finch/gntblist.c	Mon Jul 30 00:05:02 2007 +0000
@@ -354,11 +354,13 @@
 	PurpleGroup *grp;
 	GHashTable *hash = NULL;
 	PurpleConnection *gc;
+	gboolean autojoin;
 
 	account = purple_request_fields_get_account(allfields, "account");
 	name = purple_request_fields_get_string(allfields, "name");
 	alias = purple_request_fields_get_string(allfields, "alias");
 	group = purple_request_fields_get_string(allfields, "group");
+	autojoin = purple_request_fields_get_bool(allfields, "autojoin");
 
 	if (!purple_account_is_connected(account) || !name || !*name)
 		return;
@@ -380,6 +382,9 @@
 		}
 		purple_blist_add_chat(chat, grp, NULL);
 		purple_blist_alias_chat(chat, alias);
+		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gnt-autojoin", autojoin);
+		if (autojoin)
+			serv_join_chat(chat->account->gc, chat->components);
 	}
 }
 
@@ -407,6 +412,9 @@
 	field = purple_request_field_string_new("group", _("Group"), grp ? grp->name : NULL, FALSE);
 	purple_request_field_group_add_field(group, field);
 
+	field = purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE);
+	purple_request_field_group_add_field(group, field);
+
 	purple_request_fields(NULL, _("Add Chat"), NULL,
 			_("You can edit more information from the context menu later."),
 			fields, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL,
@@ -1317,8 +1325,10 @@
 	strip = purple_markup_strip_html(tmp);
 	g_string_append(str, strip);
 
-	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) {
+		g_string_append(str, "\n");
 		g_string_append(str, _("On Mobile"));
+	}
 
 	g_free(strip);
 	g_free(tmp);
--- a/finch/gntconv.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/finch/gntconv.c	Mon Jul 30 00:05:02 2007 +0000
@@ -259,7 +259,7 @@
 			gnt_text_view_scroll(GNT_TEXT_VIEW(ggc->tv), 0);
  	} else {
 		title = get_conversation_title(conv, account);
-		gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", NULL, TRUE);
+		gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", " ", TRUE);
 	}
 	gnt_screen_rename_widget(ggc->window, title);
 	g_free(title);
@@ -588,7 +588,6 @@
 	char *strip, *newline;
 	GntTextFormatFlags fl = 0;
 	int pos;
-	gboolean notify;
 
 	g_return_if_fail(ggconv != NULL);
 
@@ -601,7 +600,7 @@
 
 	pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(ggconv->tv));
 
-	notify = !!gnt_text_view_tag_change(GNT_TEXT_VIEW(ggconv->tv), "typing", NULL, TRUE);
+	gnt_text_view_tag_change(GNT_TEXT_VIEW(ggconv->tv), "typing", NULL, TRUE);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), "\n", GNT_TEXT_FLAG_NORMAL);
 
 	/* Unnecessary to print the timestamp for delayed message */
@@ -643,7 +642,8 @@
 	g_free(newline);
 	g_free(strip);
 
-	if (notify) {
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
+			purple_conv_im_get_typing_state(PURPLE_CONV_IM(conv)) == PURPLE_TYPING) {
 		strip = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_name(conv));
 		gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggconv->tv),
 					strip, GNT_TEXT_FLAG_DIM, "typing");
--- a/libpurple/Makefile.mingw	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/Makefile.mingw	Mon Jul 30 00:05:02 2007 +0000
@@ -127,8 +127,8 @@
 ## CLEAN RULES
 ##
 clean:
-	rm -f $(OBJECTS) $(RC_SRC)
-	rm -f $(TARGET).dll $(TARGET).dll.a $(PURPLE_VERSION_H)
+	rm -f $(OBJECTS) $(RC_SRC) $(PURPLE_VERSION_H)
+	rm -f $(TARGET).dll $(TARGET).dll.a $(TARGET).def
 	$(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) clean
 	$(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
 
--- a/libpurple/idle.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/idle.c	Mon Jul 30 00:05:02 2007 +0000
@@ -30,8 +30,6 @@
 #include "savedstatuses.h"
 #include "signals.h"
 
-#define IDLEMARK 600 /* 10 minutes! */
-
 typedef enum
 {
 	PURPLE_IDLE_NOT_AWAY = 0,
@@ -121,7 +119,7 @@
 	gboolean report_idle = TRUE;
 	gint away_seconds = 0;
 	gint idle_recheck_interval = 0;
-
+	gint idle_poll_seconds = purple_prefs_get_int("/purple/away/mins_before_away") * 60;
 	purple_signal_emit(purple_blist_get_handle(), "update-idle");
 
 	idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting");
@@ -132,7 +130,7 @@
 	{
 		/* Use system idle time (mouse or keyboard movement, etc.) */
 		time_idle = idle_ui_ops->get_time_idle();
-		idle_recheck_interval = 60;
+		idle_recheck_interval = 1;
 	}
 	else if (!strcmp(idle_reporting, "purple"))
 	{
@@ -153,7 +151,7 @@
 			if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
 			{
 				time_idle = idle_ui_ops->get_time_idle();
-				idle_recheck_interval = 60;
+				idle_recheck_interval = 1;
 			}
 			else
 			{
@@ -173,7 +171,7 @@
 		}
 	}
 
-	time_until_next_idle_event = IDLEMARK - time_idle;
+	time_until_next_idle_event = idle_poll_seconds - time_idle;
 	if (time_until_next_idle_event < 0)
 	{
 		/* If we're already idle, check again as appropriate. */
@@ -197,7 +195,7 @@
 	}
 
 	/* Idle reporting stuff */
-	if (report_idle && (time_idle >= IDLEMARK))
+	if (report_idle && (time_idle >= idle_poll_seconds))
 	{
 		GList *l;
 		for (l = purple_connections_get_all(); l != NULL; l = l->next)
@@ -206,7 +204,7 @@
 			set_account_idle(purple_connection_get_account(gc), time_idle);
 		}
 	}
-	else if (!report_idle || (time_idle < IDLEMARK))
+	else if (!report_idle || (time_idle < idle_poll_seconds ))
 	{
 		while (idled_accts != NULL)
 			set_account_unidle(idled_accts->data);
@@ -306,6 +304,11 @@
 
 static gboolean _do_purple_idle_touch_cb(gpointer data)
 {
+	int idle_poll_minutes = purple_prefs_get_int("/purple/away/mins_before_away");
+
+	 /* +1 more for g_timeout_add_seconds rounding. */
+	idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, check_idleness_timer, NULL);
+
 	purple_idle_touch();
 
 	return FALSE;
@@ -315,11 +318,6 @@
 void
 purple_idle_init()
 {
-	/* Add the timer to check if we're idle.
-	 * IDLEMARK + 1 as the boundary,
-	 * +1 more for g_timeout_add_seconds rounding. */
-	idle_timer = purple_timeout_add_seconds((IDLEMARK + 2), check_idleness_timer, NULL);
-
 	purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg",
 						purple_idle_get_handle(),
 						PURPLE_CALLBACK(im_msg_sent_cb), NULL);
--- a/libpurple/notify.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/notify.c	Mon Jul 30 00:05:02 2007 +0000
@@ -112,6 +112,10 @@
 		info            = g_new0(PurpleNotifyInfo, 1);
 		info->type      = PURPLE_NOTIFY_EMAIL;
 		info->handle    = handle;
+
+		purple_signal_emit(purple_notify_get_handle(), "displaying-email-notification",
+							subject, from, to, url);
+
 		info->ui_handle = ops->notify_email(handle, subject, from, to, url);
 		info->cb = cb;
 		info->cb_user_data = user_data;
@@ -164,6 +168,10 @@
 		info            = g_new0(PurpleNotifyInfo, 1);
 		info->type      = PURPLE_NOTIFY_EMAILS;
 		info->handle    = handle;
+
+		purple_signal_emit(purple_notify_get_handle(), "displaying-emails-notification",
+							subjects, froms, tos, urls, count);
+
 		info->ui_handle = ops->notify_emails(handle, count, detailed, subjects,
 											 froms, tos, urls);
 		info->cb = cb;
@@ -799,6 +807,21 @@
 {
 	gpointer handle = purple_notify_get_handle();
 
+	purple_signal_register(handle, "displaying-email-notification",
+						 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER, NULL, 4,
+						 purple_value_new(PURPLE_TYPE_STRING),
+						 purple_value_new(PURPLE_TYPE_STRING),
+						 purple_value_new(PURPLE_TYPE_STRING),
+						 purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(handle, "displaying-emails-notification",
+						 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT, NULL, 5,
+						 purple_value_new(PURPLE_TYPE_POINTER),
+						 purple_value_new(PURPLE_TYPE_POINTER),
+						 purple_value_new(PURPLE_TYPE_POINTER),
+						 purple_value_new(PURPLE_TYPE_POINTER),
+						 purple_value_new(PURPLE_TYPE_UINT));
+
 	purple_signal_register(handle, "displaying-userinfo",
 						 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/plugin.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/plugin.c	Mon Jul 30 00:05:02 2007 +0000
@@ -692,7 +692,10 @@
 
 		dependency = purple_plugins_find_with_id(dep_name);
 
-		dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id);
+		if (dependency != NULL)
+			dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id);
+		else
+			purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name);
 	}
 
 	if (plugin->native_plugin) {
--- a/libpurple/plugins/perl/common/Account.xs	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Mon Jul 30 00:05:02 2007 +0000
@@ -307,6 +307,9 @@
     }
     g_list_free(list);
 
+void
+purple_accounts_restore_current_statuses()
+
 Purple::Account
 purple_accounts_find(name, protocol)
     const char * name
--- a/libpurple/plugins/perl/common/BuddyIcon.xs	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/plugins/perl/common/BuddyIcon.xs	Mon Jul 30 00:05:02 2007 +0000
@@ -45,6 +45,10 @@
 	int *width
 	int *height
 
+char *
+purple_buddy_icon_get_full_path(icon);
+	Purple::Buddy::Icon icon
+
 MODULE = Purple::Buddy::Icon PACKAGE = Purple::Buddy::Icons   PREFIX = purple_buddy_icons_
 PROTOTYPES: ENABLE
 
--- a/libpurple/plugins/signals-test.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/plugins/signals-test.c	Mon Jul 30 00:05:02 2007 +0000
@@ -522,6 +522,26 @@
 }
 
 /**************************************************************************
+ * Notify signals callbacks
+ **************************************************************************/
+static void
+notify_email_cb(char *subject, char *from, char *to, char *url) {
+	purple_debug_misc("signals test", "notify email: subject=%s, from=%s, to=%s, url=%s\n",
+					subject, from, to, url);
+}
+
+static void
+notify_emails_cb(char **subjects, char **froms, char **tos, char **urls, guint count) {
+	int i;
+	purple_debug_misc("signals test", "notify emails: count=%d\n", count);
+	for(i=0; i<count && i<5; i++) {
+		if(subjects[i]==NULL || froms[i]==NULL || tos[i]==NULL || urls[i]==NULL) continue;
+		purple_debug_misc("signals test", "notify emails[%d]: subject=%s, from=%s, to=%s, url=%s\n",
+			i, subjects[i], froms[i], tos[i], urls[i]);
+	}
+}
+
+/**************************************************************************
  * Plugin stuff
  **************************************************************************/
 static gboolean
@@ -535,6 +555,7 @@
 	void *ciphers_handle  = purple_ciphers_get_handle();
 	void *ft_handle       = purple_xfers_get_handle();
 	void *sound_handle    = purple_sounds_get_handle();
+	void *notify_handle   = purple_notify_get_handle();
 
 	/* Accounts subsystem signals */
 	purple_signal_connect(accounts_handle, "account-connecting",
@@ -666,6 +687,12 @@
 	purple_signal_connect(sound_handle, "playing-sound-event", plugin,
 	                    PURPLE_CALLBACK(sound_playing_event_cb), NULL);
 
+	/* Notify signals */
+	purple_signal_connect(notify_handle, "displaying-email-notification",
+						plugin, PURPLE_CALLBACK(notify_email_cb), NULL);
+	purple_signal_connect(notify_handle, "displaying-emails-notification",
+						plugin, PURPLE_CALLBACK(notify_emails_cb), NULL);
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/bonjour/jabber.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Jul 30 00:05:02 2007 +0000
@@ -350,7 +350,6 @@
 	PurpleAccount *account = pb->account;
 	BonjourBuddy *bb = pb->proto_data;
 	gboolean closed_conversation = FALSE;
-	xmlnode *message_node;
 
 	/* Read the data from the socket */
 	if ((message_length = _read_data(socket, &message)) == -1) {
@@ -374,9 +373,6 @@
 		}
 	}
 
-	/* Parse the message into an XMLnode for analysis */
-	message_node = xmlnode_from_str(message, strlen(message));
-
 	/*
 	 * Check that this is not the end of the conversation.  This is
 	 * using a magic string, but xmlnode won't play nice when just
@@ -396,16 +392,22 @@
 			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
 			g_free(tmp);
 		}
-	} else if (message_node != NULL) {
-		/* Parse the message to get the data and send to the ui */
-		_jabber_parse_and_write_message_to_ui(message_node, account->gc, pb);
 	} else {
-		/* TODO: Deal with receiving only a partial message */
+		xmlnode *message_node;
+
+		/* Parse the message into an XMLnode for analysis */
+		message_node = xmlnode_from_str(message, strlen(message));
+
+		if (message_node != NULL) {
+			/* Parse the message to get the data and send to the ui */
+			_jabber_parse_and_write_message_to_ui(message_node, account->gc, pb);
+			xmlnode_free(message_node);
+		} else {
+			/* TODO: Deal with receiving only a partial message */
+		}
 	}
 
 	g_free(message);
-	if (message_node != NULL)
-		xmlnode_free(message_node);
 }
 
 struct _stream_start_data {
@@ -638,7 +640,7 @@
 	PurpleBuddy *pb = data;
 	BonjourBuddy *bb = pb->proto_data;
 	int len, ret;
-	char *stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), purple_buddy_get_name(pb));
+	char *stream_start;
 
 	bb->conversation->connect_data = NULL;
 
@@ -659,6 +661,7 @@
 		return;
 	}
 
+	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), purple_buddy_get_name(pb));
 	len = strlen(stream_start);
 
 	/* Start the stream and send queued messages */
--- a/libpurple/protocols/jabber/jabber.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Jul 30 00:05:02 2007 +0000
@@ -1975,6 +1975,10 @@
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
+
+	if (!chat)
+		return PURPLE_CMD_RET_FAILED;
+
 	jabber_chat_request_room_configure(chat);
 	return PURPLE_CMD_RET_OK;
 }
@@ -1983,6 +1987,10 @@
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
+
+	if (!chat)
+		return PURPLE_CMD_RET_FAILED;
+
 	jabber_chat_register(chat);
 	return PURPLE_CMD_RET_OK;
 }
@@ -1991,6 +1999,10 @@
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
+
+	if (!chat)
+		return PURPLE_CMD_RET_FAILED;
+
 	jabber_chat_change_topic(chat, args ? args[0] : NULL);
 	return PURPLE_CMD_RET_OK;
 }
@@ -2000,7 +2012,7 @@
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 
-	if(!args || !args[0])
+	if(!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
 	jabber_chat_change_nick(chat, args[0]);
@@ -2011,6 +2023,10 @@
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
+
+	if (!chat)
+		return PURPLE_CMD_RET_FAILED;
+
 	jabber_chat_part(chat, args ? args[0] : NULL);
 	return PURPLE_CMD_RET_OK;
 }
@@ -2020,7 +2036,7 @@
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 
-	if(!args || !args[0])
+	if(!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
 	if(!jabber_chat_ban_user(chat, args[0], args[1])) {
@@ -2036,7 +2052,7 @@
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 
-	if (!args || !args[0] || !args[1])
+	if (!chat || !args || !args[0] || !args[1])
 		return PURPLE_CMD_RET_FAILED;
 
 	if (strcmp(args[1], "owner") != 0 && 
@@ -2061,7 +2077,7 @@
 {
 	JabberChat *chat;
 
-	if (!args || !args[0] || !args[1])
+	if (!chat || !args || !args[0] || !args[1])
 		return PURPLE_CMD_RET_FAILED;
 
 	if (strcmp(args[1], "moderator") != 0 &&
@@ -2102,7 +2118,7 @@
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 	GHashTable *components;
 
-	if(!args || !args[0])
+	if(!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
@@ -2124,7 +2140,7 @@
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 
-	if(!args || !args[0])
+	if(!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
 	if(!jabber_chat_kick_user(chat, args[0], args[1])) {
@@ -2141,6 +2157,9 @@
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 	char *who;
 
+	if (!chat)
+		return PURPLE_CMD_RET_FAILED;
+
 	who = g_strdup_printf("%s@%s/%s", chat->room, chat->server, args[0]);
 
 	jabber_message_send_im(purple_conversation_get_gc(conv), who, args[1], 0);
--- a/libpurple/protocols/jabber/presence.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Jul 30 00:05:02 2007 +0000
@@ -514,6 +514,12 @@
 										_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
 										_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
 						}
+					} else if(code && !strcmp(code, "210")) {
+						/*  server rewrote room-nick */
+						if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
+							g_free(chat->handle);
+							chat->handle = g_strdup(jid->resource);
+						}
 					}
 				}
 				if((z = xmlnode_get_child(y, "item"))) {
@@ -549,7 +555,8 @@
 
 			if(chat->conv) {
 				title = g_strdup_printf(_("Error in chat %s"), from);
-				serv_got_chat_left(js->gc, chat->id);
+				if (g_hash_table_size(chat->members) == 0)
+					serv_got_chat_left(js->gc, chat->id);
 			} else {
 				title = g_strdup_printf(_("Error joining chat %s"), from);
 			}
@@ -557,7 +564,9 @@
 			g_free(title);
 			g_free(msg);
 
-			jabber_chat_destroy(chat);
+			if (g_hash_table_size(chat->members) == 0)
+				/* Only destroy the chat if the error happened while joining */
+				jabber_chat_destroy(chat);
 			jabber_id_free(jid);
 			g_free(status);
 			g_free(room_jid);
--- a/libpurple/protocols/oscar/family_chatnav.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/oscar/family_chatnav.c	Mon Jul 30 00:05:02 2007 +0000
@@ -427,7 +427,7 @@
 	if (snac2->type == 0x0002) /* request chat rights */
 		ret = parseinfo_perms(od, conn, mod, frame, snac, bs, snac2);
 	else if (snac2->type == 0x0003) /* request exchange info */
-		purple_debug_misc("oscar", "chatnav_parse_info: resposne to exchange info\n");
+		purple_debug_misc("oscar", "chatnav_parse_info: response to exchange info\n");
 	else if (snac2->type == 0x0004) /* request room info */
 		purple_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
 	else if (snac2->type == 0x0005) /* request more room info */
--- a/libpurple/protocols/oscar/family_locate.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon Jul 30 00:05:02 2007 +0000
@@ -636,13 +636,15 @@
 	 * Parse out the Type-Length-Value triples as they're found.
 	 */
 	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
+		guint16 type;
+		guint8 number, length;
 		int endpos;
-		guint16 type, length;
 
 		type = byte_stream_get16(bs);
-		length = byte_stream_get16(bs);
+		number = byte_stream_get8(bs);
+		length = byte_stream_get8(bs);
 
-		endpos = byte_stream_curpos(bs) + length;
+		endpos = byte_stream_curpos(bs) + MIN(length, byte_stream_empty(bs));
 
 		if (type == 0x0001) {
 			/*
@@ -814,27 +816,33 @@
 			 * contain information about the buddy icon the user
 			 * has stored on the server.
 			 */
-			int type2, number, length2;
+			guint16 type2;
+			guint8 number2, length2;
+			int endpos2;
 
-			while (byte_stream_curpos(bs) < endpos) {
+			/*
+			 * Continue looping as long as we're able to read type2,
+			 * number2, and length2.
+			 */
+			while (byte_stream_curpos(bs) + 4 <= endpos) {
 				type2 = byte_stream_get16(bs);
-				number = byte_stream_get8(bs);
+				number2 = byte_stream_get8(bs);
 				length2 = byte_stream_get8(bs);
 
+				endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_empty(bs));
+
 				switch (type2) {
 					case 0x0000: { /* This is an official buddy icon? */
 						/* This is always 5 bytes of "0x02 01 d2 04 72"? */
-						byte_stream_advance(bs, length2);
 					} break;
 
 					case 0x0001: { /* A buddy icon checksum */
-						if ((length2 > 0) && ((number == 0x00) || (number == 0x01))) {
+						if ((length2 > 0) && ((number2 == 0x00) || (number2 == 0x01))) {
 							g_free(outinfo->iconcsum);
-							outinfo->iconcsumtype = number;
+							outinfo->iconcsumtype = number2;
 							outinfo->iconcsum = byte_stream_getraw(bs, length2);
 							outinfo->iconcsumlen = length2;
-						} else
-							byte_stream_advance(bs, length2);
+						}
 					} break;
 
 					case 0x0002: { /* A status/available message */
@@ -879,11 +887,10 @@
 							outinfo->itmsurl_encoding = NULL;
 						}
 					} break;
+				}
 
-					default: {
-						byte_stream_advance(bs, length2);
-					} break;
-				}
+				/* Save ourselves. */
+				byte_stream_setpos(bs, endpos2);
 			}
 
 		} else if (type == 0x001e) {
--- a/libpurple/protocols/oscar/oscar.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Jul 30 00:05:02 2007 +0000
@@ -6606,7 +6606,7 @@
 			if (message) {
 				/* Spaces are encoded as '+' */
 				g_strdelimit(message, "+", ' ');
-				purple_conv_im_send(PURPLE_CONV_IM(conv), message);
+				purple_conv_send_confirm(conv, message);
 			}
 		}
 		/*else
--- a/libpurple/protocols/oscar/tlv.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/oscar/tlv.c	Mon Jul 30 00:05:02 2007 +0000
@@ -423,25 +423,6 @@
 }
 
 /**
- * Adds the given userinfo struct to a TLV chain.
- *
- * @param list Destination chain.
- * @param type TLV type to add.
- * @return The size of the value added.
- */
-int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo)
-{
-	guint8 buf[1024]; /* TODO: Don't use a fixed length buffer */
-	ByteStream bs;
-
-	byte_stream_init(&bs, buf, sizeof(buf));
-
-	aim_putuserinfo(&bs, userinfo);
-
-	return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
-}
-
-/**
  * Adds the given chatroom info to a TLV chain.
  *
  * @param list Destination chain.
--- a/libpurple/protocols/qq/qq.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/qq/qq.c	Mon Jul 30 00:05:02 2007 +0000
@@ -747,10 +747,7 @@
 {
 	PurpleAccountOption *option;
 
-	option = purple_account_option_bool_new(_("Login in TCP"), "use_tcp", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_bool_new(_("Login Hidden"), "hidden", FALSE);
+	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	option = purple_account_option_string_new(_("Server"), "server", NULL);
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Mon Jul 30 00:05:02 2007 +0000
@@ -1917,6 +1917,7 @@
 	char *msg;
 	char *url = NULL;
 	char *fullmsg;
+	PurpleAccount *account = gc->account;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -1949,6 +1950,9 @@
 			return;
 		}
 #endif
+		if (!purple_account_get_remember_password(account))
+			purple_account_set_password(account, NULL);
+
 		msg = g_strdup(_("Incorrect password."));
 		break;
 	case 14:
@@ -3915,7 +3919,7 @@
 			if (message) {
 				/* Spaces are encoded as '+' */
 				g_strdelimit(message, "+", ' ');
-				purple_conv_im_send(PURPLE_CONV_IM(conv), message);
+				purple_conv_send_confirm(conv, message);
 			}
 		}
 		/*else
--- a/libpurple/savedstatuses.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/libpurple/savedstatuses.c	Mon Jul 30 00:05:02 2007 +0000
@@ -869,9 +869,9 @@
 		purple_idle_touch();
 
 	old = purple_savedstatus_get_current();
-	purple_prefs_set_bool("/purple/savedstatus/isidleaway", idleaway);
 	saved_status = idleaway ? purple_savedstatus_get_idleaway()
 			: purple_savedstatus_get_default();
+	purple_prefs_set_bool("/purple/savedstatus/isidleaway", idleaway);
 
 	if (idleaway && (purple_savedstatus_get_type(old) != PURPLE_STATUS_AVAILABLE))
 		/* Our global status is already "away," so don't change anything */
--- a/pidgin/Makefile.mingw	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/Makefile.mingw	Mon Jul 30 00:05:02 2007 +0000
@@ -167,8 +167,9 @@
 clean:
 	$(MAKE) -C $(PIDGIN_IDLETRACK_TOP) -f $(MINGW_MAKEFILE) clean
 	$(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
+	$(MAKE) -C $(PIDGIN_PIXMAPS_TOP) -f $(MINGW_MAKEFILE) clean
 	rm -f $(PIDGIN_OBJECTS) $(PIDGIN_RC_SRC) $(EXE_OBJECTS) $(EXE_RC_SRC)
-	rm -f $(PIDGIN_TARGET).dll $(PIDGIN_TARGET).dll.a
+	rm -f $(PIDGIN_TARGET).dll $(PIDGIN_TARGET).dll.a $(PIDGIN_TARGET).def
 	rm -f $(EXE_TARGET).exe
 
 include $(PIDGIN_COMMON_TARGETS)
--- a/pidgin/gtkblist.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkblist.c	Mon Jul 30 00:05:02 2007 +0000
@@ -136,8 +136,6 @@
 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
 
-static void pidgin_blist_tooltip_destroy(void);
-
 struct _pidgin_blist_node {
 	GtkTreeRowReference *row;
 	gboolean contact_expanded;
@@ -629,8 +627,7 @@
 
 	gc = purple_account_get_connection(data->account);
 
-	while ((tmp = gtk_container_get_children(GTK_CONTAINER(data->entries_box))))
-		gtk_widget_destroy(tmp->data);
+	gtk_container_foreach(GTK_CONTAINER(data->entries_box), (GtkCallback)gtk_widget_destroy, NULL);
 
 	g_list_free(data->entries);
 	data->entries = NULL;
@@ -1151,7 +1148,8 @@
 	pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL,
 			G_CALLBACK(gtk_blist_menu_bp_cb), buddy, 0, 0, NULL);
 
-	if(((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) {
+	if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && 
+	      !sub && !contact_expanded) {
 		pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
 				G_CALLBACK(gtk_blist_menu_showlog_cb),
 				contact, 0, 0, NULL);
@@ -1165,7 +1163,8 @@
 										  (PurpleBlistNode *)buddy);
 	pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
 
-	if (((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) {
+	if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && 
+              !sub && !contact_expanded) {
 		pidgin_separator(menu);
 		pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy);
 		pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
@@ -1247,7 +1246,8 @@
 
 
 static GtkWidget *
-create_chat_menu(PurpleBlistNode *node, PurpleChat *c) {
+create_chat_menu(PurpleBlistNode *node, PurpleChat *c)
+{
 	GtkWidget *menu;
 	gboolean autojoin;
 
@@ -1305,7 +1305,8 @@
 }
 
 static GtkWidget *
-create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b) {
+create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b)
+{
 	struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
 	GtkWidget *menu;
 	GtkWidget *menuitem;
@@ -2495,7 +2496,7 @@
 }
 
 
-static void pidgin_blist_tooltip_destroy()
+void pidgin_blist_tooltip_destroy()
 {
 	while(gtkblist->tooltipdata) {
 		struct tooltip_data *td = gtkblist->tooltipdata->data;
@@ -2583,6 +2584,22 @@
 	GtkTreeIter iter;
 	PurpleBlistNode *node;
 	GValue val;
+
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
+		return FALSE;
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+	val.g_type = 0;
+	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+	node = g_value_get_pointer(&val);
+
+	pidgin_blist_draw_tooltip(node, gtkblist->window);
+
+	gtk_tree_path_free(path);
+	return FALSE;
+}
+
+void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget)
+{
 	int scr_w, scr_h, w, h, x, y;
 #if GTK_CHECK_VERSION(2,2,0)
 	int mon_num;
@@ -2591,6 +2608,9 @@
 	gboolean tooltip_top = FALSE;
 	struct _pidgin_blist_node *gtknode;
 	GdkRectangle mon_size;
+	
+	if (node == NULL)
+		return;
 
 	/*
 	 * Attempt to free the previous tooltip.  I have a feeling
@@ -2598,14 +2618,6 @@
 	 */
 	pidgin_blist_tooltip_destroy();
 
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
-		return FALSE;
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	val.g_type = 0;
-	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-	node = g_value_get_pointer(&val);
-
-	gtk_tree_path_free(path);
 
 	gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
 
@@ -2643,13 +2655,13 @@
 	} else {
 		gtk_widget_destroy(gtkblist->tipwindow);
 		gtkblist->tipwindow = NULL;
-		return FALSE;
+		return;
 	}
 
 	if (gtkblist->tooltipdata == NULL) {
 		gtk_widget_destroy(gtkblist->tipwindow);
 		gtkblist->tipwindow = NULL;
-		return FALSE;
+		return;
 	}
 
 	gtknode = node->ui_data;
@@ -2685,9 +2697,6 @@
 	  h = mon_size.height - 10;
 #endif
 
-	if (GTK_WIDGET_NO_WINDOW(gtkblist->window))
-		y+=gtkblist->window->allocation.y;
-
 	x -= ((w >> 1) + 4);
 
 	if ((y + h + 4) > scr_h || tooltip_top)
@@ -2713,7 +2722,7 @@
 	gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
 	gtk_widget_show(gtkblist->tipwindow);
 
-	return FALSE;
+	return;
 }
 
 static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context,
@@ -4000,6 +4009,7 @@
 	char *primary;
 	const char *text;
 	gboolean enabled;
+	GList *list;
 
 	account = user_data;
 	primary = g_strdup_printf(_("%s disconnected"),
@@ -4018,8 +4028,10 @@
 	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) {
+	if ((list = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons))) == NULL) {
 		gtk_widget_hide(gtkblist->error_buttons);
+	} else {
+		g_list_free(list);
 	}
 }
 
@@ -4089,7 +4101,7 @@
 	/* Remove the old error buttons */
 	for (l = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons));
 			l != NULL;
-			l = l->next)
+			l = g_list_delete_link(l, l))
 	{
 		gtk_widget_destroy(GTK_WIDGET(l->data));
 	}
@@ -5648,8 +5660,7 @@
 
 	gc = purple_account_get_connection(data->account);
 
-	while ((tmp = gtk_container_get_children(GTK_CONTAINER(data->entries_box))))
-		gtk_widget_destroy(tmp->data);
+	gtk_container_foreach(GTK_CONTAINER(data->entries_box), (GtkCallback)gtk_widget_destroy, NULL);
 
 	g_list_free(data->entries);
 
@@ -6526,7 +6537,7 @@
 		return;
 
 	/* Clear the old Accounts menu */
-	for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = l->next) {
+	for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) {
 		menuitem = l->data;
 
 		if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Add\\/Edit")))
@@ -6737,7 +6748,7 @@
 		return;
 
 	/* Clear the old menu */
-	for (l = gtk_container_get_children(GTK_CONTAINER(sortmenu)); l; l = l->next) {
+	for (l = gtk_container_get_children(GTK_CONTAINER(sortmenu)); l; l = g_list_delete_link(l, l)) {
 		menuitem = l->data;
 		gtk_widget_destroy(GTK_WIDGET(menuitem));
 	}
--- a/pidgin/gtkblist.h	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkblist.h	Mon Jul 30 00:05:02 2007 +0000
@@ -370,4 +370,21 @@
  */
 gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased);
 
+/**
+ * Creates the Buddy List tooltip at the current pointer location for the given buddy list node.
+ *
+ * This tooltip will be destroyed the next time this function is called, or when XXXX
+ * is called
+ *
+ * @param buddy The buddy to show a tooltip for
+ * @param widget The widget to draw the tooltip on
+ */
+void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget);
+
+/**
+ * Destroys the current (if any) Buddy List tooltip
+ */
+void pidgin_blist_tooltip_destroy(void);
+
+
 #endif /* _PIDGINBLIST_H_ */
--- a/pidgin/gtkcellrendererexpander.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkcellrendererexpander.c	Mon Jul 30 00:05:02 2007 +0000
@@ -285,5 +285,5 @@
 	else
 		gtk_tree_view_expand_row(GTK_TREE_VIEW(widget),path,FALSE);
 	gtk_tree_path_free(path);
-	return FALSE;
+	return TRUE;
 }
--- a/pidgin/gtkconv.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkconv.c	Mon Jul 30 00:05:02 2007 +0000
@@ -239,7 +239,6 @@
 
 	if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 		return FALSE;
-
 	if (gtkconv->auto_resize) {
 		return FALSE;
 	}
@@ -264,7 +263,6 @@
 		if (w == gtkconv->lower_hbox)
 			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height);
 	}
-
 	return FALSE;
 }
 
@@ -2367,6 +2365,8 @@
 			&(gtkconv->infopane_iter),
 			CONV_EMBLEM_COLUMN, emblem, -1);
 
+	/* XXX seanegan Why do I have to do this? */
+	gtk_widget_queue_draw(gtkconv->infopane);
 
 	if (status != NULL)
 		g_object_unref(status);
@@ -2913,22 +2913,19 @@
 	}
 }
 
-static void
-regenerate_options_items(PidginWindow *win)
-{
-	GtkWidget *menu;
+/* Returns TRUE if some items were added to the menu, FALSE otherwise */
+static gboolean
+populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolean all)
+{
 	GList *list;
-	PidginConversation *gtkconv;
 	PurpleConversation *conv;
 	PurpleBlistNode *node = NULL;
 	PurpleChat *chat = NULL;
 	PurpleBuddy *buddy = NULL;
-
-	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+	gboolean ret;
+
 	conv = gtkconv->active_conv;
 
-	menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More"));
-
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		chat = purple_blist_find_chat(conv->account, conv->name);
 
@@ -2973,23 +2970,43 @@
 	else if (buddy)
 		node = (PurpleBlistNode *)buddy;
 
+	/* Now add the stuff */
+	if (all && buddy) {
+		pidgin_blist_make_buddy_menu(menu, buddy, TRUE);
+	} else if (node) {
+		if (purple_account_is_connected(conv->account))
+			pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node);
+		pidgin_append_blist_node_extended_menu(menu, node);
+	}
+
+	if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL) {
+		ret = FALSE;
+	} else {
+		g_list_free(list);
+		ret = TRUE;
+	}
+	return ret;
+}
+
+static void
+regenerate_options_items(PidginWindow *win)
+{
+	GtkWidget *menu;
+	PidginConversation *gtkconv;
+	GList *list;
+
+	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+	menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More"));
+
 	/* Remove the previous entries */
 	for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; )
 	{
 		GtkWidget *w = list->data;
-		list = list->next;
+		list = g_list_delete_link(list, list);
 		gtk_widget_destroy(w);
 	}
 
-	/* Now add the stuff */
-	if (node)
-	{
-		if (purple_account_is_connected(conv->account))
-			pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node);
-		pidgin_append_blist_node_extended_menu(menu, node);
-	}
-
-	if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL)
+	if (!populate_menu_with_options(menu, gtkconv, FALSE))
 	{
 		GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
@@ -3370,12 +3387,11 @@
 
 	gtk_widget_show(win->menu.send_to);
 
-	menu = gtk_menu_item_get_submenu(
-		GTK_MENU_ITEM(win->menu.send_to));
+	menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(win->menu.send_to));
 
 	for (child = gtk_container_get_children(GTK_CONTAINER(menu));
 		 child != NULL;
-		 child = child->next) {
+		 child = g_list_delete_link(child, child)) {
 
 		GtkWidget *item = child->data;
 		PurpleBuddy *item_buddy;
@@ -4260,12 +4276,12 @@
         height = (oneline.height + pad_top + pad_bottom) * lines;
         height += (oneline.height + pad_inside) * (wrapped_lines - lines);
 
+	gtkconv->auto_resize = TRUE;
+        g_idle_add(reset_auto_resize_cb, gtkconv);
 	gtk_widget_size_request(gtkconv->lower_hbox, &sr);
 	if (sr.height < height + PIDGIN_HIG_BOX_SPACE) {
-		gtkconv->auto_resize = TRUE;
 		gtkconv->entry_growing = TRUE;
-	        gtk_widget_set_size_request(gtkconv->entry, -1, height);
-	        g_idle_add(reset_auto_resize_cb, gtkconv);
+	        gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE);
 	}
 }
 
@@ -4392,6 +4408,50 @@
 	gtk_container_add(GTK_CONTAINER(sw), list);
 }
 
+static int tooltip_timeout = 0;
+
+static gboolean
+pidgin_conv_tooltip_timeout(PidginConversation *gtkconv)
+{
+	PurpleBlistNode *node = NULL;
+	PurpleConversation *conv = gtkconv->active_conv;
+ 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+                node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
+	} else {
+                node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
+	}
+
+	if (node) 
+		pidgin_blist_draw_tooltip(node, gtkconv->infopane);
+	return FALSE;
+}
+
+static void 
+pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv)
+{
+	pidgin_blist_tooltip_destroy();
+	if (tooltip_timeout) {
+		g_source_remove(tooltip_timeout);
+		tooltip_timeout = 0;
+	}
+}
+
+static gboolean 
+pidgin_conv_motion_cb (GtkWidget *infopane, GdkEventMotion *event, PidginConversation *gtkconv)
+{
+	int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
+	
+	pidgin_blist_tooltip_destroy();
+	if (delay == 0)
+		return FALSE;
+
+	if (tooltip_timeout != 0) 
+		g_source_remove(tooltip_timeout);
+
+	tooltip_timeout = g_timeout_add(delay, (GSourceFunc)pidgin_conv_tooltip_timeout, gtkconv);
+	return FALSE;
+}
+
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
@@ -4412,16 +4472,24 @@
 
 	/* Setup the info pane */
 	event_box = gtk_event_box_new();
+#if GTK_CHECK_VERSION(2,4,0)
+	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), FALSE);
+#endif
 	gtk_widget_show(event_box);
 	gtkconv->infopane_hbox = gtk_hbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), event_box, FALSE, FALSE, 0);
 	gtk_container_add(GTK_CONTAINER(event_box), gtkconv->infopane_hbox);
 	gtk_widget_show(gtkconv->infopane_hbox);
 	gtk_widget_add_events(event_box,
-	                      GDK_BUTTON1_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
+	                      GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
 	g_signal_connect(G_OBJECT(event_box), "button_press_event",
 	                 G_CALLBACK(infopane_press_cb), gtkconv);
 
+        g_signal_connect(G_OBJECT(event_box), "motion-notify-event", 
+			 G_CALLBACK(pidgin_conv_motion_cb), gtkconv);
+        g_signal_connect(G_OBJECT(event_box), "leave-notify-event", 
+			 G_CALLBACK(pidgin_conv_leave_cb), gtkconv);
+
 
 	gtkconv->infopane = gtk_cell_view_new();
 	gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF);
@@ -4547,7 +4615,6 @@
 	default_formatize(gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
 	                       G_CALLBACK(clear_formatting_cb), gtkconv);
-
 	return paned;
 }
 
@@ -6522,7 +6589,6 @@
 	gtk_widget_show(event);
 
 	gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale);
-	gtkconv->auto_resize = TRUE;
 	gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon);
 	gtk_widget_show(gtkconv->u.im->icon);
 
@@ -7643,52 +7709,40 @@
 static gboolean
 infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
 {
-	int nb_x, nb_y;
-
 	if (e->type != GDK_BUTTON_PRESS)
 		return FALSE;
 
 	if (e->button == 3) {
 		/* Right click was pressed. Popup the Send To menu. */
-		GtkWidget *menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
-		if (menu)
-			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time);
-		else
+		GtkWidget *menu = gtk_menu_new(), *sub;
+		sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
+
+		if (populate_menu_with_options(menu, gtkconv, TRUE))
+			pidgin_separator(menu);
+		else if (!sub ||
+				!GTK_WIDGET_IS_SENSITIVE(gtkconv->win->menu.send_to)) {
+			gtk_widget_destroy(menu);
 			return FALSE;
-		return TRUE;
-	} else if (e->button != 1) {
-		return FALSE;
-	}
-
-	if (gtkconv->win->in_drag) {
-		purple_debug(PURPLE_DEBUG_WARNING, "gtkconv",
-				"Already in the middle of a window drag at tab_press_cb\n");
+		} else {
+			menu = sub;
+			sub = NULL;
+		}
+
+		if (sub) {
+			GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("_Send To"));
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+			gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), sub);
+			gtk_widget_show(item);
+			gtk_widget_show_all(sub);
+		}
+
+		gtk_widget_show_all(menu);
+		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time);
 		return TRUE;
 	}
-
-	gtkconv->win->in_predrag = TRUE;
-	gtkconv->win->drag_tab = gtk_notebook_page_num(GTK_NOTEBOOK(gtkconv->win->notebook), gtkconv->tab_cont);
-
-	gdk_window_get_origin(gtkconv->infopane_hbox->window, &nb_x, &nb_y);
-
-	gtkconv->win->drag_min_x = gtkconv->infopane_hbox->allocation.x      + nb_x;
-	gtkconv->win->drag_min_y = gtkconv->infopane_hbox->allocation.y      + nb_y;
-	gtkconv->win->drag_max_x = gtkconv->infopane_hbox->allocation.width  + gtkconv->win->drag_min_x;
-	gtkconv->win->drag_max_y = gtkconv->infopane_hbox->allocation.height + gtkconv->win->drag_min_y;
-
-	/* Connect the new motion signals. */
-	gtkconv->win->drag_motion_signal =
-		g_signal_connect(G_OBJECT(gtkconv->win->notebook), "motion_notify_event",
-				G_CALLBACK(notebook_motion_cb), gtkconv->win);
-
-	gtkconv->win->drag_leave_signal =
-		g_signal_connect(G_OBJECT(gtkconv->win->notebook), "leave_notify_event",
-				G_CALLBACK(notebook_leave_cb), gtkconv->win);
-
 	return FALSE;
-
-}
-
+}
+ 
 static gboolean
 notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
 {
@@ -8269,8 +8323,8 @@
 		return FALSE; /* carry on normally */
 
         /* store the position */
-        purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x",      x);
-	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y",      y);
+        purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x", x);
+	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y", y);
 	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width",  event->width);
 	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height);
 
@@ -8279,6 +8333,38 @@
 						
 }
 
+static void
+pidgin_conv_restore_position(PidginWindow *win) {
+	int conv_x, conv_y, conv_width, conv_height;
+
+	conv_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width");
+
+	 /* if the window exists, is hidden, we're saving positions, and the
+          * position is sane... */
+        if (win && win->window &&
+                !GTK_WIDGET_VISIBLE(win->window) && conv_width != 0) {
+
+                conv_x      = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x");
+                conv_y      = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y");
+                conv_height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height");
+
+	       /* ...check position is on screen... */
+                if (conv_x >= gdk_screen_width())
+                        conv_x = gdk_screen_width() - 100;
+                else if (conv_x + conv_width < 0)
+                        conv_x = 100;
+
+                if (conv_y >= gdk_screen_height())
+                        conv_y = gdk_screen_height() - 100;
+                else if (conv_y + conv_height < 0)
+                        conv_y = 100;
+
+                /* ...and move it back. */
+                gtk_window_move(GTK_WINDOW(win->window), conv_x, conv_y);
+                gtk_window_resize(GTK_WINDOW(win->window), conv_width, conv_height);
+        }
+}
+
 PidginWindow *
 pidgin_conv_window_new()
 {
@@ -8293,8 +8379,7 @@
 
 	/* Create the window. */
 	win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
-	gtk_window_set_default_size(GTK_WINDOW(win->window), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"),
-							     purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"));
+	pidgin_conv_restore_position(win);
 
 	if (available_list == NULL) {
 		create_icon_lists(win->window);
--- a/pidgin/gtkdialogs.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkdialogs.c	Mon Jul 30 00:05:02 2007 +0000
@@ -72,6 +72,7 @@
 	{"Daniel 'datallah' Atallah",	N_("developer"), NULL},
 	{"Ethan 'Paco-Paco' Blanton",	N_("developer"), NULL},
 	{"Thomas Butter",				N_("developer"), NULL},
+	{"Ka-Hing Cheung",				N_("developer"), NULL},
 	{"Sadrul Habib Chowdhury",		N_("developer"), NULL},
 	{"Mark 'KingAnt' Doliner",		N_("developer"), NULL},
 	{"Christian 'ChipX86' Hammond",	N_("developer & webmaster"), NULL},
@@ -94,7 +95,6 @@
 /* Order: Alphabetical by Last Name */
 static struct developer patch_writers[] = {
 	{"John 'rekkanoryo' Bailey",	NULL,	NULL},
-	{"Ka-Hing 'javabsp' Cheung",	NULL,	NULL},
 	{"Felipe 'shx' Contreras",		NULL,	NULL},
 	{"Decklin Foster",				NULL,	NULL},
 	{"Peter 'Bleeter' Lawler",      NULL,   NULL},
--- a/pidgin/gtkimhtml.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkimhtml.c	Mon Jul 30 00:05:02 2007 +0000
@@ -348,13 +348,28 @@
 	}
 }
 
-static void gtk_size_allocate_cb(GtkIMHtml *widget, GtkAllocation *alloc, gpointer user_data)
+static void (*parent_size_allocate)(GtkWidget *widget, GtkAllocation *alloc);
+
+static void gtk_imhtml_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
 {
+	GtkIMHtml *imhtml = GTK_IMHTML(widget);
 	GdkRectangle rect;
 	int xminus;
+	int height = 0, y = 0;
+	GtkTextIter iter;
+	gboolean scroll = TRUE;
+
+        gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
 
 	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect);
-	if(widget->old_rect.width != rect.width || widget->old_rect.height != rect.height){
+        gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
+
+	if(((y + height) - (rect.y + rect.height)) > height
+           && gtk_text_buffer_get_char_count(imhtml->text_buffer)){
+                scroll = FALSE;
+        }
+
+	if(imhtml->old_rect.width != rect.width || imhtml->old_rect.height != rect.height){
 		GList *iter = GTK_IMHTML(widget)->scalables;
 
 		xminus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(widget)) +
@@ -369,8 +384,12 @@
 		}
 	}
 
-	widget->old_rect = rect;
-	return;
+	imhtml->old_rect = rect;
+	parent_size_allocate(widget, alloc);
+	
+	/* Don't scroll here if we're in the middle of a smooth scroll */
+	if (scroll && imhtml->scroll_time == NULL)
+		gtk_imhtml_scroll_to_end(imhtml, FALSE);	
 }
 
 static gint
@@ -1345,6 +1364,9 @@
 	gobject_class->finalize = gtk_imhtml_finalize;
 	widget_class->drag_motion = gtk_text_view_drag_motion;
 	widget_class->expose_event = gtk_imhtml_expose_event;
+	parent_size_allocate = widget_class->size_allocate;
+	widget_class->size_allocate = gtk_imhtml_size_allocate;
+
 	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color",
 	                                        _("Hyperlink color"),
 	                                        _("Color to draw hyperlinks."),
@@ -1409,7 +1431,6 @@
 			g_free, (GDestroyNotify)gtk_smiley_tree_destroy);
 	imhtml->default_smilies = gtk_smiley_tree_new();
 
-	g_signal_connect(G_OBJECT(imhtml), "size-allocate", G_CALLBACK(gtk_size_allocate_cb), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "enter-notify-event", G_CALLBACK(gtk_enter_event_notify), NULL);
--- a/pidgin/gtkpounce.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkpounce.c	Mon Jul 30 00:05:02 2007 +0000
@@ -565,7 +565,7 @@
 
 	dialog->buddy_entry = gtk_entry_new();
 
-	pidgin_setup_screenname_autocomplete(dialog->buddy_entry, dialog->account_menu, FALSE);
+	pidgin_setup_screenname_autocomplete_with_filter(dialog->buddy_entry, dialog->account_menu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(FALSE));
 
 	gtk_box_pack_start(GTK_BOX(hbox), dialog->buddy_entry, TRUE, TRUE, 0);
 	gtk_widget_show(dialog->buddy_entry);
--- a/pidgin/gtkprefs.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkprefs.c	Mon Jul 30 00:05:02 2007 +0000
@@ -994,7 +994,7 @@
 	pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
 				PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
 
-	iconpref1 = pidgin_prefs_checkbox(_("Show Buddy _Details"),
+	iconpref1 = pidgin_prefs_checkbox(strchr(_("/Buddies/Show Buddy _Details")+1,'/')+1,
 			PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
 	iconpref2 = pidgin_prefs_checkbox(_("Enable buddy ic_on animation"),
 			PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons", vbox);
--- a/pidgin/gtkrequest.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkrequest.c	Mon Jul 30 00:05:02 2007 +0000
@@ -711,7 +711,7 @@
 					}
 				}
 			}
-			pidgin_setup_screenname_autocomplete(entry, optmenu, !strcmp(type_hint, "screenname-all"));
+			pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(!strcmp(type_hint, "screenname-all")));
 		}
 	}
 }
--- a/pidgin/gtkstatusbox.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Mon Jul 30 00:05:02 2007 +0000
@@ -1016,8 +1016,8 @@
 	}
 }
 
-static gboolean
-pidgin_status_box_regenerate_real(PidginStatusBox *status_box)
+static void
+pidgin_status_box_regenerate(PidginStatusBox *status_box)
 {
 	GdkPixbuf *pixbuf, *pixbuf2, *pixbuf3, *pixbuf4, *pixbuf5;
 	GtkIconSize icon_size;
@@ -1081,21 +1081,11 @@
 	}
 	gtk_tree_view_set_model(GTK_TREE_VIEW(status_box->tree_view), GTK_TREE_MODEL(status_box->dropdown_store));
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN);
-
-	return FALSE;
-}
-
-static void
-pidgin_status_box_regenerate(PidginStatusBox *status_box)
-{
-	/* we have to do this in a timeout, so we avoid recursing
-	 * to infinity (and beyond) */
-	purple_timeout_add(0, (GSourceFunc)pidgin_status_box_regenerate_real, status_box);
 }
 
 static gboolean combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
 {
-	pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
+  	pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
 	return TRUE;
 }
 
--- a/pidgin/gtkutils.c	Mon Jul 30 00:01:08 2007 +0000
+++ b/pidgin/gtkutils.c	Mon Jul 30 00:05:02 2007 +0000
@@ -137,6 +137,10 @@
 	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
 	if (title)
 		gtk_window_set_title(wnd, title);
+#ifdef _WIN32
+	else
+		gtk_window_set_title(wnd, PIDGIN_ALERT_TITLE);
+#endif
 	gtk_container_set_border_width(GTK_CONTAINER(wnd), border_width);
 	if (role)
 		gtk_window_set_role(wnd, role);
@@ -1743,16 +1747,22 @@
 # define NEW_STYLE_COMPLETION
 #endif
 
-#ifndef NEW_STYLE_COMPLETION
 typedef struct
 {
+	GtkWidget *entry;
+	GtkWidget *accountopt;
+
+	PidginFilterBuddyCompletionEntryFunc filter_func;
+	gpointer filter_func_user_data;
+
+#ifdef NEW_STYLE_COMPLETION
+	GtkListStore *store;
+#else
 	GCompletion *completion;
-
 	gboolean completion_started;
-	gboolean all;
-
+	GList *log_items;
+#endif /* NEW_STYLE_COMPLETION */
 } PidginCompletionData;
-#endif
 
 #ifndef NEW_STYLE_COMPLETION
 static gboolean
@@ -1870,15 +1880,15 @@
 }
 
 static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion,
-		GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data)
+		GtkTreeModel *model, GtkTreeIter *iter, PidginCompletionData *data)
 {
 	GValue val;
-	GtkWidget *optmenu = user_data[1];
+	GtkWidget *optmenu = data->accountopt;
 	PurpleAccount *account;
 
 	val.g_type = 0;
 	gtk_tree_model_get_value(model, iter, 1, &val);
-	gtk_entry_set_text(GTK_ENTRY(user_data[0]), g_value_get_string(&val));
+	gtk_entry_set_text(GTK_ENTRY(data->entry), g_value_get_string(&val));
 	g_value_unset(&val);
 
 	gtk_tree_model_get_value(model, iter, 4, &val);
@@ -1971,83 +1981,47 @@
 }
 #endif /* NEW_STYLE_COMPLETION */
 
-static void get_log_set_name(PurpleLogSet *set, gpointer value, gpointer **set_hash_data)
+static void get_log_set_name(PurpleLogSet *set, gpointer value, PidginCompletionData *data)
 {
-	/* 1. Don't show buddies because we will have gotten them already.
-	 * 2. Only show those with non-NULL accounts that are currently connected.
-	 * 3. The boxes that use this autocomplete code handle only IMs. */
-	if (!set->buddy &&
-	    (GPOINTER_TO_INT(set_hash_data[1]) ||
-	     (set->account != NULL && purple_account_is_connected(set->account))) &&
-		set->type == PURPLE_LOG_IM) {
+	PidginFilterBuddyCompletionEntryFunc filter_func = data->filter_func;
+	gpointer user_data = data->filter_func_user_data;
+
+ 	/* 1. Don't show buddies because we will have gotten them already.
+ 	 * 2. The boxes that use this autocomplete code handle only IMs. */
+	if (!set->buddy && set->type == PURPLE_LOG_IM) {
+		PidginBuddyCompletionEntry entry;
+		entry.is_buddy = FALSE;
+		entry.entry.logged_buddy = set;
+
+		if (filter_func(&entry, user_data)) {
 #ifdef NEW_STYLE_COMPLETION
-			add_screenname_autocomplete_entry((GtkListStore *)set_hash_data[0],
-											  NULL, NULL, set->account, set->name);
+			add_screenname_autocomplete_entry(data->store,
+												NULL, NULL, set->account, set->name);
 #else
-			GList **items = ((GList **)set_hash_data[0]);
 			/* Steal the name for the GCompletion. */
-			*items = g_list_append(*items, set->name);
+			data->log_items = g_list_append(data->log_items, set->name);
 			set->name = set->normalized_name = NULL;
 #endif /* NEW_STYLE_COMPLETION */
+		}
 	}
 }
 
-#ifdef NEW_STYLE_COMPLETION
-static void
-add_completion_list(GtkListStore *store)
-{
-	PurpleBlistNode *gnode, *cnode, *bnode;
-	gboolean all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(store), "screenname-all"));
-	GHashTable *sets;
-	gpointer set_hash_data[] = {store, GINT_TO_POINTER(all)};
-
-	gtk_list_store_clear(store);
-
-	for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next)
-	{
-		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-
-		for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
-		{
-			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-				continue;
-
-			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
-			{
-				PurpleBuddy *buddy = (PurpleBuddy *)bnode;
-
-				if (!all && !purple_account_is_connected(buddy->account))
-					continue;
-
-				add_screenname_autocomplete_entry(store,
-												  ((PurpleContact *)cnode)->alias,
-												  purple_buddy_get_contact_alias(buddy),
-												  buddy->account,
-												  buddy->name
-												 );
-			}
-		}
-	}
-
-	sets = purple_log_get_log_sets();
-	g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data);
-	g_hash_table_destroy(sets);
-}
-#else
 static void
 add_completion_list(PidginCompletionData *data)
 {
 	PurpleBlistNode *gnode, *cnode, *bnode;
-	GCompletion *completion;
-	GList *item = g_list_append(NULL, NULL);
+	PidginFilterBuddyCompletionEntryFunc filter_func = data->filter_func;
+	gpointer user_data = data->filter_func_user_data;
 	GHashTable *sets;
-	gpointer set_hash_data[2];
-
-	completion = data->completion;
-
-	g_list_foreach(completion->items, (GFunc)g_free, NULL);
-	g_completion_clear_items(completion);
+
+#ifdef NEW_STYLE_COMPLETION
+	gtk_list_store_clear(data->store);
+#else
+	GList *item = g_list_append(NULL, NULL);
+
+	g_list_foreach(data->completion->items, (GFunc)g_free, NULL);
+	g_completion_clear_items(data->completion);
+#endif /* NEW_STYLE_COMPLETION */
 
 	for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next)
 	{
@@ -2061,28 +2035,41 @@
 
 			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
 			{
-				PurpleBuddy *buddy = (PurpleBuddy *)bnode;
-
-				if (!data->all && !purple_account_is_connected(buddy->account))
-					continue;
-
+				PidginBuddyCompletionEntry entry;
+				entry.is_buddy = TRUE;
+				entry.entry.buddy = (PurpleBuddy *) bnode;
+
+				if (filter_func(&entry, user_data)) {
+#ifdef NEW_STYLE_COMPLETION
+					add_screenname_autocomplete_entry(data->store,
+														((PurpleContact *)cnode)->alias,
+														purple_buddy_get_contact_alias(entry.entry.buddy),
+														entry.entry.buddy->account,
+														entry.entry.buddy->name
+													 );
+				}
+#else
 				item->data = g_strdup(buddy->name);
 				g_completion_add_items(data->completion, item);
+#endif /* NEW_STYLE_COMPLETION */
 			}
 		}
 	}
+
+#ifndef NEW_STYLE_COMPLETION
 	g_list_free(item);
+	data->log_items = NULL;
+#endif /* NEW_STYLE_COMPLETION */
 
 	sets = purple_log_get_log_sets();
-	item = NULL;
-	set_hash_data[0] = &item;
-	set_hash_data[1] = GINT_TO_POINTER(data->all);
-	g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data);
+	g_hash_table_foreach(sets, (GHFunc)get_log_set_name, data);
 	g_hash_table_destroy(sets);
-	g_completion_add_items(data->completion, item);
-	g_list_free(item);
+
+#ifndef NEW_STYLE_COMPLETION
+	g_completion_add_items(data->completion, data->log_items);
+	g_list_free(data->log_items);
+#endif /* NEW_STYLE_COMPLETION */
 }
-#endif
 
 static void
 screenname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data)
@@ -2097,23 +2084,34 @@
 	add_completion_list(data);
 }
 
+
 void
-pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, gboolean all)
+pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *accountopt, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
 {
-	gpointer cb_data = NULL;
+	PidginCompletionData *data;
 
 #ifdef NEW_STYLE_COMPLETION
 	/* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname,
 	 * the UTF-8 normalized & casefolded value for comparison, and the account. */
-	GtkListStore *store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+	GtkListStore *store;
 
 	GtkEntryCompletion *completion;
-	gpointer *data;
-
-	g_object_set_data(G_OBJECT(store), "screenname-all", GINT_TO_POINTER(all));
-	add_completion_list(store);
-
-	cb_data = store;
+
+	data = g_new0(PidginCompletionData, 1);
+	store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+
+	data->entry = entry;
+	data->accountopt = accountopt;
+	if (filter_func == NULL) {
+		data->filter_func = pidgin_screenname_autocomplete_default_filter;
+		data->filter_func_user_data = NULL;
+	} else {
+		data->filter_func = filter_func;
+		data->filter_func_user_data = user_data;
+	}
+	data->store = store;
+
+	add_completion_list(data);
 
 	/* Sort the completion list by screenname. */
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
@@ -2122,9 +2120,6 @@
 	completion = gtk_entry_completion_new();
 	gtk_entry_completion_set_match_func(completion, screenname_completion_match_func, NULL, NULL);
 
-	data = g_new0(gpointer, 2);
-	data[0] = entry;
-	data[1] = accountopt;
 	g_signal_connect(G_OBJECT(completion), "match-selected",
 		G_CALLBACK(screenname_completion_match_selected_cb), data);
 
@@ -2137,18 +2132,25 @@
 	gtk_entry_completion_set_text_column(completion, 0);
 
 #else /* !NEW_STYLE_COMPLETION */
-	PidginCompletionData *data;
 
 	data = g_new0(PidginCompletionData, 1);
 
+	data->entry = entry;
+	data->accountopt = accountopt;
+	if (filter_func == NULL) {
+		data->filter_func = pidgin_screenname_autocomplete_default_filter;
+		data->filter_func_user_data = NULL;
+	} else {
+		data->filter_func = filter_func;
+		data->filter_func_user_data = user_data;
+	}
 	data->completion = g_completion_new(NULL);
-	data->all = all;
+	data->completion_started = FALSE;
+
+	add_completion_list(data);
 
 	g_completion_set_compare(data->completion, g_ascii_strncasecmp);
 
-	add_completion_list(data);
-	cb_data = data;
-
 	g_signal_connect(G_OBJECT(entry), "event",
 					 G_CALLBACK(completion_entry_event), data);
 	g_signal_connect(G_OBJECT(entry), "destroy",
@@ -2156,22 +2158,37 @@
 
 #endif /* !NEW_STYLE_COMPLETION */
 
-	if (!all)
-	{
-		purple_signal_connect(purple_connections_get_handle(), "signed-on", entry,
-							PURPLE_CALLBACK(repopulate_autocomplete), cb_data);
-		purple_signal_connect(purple_connections_get_handle(), "signed-off", entry,
-							PURPLE_CALLBACK(repopulate_autocomplete), cb_data);
-	}
+	purple_signal_connect(purple_connections_get_handle(), "signed-on", entry,
+						PURPLE_CALLBACK(repopulate_autocomplete), data);
+	purple_signal_connect(purple_connections_get_handle(), "signed-off", entry,
+						PURPLE_CALLBACK(repopulate_autocomplete), data);
 
 	purple_signal_connect(purple_accounts_get_handle(), "account-added", entry,
-						PURPLE_CALLBACK(repopulate_autocomplete), cb_data);
+						PURPLE_CALLBACK(repopulate_autocomplete), data);
 	purple_signal_connect(purple_accounts_get_handle(), "account-removed", entry,
-						PURPLE_CALLBACK(repopulate_autocomplete), cb_data);
+						PURPLE_CALLBACK(repopulate_autocomplete), data);
 
 	g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(screenname_autocomplete_destroyed_cb), data);
 }
 
+gboolean