changeset 22795:42dfa1139b5c

merge of '21d37789f674809d615fc7676a092f1da438b0f5' and 'aafbbc317ab5f69e31e0bb6cabc264bb70745ef2'
author Evan Schoenberg <evan.s@dreskin.net>
date Fri, 02 May 2008 22:51:14 +0000
parents d5b3afea8764 (current diff) 0d7ceae153bd (diff)
children 5e6b42e77fb7
files libpurple/protocols/jabber/auth.c
diffstat 44 files changed, 518 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Fri May 02 22:50:13 2008 +0000
+++ b/COPYRIGHT	Fri May 02 22:51:14 2008 +0000
@@ -263,6 +263,7 @@
 John Moody
 Tim Mooney
 Sergio Moretto
+Andrei Mozzhuhin
 Christian Muise
 Richard Nelson
 Dennis Nezic
--- a/ChangeLog	Fri May 02 22:50:13 2008 +0000
+++ b/ChangeLog	Fri May 02 22:51:14 2008 +0000
@@ -14,8 +14,16 @@
 	* Added a plugin (not built by default) which adds a Send button back
 	  to the conversation window. People without physical keyboards have a
 	  hard time with the lack of the button.
-	* Clicking on the buddyicon in the conversation window toggles the size of
-	  the icon between small and large.
+	* Clicking on the buddyicon in the conversation window toggles the
+	  size of the icon between small and large.
+	* The settings of a chat (e.g. Handle in an XMPP chat, or Exchange in
+	  an AIM chat) can be edited from its context menu in the buddy list.
+	* Add a "Present conversation window" preference to the Message
+	  Notification plugin, the "Raise conversation window" option does not
+	  unminimize windows or draw attention to them when they are on other
+	  workspaces the "Present" option should.
+	* Add a preference to set Escape as the keyboard shortcut for closing
+	  the conversation window.
 
 	General:
 	* The configure script now dies on more absent dependencies.  The
@@ -28,6 +36,9 @@
 	* The Contact Availability Prediction plugin must now be explicitly
 	  enabled.  Use the --enable-cap argument to configure to enable it.
 
+	Finch:
+	* New default binding ctrl+x to open context menus.
+
 version 2.4.1 (03/31/2008):
 	http://developer.pidgin.im/query?status=closed&milestone=2.4.1
 
--- a/ChangeLog.API	Fri May 02 22:50:13 2008 +0000
+++ b/ChangeLog.API	Fri May 02 22:51:14 2008 +0000
@@ -1,10 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.x.x:
-    libpurple:
-        Added:
-        * serv_got_join_chat_failed
-        * chat-join-failed signal (see conversation-signals.dox)
 	perl:
 		Added:
 		* Purple::Prefs::get_children_names.
--- a/doc/conversation-signals.dox	Fri May 02 22:50:13 2008 +0000
+++ b/doc/conversation-signals.dox	Fri May 02 22:51:14 2008 +0000
@@ -27,7 +27,6 @@
   @signal chat-invited-user
   @signal chat-invited
   @signal chat-joined
-  @signal chat-join-failed
   @signal chat-left
   @signal chat-topic-changed
   @signal conversation-extended-menu
@@ -401,16 +400,6 @@
   @param conv The conversation that joined the chat room.
  @endsignaldef
 
- @signaldef chat-join-failed
-  @signalproto
-void (*chat_join_failed)(PurpleConnection *gc, const char *name);
-  @endsignalproto
-  @signaldesc
-   Emitted when an account fails to join a chat room
-  @param gc The PurpleConnection of the account which failed to join the chat.
-  @param name The name of the chat.
- @endsignaldef
- 
  @signaldef chat-left
   @signalproto
 void (*chat_left)(PurpleConversation *conv);
--- a/finch/gntaccount.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/gntaccount.c	Fri May 02 22:51:14 2008 +0000
@@ -122,7 +122,7 @@
 	if (value == NULL || *value == '\0')
 	{
 		purple_notify_error(NULL, _("Error"), _("Account was not added"),
-				_("Screenname of an account must be non-empty."));
+				_("Username of an account must be non-empty."));
 		return;
 	}
 	
@@ -526,7 +526,7 @@
 	gnt_box_add_widget(GNT_BOX(window), hbox);
 
 	dialog->screenname = entry = gnt_entry_new(NULL);
-	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Screen name:")));
+	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Username:")));
 	gnt_box_add_widget(GNT_BOX(hbox), entry);
 
 	/* User splits */
--- a/finch/gntblist.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/gntblist.c	Fri May 02 22:51:14 2008 +0000
@@ -380,6 +380,27 @@
 		fnode = FINCH_GET_DATA(node);
 		if (fnode && fnode->signed_timer)
 			flag |= GNT_TEXT_FLAG_BLINK;
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		/* If the node is collapsed, then check to see if any of the priority buddies of
+		 * any of the contacts within this group recently signed on/off, and set the blink
+		 * flag appropriately. */
+		/* XXX: Refs #5444 */
+		/* XXX: there's no way I can ask if the node is expanded or not? *sigh*
+		 * API addition would be necessary */
+#if 0
+		if (!gnt_tree_get_expanded(GNT_TREE(ggblist->tree), node)) {
+			for (node = purple_blist_node_get_first_child(node); node;
+					node = purple_blist_node_get_sibling_next(node)) {
+				PurpleBlistNode *pnode;
+				pnode = purple_contact_get_priority_buddy((PurpleContact*)node);
+				fnode = FINCH_GET_DATA(node);
+				if (fnode && fnode->signed_timer) {
+					flag |= GNT_TEXT_FLAG_BLINK;
+					break;
+				}
+			}
+		}
+#endif
 	}
 
 	return flag;
@@ -560,7 +581,7 @@
 	PurpleBuddy *buddy;
 
 	if (!username)
-		error = _("You must provide a screename for the buddy.");
+		error = _("You must provide a username for the buddy.");
 	else if (!group)
 		error = _("You must provide a group.");
 	else if (!account)
@@ -598,7 +619,7 @@
 
 	purple_request_fields_add_group(fields, group);
 
-	field = purple_request_field_string_new("screenname", _("Screen Name"), username, FALSE);
+	field = purple_request_field_string_new("screenname", _("Username"), username, FALSE);
 	purple_request_field_group_add_field(group, field);
 
 	field = purple_request_field_string_new("alias", _("Alias (optional)"), alias, FALSE);
@@ -1034,7 +1055,11 @@
 			else
 				val = g_strdup(purple_request_field_string_get_value(field));
 
-			g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val);  /* val should not be free'd */
+			if (!val) {
+				g_hash_table_remove(purple_chat_get_components(chat), id);
+			} else {
+				g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val);  /* val should not be free'd */
+			}
 		}
 	}
 }
@@ -1065,8 +1090,13 @@
 		} else {
 			field = purple_request_field_string_new(pce->identifier, pce->label,
 					g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
+			if (pce->secret)
+				purple_request_field_string_set_masked(field, TRUE);
 		}
 
+		if (pce->required)
+			purple_request_field_set_required(field, TRUE);
+
 		purple_request_field_group_add_field(group, field);
 		g_free(pce);
 	}
