changeset 30679:74541f1faf3c

merge of '5aacaf467359c75ac94de8b4a1897a191bc2a261' and '5cf492b57fa4a297212cbaa711deb42e1e431a08'
author Marcus Lundblad <ml@update.uu.se>
date Wed, 07 Jul 2010 20:06:35 +0000
parents 5ec2d3617538 (current diff) 9bd9ef37354f (diff)
children eeee406244da
files ChangeLog
diffstat 19 files changed, 325 insertions(+), 92 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jul 07 20:04:06 2010 +0000
+++ b/ChangeLog	Wed Jul 07 20:06:35 2010 +0000
@@ -1,6 +1,11 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.7.2 (??/??/????):
+	General:
+	* Use silent build rules for automake >1.11. You can enable verbose
+	  builds with the --disable-silent-rules configure option, or using
+	  make V=1.
+
 	libpurple:
 	* Fix the TURN server settings (broken in 2.7.0).
 
--- a/ChangeLog.API	Wed Jul 07 20:04:06 2010 +0000
+++ b/ChangeLog.API	Wed Jul 07 20:06:35 2010 +0000
@@ -7,6 +7,19 @@
 		  called for a protocol that doesn't support the underlying
 		  calls and the caller does not specify a failure callback.
 
+	Pidgin:
+		Changed:
+		* Changing the visibility (gtk_widget_hide/show) of
+		  the widgets in the GtkIMHtmlToolbar should now affect
+		  the visibility of the entries in the 'lean' view
+		  (the default toolbar view).
+
+	libgnt:
+		Added:
+		* gnt_tree_row_get_key, gnt_tree_row_get_next,
+		  gnt_tree_row_get_prev, gnt_tree_row_get_child and
+		  gnt_tree_row_get_parent.
+
 version 2.7.1 (05/29/2010):
 	* No changes
 
--- a/Makefile.am	Wed Jul 07 20:04:06 2010 +0000
+++ b/Makefile.am	Wed Jul 07 20:06:35 2010 +0000
@@ -100,18 +100,18 @@
 # creates, and also make sure that the shell command exits
 # successfully; the rm -f ensures both
 package_revision_raw.txt:
-	REAL_BLDDIR=$$PWD/$(top_builddir); \
+	$(AM_V_GEN)REAL_BLDDIR=$$PWD/$(top_builddir); \
 	(cd $(srcdir) && $$REAL_BLDDIR/mtn --root=. automate get_base_revision_id) 2>/dev/null >$@ \
 	|| (cd $(srcdir) && mtn --root=. automate get_base_revision_id) 2>/dev/null >$@ \
 	|| rm -f $@
 package_revision.h: package_revision_raw.txt
-	if test -f $<; then \
+	$(AM_V_GEN)if test -f $<; then \
 	  echo "#define REVISION \"`cat $<`\"" > $@; \
 	fi
-	if test ! -f $@ -a -f $(srcdir)/$@; then \
+	$(AM_V_at)if test ! -f $@ -a -f $(srcdir)/$@; then \
 	  cp $(srcdir)/$@ $@; \
 	fi
-	test -f $@ || echo "#define REVISION \"unknown\"" > $@
+	$(AM_V_at)test -f $@ || echo "#define REVISION \"unknown\"" > $@
 
 # This is a magic directive copy-and-pasted, then modified, from the
 # automake 1.9 manual, section 13.4, "Checking the distribution".
--- a/configure.ac	Wed Jul 07 20:04:06 2010 +0000
+++ b/configure.ac	Wed Jul 07 20:06:35 2010 +0000
@@ -52,10 +52,10 @@
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
 
-m4_define([gnt_lt_current], [7])
+m4_define([gnt_lt_current], [8])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [7])
-m4_define([gnt_micro_version], [2])
+m4_define([gnt_minor_version], [8])
+m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -75,6 +75,8 @@
 AC_CANONICAL_HOST
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2])
+dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 PURPLE_MAJOR_VERSION=purple_major_version
 PURPLE_MINOR_VERSION=purple_minor_version
@@ -660,6 +662,10 @@
 		for location in $ac_ncurses_includes $NCURSES_HEADERS /usr/include/ncursesw /usr/include
 		do
 			f="$location/ncurses.h"
+			orig_CFLAGS="$CFLAGS"
+			orig_CPPFLAGS="$CPPFLAGS"
+			CFLAGS="$CFLAGS -I$location"
+			CPPFLAGS="$CPPFLAGS -I$location"
 			AC_CHECK_HEADER($f,[
 				AC_MSG_CHECKING([if $f supports wide characters])
 				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -678,9 +684,13 @@
 					fi
 
 					found_ncurses_h=yes
+					CFLAGS="$orig_CFLAGS"
+					CPPFLAGS="$orig_CPPFLAGS"
 					AC_MSG_RESULT([yes])
 					break
 				], [
+					CFLAGS="$orig_CFLAGS"
+					CPPFLAGS="$orig_CPPFLAGS"
 					AC_MSG_RESULT([no])
 				])
 			])
--- a/finch/gntaccount.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/gntaccount.c	Wed Jul 07 20:06:35 2010 +0000
@@ -125,7 +125,8 @@
 
 	if (value == NULL || *value == '\0')
 	{
-		purple_notify_error(NULL, _("Error"), _("Account was not added"),
+		purple_notify_error(NULL, _("Error"),
+				dialog->account ? _("Account was not modified") : _("Account was not added"),
 				_("Username of an account must be non-empty."));
 		return;
 	}
@@ -160,8 +161,28 @@
 		account = dialog->account;
 
 		/* Protocol */
-		purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
-		purple_account_set_username(account, username->str);
+		if (purple_account_is_disconnected(account)) {
+			purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
+			purple_account_set_username(account, username->str);
+		} else {
+			const char *old = purple_account_get_protocol_id(account);
+			char *oldprpl;
+			if (strcmp(old, purple_plugin_get_id(plugin))) {
+				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
+						_("The account's protocol cannot be changed while it is connected to the server."));
+				return;
+			}
+
+			oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account)));
+			if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) {
+				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
+						_("The account's username cannot be changed while it is connected to the server."));
+				g_free(oldprpl);
+				return;
+			}
+			g_free(oldprpl);
+			purple_account_set_username(account, username->str);
+		}
 	}
 	g_string_free(username, TRUE);
 
@@ -245,6 +266,17 @@
 		}
 	}
 
+	/* In case of a new account, the 'Accounts' window is updated from the account-added
+	 * callback. In case of changes in an existing account, we need to explicitly do it
+	 * here.
+	 */
+	if (dialog->account != NULL && accounts.window) {
+		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
+				0, purple_account_get_username(dialog->account));
+		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
+				1, purple_account_get_protocol_name(dialog->account));
+	}
+
 	gnt_widget_destroy(dialog->window);
 }
 
@@ -603,7 +635,7 @@
 	button = gnt_button_new(_("Cancel"));
 	gnt_box_add_widget(GNT_BOX(hbox), button);
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
-	
+
 	button = gnt_button_new(_("Save"));
 	gnt_box_add_widget(GNT_BOX(hbox), button);
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog);
--- a/finch/gntrequest.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/gntrequest.c	Wed Jul 07 20:06:35 2010 +0000
@@ -349,10 +349,9 @@
 			}
 			else if (type == PURPLE_REQUEST_FIELD_LIST)
 			{
-				GList *list = NULL;
+				GList *list = NULL, *iter;
 				if (purple_request_field_list_get_multi_select(field))
 				{
-					GList *iter;
 					GntWidget *tree = FINCH_GET_DATA(field);
 
 					iter = purple_request_field_list_get_items(field);
@@ -361,14 +360,23 @@
 						const char *text = iter->data;
 						gpointer key = purple_request_field_list_get_data(field, text);
 						if (gnt_tree_get_choice(GNT_TREE(tree), key))
-							list = g_list_prepend(list, key);
+							list = g_list_prepend(list, (gpointer)text);
 					}
 				}
 				else
 				{
 					GntWidget *combo = FINCH_GET_DATA(field);
 					gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
-					list = g_list_append(list, data);
+
+					iter = purple_request_field_list_get_items(field);
+					for (; iter; iter = iter->next) {
+						const char *text = iter->data;
+						gpointer key = purple_request_field_list_get_data(field, text);
+						if (key == data) {
+							list = g_list_prepend(list, (gpointer)text);
+							break;
+						}
+					}
 				}
 
 				purple_request_field_list_set_selected(field, list);