@@ -2597,7 +2627,7 @@
 
 	purple_request_fields(purple_get_blist(), _("Block/Unblock"),
 						NULL,
-						_("Please enter the screen name or alias of the person "
+						_("Please enter the username or alias of the person "
 						  "you would like to Block/Unblock."),
 						fields,
 						_("OK"), G_CALLBACK(block_select_cb),
@@ -2648,7 +2678,7 @@
 
 	purple_request_fields(purple_get_blist(), _("New Instant Message"),
 						NULL,
-						_("Please enter the screen name or alias of the person "
+						_("Please enter the username or alias of the person "
 						  "you would like to IM."),
 						fields,
 						_("OK"), G_CALLBACK(send_im_select_cb),
--- a/finch/gntlog.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/gntlog.c	Fri May 02 22:51:14 2008 +0000
@@ -139,7 +139,8 @@
 
 }
 
-static void destroy_cb(GntWidget *w, struct log_viewer_hash_t *ht) {
+static void destroy_cb(GntWidget *w, struct log_viewer_hash_t *ht)
+{
 	FinchLogViewer *lv = syslog_viewer;
 
 	if (ht != NULL) {
@@ -162,12 +163,12 @@
 	gnt_widget_destroy(w);
 }
 
-static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer) {
+static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer)
+{
 	GntTree *tree = GNT_TREE(w);
 	PurpleLog *log = NULL;
 	PurpleLogReadFlags flags;
 	char *read = NULL, *strip, *newline;
-	int h;
 
 	if (!viewer->search && !gnt_tree_get_parent_key(tree, new))
 		return;
@@ -204,8 +205,6 @@
 
 	gnt_text_view_clear(GNT_TEXT_VIEW(viewer->text));
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(viewer->text), strip, GNT_TEXT_FLAG_NORMAL);
-	gnt_widget_get_size(viewer->text, NULL, &h);
-	gnt_text_view_scroll(GNT_TEXT_VIEW(viewer->text), h - 2);
 	g_free(read);
 	g_free(strip);
 }
@@ -321,6 +320,7 @@
 	/* Viewer ************/
 	lv->text = gnt_text_view_new();
 	gnt_box_add_widget(GNT_BOX(hbox), lv->text);
+	gnt_text_view_set_flag(GNT_TEXT_VIEW(lv->text), GNT_TEXT_VIEW_TOP_ALIGN);
 
 	hbox = gnt_hbox_new(FALSE);
 	gnt_box_add_widget(GNT_BOX(vbox), hbox);
--- a/finch/gntrequest.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/gntrequest.c	Fri May 02 22:51:14 2008 +0000
@@ -295,8 +295,7 @@
 	 * updating the fields at the end like here, it updates the appropriate field
 	 * instantly whenever a change is made. That allows it to make sure the
 	 * 'required' fields are entered before the user can hit OK. It's not the case
-	 * here, althought it can be done. I am not honouring the 'required' fields
-	 * for the moment. */
+	 * here, althought it can be done. */
 	for (list = purple_request_fields_get_groups(fields); list; list = list->next)
 	{
 		PurpleRequestFieldGroup *group = list->data;
@@ -368,6 +367,15 @@
 		}
 	}
 
+	purple_notify_close_with_handle(button);
+
+	if (!purple_request_fields_all_required_filled(fields)) {
+		purple_notify_error(button, _("Error"),
+				_("You must fill all the required fields."),
+				_("The required fields are underlined."));
+		return;
+	}
+
 	if (callback)
 		callback(data, fields);
 
@@ -587,7 +595,11 @@
 
 			if (type != PURPLE_REQUEST_FIELD_BOOLEAN && label)
 			{
-				GntWidget *l = gnt_label_new(label);
+				GntWidget *l;
+				if (purple_request_field_is_required(field))
+					l = gnt_label_new_with_format(label, GNT_TEXT_FLAG_UNDERLINE);
+				else
+					l = gnt_label_new(label);
 				gnt_widget_set_size(l, 0, 1);
 				gnt_box_add_widget(GNT_BOX(hbox), l);
 			}
--- a/finch/gntsound.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/gntsound.c	Fri May 02 22:51:14 2008 +0000
@@ -103,7 +103,7 @@
 	{PURPLE_SOUND_CHAT_YOU_SAY, N_("You talk in chat"), "send_chat_msg", "send.wav", NULL},
 	{PURPLE_SOUND_CHAT_SAY,     N_("Others talk in chat"), "chat_msg_recv", "receive.wav", NULL},
 	{PURPLE_SOUND_POUNCE_DEFAULT, NULL, "pounce_default", "alert.wav", NULL},
-	{PURPLE_SOUND_CHAT_NICK,    N_("Someone says your screen name in chat"), "nick_said", "alert.wav", NULL}
+	{PURPLE_SOUND_CHAT_NICK,    N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL}
 };
 
 const char *
--- a/finch/libgnt/gntwidget.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/libgnt/gntwidget.c	Fri May 02 22:51:14 2008 +0000
@@ -257,6 +257,7 @@
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "context-menu", context_menu,
 				GNT_KEY_POPUP, NULL);
 	gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass), "context-menu", GNT_KEY_F11, NULL);
+	gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass), "context-menu", GNT_KEY_CTRL_X, NULL);
 
 	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
 	GNTDEBUG;
--- a/finch/libgnt/gntwm.c	Fri May 02 22:50:13 2008 +0000
+++ b/finch/libgnt/gntwm.c	Fri May 02 22:51:14 2008 +0000
@@ -1516,7 +1516,7 @@
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window,
 				"\033" "|", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start,
-				GNT_KEY_CTRL_G, NULL);
+				NULL, NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end,
 				"\033" GNT_KEY_CTRL_G, NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next-urgent", window_next_urgent,
--- a/libpurple/conversation.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/conversation.c	Fri May 02 22:51:14 2008 +0000
@@ -2368,12 +2368,6 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										PURPLE_SUBTYPE_CONVERSATION));
 
-	purple_signal_register(handle, "chat-join-failed",
-						   purple_marshal_VOID__POINTER_POINTER, NULL, 2,
-						   purple_value_new(PURPLE_TYPE_SUBTYPE,
-										PURPLE_SUBTYPE_CONNECTION),
-						   purple_value_new(PURPLE_TYPE_STRING));
-
 	purple_signal_register(handle, "chat-left",
 						 purple_marshal_VOID__POINTER, NULL, 1,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/idle.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/idle.c	Fri May 02 22:51:14 2008 +0000
@@ -252,7 +252,7 @@
 	PurpleAccount *account;
 
 	account = purple_connection_get_account(gc);
-	set_account_unidle(account);
+	idled_accts = g_list_remove(idled_accts, account);
 }
 
 static void
--- a/libpurple/plugins/newline.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/plugins/newline.c	Fri May 02 22:51:14 2008 +0000
@@ -102,11 +102,11 @@
 
 	"core-plugin_pack-newline",						/**< id				*/
 	N_("New Line"),									/**< name			*/
-	DISPLAY_VERSION,									/**< version		*/
+	DISPLAY_VERSION,								/**< version		*/
 	N_("Prepends a newline to displayed message."),	/**< summary		*/
 	N_("Prepends a newline to messages so that the "
 	   "rest of the message appears below the "
-	   "screen name in the conversation window."),	/**< description	*/
+	   "username in the conversation window."),		/**< description	*/
 	"Stu Tomlinson <stu@nosnilmot.com>",			/**< author			*/
 	PURPLE_WEBSITE,									/**< homepage		*/
 
--- a/libpurple/protocols/jabber/auth.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/jabber/auth.c	Fri May 02 22:51:14 2008 +0000
@@ -54,6 +54,11 @@
 				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
 				_("Server requires TLS/SSL for login.  No TLS/SSL support found."));
 			return TRUE;
+		} else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE)) {
+			purple_connection_error_reason (js->gc,
+				 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("You require encryption, but no TLS/SSL support found."));
+			return TRUE;
 		}
 	}
 
--- a/libpurple/protocols/jabber/buddy.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Fri May 02 22:51:14 2008 +0000
@@ -56,8 +56,7 @@
 {
 	g_return_if_fail(jb != NULL);
 
-	if(jb->error_msg)
-		g_free(jb->error_msg);
+	g_free(jb->error_msg);
 	while(jb->resources)
 		jabber_buddy_resource_free(jb->resources->data);
 
@@ -155,12 +154,8 @@
 	}
 	jbr->priority = priority;
 	jbr->state = state;