@@ -815,7 +823,7 @@
 	for (list = purple_request_fields_get_groups(allfields); list; list = list->next) {
 		PurpleRequestFieldGroup *group = list->data;
 		GList *fields = purple_request_field_group_get_fields(group);
-		
+
 		for (; fields ; fields = fields->next) {
 			PurpleRequestField *field = fields->data;
 			PurpleRequestFieldType type = purple_request_field_get_type(field);
@@ -826,6 +834,7 @@
 			switch (type) {
 				case PURPLE_REQUEST_FIELD_LIST:
 					val = purple_request_field_list_get_selected(field)->data;
+					val = purple_request_field_list_get_data(field, val);
 					break;
 				case PURPLE_REQUEST_FIELD_BOOLEAN:
 					val = GINT_TO_POINTER(purple_request_field_bool_get_value(field));
--- a/finch/gntsound.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/gntsound.c	Wed Jul 07 20:06:35 2010 +0000
@@ -104,7 +104,8 @@
 	{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 username in chat"), "nick_said", "alert.wav", NULL}
+	{PURPLE_SOUND_CHAT_NICK,    N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL},
+	{PURPLE_SOUND_GOT_ATTENTION, N_("Attention received"), "got_attention", "alert.wav", NULL}
 };
 
 const char *
@@ -273,6 +274,14 @@
 		play_conv_event(conv, event);
 }
 
+static void
+got_attention_cb(PurpleAccount *account, const char *who,
+	PurpleConversation *conv, guint type, PurpleSoundEventID event)
+{
+	play_conv_event(conv, event);
+}
+
+
 /*
  * We mute sounds for the 10 seconds after you log in so that
  * you don't get flooded with sounds when the blist shows all
@@ -300,34 +309,22 @@
 static void
 initialize_profile(const char *name, PurplePrefType type, gconstpointer val, gpointer null)
 {
+	FinchSoundEvent *event;
 	if (purple_prefs_exists(make_pref("")))
 		return;
 
 	purple_prefs_add_none(make_pref(""));
 	purple_prefs_add_none(make_pref("/enabled"));
 	purple_prefs_add_none(make_pref("/file"));
-	purple_prefs_add_bool(make_pref("/enabled/login"), FALSE);
-	purple_prefs_add_path(make_pref("/file/login"), "");
-	purple_prefs_add_bool(make_pref("/enabled/logout"), FALSE);
-	purple_prefs_add_path(make_pref("/file/logout"), "");
-	purple_prefs_add_bool(make_pref("/enabled/im_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/im_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/first_im_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/first_im_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/send_im"), FALSE);
-	purple_prefs_add_path(make_pref("/file/send_im"), "");
-	purple_prefs_add_bool(make_pref("/enabled/join_chat"), FALSE);
-	purple_prefs_add_path(make_pref("/file/join_chat"), "");
-	purple_prefs_add_bool(make_pref("/enabled/left_chat"), FALSE);
-	purple_prefs_add_path(make_pref("/file/left_chat"), "");
-	purple_prefs_add_bool(make_pref("/enabled/send_chat_msg"), FALSE);
-	purple_prefs_add_path(make_pref("/file/send_chat_msg"), "");
-	purple_prefs_add_bool(make_pref("/enabled/chat_msg_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/chat_msg_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/nick_said"), FALSE);
-	purple_prefs_add_path(make_pref("/file/nick_said"), "");
-	purple_prefs_add_bool(make_pref("/enabled/pounce_default"), FALSE);
-	purple_prefs_add_path(make_pref("/file/pounce_default"), "");
+
+	for (event = sounds; event - sounds < PURPLE_NUM_SOUNDS; event++) {
+		char pref[512];
+		g_snprintf(pref, sizeof(pref), "/enabled/%s", event->pref);
+		purple_prefs_add_bool(make_pref(pref), FALSE);
+		g_snprintf(pref, sizeof(pref), "/file/%s", event->pref);
+		purple_prefs_add_path(make_pref(pref), "");
+	}
+
 	purple_prefs_add_bool(make_pref("/conv_focus"), FALSE);
 	purple_prefs_add_bool(make_pref("/mute"), FALSE);
 	purple_prefs_add_path(make_pref("/command"), "");
@@ -336,6 +333,25 @@
 }
 
 static void
+update_profiles(void)
+{
+	GList *list = finch_sound_get_profiles();
+	for (; list; list = g_list_delete_link(list, list)) {
+		char pname[512];
+
+		/* got_attention was added in libpurple 2.7.0 */
+		g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s",
+				(char *)list->data, "/enabled/got_attention");
+		purple_prefs_add_bool(pname, FALSE);
+		g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s",
+				(char *)list->data, "/file/got_attention");
+		purple_prefs_add_path(pname, "");
+
+		g_free(list->data);
+	}
+}
+
+static void
 finch_sound_init(void)
 {
 	void *gnt_sound_handle = finch_sound_get_handle();
@@ -356,7 +372,7 @@
 	purple_prefs_connect_callback(gnt_sound_handle, FINCH_PREFS_ROOT "/sound/actprofile", initialize_profile, NULL);
 	purple_prefs_trigger_callback(FINCH_PREFS_ROOT "/sound/actprofile");
 
-	
+
 #ifdef USE_GSTREAMER
 	purple_debug_info("sound", "Initializing sound output drivers.\n");
 #if (GST_VERSION_MAJOR > 0 || \
@@ -399,6 +415,11 @@
 	purple_signal_connect(conv_handle, "received-chat-msg",
 						gnt_sound_handle, PURPLE_CALLBACK(chat_msg_received_cb),
 						GINT_TO_POINTER(PURPLE_SOUND_CHAT_SAY));
+	purple_signal_connect(conv_handle, "got-attention",
+						gnt_sound_handle, PURPLE_CALLBACK(got_attention_cb),
+						GINT_TO_POINTER(PURPLE_SOUND_GOT_ATTENTION));
+
+	update_profiles();
 }
 
 static void