-	if(jbr->status)
-		g_free(jbr->status);
-	if (status)
-		jbr->status = g_markup_escape_text(status, -1);
-	else
-		jbr->status = NULL;
+	g_free(jbr->status);
+	jbr->status = status != NULL ? g_markup_escape_text(status, -1) : NULL;
 
 	return jbr;
 }
@@ -502,6 +497,11 @@
 	if(((JabberStream*)gc->proto_data)->pep) {
 		/* XEP-0084: User Avatars */
 		if(img) {
+			/*
+			 * TODO: This is pretty gross.  The Jabber PRPL really shouldn't
+			 *       do voodoo to try to determine the image type, height
+			 *       and width.
+			 */
 			/* A PNG header, including the IHDR, but nothing else */
 			const struct {
 				guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */
--- a/libpurple/protocols/jabber/chat.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/jabber/chat.c	Fri May 02 22:51:14 2008 +0000
@@ -222,39 +222,33 @@
 	if(!handle)
 		handle = js->user->node;
 
-	tmp = g_strdup_printf("%s@%s", room, server);
-	room_jid = g_strdup(jabber_normalize(NULL, tmp));
-	g_free(tmp);
-
 	if(!jabber_nodeprep_validate(room)) {
 		char *buf = g_strdup_printf(_("%s is not a valid room name"), room);
 		purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"),
 				buf);
-		serv_got_join_chat_failed(gc, room_jid);
-		g_free(room_jid);
 		g_free(buf);
 		return;
 	} else if(!jabber_nameprep_validate(server)) {
 		char *buf = g_strdup_printf(_("%s is not a valid server name"), server);
 		purple_notify_error(gc, _("Invalid Server Name"),
 				_("Invalid Server Name"), buf);
-		serv_got_join_chat_failed(gc, room_jid);
-		g_free(room_jid);
 		g_free(buf);
 		return;
 	} else if(!jabber_resourceprep_validate(handle)) {
 		char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle);
 		purple_notify_error(gc, _("Invalid Room Handle"),
 				_("Invalid Room Handle"), buf);
-		serv_got_join_chat_failed(gc, room_jid);
 		g_free(buf);
-		g_free(room_jid);
 		return;
 	}
 
 	if(jabber_chat_find(js, room, server))
 		return;
 
+	tmp = g_strdup_printf("%s@%s", room, server);
+	room_jid = g_strdup(jabber_normalize(NULL, tmp));
+	g_free(tmp);
+
 	chat = g_new0(JabberChat, 1);
 	chat->js = gc->proto_data;
 
--- a/libpurple/protocols/jabber/presence.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/jabber/presence.c	Fri May 02 22:51:14 2008 +0000
@@ -369,17 +369,29 @@
 
 typedef struct _JabberPresenceCapabilities {
 	JabberStream *js;
-	JabberBuddyResource *jbr;
+	JabberBuddy *jb;
 	char *from;
 } JabberPresenceCapabilities;
 
 static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
 	JabberPresenceCapabilities *userdata = user_data;
+	JabberID *jid;
+	JabberBuddyResource *jbr;
 	GList *iter;
 
-	if(userdata->jbr->caps)
-		jabber_caps_free_clientinfo(userdata->jbr->caps);
-	userdata->jbr->caps = info;
+	jid = jabber_id_new(userdata->from);
+	jbr = jabber_buddy_find_resource(userdata->jb, jid->resource);
+	jabber_id_free(jid);
+
+	if(!jbr) {
+		g_free(userdata->from);
+		g_free(userdata);
+		return;
+	}
+
+	if(jbr->caps)
+		jabber_caps_free_clientinfo(jbr->caps);
+	jbr->caps = info;
 
 	if (info) {
 		for(iter = info->features; iter; iter = g_list_next(iter)) {
@@ -575,7 +587,6 @@
 					serv_got_chat_left(js->gc, chat->id);
 			} else {
 				title = g_strdup_printf(_("Error joining chat %s"), from);
-				serv_got_join_chat_failed(js->gc, room_jid);
 			}
 			purple_notify_error(js->gc, title, title, msg);
 			g_free(title);
@@ -742,7 +753,7 @@
 				if(node && ver) {
 					JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
 					userdata->js = js;
-					userdata->jbr = jbr;
+					userdata->jb = jb;
 					userdata->from = g_strdup(from);
 					jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata);
 				}
--- a/libpurple/protocols/jabber/usermood.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Fri May 02 22:51:14 2008 +0000
@@ -147,7 +147,7 @@
 
 static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) {
 	JabberStream *js;
-	int max_mood_idx;
+	const int max_mood_idx = sizeof(moodstrings) / sizeof(moodstrings[0]) - 1;
 	int selected_mood = purple_request_fields_get_choice(fields, "mood");
 
 	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
@@ -157,9 +157,6 @@
 
 	js = gc->proto_data;
 
-	/* This is ugly, but protects us from unexpected values. */
-	for (max_mood_idx = 0; moodstrings[max_mood_idx]; max_mood_idx++);
-
 	if (selected_mood < 0 || selected_mood >= max_mood_idx) {
 		purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood);
 		return;
--- a/libpurple/protocols/msn/error.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/msn/error.c	Fri May 02 22:51:14 2008 +0000
@@ -56,7 +56,7 @@
 			g_snprintf(msg, sizeof(msg), _("Already logged in"));
 			break;
 		case 208:
-			g_snprintf(msg, sizeof(msg), _("Invalid screen name"));
+			g_snprintf(msg, sizeof(msg), _("Invalid username"));
 			break;
 		case 209:
 			g_snprintf(msg, sizeof(msg), _("Invalid friendly name"));
--- a/libpurple/protocols/msn/state.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/msn/state.c	Fri May 02 22:51:14 2008 +0000
@@ -87,7 +87,8 @@
 msn_parse_currentmedia(const char *cmedia, CurrentMedia *media)
 {
 	char **cmedia_array;
-	int strings;
+	int strings = 0;
+	gboolean parsed = FALSE;
 
 	if ((cmedia == NULL) || (*cmedia == '\0')) {
 		purple_debug_info("msn", "No currentmedia string\n");
@@ -108,31 +109,39 @@
 	 * 6: Album
 	 * 7: ?
 	 */
-	strings = 0;
+#if GLIB_CHECK_VERSION(2,6,0)
+	strings  = g_strv_length(cmedia_array);
+#else
 	while (cmedia_array[++strings] != NULL);
+#endif
+
+	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
+		parsed = TRUE;
 
-	if (strings < 4)
-		return FALSE;
-	if (strcmp(cmedia_array[2], "1"))
-		return FALSE;
+		g_free(media->title);
+		if (strings == 4) {
+			media->title = g_strdup(cmedia_array[3]);
+		} else {
+			media->title = g_strdup(cmedia_array[4]);
+		}
 
-	if (strings == 4) {
-		media->title = g_strdup(cmedia_array[3]);
-	} else {
-		media->title = g_strdup(cmedia_array[4]);
+		g_free(media->artist);
+		if (strings > 5)
+			media->artist = g_strdup(cmedia_array[5]);
+		else
+			media->artist = NULL;
+
+		g_free(media->album);
+		if (strings > 6)
+			media->album = g_strdup(cmedia_array[6]);
+		else
+			media->album = NULL;
+
 	}
 
-	if (strings > 5)
-		media->artist = g_strdup(cmedia_array[5]);
-	else
-		media->artist = NULL;
+	g_strfreev(cmedia_array);
 
-	if (strings > 6)
-		media->album = g_strdup(cmedia_array[6]);
-	else
-		media->album = NULL;
-
-	return TRUE;
+	return parsed;
 }
 
 /* get the CurrentMedia info from the XML string */
--- a/libpurple/protocols/msn/userlist.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/msn/userlist.c	Fri May 02 22:51:14 2008 +0000
@@ -714,7 +714,7 @@
 		char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
 		
 		purple_notify_error(NULL, NULL, str,
-				  _("The screen name specified is invalid."));
+				  _("The username specified is invalid."));
 		g_free(str);
 
 		return;
--- a/libpurple/protocols/msnp9/error.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/msnp9/error.c	Fri May 02 22:51:14 2008 +0000
@@ -56,7 +56,7 @@
 			g_snprintf(msg, sizeof(msg), _("Already logged in"));
 			break;
 		case 208:
-			g_snprintf(msg, sizeof(msg), _("Invalid screen name"));
+			g_snprintf(msg, sizeof(msg), _("Invalid username"));
 			break;
 		case 209:
 			g_snprintf(msg, sizeof(msg), _("Invalid friendly name"));
--- a/libpurple/protocols/msnp9/userlist.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/msnp9/userlist.c	Fri May 02 22:51:14 2008 +0000
@@ -652,7 +652,7 @@
 		{
 			char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
 			purple_notify_error(NULL, NULL, str,
-							  _("The screen name specified is invalid."));
+							  _("The username specified is invalid."));
 			g_free(str);
 		}
 