@@ -577,7 +598,8 @@
 	if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds)
 		return;
 
-	if (event >= PURPLE_NUM_SOUNDS) {
+	if (event >= PURPLE_NUM_SOUNDS ||
+			event >= G_N_ELEMENTS(sounds)) {
 		purple_debug_error("sound", "got request for unknown sound: %d\n", event);
 		return;
 	}
--- a/finch/libgnt/configure.ac	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/libgnt/configure.ac	Wed Jul 07 20:06:35 2010 +0000
@@ -24,9 +24,9 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [7])
+m4_define([gnt_lt_current], [8])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [7])
+m4_define([gnt_minor_version], [8])
 m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
@@ -265,6 +265,10 @@
 	for location in $ac_ncurses_includes /usr/include/ncursesw /usr/include
 	do
 		f="$location/ncurses.h"
+		orig_CFLAGS="$CFLAGS"
+		orig_CPPFLAGS="$CPPFLAGS"
+		CFLAGS="$CFLAGS -I$location"
+		CPPFLAGS="$CPPFLAGS -I$location"
 		AC_CHECK_HEADER($f,[
 			AC_MSG_CHECKING([if $f supports wide characters])
 			AC_TRY_COMPILE([
@@ -283,9 +287,13 @@
 				fi
 
 				found_ncurses_h=yes
+				CFLAGS="$orig_CFLAGS"
+				CPPFLAGS="$orig_CPPFLAGS"
 				AC_MSG_RESULT([yes])
 				break
 			], [
+				CFLAGS="$orig_CFLAGS"
+				CPPFLAGS="$orig_CPPFLAGS"
 				AC_MSG_RESULT([no])
 			])
 		])