--- a/libpurple/protocols/novell/nmuser.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/novell/nmuser.c	Fri May 02 22:51:14 2008 +0000
@@ -1919,10 +1919,10 @@
 
 		case NMERR_AUTHENTICATION_FAILED:
 		case NMERR_CREDENTIALS_MISSING:
-			return _("Incorrect screen name or password");
+			return _("Incorrect username or password");
 
 		case NMERR_HOST_NOT_FOUND:
-			return _("Could not recognize the host of the screen name you entered");
+			return _("Could not recognize the host of the username you entered");
 
 		case NMERR_ACCESS_DENIED:
 			return _("Your account has been disabled because too many incorrect passwords were entered");
@@ -1935,7 +1935,7 @@
 			return _("You have reached your limit for the number of contacts allowed");
 
 		case NMERR_OBJECT_NOT_FOUND:
-			return _("You have entered an incorrect screen name");
+			return _("You have entered an incorrect username");
 
 		case NMERR_DIRECTORY_UPDATE:
 			return _("An error occurred while updating the directory");
--- a/libpurple/protocols/oscar/misc.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/oscar/misc.c	Fri May 02 22:51:14 2008 +0000
@@ -39,7 +39,6 @@
 void
 aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
 {
-	FlapFrame *frame;
 	aim_snacid_t snacid = 0x00000000;
 
 	flap_connection_send_snac(od, conn, family, subtype, 0x0000, snacid, NULL);
@@ -48,7 +47,6 @@
 void
 aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
 {
-	FlapFrame *frame;
 	aim_snacid_t snacid;
 
 	snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
--- a/libpurple/protocols/simple/simple.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/simple/simple.c	Fri May 02 22:51:14 2008 +0000
@@ -1890,7 +1890,7 @@
 	if (strpbrk(username, " \t\v\r\n") != NULL) {
 		purple_connection_error_reason(gc,
 			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
-			_("SIP screen names may not contain whitespaces or @ symbols"));
+			_("SIP usernames may not contain whitespaces or @ symbols"));
 		return;
 	}
 
--- a/libpurple/protocols/toc/toc.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/toc/toc.c	Fri May 02 22:51:14 2008 +0000
@@ -546,7 +546,7 @@
 			g_snprintf(buf, sizeof(buf), _("Failure unknown: %s."), w);
 			break;
 		case 980:
-			g_snprintf(buf, sizeof(buf), _("Incorrect screen name or password."));
+			g_snprintf(buf, sizeof(buf), _("Incorrect username or password."));
 			break;
 		case 981:
 			g_snprintf(buf, sizeof(buf), _("The service is temporarily unavailable."));
--- a/libpurple/protocols/yahoo/yahoo.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri May 02 22:51:14 2008 +0000
@@ -2124,7 +2124,7 @@
 
 	switch (err) {
 	case 3:
-		msg = g_strdup(_("Invalid screen name."));
+		msg = g_strdup(_("Invalid username."));
 		reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
 		break;
 	case 13:
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Fri May 02 22:51:14 2008 +0000
@@ -68,7 +68,8 @@
 		purple_debug_info("yahoo", "No Aliases to process.%s%s\n",
 						  error_message ? " Error:" : "", error_message ? error_message : "");
 	} else {
-		const char *yid, *full_name, *nick_name, *alias, *id, *fn, *ln, *nn;
+		gchar *full_name, *nick_name, *alias;
+		const char *yid, *id, *fn, *ln, *nn;
 		PurpleBuddy *b = NULL;
 		xmlnode *item, *contacts;
 
@@ -92,7 +93,9 @@
 				nn = xmlnode_get_attrib(item,"nn");
 				id = xmlnode_get_attrib(item,"id");
 
-		                /* Yahoo stores first and last names separately, lets put them together into a full name */
+				full_name = nick_name = alias = NULL;
+
+				/* Yahoo stores first and last names separately, lets put them together into a full name */
 				if (yd->jp)
 					full_name = g_strstrip(g_strdup_printf("%s %s", (ln != NULL ? ln : "") , (fn != NULL ? fn : "")));
 				else
@@ -103,8 +106,6 @@
 					alias = nick_name;   /* If we have a nickname from Yahoo, let's use it */
 				else if (strlen(full_name) != 0)
 					alias = full_name;  /* If no Yahoo nickname, we can use the full_name created above */
-				else
-					alias = NULL;  /* No nickname, first name or last name, then you get no alias !!  */
 
 				/*  Find the local buddy that matches */
 				b = purple_find_buddy(cb->gc->account, yid);
@@ -118,6 +119,7 @@
 					yu->firstname = g_strdup(fn);
 					yu->lastname = g_strdup(ln);
 					yu->nickname = g_strdup(nn);
+					/* TODO: Isn't there a possiblity that b->proto_data is already set? */
 					b->proto_data=yu;
 
 					/* Finally, if we received an alias, we better update the buddy list */
@@ -132,6 +134,9 @@
 				} else {
 					purple_debug_info("yahoo", "Bizarre, received alias for %s, but they are not on your list...\n", yid);
 				}
+
+				g_free(full_name);
+				g_free(nick_name);
 			}
 		}
 		xmlnode_free(contacts);
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Fri May 02 22:51:14 2008 +0000
@@ -998,6 +998,7 @@
 	url = g_strdup_printf("%ld.%ld.%ld.%ld", d, c, b, a);
 	if (!purple_url_parse(url, &(xd->host), &(xd->port), &(xd->path), NULL, NULL)) {
 		purple_xfer_cancel_remote(xfer);
+		g_free(url);
 		return;
 	}
 	g_free(url);
--- a/libpurple/server.c	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/server.c	Fri May 02 22:51:14 2008 +0000
@@ -967,12 +967,6 @@
 	purple_signal_emit(purple_conversations_get_handle(), "chat-left", conv);
 }
 
-void serv_got_join_chat_failed(PurpleConnection *gc, const char *name)
-{
-	purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed",
-					gc, name);
-}
-
 void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
 					  PurpleMessageFlags flags, const char *message, time_t mtime)
 {
--- a/libpurple/server.h	Fri May 02 22:50:13 2008 +0000
+++ b/libpurple/server.h	Fri May 02 22:51:14 2008 +0000
@@ -166,15 +166,6 @@
 
 PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
 									   int id, const char *name);
-/**
- * Called by a prpl when an attempt to join a chat via serv_join_chat()
- * fails.
- *
- * @param gc      The connection on which chat joining failed
- * @param name    The name of the chat which we did not join
- */
-void serv_got_join_chat_failed(PurpleConnection *gc, const char *name);
-	
 void serv_got_chat_left(PurpleConnection *g, int id);
 void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
 					  PurpleMessageFlags flags, const char *message, time_t mtime);
--- a/pidgin/gtkaccount.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkaccount.c	Fri May 02 22:51:14 2008 +0000
@@ -408,7 +408,7 @@
 	g_object_set(G_OBJECT(dialog->screenname_entry), "truncate-multiline", TRUE, NULL);
 #endif
 
-	add_pref_box(dialog, vbox, _("Screen _name:"), dialog->screenname_entry);
+	add_pref_box(dialog, vbox, _("_Username:"), dialog->screenname_entry);
 
 	g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed",
 					 G_CALLBACK(screenname_changed_cb), dialog);
@@ -496,10 +496,6 @@
 	dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
 										  dialog->password_entry);
 
-	/* Alias */
-	dialog->alias_entry = gtk_entry_new();
-	add_pref_box(dialog, vbox, _("_Local alias:"), dialog->alias_entry);
-
 	/* Remember Password */
 	dialog->remember_pass_check =
 		gtk_check_button_new_with_mnemonic(_("Remember pass_word"));
@@ -571,6 +567,10 @@
 	gtk_container_add(GTK_CONTAINER(frame), vbox);
 	gtk_widget_show(vbox);
 
+	/* Alias */
+	dialog->alias_entry = gtk_entry_new();
+	add_pref_box(dialog, vbox, _("_Local alias:"), dialog->alias_entry);
+
 	/* New mail notifications */
 	dialog->new_mail_check =
 		gtk_check_button_new_with_mnemonic(_("New _mail notifications"));
@@ -654,6 +654,7 @@
 		set_dialog_icon(dialog, NULL, 0, NULL);
 	}
 
+#if 0
 	if (!dialog->prpl_info ||
 			(!(dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) &&
 			 (dialog->prpl_info->icon_spec.format ==  NULL))) {
@@ -661,6 +662,7 @@
 		/* Nothing to see :( aww. */
 		gtk_widget_hide(dialog->user_frame);
 	}
+#endif
 }
 
 static void
@@ -1911,7 +1913,7 @@
 
 	/* Screen Name column */
 	column = gtk_tree_view_column_new();
-	gtk_tree_view_column_set_title(column, _("Screen Name"));
+	gtk_tree_view_column_set_title(column, _("Username"));
 	gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
 	gtk_tree_view_column_set_resizable(column, TRUE);
 
--- a/pidgin/gtkblist.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkblist.c	Fri May 02 22:51:14 2008 +0000
@@ -614,6 +614,78 @@
 	pidgin_blist_refresh(list);
 }
 
+static void
+chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields)
+{
+	GList *groups, *fields;
+
+	for (groups = purple_request_fields_get_groups(allfields); groups; groups = groups->next) {
+		fields = purple_request_field_group_get_fields(groups->data);
+		for (; fields; fields = fields->next) {
+			PurpleRequestField *field = fields->data;
+			const char *id;
+			char *val;
+
+			id = purple_request_field_get_id(field);
+			if (purple_request_field_get_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
+				val = g_strdup_printf("%d", purple_request_field_int_get_value(field));
+			else
+				val = g_strdup(purple_request_field_string_get_value(field));
+
+			if (!val) {
+				g_hash_table_remove(purple_chat_get_components(chat), id);
+			} else {
+				g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val);  /* val should not be free'd */
+			}
+		}
+	}
+}
+
+static void chat_components_edit(GtkWidget *w, PurpleBlistNode *node)
+{
+	PurpleRequestFields *fields = purple_request_fields_new();
+	PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
+	PurpleRequestField *field;
+	GList *parts, *iter;
+	struct proto_chat_entry *pce;
+	PurpleConnection *gc;
+	PurpleChat *chat = (PurpleChat*)node;
+
+	purple_request_fields_add_group(fields, group);
+
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
+	parts = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info(gc);
+
+	for (iter = parts; iter; iter = iter->next) {
+		pce = iter->data;
+		if (pce->is_int) {
+			int val;
+			const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
+			if (!str || sscanf(str, "%d", &val) != 1)
+				val = pce->min;
+			field = purple_request_field_int_new(pce->identifier, pce->label, val);
+		} else {
+			field = purple_request_field_string_new(pce->identifier, pce->label,
+					g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
+			if (pce->secret)
+				purple_request_field_string_set_masked(field, TRUE);
+		}
+
+		if (pce->required)
+			purple_request_field_set_required(field, TRUE);
+
+		purple_request_field_group_add_field(group, field);
+		g_free(pce);
+	}
+
+	g_list_free(parts);
+
+	purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please update the necessary fields."),
+			fields, _("Save"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
+			NULL, NULL, NULL,
+			chat);
+}
+
 static void gtk_blist_menu_alias_cb(GtkWidget *w, PurpleBlistNode *node)
 {
 	GtkTreeIter iter;
@@ -1510,6 +1582,8 @@
 
 	pidgin_separator(menu);
 
+	pidgin_new_item_from_stock(menu, _("_Edit Settings..."), NULL,
+				 G_CALLBACK(chat_components_edit), node, 0, 0, NULL);
 	pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
 				 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
 	pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
@@ -3665,13 +3739,13 @@
 			}
 			return text;
 		}
-		else
-			if (hidden_conv) {
-				char *tmp = esc;
-				esc = g_strdup_printf("<b>%s</b>", esc);
-				g_free(tmp);
-			}
-			return esc;
+		else if (hidden_conv)
+		{
+			char *tmp = esc;
+			esc = g_strdup_printf("<b>%s</b>", esc);
+			g_free(tmp);
+		}
+		return esc;
 	}
 
 	prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
@@ -6393,7 +6467,7 @@
 
 	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 0, 2, 0, 1);
 