--- a/finch/libgnt/gntcombobox.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/libgnt/gntcombobox.c	Wed Jul 07 20:06:35 2010 +0000
@@ -150,12 +150,11 @@
 gnt_combo_box_key_pressed(GntWidget *widget, const char *text)
 {
 	GntComboBox *box = GNT_COMBO_BOX(widget);
-	if (GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED))
-	{
-		if (text[1] == 0)
-		{
-			switch (text[0])
-			{
+	gboolean showing = !!GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED);
+
+	if (showing) {
+		if (text[1] == 0) {
+			switch (text[0]) {
 				case '\r':
 				case '\t':
 				case '\n':
@@ -166,8 +165,42 @@
 					return TRUE;
 			}
 		}
-		if (gnt_widget_key_pressed(box->dropdown, text))
-			return TRUE;
+	}
+
+	if (gnt_widget_key_pressed(box->dropdown, text)) {
+		if (!showing)
+			popup_dropdown(box);
+		return TRUE;
+	}
+
+	{
+#define SEARCH_IN_RANGE(start, end) do { \
+		GntTreeRow *row; \
+		for (row = start; row != end; \
+				row = gnt_tree_row_get_next(tree, row)) { \
+			gpointer key = gnt_tree_row_get_key(tree, row); \
+			GList *list = gnt_tree_get_row_text_list(tree, key); \
+			gboolean found = FALSE; \
+			found = (list->data && g_ascii_strncasecmp(text, list->data, len) == 0); \
+			g_list_foreach(list, (GFunc)g_free, NULL); \
+			g_list_free(list); \
+			if (found) { \
+				if (!showing) \
+					popup_dropdown(box); \
+				gnt_tree_set_selected(tree, key); \
+				return TRUE; \
+			} \
+		} \
+} while (0)
+
+		int len = strlen(text);
+		GntTree *tree = GNT_TREE(box->dropdown);
+		GntTreeRow *current = tree->current;
+
+		SEARCH_IN_RANGE(gnt_tree_row_get_next(tree, current), NULL);
+		SEARCH_IN_RANGE(tree->top, current);
+
+#undef SEARCH_IN_RANGE
 	}
 
 	return FALSE;
--- a/finch/libgnt/gnttree.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/libgnt/gnttree.c	Wed Jul 07 20:06:35 2010 +0000
@@ -1926,3 +1926,33 @@
 	return (row && row->parent) ? row->parent->key : NULL;
 }
 
+gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->key;
+}
+
+GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->next;
+}
+
+GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->prev;
+}
+
+GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->child;
+}
+
+GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->parent;
+}
+
--- a/finch/libgnt/gnttree.h	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/libgnt/gnttree.h	Wed Jul 07 20:06:35 2010 +0000
@@ -222,6 +222,61 @@
 GList * gnt_tree_get_row_text_list(GntTree *tree, gpointer key);
 
 /**
+ * Get the key of a row.
+ *
+ * @param tree   The tree
+ * @param row    The GntTreeRow object
+ *
+ * @return The key of the row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the next row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The next row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the previous row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The previous row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the child row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The child row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the parent row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The parent row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row);
+
+/**
  * Get a list of text of the current row.
  *
  * @param tree  The tree
--- a/finch/libgnt/gntutils.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/finch/libgnt/gntutils.c	Wed Jul 07 20:06:35 2010 +0000
@@ -385,7 +385,6 @@
 	const char *name;
 	char *content;
 	xmlNode *ch;
-	gboolean processed = FALSE;
 	char *url = NULL;
 	gboolean insert_nl_s = FALSE, insert_nl_e = FALSE;
 
@@ -426,17 +425,14 @@
 
 	for (ch = node->children; ch; ch = ch->next) {
 		if (ch->type == XML_ELEMENT_NODE) {
-			processed = TRUE;
 			util_parse_html_to_tv(ch, tv, flag);
+		} else if (ch->type == XML_TEXT_NODE) {
+			content = (char*)xmlNodeGetContent(ch);
+			gnt_text_view_append_text_with_flags(tv, content, flag);
+			xmlFree(content);
 		}
 	}
 
-	if (!processed) {
-		content = (char*)xmlNodeGetContent(node);
-		gnt_text_view_append_text_with_flags(tv, content, flag);
-		xmlFree(content);
-	}
-
 	if (url) {
 		char *href = g_strdup_printf(" (%s)", url);
 		gnt_text_view_append_text_with_flags(tv, href, flag);
--- a/libpurple/Makefile.am	Wed Jul 07 20:04:06 2010 +0000
+++ b/libpurple/Makefile.am	Wed Jul 07 20:06:35 2010 +0000
@@ -170,13 +170,11 @@
 purple_builtheaders = purple.h version.h marshallers.h
 
 marshallers.h: marshallers.list
-	@echo "Generating marshallers.h"
-	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
+	$(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
 
 marshallers.c: marshallers.list marshallers.h
-	@echo "Generating marshallers.c"
-	echo "#include \"marshallers.h\"" > marshallers.c
-	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
+	$(AM_V_GEN)echo "#include \"marshallers.h\"" > marshallers.c
+	$(AM_V_at)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
 
 if ENABLE_DBUS
 
@@ -212,16 +210,16 @@
 	$(srcdir)/protocols/jabber/libxmpp.c
 
 dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
 
 dbus-types.h: dbus-analyze-types.py $(purple_build_coreheaders)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
 
 dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported)
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
+	$(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
 
 dbus-signals.c: dbus-analyze-signals.py $(dbus_signals)
-	cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
+	$(AM_V_GEN)cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
 
 dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
 dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
@@ -236,11 +234,11 @@
 libpurple_client_la_LIBADD = $(DBUS_LIBS)
 
 purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported)
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
+	$(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
 
 purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
+	$(AM_V_at)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
 
 $(libpurple_client_la_OBJECTS): purple-client-bindings.h purple-client-bindings.c
 
--- a/libpurple/plugins/Makefile.mingw	Wed Jul 07 20:04:06 2010 +0000
+++ b/libpurple/plugins/Makefile.mingw	Wed Jul 07 20:06:35 2010 +0000
@@ -55,7 +55,7 @@
 	$(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
 	cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR)
 
-.c.dll:
+%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H)
 	$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $<
 	$(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@
 
--- a/libpurple/protocols/msn/slplink.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/libpurple/protocols/msn/slplink.c	Wed Jul 07 20:06:35 2010 +0000
@@ -214,11 +214,16 @@
 		slplink->swboard = NULL;
 	}
 
-	/* The slplink has no slpcalls in it, release it from the DC. */
-	if (slplink->slp_calls == NULL && slplink->dc != NULL) {
-		slplink->dc->slplink = NULL;
-		msn_dc_destroy(slplink->dc);
-		slplink->dc = NULL;
+	if (slplink->dc != NULL) {
+		if ((slplink->dc->state != DC_STATE_ESTABLISHED && slplink->dc->slpcall == slpcall)
+		 || (slplink->slp_calls == NULL)) {
+			/* The DC is not established and its corresponding slpcall is dead,
+			 * or the slplink has no slpcalls in it and no longer needs the DC.
+			 */
+			slplink->dc->slplink = NULL;
+			msn_dc_destroy(slplink->dc);
+			slplink->dc = NULL;
+		}
 	}
 }
 
--- a/libpurple/protocols/oscar/oscar.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Jul 07 20:06:35 2010 +0000
@@ -2667,10 +2667,10 @@
 					g_free(rtfmsg);
 				}
 			}
-		} else if (args->info.rtfmsg.msgtype == 26)
+		} else if (args->info.rtfmsg.msgtype == 26) {
 			purple_debug_info("oscar", "Sending X-Status Reply\n");
 			icq_relay_xstatus(od, userinfo->bn, args->cookie);
-
+		}
 	}
 	else
 	{
--- a/libpurple/protocols/oscar/util.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/libpurple/protocols/oscar/util.c	Wed Jul 07 20:06:35 2010 +0000
@@ -38,8 +38,8 @@
 static const char * const msgerrreason[] = {
 	N_("Invalid error"),
 	N_("Invalid SNAC"),
-	N_("Rate to host"),
-	N_("Rate to client"),
+	N_("Server rate limit exceeded"),
+	N_("Client rate limit exceeded"),
 	N_("Not logged in"),
 	N_("Service unavailable"),
 	N_("Service not defined"),
--- a/pidgin/gtkimhtmltoolbar.c	Wed Jul 07 20:04:06 2010 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Wed Jul 07 20:06:35 2010 +0000
@@ -1276,6 +1276,15 @@
 }
 
 static void
+button_visibility_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
+{
+	if (GTK_WIDGET_VISIBLE(button))
+		gtk_widget_hide(item);
+	else
+		gtk_widget_show(item);
+}
+
+static void
 button_sensitiveness_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
 {
 	gtk_widget_set_sensitive(item, GTK_WIDGET_IS_SENSITIVE(button));
@@ -1323,6 +1332,7 @@
 	GtkWidget *insert_menu;
 	GtkWidget *menuitem;
 	GtkWidget *sep;
+	GObject *wide_attention_button;
 	int i;
 	struct {
 		const char *label;
@@ -1393,6 +1403,8 @@
 		gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), menuitem);
 		g_signal_connect(G_OBJECT(old), "notify::sensitive",
 				G_CALLBACK(button_sensitiveness_changed), menuitem);
+		g_signal_connect(G_OBJECT(old), "notify::visible",
+				G_CALLBACK(button_visibility_changed), menuitem);
 		gtk_container_foreach(GTK_CONTAINER(menuitem), (GtkCallback)enable_markup, NULL);
 	}
 