-	label = gtk_label_new_with_mnemonic(_("Buddy's _screen name:"));
+	label = gtk_label_new_with_mnemonic(_("Buddy's _username:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
 
--- a/pidgin/gtkconv.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkconv.c	Fri May 02 22:51:14 2008 +0000
@@ -3052,7 +3052,7 @@
 	{ N_("/Options/Enable _Sounds"), NULL, menu_sounds_cb, 0, "<CheckItem>", NULL },
 	{ "/Options/sep0", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Options/Show Formatting _Toolbars"), NULL, menu_toolbar_cb, 0, "<CheckItem>", NULL },
-	{ N_("/Options/Show Ti_mestamps"), "F2", menu_timestamps_cb, 0, "<CheckItem>", NULL },
+	{ N_("/Options/Show Ti_mestamps"), NULL, menu_timestamps_cb, 0, "<CheckItem>", NULL },
 };
 
 static const int menu_item_count =
@@ -3980,11 +3980,10 @@
 }
 
 static void
-tab_complete_process_item(int *most_matched, char *entered, char **partial, char *nick_partial,
+tab_complete_process_item(int *most_matched, char *entered, gsize entered_bytes, char **partial, char *nick_partial,
 				  GList **matches, gboolean command, char *name)
 {
-	strncpy(nick_partial, name, strlen(entered));
-	nick_partial[strlen(entered)] = '\0';
+	memcpy(nick_partial, name, entered_bytes);
 	if (purple_utf8_strcasecmp(nick_partial, entered))
 		return;
 
@@ -4029,6 +4028,7 @@
 	const char *prefix;
 	GList *matches = NULL;
 	gboolean command = FALSE;
+	gsize entered_bytes = 0;
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
@@ -4048,19 +4048,24 @@
 	/* if we're at the end of ": " we need to move back 2 spaces */
 	start = strlen(text) - 1;
 
-	if (strlen(text) >= 2 && !strncmp(&text[start-1], ": ", 2)) {
+	if (start >= 1 && !strncmp(&text[start-1], ": ", 2)) {
 		gtk_text_iter_backward_chars(&word_start, 2);
-		start-=2;
-	}
-
-	/* find the start of the word that we're tabbing */
-	while (start >= 0 && text[start] != ' ') {
-		gtk_text_iter_backward_char(&word_start);
-		start--;
+	}
+
+	/* find the start of the word that we're tabbing.
+	 * Using gtk_text_iter_backward_word_start won't work, because a nick can contain
+	 * characters (e.g. '.', '/' etc.) that Pango may think are word separators. */
+	while (gtk_text_iter_backward_char(&word_start)) {
+		if (gtk_text_iter_get_char(&word_start) == ' ') {
+			/* Reached the whitespace before the start of the word. Move forward once */
+			gtk_text_iter_forward_char(&word_start);
+			break;
+		}
 	}
 
 	prefix = pidgin_get_cmd_prefix();
-	if (start == -1 && (strlen(text) >= strlen(prefix)) && !strncmp(text, prefix, strlen(prefix))) {
+	if (gtk_text_iter_get_offset(&word_start) == 0 &&
+			(strlen(text) >= strlen(prefix)) && !strncmp(text, prefix, strlen(prefix))) {
 		command = TRUE;
 		gtk_text_iter_forward_chars(&word_start, strlen(prefix));
 	}
@@ -4069,13 +4074,14 @@
 
 	entered = gtk_text_buffer_get_text(gtkconv->entry_buffer, &word_start,
 									   &cursor, FALSE);
+	entered_bytes = strlen(entered);
 
 	if (!g_utf8_strlen(entered, -1)) {
 		g_free(entered);
 		return (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) ? TRUE : FALSE;
 	}
 
-	nick_partial = g_malloc(strlen(entered)+1);
+	nick_partial = g_malloc0(entered_bytes + 1);
 
 	if (command) {
 		GList *list = purple_cmd_list(conv);
@@ -4083,7 +4089,7 @@
 
 		/* Commands */
 		for (l = list; l != NULL; l = l->next) {
-			tab_complete_process_item(&most_matched, entered, &partial, nick_partial,
+			tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial,
 									  &matches, TRUE, l->data);
 		}
 		g_list_free(list);
@@ -4096,7 +4102,7 @@
 
 		/* Users */
 		for (; l != NULL; l = l->next) {
-			tab_complete_process_item(&most_matched, entered, &partial, nick_partial,
+			tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial,
 									  &matches, TRUE, ((PurpleConvChatBuddy *)l->data)->name);
 		}
 
@@ -4114,7 +4120,7 @@
 						   -1);
 
 				if (name && alias && strcmp(name, alias))
-					tab_complete_process_item(&most_matched, entered, &partial, nick_partial,
+					tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial,
 										  &matches, FALSE, alias);
 				g_free(name);
 				g_free(alias);
@@ -4452,7 +4458,9 @@
 	GdkRectangle oneline;
 	int height, diff;
 	int pad_top, pad_inside, pad_bottom;
-	int max_height = gtkconv->tab_cont->allocation.height / 2;
+	int total_height = (gtkconv->imhtml->allocation.height + gtkconv->entry->allocation.height);
+	int max_height = total_height / 2;
+	int min_height;
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4476,12 +4484,11 @@
 	/* Make sure there's enough room for at least two lines. Allocate enough space to
 	 * prevent scrolling when the second line is a continuation of the first line, or
 	 * is the beginning of a new paragraph. */
-	height = MAX(height, 2 * (oneline.height + MAX(pad_inside, pad_top + pad_bottom)));
-
-	height = MIN(height, max_height);
+	min_height = 2 * (oneline.height + MAX(pad_inside, pad_top + pad_bottom));
+	height = CLAMP(height, min_height, max_height);
 
 	diff = height - gtkconv->entry->allocation.height;
-	if (diff == 0 || (diff < 0 && -diff < oneline.height / 2))
+	if (ABS(diff) < oneline.height / 2)
 		return FALSE;
 
 	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
@@ -4758,6 +4765,7 @@
 
 	/* Setup the gtkimhtml widget */
 	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
+	gtk_widget_set_size_request(gtkconv->imhtml, -1, 0);
 	if (chat) {
 		GtkWidget *hpaned;
 
--- a/pidgin/gtkdialogs.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkdialogs.c	Fri May 02 22:51:14 2008 +0000
@@ -760,7 +760,7 @@
 
 	purple_request_fields(purple_get_blist(), _("New Instant Message"),
 						NULL,
-						_("Please enter the screen name or alias of the person "
+						_("Please enter the username or alias of the person "
 						  "you would like to IM."),
 						fields,
 						_("OK"), G_CALLBACK(pidgin_dialogs_im_cb),
@@ -899,7 +899,7 @@
 
 	purple_request_fields(purple_get_blist(), _("Get User Info"),
 						NULL,
-						_("Please enter the screen name or alias of the person "
+						_("Please enter the username or alias of the person "
 						  "whose info you would like to view."),
 						fields,
 						_("OK"), G_CALLBACK(pidgin_dialogs_info_cb),
@@ -991,7 +991,7 @@
 
 	purple_request_fields(purple_get_blist(), _("View User Log"),
 						NULL,
-						_("Please enter the screen name or alias of the person "
+						_("Please enter the username or alias of the person "
 						  "whose log you would like to view."),
 						fields,
 						_("OK"), G_CALLBACK(pidgin_dialogs_log_cb),
--- a/pidgin/gtkprefs.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkprefs.c	Fri May 02 22:51:14 2008 +0000
@@ -825,6 +825,106 @@
 		gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
 }
 
+
+#define CONVERSATION_CLOSE_ACCEL_PATH "<main>/Conversation/Close"
+
+/* Filled in in keyboard_shortcuts(). */
+static GtkAccelKey ctrl_w = { 0, 0, 0 };
+static GtkAccelKey escape = { 0, 0, 0 };
+
+static guint escape_closes_conversation_cb_id = 0;
+
+static gboolean
+accel_is_escape(GtkAccelKey *k)
+{
+	return (k->accel_key == escape.accel_key
+		&& k->accel_mods == escape.accel_mods);
+}
+
+/* Update the tickybox in Preferences when the keybinding for Conversation ->
+ * Close is changed via Gtk.
+ */
+static void
+conversation_close_accel_changed_cb (GtkAccelMap    *object,
+                                     gchar          *accel_path,
+                                     guint           accel_key,
+                                     GdkModifierType accel_mods,
+                                     gpointer        checkbox_)
+{
+	GtkToggleButton *checkbox = GTK_TOGGLE_BUTTON(checkbox_);
+	GtkAccelKey new = { accel_key, accel_mods, 0 };
+
+	g_signal_handler_block(checkbox, escape_closes_conversation_cb_id);
+	gtk_toggle_button_set_active(checkbox, accel_is_escape(&new));
+	g_signal_handler_unblock(checkbox, escape_closes_conversation_cb_id);
+}
+
+
+static void
+escape_closes_conversation_cb(GtkWidget *w,
+                              gpointer unused)
+{
+	gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+	gboolean changed;
+	GtkAccelKey *new_key = active ? &escape : &ctrl_w;
+
+	changed = gtk_accel_map_change_entry(CONVERSATION_CLOSE_ACCEL_PATH,
+		new_key->accel_key, new_key->accel_mods, TRUE);
+
+	/* If another path is already bound to the new accelerator,
+	 * _change_entry tries to delete that binding (because it was passed
+	 * replace=TRUE).  If that other path is locked, then _change_entry
+	 * will fail.  We don't ever lock any accelerator paths, so this case
+	 * should never arise.
+	 */
+	if(!changed)
+		purple_debug_warning("gtkprefs", "Escape accel failed to change\n");
+
+	/* TODO: create pidgin_accels_schedule_save */
+	pidgin_save_accels_cb(NULL, 0, 0, NULL, NULL);
+}
+
+
+/* Creates preferences for keyboard shortcuts that it's hard to change with the
+ * standard Gtk accelerator-changing mechanism.
+ */
+static void
+keyboard_shortcuts(GtkWidget *page)
+{
+	GtkWidget *vbox = pidgin_make_frame(page, _("Keyboard Shortcuts"));
+	GtkWidget *checkbox;
+	GtkAccelKey current = { 0, 0, 0 };
+	GtkAccelMap *map = gtk_accel_map_get();
+
+	/* Maybe it would be better just to hardcode the values?
+	 * -- resiak, 2007-04-30
+	 */
+	if (ctrl_w.accel_key == 0)
+	{
+		gtk_accelerator_parse ("<Control>w", &(ctrl_w.accel_key),
+			&(ctrl_w.accel_mods));
+		g_assert(ctrl_w.accel_key != 0);
+
+		gtk_accelerator_parse ("Escape", &(escape.accel_key),
+			&(escape.accel_mods));
+		g_assert(escape.accel_key != 0);
+	}
+
+	checkbox = gtk_check_button_new_with_mnemonic(
+		_("Cl_ose conversations with the Escape key"));
+	gtk_accel_map_lookup_entry(CONVERSATION_CLOSE_ACCEL_PATH, &current);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+		accel_is_escape(&current));
+
+	escape_closes_conversation_cb_id = g_signal_connect(checkbox,
+		"clicked", G_CALLBACK(escape_closes_conversation_cb), NULL);
+
+	g_signal_connect(map, "changed::" CONVERSATION_CLOSE_ACCEL_PATH,
+		G_CALLBACK(conversation_close_accel_changed_cb), checkbox);
+
+	gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+}
+
 static GtkWidget *
 interface_page(void)
 {
@@ -904,6 +1004,10 @@
 
 	g_list_free(names);
 
+
+	keyboard_shortcuts(ret);
+
+
 	gtk_widget_show_all(ret);
 	g_object_unref(sg);
 	return ret;
--- a/pidgin/gtkrequest.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtkrequest.c	Fri May 02 22:51:14 2008 +0000
@@ -1120,6 +1120,16 @@
 	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
 	gtk_widget_show(img);
 
+	/* Cancel button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+	/* OK button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
+	data->ok_button = button;
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+	gtk_window_set_default(GTK_WINDOW(win), button);
+
 	/* Setup the vbox */
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
@@ -1272,6 +1282,7 @@
 				size_t col_offset = col_num * 2;
 				PurpleRequestFieldType type;
 				GtkWidget *widget = NULL;
+				const char *field_label;
 
 				label = NULL;
 				field = fl->data;
@@ -1282,17 +1293,17 @@
 				}
 
 				type = purple_request_field_get_type(field);
+				field_label = purple_request_field_get_label(field);
 
-				if (type != PURPLE_REQUEST_FIELD_BOOLEAN &&
-				    purple_request_field_get_label(field))
+				if (type != PURPLE_REQUEST_FIELD_BOOLEAN && field_label)
 				{
-					char *text;
+					char *text = NULL;
 
-					text = g_strdup_printf("%s:",
-						purple_request_field_get_label(field));
+					if (field_label[strlen(field_label) - 1] != ':')
+						text = g_strdup_printf("%s:", field_label);
 
 					label = gtk_label_new(NULL);
-					gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text);
+					gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text ? text : field_label);
 					g_free(text);
 
 					gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
@@ -1393,18 +1404,8 @@
 
 	g_object_unref(sg);
 
-	/* Cancel button */
-	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
-	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
-
-	/* OK button */
-	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
-	data->ok_button = button;
-	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
-	gtk_window_set_default(GTK_WINDOW(win), button);
-
 	if (!purple_request_fields_all_required_filled(fields))
-		gtk_widget_set_sensitive(button, FALSE);
+		gtk_widget_set_sensitive(data->ok_button, FALSE);
 
 	pidgin_auto_parent_window(win);
 
--- a/pidgin/gtksavedstatuses.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtksavedstatuses.c	Fri May 02 22:51:14 2008 +0000
@@ -968,7 +968,7 @@
 	/* Screen Name column */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_column_set_resizable(column, TRUE);
-	gtk_tree_view_column_set_title(column, _("Screen Name"));
+	gtk_tree_view_column_set_title(column, _("Username"));
 	gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
 	gtk_tree_view_column_set_resizable(column, TRUE);
 
--- a/pidgin/gtksound.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/gtksound.c	Fri May 02 22:51:14 2008 +0000
@@ -70,7 +70,7 @@
 	{N_("Others talk in chat"), "chat_msg_recv", "receive.wav"},
 	/* this isn't a terminator, it's the buddy pounce default sound event ;-) */
 	{NULL, "pounce_default", "alert.wav"},
-	{N_("Someone says your screen name in chat"), "nick_said", "alert.wav"}
+	{N_("Someone says your username in chat"), "nick_said", "alert.wav"}
 };
 
 static gboolean
--- a/pidgin/plugins/gevolution/new_person_dialog.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/plugins/gevolution/new_person_dialog.c	Fri May 02 22:51:14 2008 +0000
@@ -267,7 +267,7 @@
 	}
 	else
 	{
-		label = gtk_label_new(_("Please enter the buddy's screen name and "
+		label = gtk_label_new(_("Please enter the buddy's username and "
 								"account type below."));
 	}
 
@@ -291,7 +291,7 @@
 
 		/* Screen Name */
 		dialog->screenname = gtk_entry_new();
-		add_pref_box(sg, vbox, _("Screen name:"), dialog->screenname);
+		add_pref_box(sg, vbox, _("Username:"), dialog->screenname);
 
 		if (username != NULL)
 			gtk_entry_set_text(GTK_ENTRY(dialog->screenname), username);
--- a/pidgin/plugins/notify.c	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/plugins/notify.c	Fri May 02 22:51:14 2008 +0000
@@ -112,7 +112,7 @@
 
 /* notification set/unset */
 static int notify(PurpleConversation *conv, gboolean increment);
-static void notify_win(PidginWindow *purplewin);
+static void notify_win(PidginWindow *purplewin, PurpleConversation *conv);
 static void unnotify(PurpleConversation *conv, gboolean reset);
 static int unnotify_cb(GtkWidget *widget, gpointer data,
                        PurpleConversation *conv);
@@ -141,6 +141,9 @@
 /* raise function */
 static void handle_raise(PidginWindow *purplewin);
 
+/* present function */
+static void handle_present(PurpleConversation *conv);
+
 /****************************************/
 /* Begin doing stuff below this line... */
 /****************************************/
@@ -193,14 +196,14 @@
 			purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count));
 		}
 
-		notify_win(purplewin);
+		notify_win(purplewin, conv);
 	}
 
 	return 0;
 }
 
 static void
-notify_win(PidginWindow *purplewin)
+notify_win(PidginWindow *purplewin, PurpleConversation *conv)
 {
 	if (count_messages(purplewin) <= 0)
 		return;
@@ -215,6 +218,8 @@
 		handle_urgent(purplewin, TRUE);
 	if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"))
 		handle_raise(purplewin);
+	if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"))
+		handle_present(conv);
 }
 
 static void
@@ -564,6 +569,12 @@
 }
 
 static void
+handle_present(PurpleConversation *conv)
+{
+	purple_conversation_present(conv);
+}
+
+static void
 type_toggle_cb(GtkWidget *widget, gpointer data)
 {
 	gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
@@ -694,7 +705,7 @@
 	                 G_CALLBACK(type_toggle_cb), "type_chat");
 
 	ref = toggle;
-	toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your screen name"));
+	toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your username"));
 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
 	                            purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick"));
@@ -771,6 +782,14 @@
 	g_signal_connect(G_OBJECT(toggle), "toggled",
 	                 G_CALLBACK(method_toggle_cb), "method_raise");
 
+	/* Present conversation method button */
+	toggle = gtk_check_button_new_with_mnemonic(_("_Present conversation window"));
+	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
+	                             purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"));
+	g_signal_connect(G_OBJECT(toggle), "toggled",
+	                 G_CALLBACK(method_toggle_cb), "method_present");
+
 	/*---------- "Notification Removals" ----------*/
 	frame = pidgin_make_frame(ret, _("Notification Removal"));
 	vbox = gtk_vbox_new(FALSE, 5);
@@ -945,6 +964,7 @@
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE);
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE);
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE);
+	purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE);
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE);
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE);
 	purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE);
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Fri May 02 22:50:13 2008 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Fri May 02 22:51:14 2008 +0000
@@ -49,6 +49,7 @@
 !include "WordFunc.nsh"
 !insertmacro VersionCompare
 !insertmacro WordFind
+!insertmacro un.WordFind
 
 ;--------------------------------
 ;Defines
@@ -549,23 +550,18 @@
 
 ;--------------------------------
 ;URI Handling
-SectionGroup /e $(URI_HANDLERS_SECTION_TITLE) SecURIHandlers
-  Section /o "aim:" SecURI_AIM
-    Push "aim"
-    Call RegisterURIHandler
-  SectionEnd
-  Section /o "msnim:" SecURI_MSNIM
-    Push "msnim"
+
+!macro URI_SECTION proto
+  Section /o "${proto}:" SecURI_${proto}
+    Push "${proto}"
     Call RegisterURIHandler
   SectionEnd
-  Section /o "myim:" SecURI_MYIM
-    Push "myim"
-    Call RegisterURIHandler
-  SectionEnd
-  Section /o "ymsgr:" SecURI_YMSGR
-    Push "ymsgr"
-    Call RegisterURIHandler
-  SectionEnd
+!macroend
+SectionGroup /e $(URI_HANDLERS_SECTION_TITLE) SecURIHandlers
+  !insertmacro URI_SECTION "aim"
+  !insertmacro URI_SECTION "msnim"
+  !insertmacro URI_SECTION "myim"
+  !insertmacro URI_SECTION "ymsgr"
 SectionGroupEnd
 
 ;--------------------------------