@@ -1425,12 +1437,16 @@
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
 	g_signal_connect(G_OBJECT(toolbar->image), "notify::sensitive",
 			G_CALLBACK(button_sensitiveness_changed), menuitem);
+	g_signal_connect(G_OBJECT(toolbar->image), "notify::visible",
+			G_CALLBACK(button_visibility_changed), menuitem);
 
 	menuitem = gtk_menu_item_new_with_mnemonic(_("_Link"));
 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
 	g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive",
 			G_CALLBACK(button_sensitiveness_changed), menuitem);
+	g_signal_connect(G_OBJECT(toolbar->link), "notify::visible",
+			G_CALLBACK(button_visibility_changed), menuitem);
 
 	menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule"));
 	g_signal_connect(G_OBJECT(menuitem), "activate"	, G_CALLBACK(insert_hr_cb), toolbar);
@@ -1467,29 +1483,30 @@
 	gtk_widget_show_all(sep);
 
 	/* Attention */
+	wide_attention_button = g_object_get_data(G_OBJECT(toolbar), "attention");
+
 	attention_button = gtk_button_new();
 	gtk_button_set_relief(GTK_BUTTON(attention_button), GTK_RELIEF_NONE);
 	bbox = gtk_hbox_new(FALSE, 3);
 	gtk_container_add(GTK_CONTAINER(attention_button), bbox);
-	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, 
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION,
 		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
 	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
 	label = gtk_label_new_with_mnemonic(_("_Attention!"));
 	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(box), attention_button, FALSE, FALSE, 0);
-	g_signal_connect_swapped(G_OBJECT(attention_button), "clicked", 
-		G_CALLBACK(gtk_button_clicked),
-	    g_object_get_data(G_OBJECT(toolbar), "attention"));
+	g_signal_connect_swapped(G_OBJECT(attention_button), "clicked",
+		G_CALLBACK(gtk_button_clicked), wide_attention_button);
 	gtk_widget_show_all(attention_button);
-	
-	g_signal_connect(G_OBJECT(g_object_get_data(G_OBJECT(toolbar), "attention")),
-	        "notify::sensitive",
+
+	g_signal_connect(wide_attention_button, "notify::sensitive",
 			G_CALLBACK(button_sensitiveness_changed), attention_button);
+	g_signal_connect(wide_attention_button, "notify::visible",
+			G_CALLBACK(button_visibility_changed), attention_button);
 
 	/* set attention button to be greyed out until we get a conversation */
-	gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(toolbar), "attention"),
-		FALSE);
-	
+	gtk_widget_set_sensitive(GTK_WIDGET(wide_attention_button), FALSE);
+
 	gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
 	g_object_set_data(G_OBJECT(hbox), "lean-view", box);
 	gtk_widget_show(box);
--- a/pidgin/plugins/Makefile.mingw	Wed Jul 07 20:04:06 2010 +0000
+++ b/pidgin/plugins/Makefile.mingw	Wed Jul 07 20:06:35 2010 +0000
@@ -73,7 +73,7 @@
 	$(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install
 	cp *.dll $(PIDGIN_INSTALL_PLUGINS_DIR)
 
-.c.dll:
+%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H)
 	$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $<
 	$(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@