@@ -694,7 +690,19 @@
     ; The WinPrefs plugin may have left this behind..
     DeleteRegValue HKCU "${STARTUP_RUN_KEY}" "Pidgin"
     DeleteRegValue HKLM "${STARTUP_RUN_KEY}" "Pidgin"
-    ; Remove Language preference info (TODO: check if NSIS removes this)
+    ; Remove Language preference info
+    DeleteRegValue HKCU "${PIDGIN_REG_KEY}" "Installer Language"
+
+    ; Remove any URI handlers
+    ; I can't think of an easy way to maintain a list in a single place
+    Push "aim"
+    Call un.UnregisterURIHandler
+    Push "msnim"
+    Call un.UnregisterURIHandler
+    Push "myim"
+    Call un.UnregisterURIHandler
+    Push "ymsgr"
+    Call un.UnregisterURIHandler
 
     Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem"
     Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem"
@@ -903,12 +911,12 @@
   ReadRegStr $R3 HKCR "$R2" ""
   IfErrors default_on ;there is no current handler
 
-  ; Check if Pidgin is the current handler
-  ClearErrors
-  ReadRegStr $R3 HKCR "$R2\shell\Open\command" ""
-  IfErrors end_loop
-  ${WordFind} "$R3" "pidgin.exe" "E+1{" $R3
-  IfErrors end_loop default_on
+  Push $R2
+  Call CheckIfPidginIsCurrentURIHandler
+  Pop $R3
+
+  ; If Pidgin isn't the current handler, we don't steal it automatically
+  IntCmp $R3 0 end_loop
 
   ;We default the URI handler checkbox on
   default_on:
@@ -926,9 +934,58 @@
   Pop $R0
 FunctionEnd ;SelectURIHandlerSections
 
+; Check if Pidgin is the current handler
+; Returns a boolean on the stack
+!macro CheckIfPidginIsCurrentURIHandlerMacro UN
+Function ${UN}CheckIfPidginIsCurrentURIHandler
+  Exch $R0
+  ClearErrors
+
+  ReadRegStr $R0 HKCR "$R0\shell\Open\command" ""
+  IfErrors 0 +3
+    IntOp $R0 0 + 0
+    Goto done
+
+  !ifdef __UNINSTALL__
+  ${un.WordFind} "$R0" "pidgin.exe" "E+1{" $R0
+  !else
+  ${WordFind} "$R0" "pidgin.exe" "E+1{" $R0
+  !endif
+  IntOp $R0 0 + 1
+  IfErrors 0 +2
+    IntOp $R0 0 + 0
+
+  done:
+  Exch $R0
+FunctionEnd
+!macroend
+!insertmacro CheckIfPidginIsCurrentURIHandlerMacro ""
+!insertmacro CheckIfPidginIsCurrentURIHandlerMacro "un."
+
+; If Pidgin is the current URI handler for the specified protocol, remove it.
+Function un.UnregisterURIHandler
+  Exch $R0
+  Push $R1
+
+  Push $R0
+  Call un.CheckIfPidginIsCurrentURIHandler
+  Pop $R1
+
+  ; If Pidgin isn't the current handler, leave it as-is
+  IntCmp $R1 0 done
+
+  ;Unregister the URI handler
+  DetailPrint "Unregistering $R0 URI Handler"
+  DeleteRegKey HKCR "$R0"
+
+  done:
+  Pop $R1
+  Pop $R0
+FunctionEnd
 
 Function RegisterURIHandler
   Exch $R0
+  DetailPrint "Registering $R0 URI Handler"
   DeleteRegKey HKCR "$R0"
   WriteRegStr HKCR "$R0" "" "URL:$R0"
   WriteRegStr HKCR "$R0" "URL Protocol" ""
@@ -1198,12 +1255,14 @@
   StrCpy $SPELLCHECK_SEL ""
 
   ;Try to copy the old Gaim installer Lang Reg. key
+  ;(remove it after we're done to prevent this being done more than once)
   ClearErrors
   ReadRegStr $R0 HKCU "${PIDGIN_REG_KEY}" "Installer Language"
   IfErrors 0 +5
   ClearErrors
-  ReadRegStr $R0 HKCU "SOFTWARE\gaim" "Installer Language"
-  IfErrors +2
+  ReadRegStr $R0 HKCU "${OLD_GAIM_REG_KEY}" "Installer Language"
+  IfErrors +3
+  DeleteRegValue HKCU "${OLD_GAIM_REG_KEY}" "Installer Language"
   WriteRegStr HKCU "${PIDGIN_REG_KEY}" "Installer Language" "$R0"
 
   !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO}
@@ -1321,6 +1380,7 @@
 Function un.onInit
   Call un.RunCheck
   StrCpy $name "Pidgin ${PIDGIN_VERSION}"
+;LogSet on
 
   ; Get stored language preference
   !insertmacro MUI_UNGETLANGUAGE