changeset 21269:68d6de0845f9

propagate from branch 'im.pidgin.charkins.dockletgeom' (head 0a163d30bd4e04f722543b3868eb2d934e5e1db8) to branch 'im.pidgin.pidgin.next.minor' (head f286d10af3621cf62257f54718ae194d75be1916)
author Casey Harkins <charkins@pidgin.im>
date Sun, 28 Oct 2007 17:16:42 +0000
parents d41c5443f443 (current diff) 319f5ff72eba (diff)
children 133a8614a41e
files pidgin/gtkdocklet.c
diffstat 168 files changed, 4271 insertions(+), 1237 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Oct 28 17:08:49 2007 +0000
+++ b/COPYRIGHT	Sun Oct 28 17:16:42 2007 +0000
@@ -196,6 +196,7 @@
 Akuke Kok
 Konstantin Korikov
 Cole Kowalski
+Matt Kramer
 Gary Kramlich
 Jan Kratochvil
 Andrej Krivulčík
@@ -267,6 +268,7 @@
 Ruediger Oertel
 Gudmundur Bjarni Olafsson
 Bartosz Oler
+Stefan Ott
 Shawn Outman
 Nathan Owens (pianocomp81)
 John Oyler
--- a/ChangeLog	Sun Oct 28 17:08:49 2007 +0000
+++ b/ChangeLog	Sun Oct 28 17:16:42 2007 +0000
@@ -1,5 +1,31 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.3.0:
+	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
+
+	libpurple:
+	* We now honor a PURPLE_DISABLE_DEPRECATED define to allow plugins to
+	  catch deprecated functions earlier rather than later.
+
+	Pidgin:
+	* If a plugin says it can't be unloaded, we now display an error and
+	  remove the plugin from the list of saved plugins so it won't load
+	  at the next startup.  Previously, we were ignoring this case, which
+	  could lead to crashes.
+	* Mark dialog windows as transient for appropriate parent windows to
+	  help window managers do the right thing  (Gabriel Schulhof)
+
+	Finch:
+	* If a plugin says it can't be unloaded, we now display an error and
+	  remove the plugin from the list of saved plugins so it won't load
+	  at the next startup.  Previously, we were ignoring this case, which
+	  could lead to crashes.
+	* It's possible to bind key-strokes to specific menuitems in the windows.
+	  Read the 'Menus' section in the man-page for details.
+	* 'transpose-chars' operation for the entry boxes. The default key-binding
+	  is ctrl+t.
+	* 'yank' operation for the entry boxes. The default binding is ctrl+y.
+
 version 2.2.2:
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.2
 		NOTE: Due to 2.2.1 being a security fix release, some bugs
@@ -31,6 +57,8 @@
 	* Pidgin's display is now saved with the command line for session
 	  restoration.  (David Mohr)
 	* ICQ Birthday notifications are shown as buddy list emblems.
+	* Plugin actions are now available from the docklet context menu
+	  in addition to the Tool menu of the buddy list.
 
 version 2.2.1 (09/29/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.1
--- a/ChangeLog.API	Sun Oct 28 17:08:49 2007 +0000
+++ b/ChangeLog.API	Sun Oct 28 17:16:42 2007 +0000
@@ -1,13 +1,123 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-Version 2.2.2 (??/??/????):
+version 2.3.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_request_field_blist_nodes_new and its accessory functions.
+		* a PurpleConversation field and an alias field in PurpleConvMessage
+		* account-authorization signals (see account-signals.dox for
+		  details) (Stefan Ott)
+		* libpurple/purple.h, which includes #define's and #include's
+		  required to compile stand-alone plugins
+		* PURPLE_STATUS_TUNE as a new PurpleStatusPrimitive
+		* purple_plugin_disable(), which is intended to be called when
+		  a purple_plugin_unload()--which was called when a user tried
+		  to unload a plugin--fails.  This then prevents the plugin
+		  from being saved in the saved plugins list, so it'll won't
+		  be loaded at the next startup.
+		* purple_util_init()
+		* purple_util_uninit()
+
+		* pidgin_dialogs_about_with_parent()
+		* pidgin_log_show_contact_with_parent()
+		* pidgin_log_show_with_parent()
+		* pidgin_plugin_dialog_show_with_parent()
+		* pidgin_pounce_editor_show_with_parent()
+		* pidgin_pounces_manager_show_with_parent()
+		* pidgin_syslog_show_with_parent()
+		* pidgin_docklet_get_geometry()
+		* pidgin_docklet_get_gdk_screen()
+
+		* purple_request_accept_cancel_with_hint()
+		* purple_request_action_varg_with_hint()
+		* purple_request_action_with_hint()
+		* purple_request_choice_varg_with_hint()
+		* purple_request_choice_with_hint()
+		* purple_request_fields_with_hint()
+		* purple_request_file_with_hint()
+		* purple_request_folder_with_hint()
+		* purple_request_input_with_hint()
+		* purple_request_ok_cancel_with_hint()
+		* purple_request_yes_no_with_hint()
+
+		  UI hints set by libpurple:
+		* PURPLE_REQUEST_UI_HINT_ACCOUNT
+		* PURPLE_REQUEST_UI_HINT_BLIST
+		* PURPLE_REQUEST_UI_HINT_BUDDY
+		* PURPLE_REQUEST_UI_HINT_CONV
+		* PURPLE_REQUEST_UI_HINT_REGISTER
+		* PURPLE_REQUEST_UI_HINT_XFER
+
+		  Common UI hints set by UIs:
+		* PURPLE_REQUEST_UI_HINT_ACCOUNTMGR
+		* PURPLE_REQUEST_UI_HINT_CERTMGR
+		* PURPLE_REQUEST_UI_HINT_PLUGINMGR
+		* PURPLE_REQUEST_UI_HINT_POUNCEMGR
+		* PURPLE_REQUEST_UI_HINT_PREFSMGR
+		* PURPLE_REQUEST_UI_HINT_STATUSMGR
+
+		Changed:
+		* purple_plugin_unload() now honors the return value of a
+		  plugin's unload function and can actually return FALSE now.
+		* purple_plugin_unload() no longer does its own notifications
+		  when a dependent plugin fails to unload.  The UI should do
+		  something appropriate.
+
+		Deprecated:
+		* pidgin_dialogs_about()
+		* pidgin_log_show_contact()
+		* pidgin_log_show()
+		* pidgin_plugin_dialog_show()
+		* pidgin_pounce_editor_show()
+		* pidgin_pounces_manager_show()
+		* pidgin_syslog_show()
+
+		* purple_request_accept_cancel()
+		* purple_request_action_varg()
+		* purple_request_action()
+		* purple_request_choice_varg()
+		* purple_request_choice()
+		* purple_request_fields()
+		* purple_request_file()
+		* purple_request_folder()
+		* purple_request_input()
+		* purple_request_ok_cancel()
+		* purple_request_yes_no()
+
+		MSN:
+		* A new independant status type with PURPLE_STATUS_TUNE primitive, and
+		  PURPLE_TUNE_ARTIST, PURPLE_TUNE_ALBUM and PURPLE_TUNE_TITLE
+		  attributes.
+
+		XMPP:
+		* A new independant status type with PURPLE_STATUS_TUNE primitive, and
+		  PURPLE_TUNE_{ARTIST, TITLE, ALBUM, GENRE, COMMENT, TRACK, TIME,
+		  YEAR, URL} attributes.
+
+	Finch:
+		libgnt:
+		* Added gnt_color_pair, which will try to intelligenty set text
+		  attributes in place of colors if the terminal doesn't have color
+		  support. (Bug: #3560) All future code should use gnt_color_pair
+		  instead of COLOR_PAIR.
+		* Added gnt_menuitem_set_id and gnt_menuitem_get_id to set and get the
+		  string id of a menuitem respectively.
+		* Added gnt_window_get_accel_item, which returns a the id of a menuitem
+		  bound to a keystroke.
+		* Added gnt_menu_get_item to activate and return a menuitem of the
+		  given id from a menu.
+		* Added gnt_menuitem_activate, which triggers the 'activate' signal on
+		  the menuitem and calls the callback function, if available.
+		* Added GntEntryKillRing in GntEntry.
+
+version 2.2.2 (??/??/????):
 	libpurple:
 		Changed:
 		* The size parameter of purple_util_write_data_to_file_absolute
 		  has been changed to gssize instead of a size_t to correctly
 		  indicate that -1 can be used for a nul-delimited string.
 
-Version 2.2.0 (09/13/2007):
+version 2.2.0 (09/13/2007):
 	libpurple:
 		Added:
 		* PURPLE_MESSAGE_INVISIBLE flag, which can be used by
@@ -64,7 +174,7 @@
 		* gnt_util_parse_xhtml_to_textview to parse XHTML strings in a
 		  GntTextView (this works only if libxml2 is available)
 
-Version 2.1.1 (08/20/2007):
+version 2.1.1 (08/20/2007):
 	libpurple:
 		Changed:
 		* PurpleAccountUiOps.request_authorize's authorize_cb and
--- a/Doxyfile.in	Sun Oct 28 17:08:49 2007 +0000
+++ b/Doxyfile.in	Sun Oct 28 17:16:42 2007 +0000
@@ -169,7 +169,8 @@
                          "endsignalproto=@endcode" \
                          "signaldesc=@par Description:" \
                          "signals=@b Signals:" \
-                         "endsignals="
+                         "endsignals=" \
+                         "constreturn=@note The return value of this function must not be modified or freed. @return"
 
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
 # only. Doxygen will then generate output that is more tailored for C. 
@@ -457,7 +458,8 @@
 # excluded from the INPUT source files. This way you can easily exclude a 
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
 
-EXCLUDE                = 
+EXCLUDE                = libpurple/purple-client.h \
+                         libpurple/purple-client-bindings.h
 
 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
 # that are symbolic links (a Unix filesystem feature) are excluded from the input.
@@ -857,7 +859,7 @@
 # feature is still experimental and incomplete at the 
 # moment.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. 
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be 
@@ -1160,7 +1162,7 @@
 # not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
 # If 0 is used for the depth value (the default), the graph is not depth-constrained.
 
-MAX_DOT_GRAPH_DEPTH    = 0
+MAX_DOT_GRAPH_DEPTH    = 2
 
 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
 # background. This is disabled by default, which results in a white background. 
--- a/Makefile.am	Sun Oct 28 17:08:49 2007 +0000
+++ b/Makefile.am	Sun Oct 28 17:16:42 2007 +0000
@@ -48,6 +48,13 @@
 if HAVE_DOXYGEN
 	@echo "Running doxygen..."
 	@doxygen
+if HAVE_XSLTPROC
+	@echo "Generating devhelp index..."
+	@xsltproc doxy2devhelp.xsl doc/xml/index.xml > doc/html/pidgin.devhelp
+	@echo "(Symlink doc/html to ~/.local/share/gtk-doc/html/pidgin to make devhelp see the documentation)"
+else
+	@echo "Not generating devhelp index: xsltproc was not found by configure"
+endif
 else
 	@echo "doxygen was not found during configure.  Aborting."
 	@echo;
--- a/configure.ac	Sun Oct 28 17:08:49 2007 +0000
+++ b/configure.ac	Sun Oct 28 17:16:42 2007 +0000
@@ -43,10 +43,10 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [2])
+m4_define([purple_lt_current], [3])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [2])
-m4_define([purple_micro_version], [2])
+m4_define([purple_minor_version], [3])
+m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
@@ -2101,6 +2101,10 @@
 	[AC_HELP_STRING([--enable-dot],
 		[enable graphs in doxygen via 'dot'])],
 	enable_dot="$enableval", enable_dot="yes")
+AC_ARG_ENABLE(devhelp,
+	[AC_HELP_STRING([--enable-devhelp],
+		[enable building index for devhelp documentation browser])],
+	enable_devhelp="$enableval", enable_devhelp="yes")
 
 if test "x$enable_doxygen" = xyes; then
 	AC_CHECK_PROG(DOXYGEN, doxygen, true, false)
@@ -2120,14 +2124,28 @@
 				AC_DEFINE_UNQUOTED(HAVE_DOT, 1, [whether or not we have dot])
 			fi
 		fi
+
+		if test "x$enable_devhelp" = "xyes"; then
+			AC_CHECK_PROG(XSLTPROC, xsltproc, true, false)
+
+			if test $XSLTPROC = false; then
+				enable_devhelp="no";
+				AC_MSG_WARN([*** xsltproc not found; devhelp index will not be created])
+			else
+				AC_DEFINE_UNQUOTED(HAVE_XSLTPROC, 1, [whether or not we have xsltproc for devhelp index])
+			fi
+		fi
 	fi
 else
 	enable_dot="no"
+	enable_devhelp="no"
 fi
 
 AC_SUBST(enable_doxygen)
 AC_SUBST(enable_dot)
+AC_SUBST(enable_devhelp)
 AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes")
+AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes")
 
 AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
 	[compile with debugging support])], , enable_debug=no)
--- a/doc/account-signals.dox	Sun Oct 28 17:08:49 2007 +0000
+++ b/doc/account-signals.dox	Sun Oct 28 17:16:42 2007 +0000
@@ -9,6 +9,9 @@
   @signal account-setting-info
   @signal account-set-info
   @signal account-status-changed
+  @signal account-authorization-requested
+  @signal account-authorization-denied
+  @signal account-authorization-granted
  @endsignals
 
  @see account.h
@@ -102,5 +105,41 @@
   @param old     The alias before change.
  @endsignaldef
 
+ @signaldef account-authorization-requested
+  @signalproto
+void (*account_authorization_requested)(PurpleAccount *account, const char *user);
+  @endsignalproto
+  @signaldesc
+   Emitted when a user requests authorization.
+  @param account The account.
+  @param user    The name of the user requesting authorization.
+  @return Less than zero to deny the request without prompting, greater
+          than zero if the request should be granted. If zero is returned,
+          then the user will be prompted with the request.
+  @since 2.3.0
+ @endsignaldef
+
+ @signaldef account-authorization-denied
+  @signalproto
+void (*account_authorization_denied)(PurpleAccount *account, const char *user);
+  @endsignalproto
+  @signaldesc
+   Emitted when the authorization request for a buddy is denied.
+  @param account The account.
+  @param user    The name of the user requesting authorization.
+  @since 2.3.0
+ @endsignaldef
+
+ @signaldef account-authorization-granted
+  @signalproto
+void (*account_authorization_granted)(PurpleAccount *account, const char *user);
+  @endsignalproto
+  @signaldesc
+   Emitted when the authorization request for a buddy is granted.
+  @param account The account.
+  @param user    The name of the user requesting authorization.
+  @since 2.3.0
+ @endsignaldef
+
  */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/finch.1.in	Sun Oct 28 17:08:49 2007 +0000
+++ b/doc/finch.1.in	Sun Oct 28 17:16:42 2007 +0000
@@ -351,6 +351,35 @@
 one of \fBa-\fR, \fBalt-\fR, \fBm-\fR or \fBmeta-\fR. You can also use
 \fBhome\fR, \fBend\fR, \fBleft\fR, \fBright\fR etc. keys.
 
+.SH Menus
+You can also specify key-bindings to trigger specific menuitems in windows. For example, the following entry in \fI~/.gntrc\fR will bind \fBCtrl + t\fR to the 'Send IM...' item in the buddylist:
+
+[buddylist::menu]
+.br
+c-t = send-im
+
+The following is the list of IDs of the current menuitems in the buddylist:
+
+send-im
+.br
+join-chat
+.br
+show-empty-groups
+.br
+show-offline-buddies
+.br
+sort-status
+.br
+sort-alpha
+.br
+sort-log
+.br
+add-buddy
+.br
+add-chat
+.br
+add-group
+
 .SH Mouse Support
 There is experimental mouse support. You can focus windows, activate buttons,
 select rows in a list, scroll using the wheel-scroll etc. Selecting text in a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doxy2devhelp.xsl	Sun Oct 28 17:16:42 2007 +0000
@@ -0,0 +1,98 @@
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:fo="http://www.w3.org/1999/XSL/Format"
+    version="1.0">
+
+<!-- Based on http://bur.st/~eleusis/devhelp/doxy2devhelp.xsl
+             (http://bur.st/~eleusis/devhelp/README)
+     which is based on http://bugzilla.gnome.org/show_bug.cgi?id=122450
+-->
+
+<xsl:output method="xml" version="1.0" indent="yes"/>
+
+<xsl:param name="reference_prefix"></xsl:param>
+
+<xsl:template match="/">
+  <book title="Pidgin Documentation"
+        name="pidgin"
+        link="{$reference_prefix}main.html">
+  <chapters>
+    <sub name="Modules" link="{$reference_prefix}modules.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='group']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <!-- annotated.html has the short descriptions beside each struct.  is
+         that more useful than being grouped alphabetically?
+      -->
+    <sub name="Structs" link="{$reference_prefix}classes.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='struct']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <!-- This is redundant given Modules -->
+    <!--
+    <sub name="Directories" link="{$reference_prefix}dirs.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='dir']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    -->
+    <!-- FIXME: Some files show up here but are broken links; mostly
+                files that are under pages...
+      -->
+    <sub name="Files" link="{$reference_prefix}files.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='file']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <sub name="Signals, HOWTOs, Other" link="{$reference_prefix}pages.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='page']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+  </chapters>
+
+  <functions>
+    <!-- @todo: maybe select only the real functions, ie those with kind=="function"? -->
+    <xsl:apply-templates select="doxygenindex/compound/member" mode="as-function"/>
+  </functions>
+  </book>
+</xsl:template>
+
+<xsl:template match="compound">
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="@refid"/>.html</xsl:param>
+  <sub name="{$name}" link="{$reference_prefix}{$link}">
+  <xsl:apply-templates select="member" mode="as-sub">
+    <xsl:sort select="."/>
+  </xsl:apply-templates>
+  </sub>
+</xsl:template>
+
+<xsl:template match="member" mode="as-function">
+  <!--
+  <function name="atk_set_value" link="atk-atkvalue.html#ATK-SET-VALUE"/>
+  -->
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
+  <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
+  <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
+  <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
+  <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
+  <function name="{$name}" link="{$reference_prefix}{$link}"/>
+</xsl:template>
+
+<xsl:template match="member" mode="as-sub">
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
+  <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
+  <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
+  <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
+  <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
+  <sub name="{$name}" link="{$reference_prefix}{$link}"/>
+</xsl:template>
+
+</xsl:stylesheet>
--- a/finch/gntaccount.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntaccount.c	Sun Oct 28 17:16:42 2007 +0000
@@ -632,8 +632,8 @@
 	prompt = g_strdup_printf(_("Are you sure you want to delete %s?"),
 			purple_account_get_username(account));
 
-	purple_request_action(account, _("Delete Account"), prompt, NULL, 0,
-						  account, NULL, NULL, account, 2,
+	purple_request_action_with_hint(account, _("Delete Account"), prompt, NULL, 0,
+						  account, NULL, NULL, PURPLE_REQUEST_UI_HINT_ACCOUNTMGR, account, 2,
 						  _("Delete"), really_delete_account,
 						  _("Cancel"), NULL);
 	g_free(prompt);
@@ -881,10 +881,10 @@
 	data->alias    = (alias != NULL ? g_strdup(alias) : NULL);
 
 	buffer = make_info(account, gc, remote_user, id, alias, msg);
-	purple_request_action(NULL, NULL, _("Add buddy to your list?"),
+	purple_request_action_with_hint(NULL, NULL, _("Add buddy to your list?"),
 	                    buffer, PURPLE_DEFAULT_ACTION_NONE,
 						account, remote_user, NULL,
-						data, 2,
+						PURPLE_REQUEST_UI_HINT_BLIST, data, 2,
 	                    _("Add"),    G_CALLBACK(add_user_cb),
 	                    _("Cancel"), G_CALLBACK(free_add_user_data));
 	g_free(buffer);
@@ -969,10 +969,10 @@
 		gnt_box_set_title(GNT_BOX(uihandle), _("Authorize buddy?"));
 		gnt_box_set_pad(GNT_BOX(uihandle), 0);
 
-		widget = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
+		widget = purple_request_action_with_hint(NULL, _("Authorize buddy?"), buffer, NULL,
 			PURPLE_DEFAULT_ACTION_NONE,
 			account, remote_user, NULL,
-			aa, 2,
+			PURPLE_REQUEST_UI_HINT_BLIST, aa, 2,
 			_("Authorize"), authorize_and_add_cb,
 			_("Deny"), deny_no_add_cb);
 		gnt_screen_release(widget);
@@ -996,10 +996,10 @@
 
 		g_signal_connect_swapped(G_OBJECT(uihandle), "destroy", G_CALLBACK(free_auth_and_add), aa);
 	} else {
-		uihandle = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
+		uihandle = purple_request_action_with_hint(NULL, _("Authorize buddy?"), buffer, NULL,
 			PURPLE_DEFAULT_ACTION_NONE,
 			account, remote_user, NULL,
-			user_data, 2,
+			PURPLE_REQUEST_UI_HINT_BLIST, user_data, 2,
 			_("Authorize"), auth_cb,
 			_("Deny"), deny_cb);
 	}
--- a/finch/gntblist.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntblist.c	Sun Oct 28 17:16:42 2007 +0000
@@ -338,12 +338,12 @@
 		purple_request_field_account_set_value(field, account);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(NULL, _("Add Buddy"), NULL, _("Please enter buddy information."),
+	purple_request_fields_with_hint(NULL, _("Add Buddy"), NULL, _("Please enter buddy information."),
 			fields,
 			_("Add"), G_CALLBACK(add_buddy_cb),
 			_("Cancel"), NULL,
 			account, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -416,11 +416,11 @@
 	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,
+	purple_request_fields_with_hint(NULL, _("Add Chat"), NULL,
 			_("You can edit more information from the context menu later."),
 			fields, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -451,11 +451,11 @@
 static void
 finch_request_add_group()
 {
-	purple_request_input(NULL, _("Add Group"), NULL, _("Enter the name of the group"),
+	purple_request_input_with_hint(NULL, _("Add Group"), NULL, _("Enter the name of the group"),
 			NULL, FALSE, FALSE, NULL,
 			_("Add"), G_CALLBACK(add_group_cb), _("Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static PurpleBlistUiOps blist_ui_ops =
@@ -799,10 +799,10 @@
 
 	g_list_free(parts);
 
-	purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."),
+	purple_request_fields_with_hint(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."),
 			fields, _("Edit"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
 			NULL, NULL, NULL,
-			chat);
+			PURPLE_REQUEST_UI_HINT_BLIST, chat);
 }
 
 static void
@@ -1000,11 +1000,11 @@
 	prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
 
 	text = PURPLE_BLIST_NODE_IS_GROUP(node) ? _("Rename") : _("Set Alias");
-	purple_request_input(node, text, prompt, _("Enter empty string to reset the name."),
+	purple_request_input_with_hint(node, text, prompt, _("Enter empty string to reset the name."),
 			name, FALSE, FALSE, NULL, text, G_CALLBACK(rename_blist_node),
 			_("Cancel"), NULL,
 			NULL, NULL, NULL,
-			node);
+			PURPLE_REQUEST_UI_HINT_BLIST, node);
 
 	g_free(prompt);
 }
@@ -1092,11 +1092,11 @@
 	primary = g_strdup_printf(_("Are you sure you want to remove %s?"), name);
 
 	/* XXX: anything to do with the returned ui-handle? */
-	purple_request_action(node, _("Confirm Remove"),
+	purple_request_action_with_hint(node, _("Confirm Remove"),
 			primary, sec,
 			1,
 			account, name, NULL,
-			node, 2,
+			PURPLE_REQUEST_UI_HINT_BLIST, node, 2,
 			_("Remove"), finch_blist_remove_node,
 			_("Cancel"), NULL);
 	g_free(primary);
@@ -2215,7 +2215,7 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(purple_get_blist(), _("New Instant Message"),
+	purple_request_fields_with_hint(purple_get_blist(), _("New Instant Message"),
 						NULL,
 						_("Please enter the screen name or alias of the person "
 						  "you would like to IM."),
@@ -2223,7 +2223,7 @@
 						_("OK"), G_CALLBACK(send_im_select_cb),
 						_("Cancel"), NULL,
 						NULL, NULL, NULL,
-						NULL);
+						PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -2279,14 +2279,14 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(purple_get_blist(), _("Join a Chat"),
+	purple_request_fields_with_hint(purple_get_blist(), _("Join a Chat"),
 						NULL,
 						_("Please enter the name of the chat you want to join."),
 						fields,
 						_("Join"), G_CALLBACK(join_chat_select_cb),
 						_("Cancel"), NULL,
 						NULL, NULL, NULL,
-						NULL);
+						PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -2328,10 +2328,12 @@
 	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
 
 	item = gnt_menuitem_new(_("Send IM..."));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "send-im");
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL);
 
 	item = gnt_menuitem_new(_("Join Chat..."));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "join-chat");
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), join_chat_select, NULL);
 
@@ -2341,12 +2343,14 @@
 	gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
 
 	item = gnt_menuitem_check_new(_("Empty groups"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-empty-groups");
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
 				purple_prefs_get_bool(PREF_ROOT "/emptygroups"));
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups");
 	
 	item = gnt_menuitem_check_new(_("Offline buddies"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-offline-buddies");
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
 				purple_prefs_get_bool(PREF_ROOT "/showoffline"));
 	gnt_menu_add_item(GNT_MENU(subsub), item);
@@ -2358,14 +2362,17 @@
 	gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
 
 	item = gnt_menuitem_new(_("By Status"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-status");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "status");
 
 	item = gnt_menuitem_new(_("Alphabetically"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-alpha");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "text");
 
 	item = gnt_menuitem_new(_("By Log Size"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-log");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log");
 
@@ -2376,14 +2383,17 @@
 	gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
 
 	item = gnt_menuitem_new("Buddy");
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-buddy");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL);
 
 	item = gnt_menuitem_new("Chat");
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-chat");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL);
 
 	item = gnt_menuitem_new("Group");
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-group");
 	gnt_menu_add_item(GNT_MENU(subsub), item);
 	gnt_menuitem_set_callback(item, menu_add_group_cb, NULL);
 
--- a/finch/gntcertmgr.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntcertmgr.c	Sun Oct 28 17:16:42 2007 +0000
@@ -82,7 +82,7 @@
 	if (crt != NULL) {
 		gchar *default_hostname;
 		default_hostname = purple_certificate_get_subject_name(crt);
-		purple_request_input(NULL,
+		purple_request_input_with_hint(NULL,
 				_("Certificate Import"),
 				_("Specify a hostname"),
 				_("Type the host name this certificate is for."),
@@ -90,7 +90,7 @@
 				_("OK"), G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
 				_("Cancel"), G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
 				NULL, NULL, NULL,
-				crt);
+				PURPLE_REQUEST_UI_HINT_CERTMGR, crt);
 		g_free(default_hostname);
 	} else {
 		gchar * secondary;
@@ -106,13 +106,13 @@
 static void
 add_cert_cb(GntWidget *button, gpointer null)
 {
-	purple_request_file(NULL,
+	purple_request_file_with_hint(NULL,
 			_("Select a PEM certificate"),
 			"certificate.pem",
 			FALSE,
 			G_CALLBACK(tls_peers_mgmt_import_ok_cb),
 			NULL,
-			NULL, NULL, NULL, NULL );
+			NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_CERTMGR, NULL );
 }
 
 /* Save certs in some file */
@@ -155,13 +155,13 @@
 		return;
 	}
 
-	purple_request_file((void*)key,
+	purple_request_file_with_hint((void*)key,
 			_("PEM X.509 Certificate Export"),
 			"certificate.pem", TRUE,
 			G_CALLBACK(tls_peers_mgmt_export_ok_cb),
 			G_CALLBACK(purple_certificate_destroy),
 			NULL, NULL, NULL,
-			crt);
+			PURPLE_REQUEST_UI_HINT_CERTMGR, crt);
 }
 
 /* Show information about a cert */
@@ -233,10 +233,10 @@
 	primary = g_strdup_printf(_("Really delete certificate for %s?"), key);
 
 	purple_request_close_with_handle((void *)key);
-	purple_request_yes_no((void *)key, _("Confirm certificate delete"),
+	purple_request_yes_no_with_hint((void *)key, _("Confirm certificate delete"),
 			primary, NULL,
 			2,
-			NULL, NULL, NULL,
+			NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_CERTMGR,
 			g_strdup(key),
 			tls_peers_mgmt_delete_confirm_cb,
 			g_free);
--- a/finch/gntconn.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntconn.c	Sun Oct 28 17:16:42 2007 +0000
@@ -130,9 +130,9 @@
 				"Finch will not attempt to reconnect the account until you "
 				"correct the error and re-enable the account."), text);
 
-		purple_request_action(account, NULL, primary, secondary, 2,
+		purple_request_action_with_hint(account, NULL, primary, secondary, 2,
 							account, NULL, NULL,
-							account, 3,
+							PURPLE_REQUEST_UI_HINT_BLIST, account, 3,
 							_("OK"), NULL,
 							_("Modify Account"), PURPLE_CALLBACK(ce_modify_account_cb),
 							_("Re-enable Account"), PURPLE_CALLBACK(ce_enable_account_cb));
--- a/finch/gntnotify.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntnotify.c	Sun Oct 28 17:16:42 2007 +0000
@@ -194,6 +194,7 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 	GString *message = g_string_new(NULL);
 	void *ret;
+	static int key = 0;
 
 	if (!detailed)
 	{
@@ -212,7 +213,7 @@
 
 		to = g_strdup_printf("%s (%s)", tos ? *tos : purple_account_get_username(account),
 					purple_account_get_protocol_name(account));
-		gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(time(NULL)),
+		gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(++key),
 				gnt_tree_create_row(GNT_TREE(emaildialog.tree), to,
 					froms ? *froms : "[Unknown sender]",
 					*subjects),
@@ -360,7 +361,8 @@
 	i = 0;
 	for (iter = results->columns; iter; iter = iter->next)
 	{
-		gnt_tree_set_column_title(GNT_TREE(tree), i, iter->data);
+		PurpleNotifySearchColumn *column = iter->data;
+		gnt_tree_set_column_title(GNT_TREE(tree), i, column->title);
 		i++;
 	}
 
--- a/finch/gntplugin.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntplugin.c	Sun Oct 28 17:16:42 2007 +0000
@@ -83,6 +83,7 @@
 
 		if (!purple_plugin_unload(plugin)) {
 			purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL);
+			purple_plugin_disable(plugin);
 			gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE);
 		}
 
@@ -468,9 +469,9 @@
 		}
 	}
 
-	return purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
+	return purple_request_fields_with_hint(NULL, _("Preferences"), NULL, NULL, fields,
 			_("Save"), G_CALLBACK(finch_request_save_in_prefs), _("Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_PLUGINMGR, NULL);
 }
 
--- a/finch/gntpounce.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntpounce.c	Sun Oct 28 17:16:42 2007 +0000
@@ -660,9 +660,9 @@
 	pouncer = purple_account_get_username(account);
 	pouncee = purple_pounce_get_pouncee(pounce);
 	buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
-	purple_request_action(pounce, NULL, buf, NULL, 0,
+	purple_request_action_with_hint(pounce, NULL, buf, NULL, 0,
 						account, pouncee, NULL,
-						pounce, 2,
+						PURPLE_REQUEST_UI_HINT_POUNCEMGR, pounce, 2,
 						_("Delete"), pounces_manager_delete_confirm_cb,
 						_("Cancel"), NULL);
 	g_free(buf);
--- a/finch/gntprefs.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntprefs.c	Sun Oct 28 17:16:42 2007 +0000
@@ -261,9 +261,9 @@
 	add_pref_group(fields, _("Idle"), idle);
 
 	pref_request.showing = TRUE;
-	pref_request.window = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
+	pref_request.window = purple_request_fields_with_hint(NULL, _("Preferences"), NULL, NULL, fields,
 			_("Save"), G_CALLBACK(save_cb), _("Cancel"), free_strings,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_PREFSMGR, NULL);
 }
 
--- a/finch/gntrequest.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntrequest.c	Sun Oct 28 17:16:42 2007 +0000
@@ -148,7 +148,7 @@
 		const char *ok_text, GCallback ok_cb,
 		const char *cancel_text, GCallback cancel_cb,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
-		void *user_data)
+		const char *ui_hint, void *user_data)
 {
 	GntWidget *window, *box, *entry;
 
@@ -205,7 +205,7 @@
 		const char *ok_text, GCallback ok_cb,
 		const char *cancel_text, GCallback cancel_cb,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
-		void *user_data, va_list choices)
+		const char *ui_hint, void *user_data, va_list choices)
 {
 	GntWidget *window, *combo, *box;
 	const char *text;
@@ -249,7 +249,7 @@
 finch_request_action(const char *title, const char *primary,
 		const char *secondary, int default_value,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
-		void *user_data, size_t actioncount,
+		const char *ui_hint, void *user_data, size_t actioncount,
 		va_list actions)
 {
 	GntWidget *window, *box, *button;
@@ -393,7 +393,7 @@
 		const char *ok, GCallback ok_cb,
 		const char *cancel, GCallback cancel_cb,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
-		void *userdata)
+		const char *ui_hint, void *userdata)
 {
 	GntWidget *window, *box;
 	GList *grlist;
@@ -645,7 +645,7 @@
 				gboolean savedialog,
 				GCallback ok_cb, GCallback cancel_cb,
 				PurpleAccount *account, const char *who, PurpleConversation *conv,
-				void *user_data)
+				const char *ui_hint, void *user_data)
 {
 	GntWidget *window = gnt_file_sel_new();
 	GntFileSel *sel = GNT_FILE_SEL(window);
--- a/finch/gntstatus.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/gntstatus.c	Sun Oct 28 17:16:42 2007 +0000
@@ -138,9 +138,9 @@
 	ask = g_strdup_printf(_("Are you sure you want to delete \"%s\""),
 			purple_savedstatus_get_title(saved));
 
-	purple_request_action(saved, _("Delete Status"), ask, NULL, 0,
+	purple_request_action_with_hint(saved, _("Delete Status"), ask, NULL, 0,
 			NULL, NULL, NULL,
-			saved, 2,
+			PURPLE_REQUEST_UI_HINT_STATUSMGR, saved, 2,
 			_("Delete"), really_delete_status,
 			_("Cancel"), NULL);
 	g_free(ask);
--- a/finch/libgnt/gnt.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gnt.h	Sun Oct 28 17:16:42 2007 +0000
@@ -46,19 +46,19 @@
 #endif
 
 /**
- * 
+ * Initialize GNT.
  */
 void gnt_init(void);
 
 /**
- * 
+ * Start running the mainloop for gnt.
  */
 void gnt_main(void);
 
 /**
- * 
+ * Check whether the terminal is capable of UTF8 display.
  *
- * @return
+ * @return  @c FALSE if the terminal is capable of drawing UTF-8, @c TRUE otherwise.
  */
 gboolean gnt_ascii_only(void);
 
@@ -71,106 +71,133 @@
  * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_window_present(GntWidget *window);
+
 /**
- * 
- * @param widget
+ * @internal
+ * Use #gnt_widget_show instead.
  */
 void gnt_screen_occupy(GntWidget *widget);
 
 /**
- * 
- * @param widget
+ * @internal
+ * Use #gnt_widget_hide instead.
  */
 void gnt_screen_release(GntWidget *widget);
 
 /**
- * 
- * @param widget
+ * @internal
+ * Use #gnt_widget_draw instead.
  */
 void gnt_screen_update(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param width
- * @param height
+ * Resize a widget.
+ *
+ * @param widget  The widget to resize.
+ * @param width   The desired width.
+ * @param height  The desired height.
  */
 void gnt_screen_resize_widget(GntWidget *widget, int width, int height);
 
 /**
- * 
- * @param widget
- * @param x
- * @param y
+ * Move a widget.
+ *
+ * @param widget The widget to move.
+ * @param x      The desired x-coordinate.
+ * @param y      The desired y-coordinate.
  */
 void gnt_screen_move_widget(GntWidget *widget, int x, int y);
 
 /**
- * 
- * @param widget
- * @param text
+ * Rename a widget.
+ *
+ * @param widget  The widget to rename.
+ * @param text    The new name for the widget.
  */
 void gnt_screen_rename_widget(GntWidget *widget, const char *text);
 
 /**
- * 
- * @param widget
+ * Check whether a widget has focus.
  *
- * @return
+ * @param widget  The widget.
+ *
+ * @return  @c TRUE if the widget has the current focus, @c FALSE otherwise.
  */
 gboolean gnt_widget_has_focus(GntWidget *widget);
 
 /**
- * 
- * @param widget
+ * Set the URGENT hint for a widget.
+ *
+ * @param widget  The widget to set the URGENT hint for.
  */
 void gnt_widget_set_urgent(GntWidget *widget);
 
 /**
- * 
- * @param label
- * @param callback
+ * Register a global action.
+ *
+ * @param label      The user-visible label for the action.
+ * @param callback   The callback function for the action.
  */
 void gnt_register_action(const char *label, void (*callback)());
 
 /**
- * 
- * @param menu
+ * Show a menu.
  *
- * @return
+ * @param menu  The menu to display.
+ *
+ * @return @c TRUE if the menu is displayed, @c FALSE otherwise (e.g., if another menu is currently displayed).
  */
 gboolean gnt_screen_menu_show(gpointer menu);
 
 /**
- * 
+ * Terminate the mainloop of gnt.
  */
 void gnt_quit(void);
 
 /**
- * 
+ * Get the global clipboard.
  *
- * @return
+ * @return  The clipboard.
  */
 GntClipboard * gnt_get_clipboard(void);
 
 /**
- * 
+ * Get the string in the clipboard.
  *
- * @return
+ * @return A copy of the string in the clipboard. The caller must @c g_free the string.
  */
 gchar * gnt_get_clipboard_string(void);
 
 /**
- * 
- * @param string
+ * Set the contents of the global clipboard.
+ *
+ * @param string  The new content of the new clipboard.
  */
-void gnt_set_clipboard_string(gchar *string);
+void gnt_set_clipboard_string(const gchar *string);
 
 /**
  * Spawn a different application that will consume the console.
+ *
+ * @param wd    The working directory for the new application.
+ * @param argv  The argument vector.
+ * @param envp  The environment, or @c NULL.
+ * @param stin  Location to store the child's stdin, or @c NULL.
+ * @param stout Location to store the child's stdout, or @c NULL.
+ * @param sterr Location to store the child's stderr, or @c NULL.
+ * @param callback   The callback to call after the child exits.
+ * @param data  The data to pass to the callback.
+ *
+ * @return  @c TRUE if the child was successfully spawned, @c FALSE otherwise.
  */
 gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
 		gint *stin, gint *stout, gint *sterr,
 		void (*callback)(int status, gpointer data), gpointer data);
 
+/**
+ * Check whether a child process is in control of the current terminal.
+ *
+ * @return @c TRUE if a child process (eg., PAGER) is occupying the current
+ *         terminal, @c FALSE otherwise.
+ */
 gboolean gnt_is_refugee(void);
+
--- a/finch/libgnt/gntbindable.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntbindable.h	Sun Oct 28 17:16:42 2007 +0000
@@ -160,8 +160,8 @@
 
 /**
  * Returns a GntTree populated with "key" -> "binding" for the widget.
- * 
- * @param widget  The object to list the bindings for.
+ *
+ * @param bind  The object to list the bindings for.
  *
  * @return   The GntTree.
  */
@@ -170,9 +170,9 @@
 /**
  * Builds a window that list the key bindings for a GntBindable object.
  * From this window a user can select a listing to rebind a new key for the given action.
- * 
+ *
  * @param bindable   The object to list the bindings for.
- *	
+ *
  * @return  @c TRUE
  */
 
--- a/finch/libgnt/gntbox.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntbox.c	Sun Oct 28 17:16:42 2007 +0000
@@ -27,6 +27,13 @@
 
 enum
 {
+	PROP_0,
+	PROP_VERTICAL,
+	PROP_HOMO        /* ... */
+};
+
+enum
+{
 	SIGS = 1,
 };
 
@@ -80,12 +87,12 @@
 		get_title_thingies(box, title, &pos, &right);
 
 		if (gnt_widget_has_focus(widget))
-			wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TITLE));
+			wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TITLE));
 		else
-			wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TITLE_D));
-		mvwaddch(widget->window, 0, pos-1, ACS_RTEE | COLOR_PAIR(GNT_COLOR_NORMAL));
+			wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TITLE_D));
+		mvwaddch(widget->window, 0, pos-1, ACS_RTEE | gnt_color_pair(GNT_COLOR_NORMAL));
 		mvwaddstr(widget->window, 0, pos, title);
-		mvwaddch(widget->window, 0, right, ACS_LTEE | COLOR_PAIR(GNT_COLOR_NORMAL));
+		mvwaddch(widget->window, 0, right, ACS_LTEE | gnt_color_pair(GNT_COLOR_NORMAL));
 		g_free(title);
 	}
 	
@@ -511,8 +518,44 @@
 }
 
 static void
+gnt_box_set_property(GObject *obj, guint prop_id, const GValue *value,
+		GParamSpec *spec)
+{
+	GntBox *box = GNT_BOX(obj);
+	switch (prop_id) {
+		case PROP_VERTICAL:
+			box->vertical = g_value_get_boolean(value);
+			break;
+		case PROP_HOMO:
+			box->homogeneous = g_value_get_boolean(value);
+			break;
+		default:
+			g_return_if_reached();
+			break;
+	}
+}
+
+static void
+gnt_box_get_property(GObject *obj, guint prop_id, GValue *value,
+		GParamSpec *spec)
+{
+	GntBox *box = GNT_BOX(obj);
+	switch (prop_id) {
+		case PROP_VERTICAL:
+			g_value_set_boolean(value, box->vertical);
+			break;
+		case PROP_HOMO:
+			g_value_set_boolean(value, box->homogeneous);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
 gnt_box_class_init(GntBoxClass *klass)
 {
+	GObjectClass *gclass = G_OBJECT_CLASS(klass);
 	parent_class = GNT_WIDGET_CLASS(klass);
 	parent_class->destroy = gnt_box_destroy;
 	parent_class->draw = gnt_box_draw;
@@ -527,7 +570,24 @@
 	parent_class->confirm_size = gnt_box_confirm_size;
 	parent_class->size_changed = gnt_box_size_changed;
 
-	GNTDEBUG;
+	gclass->set_property = gnt_box_set_property;
+	gclass->get_property = gnt_box_get_property;
+	g_object_class_install_property(gclass,
+			PROP_VERTICAL,
+			g_param_spec_boolean("vertical", "Vertical",
+				"Whether the child widgets in the box should be stacked vertically.",
+				TRUE,
+				G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+			)
+		);
+	g_object_class_install_property(gclass,
+			PROP_HOMO,
+			g_param_spec_boolean("homogeneous", "Homogeneous",
+				"Whether the child widgets in the box should have the same size.",
+				TRUE,
+				G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+			)
+		);
 }
 
 static void
@@ -603,7 +663,7 @@
 		/* Erase the old title */
 		int pos, right;
 		get_title_thingies(b, prev, &pos, &right);
-		mvwhline(w->window, 0, pos - 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwhline(w->window, 0, pos - 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				right - pos + 2);
 		g_free(prev);
 	}
--- a/finch/libgnt/gntbutton.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntbutton.c	Sun Oct 28 17:16:42 2007 +0000
@@ -47,7 +47,7 @@
 	else
 		type = GNT_COLOR_NORMAL;
 
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(type));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(type));
 	mvwaddstr(widget->window, (small_button) ? 0 : 1, 2, button->priv->text);
 	if (small_button) {
 		type = GNT_COLOR_HIGHLIGHT;
@@ -126,6 +126,7 @@
 	widget->priv.minh = small_button ? 1 : 3;
 	if (small_button)
 		GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
+	GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y);
 	GNTDEBUG;
 }
 
--- a/finch/libgnt/gntcheckbox.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntcheckbox.c	Sun Oct 28 17:16:42 2007 +0000
@@ -43,13 +43,13 @@
 	else
 		type = GNT_COLOR_NORMAL;
 	
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(type));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(type));
 
 	text = g_strdup_printf("[%c]", cb->checked ? 'X' : ' ');
 	mvwaddstr(widget->window, 0, 0, text);
 	g_free(text);
 
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 	mvwaddstr(widget->window, 0, 4, GNT_BUTTON(cb)->priv->text);
 	
 	GNTDEBUG;
--- a/finch/libgnt/gntcolors.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntcolors.c	Sun Oct 28 17:16:42 2007 +0000
@@ -32,6 +32,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+static gboolean hascolors;
 static struct
 {
 	short r, g, b;
@@ -75,6 +76,8 @@
 	init = TRUE;
 
 	start_color();
+	if (!(hascolors = has_colors()))
+		return;
 	defaults = use_default_colors();
 
 	if (can_use_custom_color())
@@ -276,3 +279,11 @@
 }
 
 #endif  /* GKeyFile */
+
+int gnt_color_pair(int pair)
+{
+	return (hascolors ? COLOR_PAIR(pair) :
+		((pair == GNT_COLOR_NORMAL || pair == GNT_COLOR_HIGHLIGHT_D ||
+		  pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : A_STANDOUT));
+}
+
--- a/finch/libgnt/gntcolors.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntcolors.h	Sun Oct 28 17:16:42 2007 +0000
@@ -88,4 +88,17 @@
 
 #endif
 
+/**
+ * Return the appropriate character attribute for a specified color.
+ * If the terminal doesn't have color support, this returns A_STANDOUT
+ * when deemed appropriate.
+ *
+ * @param color   The color code.
+ *
+ * @return  A character attribute.
+ *
+ * @since 2.3.0
+ */
+int gnt_color_pair(int color);
+
 #endif
--- a/finch/libgnt/gntcombobox.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntcombobox.c	Sun Oct 28 17:16:42 2007 +0000
@@ -85,15 +85,15 @@
 	else
 		type = GNT_COLOR_NORMAL;
 
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(type));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(type));
 
 	s = (char*)gnt_util_onscreen_width_to_pointer(text, widget->priv.width - 4, &len);
 	*s = '\0';
 
 	mvwaddstr(widget->window, 1, 1, text);
-	whline(widget->window, ' ' | COLOR_PAIR(type), widget->priv.width - 4 - len);
-	mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL));
-	mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | COLOR_PAIR(GNT_COLOR_NORMAL));
+	whline(widget->window, ' ' | gnt_color_pair(type), widget->priv.width - 4 - len);
+	mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL));
+	mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | gnt_color_pair(GNT_COLOR_NORMAL));
 
 	g_free(text);
 	GNTDEBUG;
--- a/finch/libgnt/gntentry.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntentry.c	Sun Oct 28 17:16:42 2007 +0000
@@ -36,6 +36,24 @@
 	SIG_COMPLETION,
 	SIGS,
 };
+
+typedef enum
+{
+	ENTRY_JAIL = -1,    /* Suspend the kill ring. */
+	ENTRY_DEL_BWD_WORD = 1,
+	ENTRY_DEL_BWD_CHAR,
+	ENTRY_DEL_FWD_WORD,
+	ENTRY_DEL_FWD_CHAR,
+	ENTRY_DEL_EOL,
+	ENTRY_DEL_BOL,
+} GntEntryAction;
+
+struct _GntEntryKillRing
+{
+	GString *buffer;
+	GntEntryAction last;
+};
+
 static guint signals[SIGS] = { 0 };
 
 static GntWidgetClass *parent_class = NULL;
@@ -43,6 +61,58 @@
 static gboolean gnt_entry_key_pressed(GntWidget *widget, const char *text);
 static void gnt_entry_set_text_internal(GntEntry *entry, const char *text);
 
+static gboolean
+update_kill_ring(GntEntry *entry, GntEntryAction action, const char *text, int len)
+{
+	if (action < 0) {
+		entry->killring->last = action;
+		return FALSE;
+	}
+
+	if (len == 0)
+		len = strlen(text);
+	else if (len < 0) {
+		text += len;
+		len = -len;
+	}
+
+	if (action != entry->killring->last) {
+		struct {
+			GntEntryAction one;
+			GntEntryAction two;
+		} merges[] = {
+			{ENTRY_DEL_BWD_WORD, ENTRY_DEL_FWD_WORD},
+			{ENTRY_DEL_BWD_CHAR, ENTRY_DEL_FWD_CHAR},
+			{ENTRY_DEL_BOL, ENTRY_DEL_EOL},
+			{ENTRY_JAIL, ENTRY_JAIL},
+		};
+		int i;
+
+		for (i = 0; merges[i].one != ENTRY_JAIL; i++) {
+			if (merges[i].one == entry->killring->last &&
+					merges[i].two == action) {
+				g_string_append_len(entry->killring->buffer, text, len);
+				break;
+			} else if (merges[i].one == action &&
+					merges[i].two == entry->killring->last) {
+				g_string_prepend_len(entry->killring->buffer, text, len);
+				break;
+			}
+		}
+		if (merges[i].one == ENTRY_JAIL) {
+			g_string_assign(entry->killring->buffer, text);
+			g_string_truncate(entry->killring->buffer, len);
+		}
+		entry->killring->last = action;
+	} else {
+		if (action == ENTRY_DEL_BWD_CHAR || action == ENTRY_DEL_BWD_WORD)
+			g_string_prepend_len(entry->killring->buffer, text, len);
+		else
+			g_string_append_len(entry->killring->buffer, text, len);
+	}
+	return TRUE;
+}
+
 static void
 destroy_suggest(GntEntry *entry)
 {
@@ -97,6 +167,7 @@
 	if (changed)
 		g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0,
 				entry->start + offstart, entry->start + offend);
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	return changed;
 }
 
@@ -201,9 +272,9 @@
 	gboolean focus;
 
 	if ((focus = gnt_widget_has_focus(widget)))
-		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TEXT_NORMAL));
+		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TEXT_NORMAL));
 	else
-		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 
 	if (entry->masked)
 	{
@@ -264,6 +335,7 @@
 	entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor);
 	if (entry->cursor < entry->scroll)
 		entry->scroll = entry->cursor;
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	entry_redraw(GNT_WIDGET(entry));
 	return TRUE;
 }
@@ -277,6 +349,7 @@
 	entry->cursor = g_utf8_find_next_char(entry->cursor, NULL);
 	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
 		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	entry_redraw(GNT_WIDGET(entry));
 	return TRUE;
 }
@@ -289,9 +362,11 @@
 
 	if (entry->cursor <= entry->start)
 		return TRUE;
-	
+
 	len = entry->cursor - g_utf8_find_prev_char(entry->start, entry->cursor);
+	update_kill_ring(entry, ENTRY_DEL_BWD_CHAR, entry->cursor, -len);
 	entry->cursor -= len;
+
 	memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor);
 	entry->end -= len;
 
@@ -313,8 +388,9 @@
 
 	if (entry->cursor >= entry->end)
 		return FALSE;
-	
+
 	len = g_utf8_find_next_char(entry->cursor, NULL) - entry->cursor;
+	update_kill_ring(entry, ENTRY_DEL_FWD_CHAR, entry->cursor, len);
 	memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor - len + 1);
 	entry->end -= len;
 	entry_redraw(GNT_WIDGET(entry));
@@ -331,6 +407,7 @@
 	GntEntry *entry = GNT_ENTRY(bind);
 	entry->scroll = entry->cursor = entry->start;
 	entry_redraw(GNT_WIDGET(entry));
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	return TRUE;
 }
 
@@ -343,6 +420,7 @@
 	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
 		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
 	entry_redraw(GNT_WIDGET(entry));
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	return TRUE;
 }
 
@@ -357,6 +435,7 @@
 		destroy_suggest(entry);
 		entry_text_changed(entry);
 
+		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 		return TRUE;
 	}
 	return FALSE;
@@ -381,6 +460,7 @@
 		destroy_suggest(entry);
 		entry_text_changed(entry);
 
+		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 		return TRUE;
 	}
 	return FALSE;
@@ -400,6 +480,7 @@
 	a = g_strndup(entry->start, entry->cursor - entry->start);
 	all = g_strconcat(a, text, entry->cursor, NULL);
 	gnt_entry_set_text_internal(entry, all);
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	g_free(a);
 	g_free(text);
 	g_free(all);
@@ -445,6 +526,7 @@
 	GntEntry *entry = GNT_ENTRY(bind);
 	if (entry->cursor <= entry->start)
 		return TRUE;
+	update_kill_ring(entry, ENTRY_DEL_BOL, entry->start, entry->cursor - entry->start);
 	memmove(entry->start, entry->cursor, entry->end - entry->cursor);
 	entry->end -= (entry->cursor - entry->start);
 	entry->cursor = entry->scroll = entry->start;
@@ -460,6 +542,7 @@
 	GntEntry *entry = GNT_ENTRY(bind);
 	if (entry->end <= entry->cursor)
 		return TRUE;
+	update_kill_ring(entry, ENTRY_DEL_EOL, entry->cursor, entry->end - entry->cursor);
 	entry->end = entry->cursor;
 	memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
 	entry_redraw(GNT_WIDGET(bind));
@@ -517,6 +600,7 @@
 	entry->cursor = (char*)iter;
 	if (entry->cursor < entry->scroll)
 		entry->scroll = entry->cursor;
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	entry_redraw(GNT_WIDGET(bind));
 	return TRUE;
 }
@@ -533,6 +617,7 @@
 		return TRUE;
 	iter = (char*)begin_word(iter, entry->start);
 	count = entry->cursor - iter;
+	update_kill_ring(entry, ENTRY_DEL_BWD_WORD, iter, count);
 	memmove(iter, entry->cursor, entry->end - entry->cursor);
 	entry->end -= count;
 	entry->cursor = iter;
@@ -557,6 +642,7 @@
 	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width) {
 		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
 	}
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 	entry_redraw(widget);
 	return TRUE;
 }
@@ -570,6 +656,7 @@
 	int len = entry->end - iter + 1;
 	if (len <= 0)
 		return TRUE;
+	update_kill_ring(entry, ENTRY_DEL_FWD_WORD, entry->cursor, iter - entry->cursor);
 	memmove(entry->cursor, iter, len);
 	len = iter - entry->cursor;
 	entry->end -= len;
@@ -580,6 +667,42 @@
 }
 
 static gboolean
+transpose_chars(GntBindable *bind, GList *null)
+{
+	GntEntry *entry = GNT_ENTRY(bind);
+	char *current, *prev;
+	char hold[8];  /* that's right */
+
+	if (entry->cursor <= entry->start)
+		return FALSE;
+
+	if (!*entry->cursor)
+		entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor);
+
+	current = entry->cursor;
+	prev = g_utf8_find_prev_char(entry->start, entry->cursor);
+	move_forward(bind, null);
+
+	/* Let's do this dance! */
+	memcpy(hold, prev, current - prev);
+	memmove(prev, current, entry->cursor - current);
+	memcpy(prev + (entry->cursor - current), hold, current - prev);
+
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
+	entry_redraw(GNT_WIDGET(entry));
+	entry_text_changed(entry);
+	return TRUE;
+}
+
+static gboolean
+entry_yank(GntBindable *bind, GList *null)
+{
+	GntEntry *entry = GNT_ENTRY(bind);
+	gnt_entry_key_pressed(GNT_WIDGET(entry), entry->killring->buffer->str);
+	return TRUE;
+}
+
+static gboolean
 gnt_entry_key_pressed(GntWidget *widget, const char *text)
 {
 	GntEntry *entry = GNT_ENTRY(widget);
@@ -602,6 +725,7 @@
 			destroy_suggest(entry);
 			complete_suggest(entry, text);
 			g_free(text);
+			update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 			entry_text_changed(entry);
 			return TRUE;
 		}
@@ -658,6 +782,7 @@
 				if (entry->ddown)
 					show_suggest_dropdown(entry);
 			}
+			update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
 			entry_redraw(widget);
 			entry_text_changed(entry);
 			return TRUE;
@@ -668,6 +793,13 @@
 }
 
 static void
+jail_killring(GntEntryKillRing *kr)
+{
+	g_string_free(kr->buffer, TRUE);
+	g_free(kr);
+}
+
+static void
 gnt_entry_destroy(GntWidget *widget)
 {
 	GntEntry *entry = GNT_ENTRY(widget);
@@ -690,6 +822,8 @@
 	{
 		gnt_widget_destroy(entry->ddown->parent);
 	}
+
+	jail_killring(entry->killring);
 }
 
 static void
@@ -762,6 +896,10 @@
 				"\033" "f", NULL);
 	gnt_bindable_class_register_action(bindable, "delete-next-word", delete_forward_word,
 				"\033" "d", NULL);
+	gnt_bindable_class_register_action(bindable, "transpose-chars", transpose_chars,
+				GNT_KEY_CTRL_T, NULL);
+	gnt_bindable_class_register_action(bindable, "yank", entry_yank,
+				GNT_KEY_CTRL_Y, NULL);
 	gnt_bindable_class_register_action(bindable, "suggest-show", suggest_show,
 				"\t", NULL);
 	gnt_bindable_class_register_action(bindable, "suggest-next", suggest_next,
@@ -779,6 +917,14 @@
 	GNTDEBUG;
 }
 
+static GntEntryKillRing *
+new_killring()
+{
+	GntEntryKillRing *kr = g_new0(GntEntryKillRing, 1);
+	kr->buffer = g_string_new(NULL);
+	return kr;
+}
+
 static void
 gnt_entry_init(GTypeInstance *instance, gpointer class)
 {
@@ -787,13 +933,14 @@
 
 	entry->flag = GNT_ENTRY_FLAG_ALL;
 	entry->max = 0;
-	
+
 	entry->histlength = 0;
 	entry->history = NULL;
 
 	entry->word = TRUE;
 	entry->always = FALSE;
 	entry->suggests = NULL;
+	entry->killring = new_killring();
 
 	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry),
 			GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS);
@@ -801,7 +948,7 @@
 
 	widget->priv.minw = 3;
 	widget->priv.minh = 1;
-	
+
 	GNTDEBUG;
 }
 
@@ -992,7 +1139,7 @@
 
 	if (!text || !*text)
 		return;
-	
+
 	find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate);
 	if (find)
 		return;
--- a/finch/libgnt/gntentry.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntentry.h	Sun Oct 28 17:16:42 2007 +0000
@@ -48,6 +48,7 @@
 typedef struct _GntEntry			GntEntry;
 typedef struct _GntEntryPriv		GntEntryPriv;
 typedef struct _GntEntryClass	GntEntryClass;
+typedef struct _GntEntryKillRing    GntEntryKillRing;
 
 typedef enum
 {
@@ -71,9 +72,9 @@
 	char *scroll;   /* Current scrolling position */
 	char *cursor;   /* Cursor location */
 	                /* 0 <= cursor - scroll < widget-width */
-	
+
 	size_t buffer;  /* Size of the buffer */
-	
+
 	int max;        /* 0 means infinite */
 	gboolean masked;
 
@@ -84,6 +85,7 @@
 	gboolean word;      /* Are the suggestions for only a word, or for the whole thing? */
 	gboolean always;    /* Should the list of suggestions show at all times, or only on tab-press? */
 	GntWidget *ddown;   /* The dropdown with the suggested list */
+	GntEntryKillRing *killring; /**< @since 2.3.0 */
 };
 
 struct _GntEntryClass
--- a/finch/libgnt/gntfilesel.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntfilesel.c	Sun Oct 28 17:16:42 2007 +0000
@@ -718,6 +718,7 @@
 
 void gnt_file_sel_set_suggested_filename(GntFileSel *sel, const char *suggest)
 {
+	g_free(sel->suggest);
 	sel->suggest = g_strdup(suggest);
 }
 
--- a/finch/libgnt/gntfilesel.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntfilesel.h	Sun Oct 28 17:16:42 2007 +0000
@@ -98,113 +98,123 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntFileSel.
  */
 GType gnt_file_sel_get_gtype(void);
 
 /**
- * 
+ * Create a new file selector.
  *
- * @return
+ * @return  The newly created file selector.
  */
 GntWidget * gnt_file_sel_new(void);
 
 /**
- * 
- * @param sel
- * @param path
+ * Set the current location of the file selector.
  *
- * @return
+ * @param sel   The file selector.
+ * @param path  The current path of the selector.
+ *
+ * @return @c TRUE if the current location was successfully changed, @c FALSE otherwise.
  */
 gboolean gnt_file_sel_set_current_location(GntFileSel *sel, const char *path);
 
 /**
- * 
- * @param sel
- * @param dirs
+ * Set wheter to only allow selecting directories.
+ *
+ * @param sel    The file selector.
+ * @param dirs   @c TRUE if only directories can be selected, @c FALSE if files
+ *               can also be selected.
  */
 void gnt_file_sel_set_dirs_only(GntFileSel *sel, gboolean dirs);
 
 /**
- * 
- * @param sel
+ * Check whether the file selector allows only selecting directories.
  *
- * @return
+ * @param sel  The file selector.
+ *
+ * @return  @c TRUE if only directories can be selected.
  */
 gboolean gnt_file_sel_get_dirs_only(GntFileSel *sel);
 
 /**
- * 
- * @param sel
- * @param must
+ * Set whether a selected file must exist.
+ *
+ * @param sel   The file selector.
+ * @param must  @c TRUE if the selected file must exist.
  */
 void gnt_file_sel_set_must_exist(GntFileSel *sel, gboolean must);
 
 /**
- * 
- * @param sel
+ * Check whether the selector allows selecting non-existent files.
  *
- * @return
+ * @param sel  The file selector.
+ *
+ * @return  @c TRUE if the selected file must exist, @c FALSE if a non-existent
+ *          file can be selected.
  */
 gboolean gnt_file_sel_get_must_exist(GntFileSel *sel);
 
 /**
- * 
- * @param sel
+ * Get the selected file in the selector.
  *
- * @return
+ * @param sel  The file selector.
+ *
+ * @return The path of the selected file. The caller should g_free the returned
+ *         string.
  */
 char * gnt_file_sel_get_selected_file(GntFileSel *sel);
 
-  /* The returned value should be free'd */
-
 /**
- * 
- * @param sel
+ * Get the list of selected files in the selector.
  *
- * @return
+ * @param sel  The file selector.
+ *
+ * @return  A list of paths for the selected files. The caller must g_free the
+ *          contents of the list, and g_list_free the list.
  */
 GList * gnt_file_sel_get_selected_multi_files(GntFileSel *sel);
 
 /**
- * 
- * @param sel
- * @param set
+ * Allow selecting multiple files.
+ *
+ * @param sel  The file selector.
+ * @param set  @c TRUE if selecting multiple files should be allowed.
  */
 void gnt_file_sel_set_multi_select(GntFileSel *sel, gboolean set);
 
 /**
- * 
- * @param sel
- * @param suggest
+ * Set the suggested file to have selected at startup.
+ *
+ * @param sel      The file selector.
+ * @param suggest  The suggested filename.
  */
 void gnt_file_sel_set_suggested_filename(GntFileSel *sel, const char *suggest);
 
 /**
- * 
- * @param sel
- * @param path
- * @param files
- * @param error)
+ * Set custom functions to read the names of files.
+ *
+ * @param sel      The file selector.
+ * @param read_fn  The custom read function.
  */
 void gnt_file_sel_set_read_fn(GntFileSel *sel, gboolean (*read_fn)(const char *path, GList **files, GError **error));
 
 /**
- * 
- * @param name
- * @param size
+ * Create a new GntFile.
  *
- * @return
+ * @param name   The name of the file.
+ * @param size   The size of the file.
+ *
+ * @return  The newly created GntFile.
  */
 GntFile* gnt_file_new(const char *name, unsigned long size);
 
 /**
- * 
- * @param name
+ * Create a new GntFile for a directory.
  *
- * @return
+ * @param name  The name of the directory.
+ *
+ * @return  The newly created GntFile.
  */
 GntFile* gnt_file_new_dir(const char *name);
 
--- a/finch/libgnt/gntkeys.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntkeys.h	Sun Oct 28 17:16:42 2007 +0000
@@ -104,41 +104,59 @@
 #define GNT_KEY_F12        SAFE(key_f12)
 
 /**
- * This will do stuff with the terminal settings and stuff.
- */
-/**
- * 
+ * Initialize the keys.
  */
 void gnt_init_keys(void);
 
 /**
- * 
- * @param text
+ * Refine input text. This usually looks at what the terminal claims it is,
+ * and tries to change the text to work around some oft-broken terminfo entries.
+ *
+ * @param text  The input text to refine.
  */
 void gnt_keys_refine(char *text);
 
+/**
+ * Translate a user-readable representation of an input to a machine-readable representation.
+ *
+ * @param name   The user-readable representation of an input (eg.: c-t)
+ *
+ * @return  A machine-readable representation of the input.
+ */
 const char *gnt_key_translate(const char *name);
+
+/**
+ * Translate a machine-readable representation of an input to a user-readable representation.
+ *
+ * @param key  The machine-readable representation of an input.
+ *
+ * @return  A user-readable representation of the input (eg.: c-t).
+ */
 const char *gnt_key_lookup(const char *key);
 
 /**
- * 
- * @param path
+ * Add a key combination to the internal key-tree.
+ *
+ * @param key  The key to add
  */
-void gnt_keys_add_combination(const char *path);
+void gnt_keys_add_combination(const char *key);
 
 /**
- * 
- * @param path
+ * Remove a key combination from the internal key-tree.
+ *
+ * @param key The key to remove.
  */
-void gnt_keys_del_combination(const char *path);
+void gnt_keys_del_combination(const char *key);
 
 /**
- * 
- * @param path
+ * Find a combination from the given string.
+ *
+ * @param key  The input string.
  *
- * @return
+ * @return The number of bytes in the combination that starts at the beginning
+ *         of key (can be 0).
  */
-int gnt_keys_find_combination(const char *path);
+int gnt_keys_find_combination(const char *key);
 
 /* A lot of commonly used variable names are defined in <term.h>. 
  * #undef them to make life easier for everyone. */
--- a/finch/libgnt/gntlabel.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntlabel.h	Sun Oct 28 17:16:42 2007 +0000
@@ -67,33 +67,34 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntLabel.
  */
 GType gnt_label_get_gtype(void);
 
 /**
- * 
- * @param text
+ * Create a new GntLabel.
  *
- * @return
+ * @param text  The text of the label.
+ *
+ * @return  The newly created label.
  */
 GntWidget * gnt_label_new(const char *text);
 
 /**
- * 
- * @param text
- * @param flags
+ * Create a new label with specified text attributes.
  *
- * @return
+ * @param text    The text.
+ * @param flags   Text attributes for the text.
+ *
+ * @return  The newly created label.
  */
 GntWidget * gnt_label_new_with_format(const char *text, GntTextFormatFlags flags);
 
 /**
- * 
- * @param label
- * @param text
+ * Change the text of a label.
+ *
+ * @param label  The label.
+ * @param text   The new text to set in the label.
  */
 void gnt_label_set_text(GntLabel *label, const char *text);
 
--- a/finch/libgnt/gntline.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntline.c	Sun Oct 28 17:16:42 2007 +0000
@@ -40,10 +40,10 @@
 {
 	GntLine *line = GNT_LINE(widget);
 	if (line->vertical)
-		mvwvline(widget->window, 1, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwvline(widget->window, 1, 0, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				widget->priv.height - 2);
 	else
-		mvwhline(widget->window, 0, 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwhline(widget->window, 0, 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				widget->priv.width - 2);
 }
 
--- a/finch/libgnt/gntline.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntline.h	Sun Oct 28 17:16:42 2007 +0000
@@ -67,9 +67,7 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntLine.
  */
 GType gnt_line_get_gtype(void);
 
@@ -77,10 +75,11 @@
 #define gnt_vline_new() gnt_line_new(TRUE)
 
 /**
- * 
- * @param vertical
+ * Create new line
  *
- * @return
+ * @param vertical  @c TRUE if the line should be vertical, @c FALSE for a horizontal line.
+ *
+ * @return  The newly created line.
  */
 GntWidget * gnt_line_new(gboolean vertical);
 
--- a/finch/libgnt/gntmain.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmain.c	Sun Oct 28 17:16:42 2007 +0000
@@ -482,7 +482,7 @@
 
 	gnt_init_colors();
 
-	wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(stdscr, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 	refresh();
 
 #ifdef ALL_MOUSE_EVENTS
@@ -490,7 +490,7 @@
 		mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
 #endif
 
-	wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(stdscr, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 	werase(stdscr);
 	wrefresh(stdscr);
 
@@ -646,7 +646,7 @@
 	return TRUE;
 }
 
-void gnt_set_clipboard_string(gchar *string)
+void gnt_set_clipboard_string(const gchar *string)
 {
 	gnt_clipboard_set_string(clipboard, string);
 }
--- a/finch/libgnt/gntmenu.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Sun Oct 28 17:16:42 2007 +0000
@@ -48,6 +48,14 @@
 static gboolean (*org_key_pressed)(GntWidget *w, const char *t);
 
 static void
+menu_hide_all(GntMenu *menu)
+{
+	while (menu->parentmenu)
+		menu = menu->parentmenu;
+	gnt_widget_hide(GNT_WIDGET(menu));
+}
+
+static void
 gnt_menu_draw(GntWidget *widget)
 {
 	GntMenu *menu = GNT_MENU(widget);
@@ -56,12 +64,12 @@
 	int i;
 
 	if (menu->type == GNT_MENU_TOPLEVEL) {
-		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 		werase(widget->window);
 
 		for (i = 0, iter = menu->list; iter; iter = iter->next, i++) {
 			GntMenuItem *item = GNT_MENU_ITEM(iter->data);
-			type = ' ' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT);
+			type = ' ' | gnt_color_pair(GNT_COLOR_HIGHLIGHT);
 			if (i == menu->selected)
 				type |= A_REVERSE;
 			item->priv.x = getcurx(widget->window) + widget->priv.x;
@@ -181,7 +189,12 @@
 static void
 menuitem_activate(GntMenu *menu, GntMenuItem *item)
 {
-	if (item) {
+	if (!item)
+		return;
+
+	if (gnt_menuitem_activate(item)) {
+		menu_hide_all(menu);
+	} else {
 		if (item->submenu) {
 			GntMenu *sub = GNT_MENU(item->submenu);
 			menu->submenu = sub;
@@ -195,12 +208,8 @@
 			gnt_widget_set_position(GNT_WIDGET(sub), item->priv.x, item->priv.y);
 			GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(sub), GNT_WIDGET_INVISIBLE);
 			gnt_widget_draw(GNT_WIDGET(sub));
-		} else if (item->callback) {
-			item->callback(item, item->callbackdata);
-			while (menu) {
-				gnt_widget_hide(GNT_WIDGET(menu));
-				menu = menu->parentmenu;
-			}
+		} else {
+			menu_hide_all(menu);
 		}
 	}
 }
@@ -286,10 +295,8 @@
 
 		if (current != menu->selected) {
 			GntMenu *sub = menu->submenu;
-			while (sub) {
+			if (sub)
 				gnt_widget_hide(GNT_WIDGET(sub));
-				sub = sub->submenu;
-			}
 			gnt_widget_draw(widget);
 			return TRUE;
 		}
@@ -326,8 +333,7 @@
 	GntMenu *menu = GNT_MENU(tree);
 	gboolean check = gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item));
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), !check);
-	if (item->callback)
-		item->callback(item, item->callbackdata);
+	gnt_menuitem_activate(item);
 	while (menu) {
 		gnt_widget_hide(GNT_WIDGET(menu));
 		menu = menu->parentmenu;
@@ -459,3 +465,34 @@
 	menu->list = g_list_append(menu->list, item);
 }
 
+GntMenuItem *gnt_menu_get_item(GntMenu *menu, const char *id)
+{
+	GntMenuItem *item = NULL;
+	GList *iter = menu->list;
+
+	if (!id || !*id)
+		return NULL;
+
+	for (; iter; iter = iter->next) {
+		GntMenu *sub;
+		item = iter->data;
+		sub = gnt_menuitem_get_submenu(item);
+		if (sub) {
+			item = gnt_menu_get_item(sub, id);
+			if (item)
+				break;
+		} else {
+			const char *itid = gnt_menuitem_get_id(item);
+			if (itid && strcmp(itid, id) == 0)
+				break;
+			/* XXX: Perhaps look at the menu-label as well? */
+		}
+		item = NULL;
+	}
+
+	if (item)
+		menuitem_activate(menu, item);
+
+	return item;
+}
+
--- a/finch/libgnt/gntmenu.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmenu.h	Sun Oct 28 17:16:42 2007 +0000
@@ -86,27 +86,39 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return  The GType for GntMenu.
  */
 GType gnt_menu_get_gtype(void);
 
 /**
- * 
- * @param type
+ * Create a new menu.
  *
- * @return
+ * @param type  The type of the menu, whether it's a toplevel menu or a popup menu.
+ *
+ * @return  The newly created menu.
  */
 GntWidget * gnt_menu_new(GntMenuType type);
 
 /**
- * 
- * @param menu
- * @param item
+ * Add an item to the menu.
+ *
+ * @param menu   The menu.
+ * @param item   The item to add to the menu.
  */
 void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item);
 
+/**
+ * Activate and return the GntMenuItem with the given ID.
+ *
+ * @param menu   The menu.
+ * @param id     The ID for an item.
+ *
+ * @return  The menuitem with the given ID, or @c NULL.
+ *
+ * @since 2.3.0
+ */
+GntMenuItem *gnt_menu_get_item(GntMenu *menu, const char *id);
+
 G_END_DECLS
 
 #endif /* GNT_MENU_H */
--- a/finch/libgnt/gntmenuitem.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmenuitem.c	Sun Oct 28 17:16:42 2007 +0000
@@ -23,6 +23,13 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 
+enum
+{
+	SIG_ACTIVATE,
+	SIGS
+};
+static guint signals[SIGS] = { 0 };
+
 static GObjectClass *parent_class = NULL;
 
 static void
@@ -33,6 +40,7 @@
 	item->text = NULL;
 	if (item->submenu)
 		gnt_widget_destroy(GNT_WIDGET(item->submenu));
+	g_free(item->priv.id);
 	parent_class->dispose(obj);
 }
 
@@ -43,10 +51,18 @@
 	parent_class = g_type_class_peek_parent(klass);
 
 	obj_class->dispose = gnt_menuitem_destroy;
+
+	signals[SIG_ACTIVATE] =
+		g_signal_new("activate",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
 }
 
 static void
-gnt_menuitem_init(GTypeInstance *instance, gpointer class)
+gnt_menuitem_init(GTypeInstance *instance, gpointer klass)
 {
 }
 
@@ -104,6 +120,11 @@
 	item->submenu = menu;
 }
 
+GntMenu *gnt_menuitem_get_submenu(GntMenuItem *item)
+{
+	return item->submenu;
+}
+
 void gnt_menuitem_set_trigger(GntMenuItem *item, char trigger)
 {
 	item->priv.trigger = trigger;
@@ -114,3 +135,24 @@
 	return item->priv.trigger;
 }
 
+void gnt_menuitem_set_id(GntMenuItem *item, const char *id)
+{
+	g_free(item->priv.id);
+	item->priv.id = g_strdup(id);
+}
+
+const char * gnt_menuitem_get_id(GntMenuItem *item)
+{
+	return item->priv.id;
+}
+
+gboolean gnt_menuitem_activate(GntMenuItem *item)
+{
+	g_signal_emit(item, signals[SIG_ACTIVATE], 0);
+	if (item->callback) {
+		item->callback(item, item->callbackdata);
+		return TRUE;
+	}
+	return FALSE;
+}
+
--- a/finch/libgnt/gntmenuitem.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmenuitem.h	Sun Oct 28 17:16:42 2007 +0000
@@ -53,6 +53,7 @@
 	int x;
 	int y;
 	char trigger;
+	char *id;
 };
 
 typedef void (*GntMenuItemCallback)(GntMenuItem *item, gpointer data);
@@ -86,36 +87,48 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntMenuItem.
  */
 GType gnt_menuitem_get_gtype(void);
 
 /**
- * 
- * @param text
+ * Create a new menuitem.
  *
- * @return
+ * @param text   Label for the menuitem.
+ *
+ * @return  The newly created menuitem.
  */
 GntMenuItem * gnt_menuitem_new(const char *text);
 
 /**
- * 
- * @param item
- * @param callback
- * @param data
+ * Set a callback function for a menuitem.
+ *
+ * @param item       The menuitem.
+ * @param callback   The callback function.
+ * @param data       Data to send to the callback function.
  */
 void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data);
 
 /**
- * 
- * @param item
- * @param menu
+ * Set a submenu for a menuitem. A menuitem with a submenu cannot have a callback.
+ *
+ * @param item  The menuitem.
+ * @param menu  The submenu.
  */
 void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu);
 
 /**
+ * Get the submenu for a menuitem.
+ *
+ * @param item   The menuitem.
+ *
+ * @return  The submenu, or @c NULL.
+ *
+ * @since 2.3.0
+ */
+GntMenu *gnt_menuitem_get_submenu(GntMenuItem *item);
+
+/**
  * Set a trigger key for the item.
  *
  * @param item     The menuitem
@@ -134,6 +147,40 @@
  */
 char gnt_menuitem_get_trigger(GntMenuItem *item);
 
+/**
+ * Set an ID for the menuitem.
+ *
+ * @param item   The menuitem.
+ * @param id     The ID for the menuitem.
+ *
+ * @since 2.3.0
+ */
+void gnt_menuitem_set_id(GntMenuItem *item, const char *id);
+
+/**
+ * Get the ID of the menuitem.
+ *
+ * @param item   The menuitem.
+ *
+ * @return  The ID for the menuitem.
+ *
+ * @since 2.3.0
+ */
+const char * gnt_menuitem_get_id(GntMenuItem *item);
+
+/**
+ * Activate a menuitem.
+ * Activating the menuitem will first trigger the 'activate' signal for the
+ * menuitem. Then the callback for the menuitem is triggered, if there is one.
+ *
+ * @param item   The menuitem.
+ *
+ * @return  Whether the callback for the menuitem was called.
+ *
+ * @since 2.3.0
+ */
+gboolean gnt_menuitem_activate(GntMenuItem *item);
+
 G_END_DECLS
 
 #endif /* GNT_MENUITEM_H */
--- a/finch/libgnt/gntmenuitemcheck.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntmenuitemcheck.h	Sun Oct 28 17:16:42 2007 +0000
@@ -66,32 +66,33 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntMenuItemCheck.
  */
 GType gnt_menuitem_check_get_gtype(void);
 
 /**
- * 
- * @param text
+ * Create a new menuitem.
  *
- * @return
+ * @param text  The text for the menuitem.
+ *
+ * @return  The newly created menuitem.
  */
 GntMenuItem * gnt_menuitem_check_new(const char *text);
 
 /**
- * 
- * @param item
+ * Check whether the menuitem is checked or not.
  *
- * @return
+ * @param item  The menuitem.
+ *
+ * @return @c TRUE if the item is checked, @c FALSE otherwise.
  */
 gboolean gnt_menuitem_check_get_checked(GntMenuItemCheck *item);
 
 /**
- * 
- * @param item
- * @param set
+ * Set whether the menuitem is checked or not.
+ *
+ * @param item  The menuitem.
+ * @param set   @c TRUE if the item should be checked, @c FALSE otherwise.
  */
 void gnt_menuitem_check_set_checked(GntMenuItemCheck *item, gboolean set);
 
--- a/finch/libgnt/gntslider.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntslider.c	Sun Oct 28 17:16:42 2007 +0000
@@ -84,21 +84,21 @@
 	else
 		position = 0;
 	if (slider->vertical) {
-		mvwvline(widget->window, size-position, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL) | A_BOLD,
+		mvwvline(widget->window, size-position, 0, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL) | A_BOLD,
 				position);
-		mvwvline(widget->window, 0, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwvline(widget->window, 0, 0, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				size-position);
 	} else {
-		mvwhline(widget->window, 0, 0, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL) | A_BOLD,
+		mvwhline(widget->window, 0, 0, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL) | A_BOLD,
 				position);
-		mvwhline(widget->window, 0, position, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwhline(widget->window, 0, position, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				size - position);
 	}
 
 	mvwaddch(widget->window,
 			slider->vertical ? (size - position - 1) : 0,
 			slider->vertical ? 0 : position,
-			ACS_CKBOARD | COLOR_PAIR(attr));
+			ACS_CKBOARD | gnt_color_pair(attr));
 }
 
 static void
--- a/finch/libgnt/gntstyle.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Sun Oct 28 17:16:42 2007 +0000
@@ -227,6 +227,65 @@
 #endif
 }
 
+gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table)
+{
+#if GLIB_CHECK_VERSION(2,6,0)
+	char *kname;
+	GError *error = NULL;
+	gboolean ret = FALSE;
+
+	kname = g_strdup_printf("%s::menu", name);
+
+	if (g_key_file_has_group(gkfile, kname))
+	{
+		gsize len = 0;
+		char **keys;
+		
+		keys = g_key_file_get_keys(gkfile, kname, &len, &error);
+		if (error)
+		{
+			g_printerr("GntStyle: %s\n", error->message);
+			g_error_free(error);
+			g_free(kname);
+			return ret;
+		}
+
+		while (len--)
+		{
+			char *key, *menuid;
+
+			key = g_strdup(keys[len]);
+			menuid = g_key_file_get_string(gkfile, kname, keys[len], &error);
+
+			if (error)
+			{
+				g_printerr("GntStyle: %s\n", error->message);
+				g_error_free(error);
+				error = NULL;
+			}
+			else
+			{
+				const char *keycode = parse_key(key);
+				if (keycode == NULL) {
+					g_printerr("GntStyle: Invalid key-binding %s\n", key);
+				} else {
+					ret = TRUE;
+					g_hash_table_replace(table, g_strdup(keycode), menuid);
+					menuid = NULL;
+				}
+			}
+			g_free(key);
+			g_free(menuid);
+		}
+		g_strfreev(keys);
+	}
+
+	g_free(kname);
+	return ret;
+#endif
+	return FALSE;
+}
+
 void gnt_styles_get_keyremaps(GType type, GHashTable *hash)
 {
 #if GLIB_CHECK_VERSION(2,6,0)
@@ -354,11 +413,14 @@
 void gnt_uninit_styles()
 {
 	int i;
-	for (i = 0; i < GNT_STYLES; i++)
+	for (i = 0; i < GNT_STYLES; i++) {
 		g_free(str_styles[i]);
+		str_styles[i] = NULL;
+	}
 
 #if GLIB_CHECK_VERSION(2,6,0)
 	g_key_file_free(gkfile);
+	gkfile = NULL;
 #endif
 }
 
--- a/finch/libgnt/gntstyle.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Sun Oct 28 17:16:42 2007 +0000
@@ -38,11 +38,17 @@
 } GntStyle;
 
 /**
- * 
- * @param filename
+ * Read configuration from a file.
+ *
+ * @param filename  The filename to read configuration from.
  */
 void gnt_style_read_configure_file(const char *filename);
 
+/**
+ * Get the user-setting for a style.
+ * @param style  The style.
+ * @return  The user-setting, or @c NULL.
+ */
 const char *gnt_style_get(GntStyle style);
 
 /**
@@ -70,38 +76,49 @@
 gboolean gnt_style_parse_bool(const char *value);
 
 /**
- * 
- * @param style
- * @param def
+ * Get the boolean value for a user-setting.
  *
- * @return
+ * @param style  The style.
+ * @param def    The default value (i.e, the value if the user didn't define
+ *               any value)
+ *
+ * @return  The value of the setting.
  */
 gboolean gnt_style_get_bool(GntStyle style, gboolean def);
 
-/* This should be called only once for the each type */
 /**
- * 
- * @param type
- * @param hash
+ * @internal
  */
 void gnt_styles_get_keyremaps(GType type, GHashTable *hash);
 
 /**
- * 
- * @param type
- * @param klass
+ * @internal
  */
 void gnt_style_read_actions(GType type, GntBindableClass *klass);
 
+/**
+ * Read menu-accels from ~/.gntrc
+ *
+ * @param name  The name of the window.
+ * @param table The hastable to store the accel information.
+ *
+ * @return  @c TRUE if some accels were read, @c FALSE otherwise.
+ */
+gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table);
+
+/**
+ * @internal
+ * Read workspace information.
+ */
 void gnt_style_read_workspaces(GntWM *wm);
 
 /**
- * 
+ * Initialize style settings.
  */
 void gnt_init_styles(void);
 
 /**
- * 
+ * Uninitialize style settings.
  */
 void gnt_uninit_styles(void);
 
--- a/finch/libgnt/gnttextview.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Sun Oct 28 17:16:42 2007 +0000
@@ -71,7 +71,7 @@
 	int comp = 0;          /* Used for top-aligned text */
 	gboolean has_scroll = !(view->flags & GNT_TEXT_VIEW_NO_SCROLL);
 
-	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgd(widget->window, gnt_color_pair(GNT_COLOR_NORMAL));
 	werase(widget->window);
 
 	if ((view->flags & GNT_TEXT_VIEW_TOP_ALIGN) &&
@@ -158,15 +158,15 @@
 			position = rows - showing;
 
 		mvwvline(widget->window, position + 1, scrcol,
-				ACS_CKBOARD | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D), showing);
+				ACS_CKBOARD | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D), showing);
 	}
 
 	if (has_scroll) {
 		mvwaddch(widget->window, 0, scrcol,
-				(lines ? ACS_UARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+				(lines ? ACS_UARROW : ' ') | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 		mvwaddch(widget->window, widget->priv.height - 1, scrcol,
 				((view->list && view->list->prev) ? ACS_DARROW : ' ') |
-					COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+					gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 	}
 
 	GNTDEBUG;
@@ -645,11 +645,11 @@
 		fl |= A_BLINK;
 
 	if (flags & GNT_TEXT_FLAG_DIM)
-		fl |= (A_DIM | COLOR_PAIR(GNT_COLOR_DISABLED));
+		fl |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
 	else if (flags & GNT_TEXT_FLAG_HIGHLIGHT)
-		fl |= (A_DIM | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+		fl |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 	else
-		fl |= COLOR_PAIR(GNT_COLOR_NORMAL);
+		fl |= gnt_color_pair(GNT_COLOR_NORMAL);
 
 	return fl;
 }
--- a/finch/libgnt/gnttextview.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gnttextview.h	Sun Oct 28 17:16:42 2007 +0000
@@ -88,116 +88,144 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return  GType for GntTextView.
  */
 GType gnt_text_view_get_gtype(void);
 
-/* XXX: For now, don't set a textview to have any border.
- *      If you want borders real bad, put it in a box. */
 /**
- * 
+ * Create a new textview.
  *
- * @return
+ * @return The newly created textview.
  */
 GntWidget * gnt_text_view_new(void);
 
-/* scroll > 0 means scroll up, < 0 means scroll down, == 0 means scroll to the end */
 /**
- * 
- * @param view
- * @param scroll
+ * Scroll the textview.
+ * @param view     The textview to scroll.
+ * @param scroll   scroll > 0 means scroll up, < 0 means scroll down, == 0 means scroll to the end.
  */
 void gnt_text_view_scroll(GntTextView *view, int scroll);
 
 /**
- * 
- * @param view
- * @param text
- * @param flags
+ * Append new text in a textview.
+ *
+ * @param view   The textview.
+ * @param text   The text to append to the textview.
+ * @param flags  The text-flags to apply to the new text.
  */
 void gnt_text_view_append_text_with_flags(GntTextView *view, const char *text, GntTextFormatFlags flags);
 
 /**
- * 
- * @param view
- * @param text
- * @param flags
- * @param tag
+ * Append text in the textview, with some identifier (tag) for the added text.
+ *
+ * @param view   The textview.
+ * @param text   The text to append.
+ * @param flags  The text-flags to apply to the new text.
+ * @param tag    The tag for the appended text, so it can be changed later (@see gnt_text_view_tag_change)
  */
 void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text, GntTextFormatFlags flags, const char *tag);
 
-/* Move the cursor to the beginning of the next line and resets text-attributes.
- * It first completes the current line with the current text-attributes. */
 /**
- * 
- * @param view
+ * Move the cursor to the beginning of the next line and resets text-attributes.
+ * It first completes the current line with the current text-attributes.
+ *
+ * @param view  The textview.
  */
 void gnt_text_view_next_line(GntTextView *view);
 
 /**
- * 
- * @param flags
+ * Convert GNT-text formats to ncurses-text attributes.
  *
- * @return
+ * @param flags  The GNT text format.
+ *
+ * @return  Nucrses text attribute.
  */
 chtype gnt_text_format_flag_to_chtype(GntTextFormatFlags flags);
 
 /**
- * 
- * @param view
+ * Clear the contents of the textview.
+ *
+ * @param view  The textview.
  */
 void gnt_text_view_clear(GntTextView *view);
 
 /**
- * 
- * @param view
+ * The number of lines below the bottom-most visible line.
  *
- * @return
+ * @param view  The textview.
+ *
+ * @return  Number of lines below the bottom-most visible line.
  */
 int gnt_text_view_get_lines_below(GntTextView *view);
 
 /**
- * 
- * @param view
+ * The number of lines above the topmost visible line.
  *
- * @return
+ * @param view  The textview.
+ *
+ * @return  Number of lines above the topmost visible line.
  */
 int gnt_text_view_get_lines_above(GntTextView *view);
 
-/* If text is NULL, then the tag is removed. */
 /**
- * 
- * @param view
- * @param name
- * @param text
- * @param all
+ * Change the text of a tag.
  *
- * @return
+ * @param view   The textview.
+ * @param name   The name of the tag.
+ * @param text   The new text for the text. If 'text' is @c NULL, the tag is removed.
+ * @param all    @c TRUE if all of the instancess of the tag should be changed, @c FALSE if
+ *               only the first instance should be changed.
+ *
+ * @return  The number of instances changed.
  */
 int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all);
 
 /**
- * 
- * @param view
- * @param widget
+ * Setup hooks so that pressing up/down/page-up/page-down keys when 'widget' is
+ * in focus scrolls the textview.
+ *
+ * @param view    The textview.
+ * @param widget  The trigger widget.
  */
 void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget);
 
 /**
- * 
- * @param view
- * @param widget
+ * Setup appropriate hooks so that pressing some keys when the 'pager' widget
+ * is in focus triggers the PAGER to popup with the contents of the textview
+ * in it.
+ *
+ * The default key-combination to trigger the pager is a-v, and the default
+ * PAGER application is $PAGER. Both can be changed in ~/.gntrc like this:
+ *
+ * @code
+ * [pager]
+ * key = a-v
+ * path = /path/to/pager
+ * @endcode
+ *
+ * @param view    The textview.
+ * @param pager   The widget to trigger the PAGER.
  */
 void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager);
 
 /**
- * 
- * @param view
- * @param widget
+ * Setup appropriate hooks so that pressing some keys when 'widget'
+ * is in focus triggers the EDITOR to popup with the contents of the textview
+ * in it.
+ *
+ * The default key-combination to trigger the pager is a-e, and the default
+ * EDITOR application is $EDITOR. Both can be changed in ~/.gntrc like this:
+ *
+ * @code
+ * [editor]
+ * key = a-e
+ * path = /path/to/editor
+ * @endcode
+ *
+ * @param view     The textview.
+ * @param widget   The widget to trigger the EDITOR.
  */
-void gnt_text_view_attach_editor_widget(GntTextView *view, GntWidget *pager);
+void gnt_text_view_attach_editor_widget(GntTextView *view, GntWidget *widget);
 
 /**
  * Set a GntTextViewFlag for the textview widget.
--- a/finch/libgnt/gnttree.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gnttree.c	Sun Oct 28 17:16:42 2007 +0000
@@ -432,7 +432,7 @@
 		tree_selection_changed(tree, NULL, tree->current);
 	}
 
-	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgd(widget->window, gnt_color_pair(GNT_COLOR_NORMAL));
 
 	start = 0;
 	if (tree->show_title)
@@ -440,9 +440,9 @@
 		int i;
 		int x = pos;
 
-		mvwhline(widget->window, pos + 1, pos, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwhline(widget->window, pos + 1, pos, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				widget->priv.width - pos - 1);
-		mvwhline(widget->window, pos, pos, ' ' | COLOR_PAIR(GNT_COLOR_NORMAL),
+		mvwhline(widget->window, pos, pos, ' ' | gnt_color_pair(GNT_COLOR_NORMAL),
 				widget->priv.width - pos - 1);
 
 		for (i = 0; i < tree->ncol; i++)
@@ -455,14 +455,15 @@
 		}
 		if (pos)
 		{
-			tree_mark_columns(tree, pos, 0, ACS_TTEE | COLOR_PAIR(GNT_COLOR_NORMAL));
+			tree_mark_columns(tree, pos, 0,
+					(tree->show_separator ? ACS_TTEE : ACS_HLINE) | gnt_color_pair(GNT_COLOR_NORMAL));
 			tree_mark_columns(tree, pos, widget->priv.height - pos,
-					ACS_BTEE | COLOR_PAIR(GNT_COLOR_NORMAL));
+					(tree->show_separator ? ACS_BTEE : ACS_HLINE) | gnt_color_pair(GNT_COLOR_NORMAL));
 		}
 		tree_mark_columns(tree, pos, pos + 1,
-			(tree->show_separator ? ACS_PLUS : ACS_HLINE) | COLOR_PAIR(GNT_COLOR_NORMAL));
+			(tree->show_separator ? ACS_PLUS : ACS_HLINE) | gnt_color_pair(GNT_COLOR_NORMAL));
 		tree_mark_columns(tree, pos, pos,
-			(tree->show_separator ? ACS_VLINE : ' ') | COLOR_PAIR(GNT_COLOR_NORMAL));
+			(tree->show_separator ? ACS_VLINE : ' ') | gnt_color_pair(GNT_COLOR_NORMAL));
 		start = 2;
 	}
 
@@ -514,18 +515,18 @@
 		if (row == tree->current)
 		{
 			if (gnt_widget_has_focus(widget))
-				attr |= COLOR_PAIR(GNT_COLOR_HIGHLIGHT);
+				attr |= gnt_color_pair(GNT_COLOR_HIGHLIGHT);
 			else
-				attr |= COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D);
+				attr |= gnt_color_pair(GNT_COLOR_HIGHLIGHT_D);
 		}
 		else
 		{
 			if (flags & GNT_TEXT_FLAG_DIM)
-				attr |= (A_DIM | COLOR_PAIR(GNT_COLOR_DISABLED));
+				attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
 			else if (flags & GNT_TEXT_FLAG_HIGHLIGHT)
-				attr |= (A_DIM | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+				attr |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 			else
-				attr |= COLOR_PAIR(GNT_COLOR_NORMAL);
+				attr |= gnt_color_pair(GNT_COLOR_NORMAL);
 		}
 
 		wbkgdset(widget->window, '\0' | attr);
@@ -537,7 +538,7 @@
 			(tree->show_separator ? ACS_VLINE : ' ') | attr);
 	}
 
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 	while (i < widget->priv.height - pos)
 	{
 		mvwhline(widget->window, i, pos, ' ',
@@ -576,22 +577,22 @@
 		position += pos + start + 1;
 
 		mvwvline(widget->window, pos + start + 1, scrcol,
-				' ' | COLOR_PAIR(GNT_COLOR_NORMAL), rows);
+				' ' | gnt_color_pair(GNT_COLOR_NORMAL), rows);
 		mvwvline(widget->window, position, scrcol,
-				ACS_CKBOARD | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D), showing);
+				ACS_CKBOARD | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D), showing);
 	}
 
 	mvwaddch(widget->window, start + pos, scrcol,
 			((tree->top != tree->root) ?  ACS_UARROW : ' ') |
-				COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+				gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 
 	mvwaddch(widget->window, widget->priv.height - pos - 1, scrcol,
-			(row ?  ACS_DARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+			(row ?  ACS_DARROW : ' ') | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 
 	/* If there's a search-text, show it in the bottom of the tree */
 	if (tree->priv->search && tree->priv->search->len > 0) {
 		const char *str = gnt_util_onscreen_width_to_pointer(tree->priv->search->str, scrcol - 1, NULL);
-		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
 		mvwaddnstr(widget->window, widget->priv.height - pos - 1, pos,
 				tree->priv->search->str, str - tree->priv->search->str);
 	}
@@ -1328,7 +1329,6 @@
 			tree->list = g_list_insert(tree->list, key, position + 1);
 		}
 	}
-
 	redraw_tree(tree);
 
 	return row;
@@ -1776,6 +1776,7 @@
 					break;
 			}
 	}
+	readjust_columns(tree);
 }
 
 void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res)
--- a/finch/libgnt/gntutils.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntutils.h	Sun Oct 28 17:16:42 2007 +0000
@@ -153,5 +153,5 @@
  *
  * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
-void gnt_util_set_trigger_widget(GntWidget *wid, const char *text, GntWidget *button);
+void gnt_util_set_trigger_widget(GntWidget *widget, const char *key, GntWidget *button);
 
--- a/finch/libgnt/gntwidget.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwidget.c	Sun Oct 28 17:16:42 2007 +0000
@@ -420,7 +420,7 @@
 gnt_widget_hide(GntWidget *widget)
 {
 	g_signal_emit(widget, signals[SIG_HIDE], 0);
-	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 #if 0
 	/* XXX: I have no clue why, but this seemed to be necessary. */
 	if (gnt_widget_has_shadow(widget))
@@ -477,31 +477,31 @@
 	if (!gnt_widget_has_shadow(widget))
 		shadow = FALSE;
 
-	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgd(widget->window, gnt_color_pair(GNT_COLOR_NORMAL));
 	werase(widget->window);
 
 	if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_NO_BORDER))
 	{
 		/* - This is ugly. */
 		/* - What's your point? */
-		mvwvline(widget->window, 0, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.height);
+		mvwvline(widget->window, 0, 0, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL), widget->priv.height);
 		mvwvline(widget->window, 0, widget->priv.width - 1,
-				ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.height);
+				ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL), widget->priv.height);
 		mvwhline(widget->window, widget->priv.height - 1, 0,
-				ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.width);
-		mvwhline(widget->window, 0, 0, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.width);
-		mvwaddch(widget->window, 0, 0, ACS_ULCORNER | COLOR_PAIR(GNT_COLOR_NORMAL));
+				ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), widget->priv.width);
+		mvwhline(widget->window, 0, 0, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), widget->priv.width);
+		mvwaddch(widget->window, 0, 0, ACS_ULCORNER | gnt_color_pair(GNT_COLOR_NORMAL));
 		mvwaddch(widget->window, 0, widget->priv.width - 1,
-				ACS_URCORNER | COLOR_PAIR(GNT_COLOR_NORMAL));
+				ACS_URCORNER | gnt_color_pair(GNT_COLOR_NORMAL));
 		mvwaddch(widget->window, widget->priv.height - 1, 0,
-				ACS_LLCORNER | COLOR_PAIR(GNT_COLOR_NORMAL));
+				ACS_LLCORNER | gnt_color_pair(GNT_COLOR_NORMAL));
 		mvwaddch(widget->window, widget->priv.height - 1, widget->priv.width - 1,
-				ACS_LRCORNER | COLOR_PAIR(GNT_COLOR_NORMAL));
+				ACS_LRCORNER | gnt_color_pair(GNT_COLOR_NORMAL));
 	}
 
 	if (shadow)
 	{
-		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_SHADOW));
+		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_SHADOW));
 		mvwvline(widget->window, 1, widget->priv.width, ' ', widget->priv.height);
 		mvwhline(widget->window, widget->priv.height, 1, ' ', widget->priv.width);
 	}
@@ -617,7 +617,7 @@
 		return;
 	while (widget->parent)
 		widget = widget->parent;
-	
+
 	if (!g_object_get_data(G_OBJECT(widget), "gnt:queue_update"))
 	{
 		int id = g_timeout_add(0, update_queue_callback, widget);
--- a/finch/libgnt/gntwidget.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwidget.h	Sun Oct 28 17:16:42 2007 +0000
@@ -140,167 +140,176 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return  GType for GntWidget.
  */
 GType gnt_widget_get_gtype(void);
 
 /**
- * 
- * @param widget
+ * Destroy a widget.
+ * @param widget The widget to destroy.
  */
 void gnt_widget_destroy(GntWidget *widget);
 
 /**
- * 
- * @param widget
+ * Show a widget. This should only be used for toplevel widgets. For the rest
+ * of the widgets, use #gnt_widget_draw instead.
+ *
+ * @param widget  The widget to show.
  */
 void gnt_widget_show(GntWidget *widget);
 
 /**
- * 
- * @param widget
+ * Draw a widget.
+ * @param widget   The widget to draw.
  */
 void gnt_widget_draw(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param x
- * @param y
- * @param width
- * @param height
+ * @internal
+ * Expose part of a widget.
  */
 void gnt_widget_expose(GntWidget *widget, int x, int y, int width, int height);
 
 /**
- * 
- * @param widget
+ * Hide a widget.
+ * @param widget   The widget to hide.
  */
 void gnt_widget_hide(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param x
- * @param y
+ * Get the position of a widget.
+ *
+ * @param widget  The widget.
+ * @param x       Location to store the x-coordinate of the widget.
+ * @param y       Location to store the y-coordinate of the widget.
  */
 void gnt_widget_get_position(GntWidget *widget, int *x, int *y);
 
 /**
- * 
- * @param widget
- * @param x
- * @param y
+ * Set the position of a widget.
+ * @param widget   The widget to reposition.
+ * @param x        The x-coordinate of the widget.
+ * @param y        The x-coordinate of the widget.
  */
 void gnt_widget_set_position(GntWidget *widget, int x, int y);
 
 /**
- * 
- * @param widget
+ * Request a widget to calculate its desired size.
+ * @param widget  The widget.
  */
 void gnt_widget_size_request(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param width
- * @param height
+ * Get the size of a widget.
+ * @param widget    The widget.
+ * @param width     Location to store the width of the widget.
+ * @param height    Location to store the height of the widget.
  */
 void gnt_widget_get_size(GntWidget *widget, int *width, int *height);
 
 /**
- * 
- * @param widget
- * @param width
- * @param height
+ * Set the size of a widget.
  *
- * @return
+ * @param widget  The widget to resize.
+ * @param width   The width of the widget.
+ * @param height  The height of the widget.
+ *
+ * @return  If the widget was resized to the new size.
  */
 gboolean gnt_widget_set_size(GntWidget *widget, int width, int height);
 
 /**
- * 
- * @param widget
- * @param width
- * @param height
+ * Confirm a requested a size for a widget.
  *
- * @return
+ * @param widget   The widget.
+ * @param width    The requested width.
+ * @param height    The requested height.
+ *
+ * @return  @c TRUE if the new size was confirmed, @c FALSE otherwise.
  */
 gboolean gnt_widget_confirm_size(GntWidget *widget, int width, int height);
 
 /**
- * 
- * @param widget
- * @param keys
+ * Trigger the key-press callbacks for a widget.
  *
- * @return
+ * @param widget  The widget.
+ * @param keys    The keypress on the widget.
+ *
+ * @return  @c TRUE if the key-press was handled, @c FALSE otherwise.
  */
 gboolean gnt_widget_key_pressed(GntWidget *widget, const char *keys);
 
 /**
- * 
- * @param widget
- * @param event
- * @param x
- * @param y
+ * Trigger the 'click' callback of a widget.
  *
- * @return
+ * @param widget   The widget.
+ * @param event    The mouseevent.
+ * @param x        The x-coordinate of the mouse.
+ * @param y        The y-coordinate of the mouse.
+ *
+ * @return  @c TRUE if the event was handled, @c FALSE otherwise.
  */
 gboolean gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y);
 
 /**
- * 
- * @param widget
- * @param set
+ * Give or remove focus to a widget.
+ * @param widget  The widget.
+ * @param set     @c TRUE of focus should be given to the widget, @c FALSE if
+ *                focus should be removed.
  *
- * @return
+ * @return @c TRUE if the focus has been changed, @c FALSE otherwise.
  */
 gboolean gnt_widget_set_focus(GntWidget *widget, gboolean set);
 
 /**
- * 
- * @param widget
+ * Activate a widget. This only applies to widgets that can be activated (eg. GntButton)
+ * @param widget  The widget to activate.
  */
 void gnt_widget_activate(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param name
+ * Set the name of a widget.
+ * @param widget   The widget.
+ * @param name     A new name for the widget.
  */
 void gnt_widget_set_name(GntWidget *widget, const char *name);
 
+/**
+ * Get the name of a widget.
+ * @param widget   The widget.
+ * @return The name of the widget.
+ */
 const char *gnt_widget_get_name(GntWidget *widget);
 
-/* Widget-subclasses should call this from the draw-callback.
- * Applications should just call gnt_widget_draw instead of this. */
 /**
- * 
- * @param widget
+ * @internal
+ * Use #gnt_widget_draw instead.
  */
 void gnt_widget_queue_update(GntWidget *widget);
 
 /**
- * 
- * @param widget
- * @param set
+ * Set whether a widget can take focus or not.
+ *
+ * @param widget   The widget.
+ * @param set      @c TRUE if the widget can take focus.
  */
 void gnt_widget_set_take_focus(GntWidget *widget, gboolean set);
 
 /**
- * 
- * @param widget
- * @param set
+ * Set the visibility of a widget.
+ *
+ * @param widget  The widget.
+ * @param set     Whether the widget is visible or not.
  */
 void gnt_widget_set_visible(GntWidget *widget, gboolean set);
 
 /**
- * 
- * @param widget
+ * Check whether the widget has shadows.
  *
- * @return
+ * @param widget  The widget.
+ *
+ * @return  @c TRUE if the widget has shadows. This checks both the user-setting
+ *          and whether the widget can have shadows at all.
  */
 gboolean gnt_widget_has_shadow(GntWidget *widget);
 
--- a/finch/libgnt/gntwindow.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwindow.c	Sun Oct 28 17:16:42 2007 +0000
@@ -25,6 +25,11 @@
 
 #include <string.h>
 
+struct _GntWindowPriv
+{
+	GHashTable *accels;   /* key => menuitem-id */
+};
+
 enum
 {
 	SIG_WORKSPACE_HIDE,
@@ -55,6 +60,10 @@
 	GntWindow *window = GNT_WINDOW(widget);
 	if (window->menu)
 		gnt_widget_destroy(GNT_WIDGET(window->menu));
+	if (window->priv) {
+		g_hash_table_destroy(window->priv->accels);
+		g_free(window->priv);
+	}
 	org_destroy(widget);
 }
 
@@ -98,8 +107,11 @@
 gnt_window_init(GTypeInstance *instance, gpointer class)
 {
 	GntWidget *widget = GNT_WIDGET(instance);
+	GntWindow *win = GNT_WINDOW(widget);
 	GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS);
+	win->priv = g_new0(GntWindowPriv, 1);
+	win->priv->accels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 	GNTDEBUG;
 }
 
@@ -170,8 +182,23 @@
 void gnt_window_set_menu(GntWindow *window, GntMenu *menu)
 {
 	/* If a menu already existed, then destroy that first. */
+	const char *name = gnt_widget_get_name(GNT_WIDGET(window));
 	if (window->menu)
 		gnt_widget_destroy(GNT_WIDGET(window->menu));
 	window->menu = menu;
+	if (name && window->priv) {
+		if (!gnt_style_read_menu_accels(name, window->priv->accels)) {
+			g_hash_table_destroy(window->priv->accels);
+			g_free(window->priv);
+			window->priv = NULL;
+		}
+	}
 }
 
+const char * gnt_window_get_accel_item(GntWindow *window, const char *key)
+{
+	if (window->priv)
+		return g_hash_table_lookup(window->priv->accels, key);
+	return NULL;
+}
+
--- a/finch/libgnt/gntwindow.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwindow.h	Sun Oct 28 17:16:42 2007 +0000
@@ -52,6 +52,7 @@
 {
 	GntBox parent;
 	GntMenu *menu;
+	GntWindowPriv *priv;
 };
 
 struct _GntWindowClass
@@ -67,9 +68,7 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return  GType for GntWindow.
  */
 GType gnt_window_get_gtype(void);
 
@@ -77,28 +76,42 @@
 #define gnt_hwindow_new(homo) gnt_window_box_new(homo, FALSE)
 
 /**
- * 
+ * Create a new window.
  *
- * @return
+ * @return The newly created window.
  */
 GntWidget * gnt_window_new(void);
 
 /**
- * 
- * @param homo
- * @param vert
+ * Create a new window.
  *
- * @return
+ * @param homo  @c TRUE if the widgets inside the window should have the same dimensions.
+ * @param vert  @c TRUE if the widgets inside the window should be stacked vertically.
+ *
+ * @return  The newly created window.
  */
 GntWidget * gnt_window_box_new(gboolean homo, gboolean vert);
 
 /**
- * 
- * @param window
- * @param menu
+ * Set the menu for a window.
+ *
+ * @param window  The window.
+ * @param menu    The menu for the window.
  */
 void gnt_window_set_menu(GntWindow *window, GntMenu *menu);
 
+/**
+ * Return the id of a menuitem specified to a keystroke.
+ *
+ * @param window    The window.
+ * @param key       The keystroke.
+ *
+ * @return The id of the menuitem bound to the keystroke, or @c NULL.
+ *
+ * @since 2.3.0
+ */
+const char * gnt_window_get_accel_item(GntWindow *window, const char *key);
+
 void gnt_window_workspace_hiding(GntWindow *);
 void gnt_window_workspace_showing(GntWindow *);
 
--- a/finch/libgnt/gntwm.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwm.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1046,7 +1046,7 @@
 	GntWM *wm = GNT_WM(wim);
 	GntWidget *w = GNT_WIDGET(wid);
 	wm->tagged = g_list_remove(wm->tagged, w);
-	mvwhline(w->window, 0, 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), 3);
+	mvwhline(w->window, 0, 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), 3);
 	gnt_widget_draw(w);
 }
 
@@ -1066,7 +1066,7 @@
 	}
 
 	wm->tagged = g_list_prepend(wm->tagged, widget);
-	wbkgdset(widget->window, ' ' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+	wbkgdset(widget->window, ' ' | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 	mvwprintw(widget->window, 0, 1, "[T]");
 	gnt_widget_draw(widget);
 	return TRUE;
@@ -1675,7 +1675,7 @@
 {
 	while (widget->parent)
 		widget = widget->parent;
-	
+
 	if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_INVISIBLE) ||
 			g_hash_table_lookup(wm->nodes, widget)) {
 		update_screen(wm);
@@ -1852,8 +1852,19 @@
 		ret = gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys);
 	else if (wm->_list.window)
 		ret = gnt_widget_key_pressed(wm->_list.window, keys);
-	else if (wm->cws->ordered)
-		ret = gnt_widget_key_pressed(GNT_WIDGET(wm->cws->ordered->data), keys);
+	else if (wm->cws->ordered) {
+		GntWidget *win = wm->cws->ordered->data;
+		if (GNT_IS_WINDOW(win)) {
+			GntMenu *menu = GNT_WINDOW(win)->menu;
+			if (menu) {
+				const char *id = gnt_window_get_accel_item(GNT_WINDOW(win), keys);
+				if (id)
+					ret = (gnt_menu_get_item(menu, id) != NULL);
+			}
+		}
+		if (!ret)
+			ret = gnt_widget_key_pressed(win, keys);
+	}
 	return ret;
 }
 
--- a/finch/libgnt/gntwm.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntwm.h	Sun Oct 28 17:16:42 2007 +0000
@@ -113,7 +113,7 @@
 	 * whether to give focus to a new window.
 	 */
 	gboolean event_stack;
-	
+
 	GntKeyPressMode mode;
 
 	GHashTable *positions;
@@ -184,108 +184,149 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return GType for GntWM.
  */
 GType gnt_wm_get_gtype(void);
 
+/**
+ * Add a workspace.
+ * @param wm   The window-manager.
+ * @param ws   The workspace to add.
+ */
 void gnt_wm_add_workspace(GntWM *wm, GntWS *ws);
 
+/**
+ * Switch to a workspace.
+ * @param wm   The window-manager.
+ * @param n    Index of the workspace to switch to.
+ *
+ * @return   @c TRUE if the switch was successful.
+ */
 gboolean gnt_wm_switch_workspace(GntWM *wm, gint n);
+
+/**
+ * Switch to the previous workspace from the current one.
+ * @param wm  The window-manager.
+ */
 gboolean gnt_wm_switch_workspace_prev(GntWM *wm);
+
+/**
+ * Switch to the next workspace from the current one.
+ * @param wm  The window-manager.
+ */
 gboolean gnt_wm_switch_workspace_next(GntWM *wm);
+
+/**
+ * Move a window to a specific workspace.
+ * @param wm     The window manager.
+ * @param neww   The new workspace.
+ * @param widget The widget to move.
+ */
 void gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget);
+
+/**
+ * Set the list of workspaces .
+ * @param wm            The window manager.
+ * @param workspaces    The list of workspaces.
+ */
 void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces);
+
+/**
+ * Find the workspace that contains a specific widget.
+ * @param wm       The window-manager.
+ * @param widget   The widget to find.
+ * @return   The workspace that has the widget.
+ */
 GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param widget
+ * Process a new window.
+ *
+ * @param wm       The window-manager.
+ * @param widget   The new window.
  */
 void gnt_wm_new_window(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param widget
+ * Decorate a window.
+ * @param wm       The window-manager.
+ * @param widget   The widget to decorate.
  */
 void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param widget
+ * Close a window.
+ * @param wm       The window-manager.
+ * @param widget   The window to close.
  */
 void gnt_wm_window_close(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param string
+ * Process input.
  *
- * @return
+ * @param wm      The window-manager.
+ * @param string  The input string to process.
+ *
+ * @return @c TRUE of the string was processed, @c FALSE otherwise.
  */
 gboolean gnt_wm_process_input(GntWM *wm, const char *string);
 
 /**
- * 
- * @param wm
- * @param event
- * @param x
- * @param y
- * @param widget
+ * Process a click event.
+ * @param wm      The window manager.
+ * @param event   The mouse event.
+ * @param x       The x-coordinate of the mouse.
+ * @param y       The y-coordinate of the mouse.
+ * @param widget  The widget under the mouse.
  *
- * @return
+ * @return  @c TRUE if the event was handled, @c FALSE otherwise.
  */
 gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param widget
- * @param width
- * @param height
+ * Resize a window.
+ * @param wm        The window manager.
+ * @param widget    The window to resize.
+ * @param width     The desired width of the window.
+ * @param height    The desired height of the window.
  */
 void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height);
 
 /**
- * 
- * @param wm
- * @param widget
- * @param x
- * @param y
+ * Move a window.
+ * @param wm      The window manager.
+ * @param widget  The window to move.
+ * @param x       The desired x-coordinate of the window.
+ * @param y       The desired y-coordinate of the window.
  */
 void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y);
 
 /**
- * 
- * @param wm
- * @param widget
+ * Update a window.
+ * @param wm      The window-manager.
+ * @param widget  The window to update.
  */
 void gnt_wm_update_window(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param widget
+ * Raise a window.
+ * @param wm      The window-manager.
+ * @param widget  The window to raise.
  */
 void gnt_wm_raise_window(GntWM *wm, GntWidget *widget);
 
 /**
- * 
- * @param wm
- * @param set
+ * @internal
  */
 void gnt_wm_set_event_stack(GntWM *wm, gboolean set);
 
+/**
+ * @internal
+ */
 void gnt_wm_copy_win(GntWidget *widget, GntNode *node);
 
 /**
- * 
- *
- * @return
+ * @return  The idle time of the user.
  */
 time_t gnt_wm_get_idle_time(void);
 
--- a/finch/libgnt/gntws.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/finch/libgnt/gntws.c	Sun Oct 28 17:16:42 2007 +0000
@@ -45,7 +45,7 @@
 		mvwin(taskbar, Y_MAX, 0);
 	}
 
-	wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
+	wbkgdset(taskbar, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
 	werase(taskbar);
 
 	n = g_list_length(ws->list);
@@ -66,15 +66,15 @@
 		} else {
 			color = GNT_COLOR_NORMAL;
 		}
-		wbkgdset(taskbar, '\0' | COLOR_PAIR(color));
+		wbkgdset(taskbar, '\0' | gnt_color_pair(color));
 		if (iter->next)
-			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width);
+			mvwhline(taskbar, 0, width * i, ' ' | gnt_color_pair(color), width);
 		else
-			mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), getmaxx(stdscr) - width * i);
+			mvwhline(taskbar, 0, width * i, ' ' | gnt_color_pair(color), getmaxx(stdscr) - width * i);
 		title = GNT_BOX(w)->title;
 		mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>");
 		if (i)
-			mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL));
+			mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | gnt_color_pair(GNT_COLOR_NORMAL));
 	}
 	wrefresh(taskbar);
 }
--- a/libpurple/Makefile.am	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/Makefile.am	Sun Oct 28 17:16:42 2007 +0000
@@ -114,6 +114,7 @@
 	privacy.h \
 	proxy.h \
 	prpl.h \
+	purple.h \
 	request.h \
 	roomlist.h \
 	savedstatuses.h \
--- a/libpurple/account.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/account.c	Sun Oct 28 17:16:42 2007 +0000
@@ -63,7 +63,10 @@
 	PurpleAccountRequestType type;
 	PurpleAccount *account;
 	void *ui_handle;
-
+	char *user;
+	gpointer userdata;
+	PurpleAccountRequestAuthorizationCb auth_cb;
+	PurpleAccountRequestAuthorizationCb deny_cb;
 } PurpleAccountRequestInfo;
 
 static PurpleAccountUiOps *account_ui_ops = NULL;
@@ -1004,7 +1007,7 @@
 	field = purple_request_field_bool_new("remember", _("Save password"), FALSE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(account,
+	purple_request_fields_with_hint(account,
                         NULL,
                         primary,
                         NULL,
@@ -1012,7 +1015,7 @@
                         _("OK"), ok_cb,
                         _("Cancel"), cancel_cb,
 						account, NULL, NULL,
-                        user_data);
+                        PURPLE_REQUEST_UI_HINT_ACCOUNT, user_data);
 	g_free(primary);
 }
 
@@ -1157,6 +1160,28 @@
 	}
 }
 
+static void
+request_auth_cb(void *data)
+{
+	PurpleAccountRequestInfo *info = data;
+	info->auth_cb(info->userdata);
+	purple_signal_emit(purple_accounts_get_handle(),
+			"account-authorization-granted", info->account, info->user);
+	g_free(info->user);
+	g_free(info);
+}
+
+static void
+request_deny_cb(void *data)
+{
+	PurpleAccountRequestInfo *info = data;
+	info->deny_cb(info->userdata);
+	purple_signal_emit(purple_accounts_get_handle(),
+			"account-authorization-denied", info->account, info->user);
+	g_free(info->user);
+	g_free(info);
+}
+
 void *
 purple_account_request_authorization(PurpleAccount *account, const char *remote_user,
 				     const char *id, const char *alias, const char *message, gboolean on_list,
@@ -1164,18 +1189,35 @@
 {
 	PurpleAccountUiOps *ui_ops;
 	PurpleAccountRequestInfo *info;
+	int plugin_return;
 
 	g_return_val_if_fail(account     != NULL, NULL);
 	g_return_val_if_fail(remote_user != NULL, NULL);
 
 	ui_ops = purple_accounts_get_ui_ops();
 
+	plugin_return = GPOINTER_TO_INT(
+			purple_signal_emit_return_1(purple_accounts_get_handle(),
+				"account-authorization-requested", account, remote_user));
+
+	if (plugin_return > 0) {
+		auth_cb(user_data);
+		return NULL;
+	} else if (plugin_return < 0) {
+		deny_cb(user_data);
+		return NULL;
+	}
+
 	if (ui_ops != NULL && ui_ops->request_authorize != NULL) {
 		info            = g_new0(PurpleAccountRequestInfo, 1);
 		info->type      = PURPLE_ACCOUNT_REQUEST_AUTHORIZATION;
 		info->account   = account;
+		info->auth_cb   = auth_cb;
+		info->deny_cb   = deny_cb;
+		info->userdata  = user_data;
+		info->user      = g_strdup(remote_user);
 		info->ui_handle = ui_ops->request_authorize(account, remote_user, id, alias, message,
-							    on_list, auth_cb, deny_cb, user_data);
+							    on_list, request_auth_cb, request_deny_cb, info);
 
 		handles = g_list_append(handles, info);
 		return info->ui_handle;
@@ -1253,7 +1295,7 @@
 
 	/* I'm sticking this somewhere in the code: bologna */
 
-	purple_request_fields(purple_account_get_connection(account),
+	purple_request_fields_with_hint(purple_account_get_connection(account),
 						NULL,
 						primary,
 						_("Please enter your current password and your "
@@ -1262,7 +1304,7 @@
 						_("OK"), G_CALLBACK(change_password_cb),
 						_("Cancel"), NULL,
 						account, NULL, NULL,
-						account);
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, account);
 }
 
 static void
@@ -1290,14 +1332,14 @@
 			   _("Change user information for %s"),
 			   purple_account_get_username(account));
 
-	purple_request_input(gc, _("Set User Info"), primary, NULL,
+	purple_request_input_with_hint(gc, _("Set User Info"), primary, NULL,
 					   purple_account_get_user_info(account),
 					   TRUE, FALSE, ((gc != NULL) &&
 					   (gc->flags & PURPLE_CONNECTION_HTML) ? "html" : NULL),
 					   _("Save"), G_CALLBACK(set_user_info_cb),
 					   _("Cancel"), NULL,
 					   account, NULL, NULL,
-					   account);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, account);
 }
 
 void
@@ -2452,6 +2494,25 @@
 							 			PURPLE_SUBTYPE_ACCOUNT),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "account-authorization-requested",
+						purple_marshal_INT__POINTER_POINTER,
+						purple_value_new(PURPLE_TYPE_INT), 2,
+						purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(handle, "account-authorization-denied",
+						purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+						purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(handle, "account-authorization-granted",
+						purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+						purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						purple_value_new(PURPLE_TYPE_STRING));
+
 	load_accounts();
 
 }
--- a/libpurple/blist.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/blist.h	Sun Oct 28 17:16:42 2007 +0000
@@ -40,6 +40,8 @@
 typedef struct _PurpleContact PurpleContact;
 typedef struct _PurpleBuddy PurpleBuddy;
 
+typedef gboolean (*PurpleFilterBlistFunc)(PurpleBlistNode *node);
+
 /**************************************************************************/
 /* Enumerations                                                           */
 /**************************************************************************/
@@ -65,9 +67,12 @@
 typedef enum
 {
 	PURPLE_BLIST_NODE_FLAG_NO_SAVE      = 1 << 0, /**< node should not be saved with the buddy list */
+	PURPLE_BLIST_NODE_HAS_CONVERSATION  = 1 << 1, /**< node (buddy or chat) has an open conversation */
 
 } PurpleBlistNodeFlags;
 
+#define PURPLE_BLIST_NODE_SET_FLAG(node, f)    (((PurpleBlistNode *)node)->flags |= (f))
+#define PURPLE_BLIST_NODE_UNSET_FLAG(node, f)  (((PurpleBlistNode *)node)->flags &= ~(f))
 #define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (((PurpleBlistNode*)(b))->flags & (f))
 #define PURPLE_BLIST_NODE_SHOULD_SAVE(b) (! PURPLE_BLIST_NODE_HAS_FLAG(b, PURPLE_BLIST_NODE_FLAG_NO_SAVE))
 
@@ -482,6 +487,7 @@
  */
 PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Sets the alias for a contact.
  *
@@ -491,6 +497,7 @@
  * @deprecated Use purple_blist_alias_contact() instead.
  */
 void purple_contact_set_alias(PurpleContact *contact, const char *alias);
+#endif
 
 /**
  * Gets the alias for a contact.
--- a/libpurple/certificate.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/certificate.c	Sun Oct 28 17:16:42 2007 +0000
@@ -563,7 +563,7 @@
 	secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc);
 	
 	/* Make a semi-pretty display */
-	purple_request_accept_cancel(
+	purple_request_accept_cancel_with_hint(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
 		_("Single-use Certificate Verification"),
 		primary,
@@ -572,6 +572,7 @@
 		NULL,         /* No account */
 		NULL,         /* No other user */
 		NULL,         /* No associated conversation */
+		PURPLE_REQUEST_UI_HINT_BLIST,
 		vrq,
 		x509_singleuse_verify_cb,
 		x509_singleuse_verify_cb );
@@ -1185,7 +1186,7 @@
 				  vrq->subject_name);
 		
 	/* Make a semi-pretty display */
-	purple_request_action(
+	purple_request_action_with_hint(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
 		_("SSL Certificate Verification"),
 		primary,
@@ -1194,6 +1195,7 @@
 		NULL,         /* No account */
 		NULL,         /* No other user */
 		NULL,         /* No associated conversation */
+		PURPLE_REQUEST_UI_HINT_BLIST,
 		x509_tls_cached_ua_ctx_new(vrq, reason),
 		3,            /* Number of actions */
 		_("Accept"), x509_tls_cached_user_auth_accept_cb,
--- a/libpurple/certificate.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/certificate.h	Sun Oct 28 17:16:42 2007 +0000
@@ -435,7 +435,7 @@
  *
  * @return TRUE if 'crt' has a valid signature made by 'issuer',
  *         otherwise FALSE
- * @TODO Find a way to give the reason (bad signature, not the issuer, etc.) 
+ * @todo Find a way to give the reason (bad signature, not the issuer, etc.) 
  */
 gboolean
 purple_certificate_signed_by(PurpleCertificate *crt, PurpleCertificate *issuer);
@@ -450,7 +450,7 @@
  * @param chain      List of PurpleCertificate instances comprising the chain,
  *                   in the order certificate, issuer, issuer's issuer, etc.
  * @return TRUE if the chain is valid. See description.
- * @TODO Specify which certificate in the chain caused a failure
+ * @todo Specify which certificate in the chain caused a failure
  */
 gboolean
 purple_certificate_check_signature_chain(GList *chain);
@@ -781,7 +781,7 @@
  * Displays a window showing X.509 certificate information
  *
  * @param crt    Certificate under an "x509" Scheme
- * @TODO Will break on CA certs, as they have no Common Name
+ * @todo Will break on CA certs, as they have no Common Name
  */
 void
 purple_certificate_display_x509(PurpleCertificate *crt);
--- a/libpurple/connection.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/connection.h	Sun Oct 28 17:16:42 2007 +0000
@@ -150,6 +150,7 @@
 /**************************************************************************/
 /*@{*/
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * This function should only be called by purple_account_connect()
  * in account.c.  If you're trying to sign on an account, use that
@@ -166,10 +167,14 @@
  * @param regist   Whether we are registering a new account or just
  *                 trying to do a normal signon.
  * @param password The password to use.
+ *
+ * @deprecated As this is internal, we should make it private in 3.0.0.
  */
 void purple_connection_new(PurpleAccount *account, gboolean regist,
 									const char *password);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * This function should only be called by purple_account_unregister()
  * in account.c.
@@ -179,9 +184,15 @@
  *
  * @param account  The account to unregister
  * @param password The password to use.
+ * @param cb Optional callback to be called when unregistration is complete
+ * @param user_data user data to pass to the callback
+ *
+ * @deprecated As this is internal, we should make it private in 3.0.0.
  */
 void purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Disconnects and destroys a PurpleConnection.
  *
@@ -190,8 +201,11 @@
  * function instead.
  *
  * @param gc The purple connection to destroy.
+ *
+ * @deprecated As this is internal, we should make it private in 3.0.0.
  */
 void purple_connection_destroy(PurpleConnection *gc);
+#endif
 
 /**
  * Sets the connection state.  PRPLs should call this and pass in
--- a/libpurple/conversation.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/conversation.c	Sun Oct 28 17:16:42 2007 +0000
@@ -205,8 +205,8 @@
 /* Functions that deal with PurpleConvMessage */
 
 static void
-add_message_to_history(PurpleConversation *conv, const char *who, const char *message,
-		PurpleMessageFlags flags, time_t when)
+add_message_to_history(PurpleConversation *conv, const char *who, const char *alias,
+		const char *message, PurpleMessageFlags flags, time_t when)
 {
 	PurpleConvMessage *msg;
 
@@ -218,13 +218,15 @@
 			me = conv->account->username;
 		who = me;
 	}
-	
+
 	msg = g_new0(PurpleConvMessage, 1);
 	PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage);
 	msg->who = g_strdup(who);
+	msg->alias = g_strdup(alias);
 	msg->flags = flags;
 	msg->what = g_strdup(message);
 	msg->when = when;
+	msg->conv = conv;
 
 	conv->message_history = g_list_prepend(conv->message_history, msg);
 }
@@ -233,6 +235,7 @@
 free_conv_message(PurpleConvMessage *msg)
 {
 	g_free(msg->who);
+	g_free(msg->alias);
 	g_free(msg->what);
 	PURPLE_DBUS_UNREGISTER_POINTER(msg);
 	g_free(msg);
@@ -292,14 +295,10 @@
 	/* Check if this conversation already exists. */
 	if ((conv = purple_find_conversation_with_account(type, name, account)) != NULL)
 	{
-		if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT ||
-		    purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
-		{
-			if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
-				purple_conversation_chat_cleanup_for_rejoin(conv);
-
-			return conv;
-		}
+		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
+				purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
+			purple_conversation_chat_cleanup_for_rejoin(conv);
+		return conv;
 	}
 
 	gc = purple_account_get_connection(account);
@@ -933,7 +932,8 @@
 
 	if (ops && ops->write_conv)
 		ops->write_conv(conv, who, alias, displayed, flags, mtime);
-	add_message_to_history(conv, who, message, flags, mtime);
+
+	add_message_to_history(conv, who, alias, message, flags, mtime);
 
 	purple_signal_emit(purple_conversations_get_handle(),
 		(type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
@@ -1208,9 +1208,9 @@
 	data[0] = conv;
 	data[1] = (gpointer)message;
 
-	purple_request_action(conv, NULL, _("Send Message"), text, 0,
+	purple_request_action_with_hint(conv, NULL, _("Send Message"), text, 0,
 						  purple_conversation_get_account(conv), NULL, conv,
-						  data, 2,
+						  PURPLE_REQUEST_UI_HINT_CONV, data, 2,
 						  _("_Send Message"), G_CALLBACK(purple_conv_send_confirm_cb),
 						  _("Cancel"), NULL);
 }
--- a/libpurple/conversation.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/conversation.h	Sun Oct 28 17:16:42 2007 +0000
@@ -294,6 +294,8 @@
 	char *what;
 	PurpleMessageFlags flags;
 	time_t when;
+	PurpleConversation *conv;  /**< @since 2.3.0 */
+	char *alias;               /**< @since 2.3.0 */
 };
 
 /**
--- a/libpurple/core.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/core.c	Sun Oct 28 17:16:42 2007 +0000
@@ -94,6 +94,8 @@
 	/* The signals subsystem is important and should be first. */
 	purple_signals_init();
 
+	purple_util_init();
+
 	purple_signal_register(core, "uri-handler",
 		purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
 		purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
@@ -229,6 +231,9 @@
 #ifdef HAVE_DBUS
 	purple_dbus_uninit();
 #endif
+
+	purple_util_uninit();
+
 	purple_signals_uninit();
 
 	g_free(core->ui);
--- a/libpurple/dnsquery.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/dnsquery.h	Sun Oct 28 17:16:42 2007 +0000
@@ -59,7 +59,7 @@
 	                         PurpleDnsQueryFailedCallback failed_cb);
 
 	/** Called just before @a query_data is freed; this should cancel any
-	 *  further use of @q query_data the UI would make. Unneeded if
+	 *  further use of @a query_data the UI would make. Unneeded if
 	 *  #resolve_host is not implemented.
 	 */
 	void (*destroy)(PurpleDnsQueryData *query_data);
--- a/libpurple/ft.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/ft.c	Sun Oct 28 17:16:42 2007 +0000
@@ -309,12 +309,12 @@
 static int
 purple_xfer_choose_file(PurpleXfer *xfer)
 {
-	purple_request_file(xfer, NULL, purple_xfer_get_filename(xfer),
+	purple_request_file_with_hint(xfer, NULL, purple_xfer_get_filename(xfer),
 					  (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE),
 					  G_CALLBACK(purple_xfer_choose_file_ok_cb),
 					  G_CALLBACK(purple_xfer_choose_file_cancel_cb),
 					  purple_xfer_get_account(xfer), xfer->who, NULL,
-					  xfer);
+					  PURPLE_REQUEST_UI_HINT_XFER, xfer);
 
 	return 0;
 }
@@ -359,10 +359,10 @@
 			serv_got_im(purple_account_get_connection(xfer->account),
 								 xfer->who, xfer->message, 0, time(NULL));
 
-		purple_request_accept_cancel(xfer, NULL, buf, NULL,
+		purple_request_accept_cancel_with_hint(xfer, NULL, buf, NULL,
 								  PURPLE_DEFAULT_ACTION_NONE,
 								  xfer->account, xfer->who, NULL,
-								  xfer,
+								  PURPLE_REQUEST_UI_HINT_XFER, xfer,
 								  G_CALLBACK(purple_xfer_choose_file),
 								  G_CALLBACK(cancel_recv_cb));
 
@@ -402,10 +402,10 @@
 					 "Remote host: %s\nRemote port: %d"),
 					   purple_xfer_get_remote_ip(xfer),
 					   purple_xfer_get_remote_port(xfer));
-	purple_request_accept_cancel(xfer, NULL, buf, buf2,
+	purple_request_accept_cancel_with_hint(xfer, NULL, buf, buf2,
 							   PURPLE_DEFAULT_ACTION_NONE,
 							   xfer->account, xfer->who, NULL,
-							   xfer,
+							   PURPLE_REQUEST_UI_HINT_XFER, xfer,
 							   G_CALLBACK(ask_accept_ok),
 							   G_CALLBACK(ask_accept_cancel));
 	g_free(buf);
--- a/libpurple/notify.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/notify.h	Sun Oct 28 17:16:42 2007 +0000
@@ -289,7 +289,7 @@
  */
 void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results,
 									   GList *row);
-
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Returns a number of the rows in the search results object.
  *
@@ -308,7 +308,9 @@
  * @return Number of the result rows.
  */
 guint purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Returns a number of the columns in the search results object.
  *
@@ -327,7 +329,9 @@
  * @return Number of the columns.
  */
 guint purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Returns a row of the results from the search results object.
  *
@@ -348,7 +352,9 @@
  */
 GList *purple_notify_searchresults_row_get(PurpleNotifySearchResults *results,
 										 unsigned int row_id);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Returns a title of the search results object's column.
  *
@@ -367,6 +373,7 @@
  */
 char *purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results,
 												 unsigned int column_id);
+#endif
 
 /*@}*/
 
--- a/libpurple/plugin.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugin.c	Sun Oct 28 17:16:42 2007 +0000
@@ -58,13 +58,9 @@
 #ifdef PURPLE_PLUGINS
 static GList *load_queue       = NULL;
 static GList *plugin_loaders   = NULL;
+static GList *plugins_to_disable = NULL;
 #endif
 
-/*
- * TODO: I think the intention was to allow multiple load and unload
- *       callback functions.  Perhaps using a GList instead of a
- *       pointer to a single function.
- */
 static void (*probe_cb)(void *) = NULL;
 static void *probe_cb_data = NULL;
 static void (*load_cb)(PurplePlugin *, void *) = NULL;
@@ -254,7 +250,6 @@
 		 * plugins being added to the global name space.
 		 *
 		 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
-		 * TODO: I guess there's nothing we can do about that?
 		 */
 #if GLIB_CHECK_VERSION(2,3,3)
 		plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL);
@@ -625,7 +620,6 @@
 
 	plugin->loaded = TRUE;
 
-	/* TODO */
 	if (load_cb != NULL)
 		load_cb(plugin, load_cb_data);
 
@@ -643,43 +637,37 @@
 {
 #ifdef PURPLE_PLUGINS
 	GList *l;
+	GList *ll;
 
 	g_return_val_if_fail(plugin != NULL, FALSE);
-
-	loaded_plugins = g_list_remove(loaded_plugins, plugin);
-	if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin))
-		protocol_plugins = g_list_remove(protocol_plugins, plugin);
-
 	g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE);
 
 	purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name);
 
-	/* cancel any pending dialogs the plugin has */
-	purple_request_close_with_handle(plugin);
-	purple_notify_close_with_handle(plugin);
-
-	plugin->loaded = FALSE;
-
 	/* Unload all plugins that depend on this plugin. */
-	while ((l = plugin->dependent_plugins) != NULL)
-	{
+	for (l = plugin->dependent_plugins; l != NULL; l = ll) {
 		const char * dep_name = (const char *)l->data;
 		PurplePlugin *dep_plugin;
 
+		/* Store a pointer to the next element in the list.
+		 * This is because we'll be modifying this list in the loop. */
+		ll = l->next;
+
 		dep_plugin = purple_plugins_find_with_id(dep_name);
 
 		if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin))
 		{
 			if (!purple_plugin_unload(dep_plugin))
 			{
-				char *tmp;
-
-				tmp = g_strdup_printf(_("The dependent plugin %s failed to unload."),
-				                      _(dep_plugin->info->name));
-
-				purple_notify_error(NULL, NULL,
-				                  _("There were errors unloading the plugin."), tmp);
-				g_free(tmp);
+				g_free(plugin->error);
+				plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."),
+				                                _(plugin->info->name),
+				                                _(dep_plugin->info->name));
+				return FALSE;
+			}
+			else
+			{
+				plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l);
 			}
 		}
 	}
@@ -699,8 +687,8 @@
 	}
 
 	if (plugin->native_plugin) {
-		if (plugin->info->unload != NULL)
-			plugin->info->unload(plugin);
+		if (plugin->info->unload && !plugin->info->unload(plugin))
+			return FALSE;
 
 		if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) {
 			PurplePluginProtocolInfo *prpl_info;
@@ -724,8 +712,7 @@
 				prpl_info->protocol_options = NULL;
 			}
 		}
-	}
-	else {
+	} else {
 		PurplePlugin *loader;
 		PurplePluginLoaderInfo *loader_info;
 
@@ -736,14 +723,30 @@
 
 		loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
 
-		if (loader_info->unload != NULL)
-			loader_info->unload(plugin);
+		if (loader_info->unload && !loader_info->unload(plugin))
+			return FALSE;
 	}
 
+	/* cancel any pending dialogs the plugin has */
+	purple_request_close_with_handle(plugin);
+	purple_notify_close_with_handle(plugin);
+
 	purple_signals_disconnect_by_handle(plugin);
 	purple_plugin_ipc_unregister_all(plugin);
 
-	/* TODO */
+	loaded_plugins = g_list_remove(loaded_plugins, plugin);
+	if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin))
+		protocol_plugins = g_list_remove(protocol_plugins, plugin);
+	plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
+	plugin->loaded = FALSE;
+
+	/* We wouldn't be anywhere near here if the plugin wasn't loaded, so
+	 * if plugin->error is set at all, it had to be from a previous
+	 * unload failure.  It's obviously okay now.
+	 */
+	g_free(plugin->error);
+	plugin->error = NULL;
+
 	if (unload_cb != NULL)
 		unload_cb(plugin, unload_cb_data);
 
@@ -757,6 +760,15 @@
 #endif /* PURPLE_PLUGINS */
 }
 
+void
+purple_plugin_disable(PurplePlugin *plugin)
+{
+	g_return_if_fail(plugin != NULL);
+
+	if (!g_list_find(plugins_to_disable, plugin))
+		plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
+}
+
 gboolean
 purple_plugin_reload(PurplePlugin *plugin)
 {
@@ -1222,14 +1234,14 @@
 #ifdef PURPLE_PLUGINS
 	GList *pl;
 	GList *files = NULL;
-	PurplePlugin *p;
 
 	for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
-		p = pl->data;
+		PurplePlugin *plugin = pl->data;
 
-		if (p->info->type != PURPLE_PLUGIN_PROTOCOL &&
-			p->info->type != PURPLE_PLUGIN_LOADER) {
-				files = g_list_append(files, p->path);
+		if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL &&
+		    plugin->info->type != PURPLE_PLUGIN_LOADER &&
+		    !g_list_find(plugins_to_disable, plugin)) {
+			files = g_list_append(files, plugin->path);
 		}
 	}
 
@@ -1391,6 +1403,7 @@
 
 	if (probe_cb != NULL)
 		probe_cb(probe_cb_data);
+
 #endif /* PURPLE_PLUGINS */
 }
 
@@ -1464,7 +1477,6 @@
 void
 purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
 {
-	/* TODO */
 	probe_cb = func;
 	probe_cb_data = data;
 }
@@ -1472,7 +1484,6 @@
 void
 purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
 {
-	/* TODO */
 	probe_cb = NULL;
 	probe_cb_data = NULL;
 }
@@ -1481,7 +1492,6 @@
 purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
 									 void *data)
 {
-	/* TODO */
 	load_cb = func;
 	load_cb_data = data;
 }
@@ -1489,7 +1499,6 @@
 void
 purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
 {
-	/* TODO */
 	load_cb = NULL;
 	load_cb_data = NULL;
 }
@@ -1498,7 +1507,6 @@
 purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
 									   void *data)
 {
-	/* TODO */
 	unload_cb = func;
 	unload_cb_data = data;
 }
@@ -1506,7 +1514,6 @@
 void
 purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
 {
-	/* TODO */
 	unload_cb = NULL;
 	unload_cb_data = NULL;
 }
--- a/libpurple/plugin.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugin.h	Sun Oct 28 17:16:42 2007 +0000
@@ -70,11 +70,6 @@
  *
  * This is used in the version 2.0 API and up.
  */
-/* TODO We need to figure out exactly what parts of this are required. The
- * dependent plugin unloading stuff was causing crashes with perl and tcl
- * plugins because they didn't set ids and the dependency code was requiring
- * them. Then we need to actually make sure that plugins have all the right
- * parts before loading them. */
 struct _PurplePluginInfo
 {
 	unsigned int magic;
@@ -296,6 +291,18 @@
 gboolean purple_plugin_unload(PurplePlugin *plugin);
 
 /**
+ * Disable a plugin.
+ *
+ * This function adds the plugin to a list of plugins to "disable at the next
+ * startup" by excluding said plugins from the list of plugins to save.  The
+ * UI needs to call purple_plugins_save_loaded() after calling this for it
+ * to have any effect.
+ *
+ * @since 2.3.0
+ */
+void purple_plugin_disable(PurplePlugin *plugin);
+
+/**
  * Reloads a plugin.
  *
  * @param plugin The old plugin handle.
@@ -525,53 +532,71 @@
  */
 gboolean purple_plugins_enabled(void);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Registers a function that will be called when probing is finished.
  *
  * @param func The callback function.
  * @param data Data to pass to the callback.
+ * @deprecated If you need this, ask for a plugin-probe signal to be added.
  */
 void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Unregisters a function that would be called when probing is finished.
  *
  * @param func The callback function.
+ * @deprecated If you need this, ask for a plugin-probe signal to be added.
  */
 void purple_plugins_unregister_probe_notify_cb(void (*func)(void *));
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Registers a function that will be called when a plugin is loaded.
  *
  * @param func The callback function.
  * @param data Data to pass to the callback.
+ * @deprecated Use the plugin-load signal instead.
  */
 void purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
 										  void *data);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Unregisters a function that would be called when a plugin is loaded.
  *
  * @param func The callback function.
+ * @deprecated Use the plugin-load signal instead.
  */
 void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *));
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Registers a function that will be called when a plugin is unloaded.
  *
  * @param func The callback function.
  * @param data Data to pass to the callback.
+ * @deprecated Use the plugin-unload signal instead.
  */
 void purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
 											void *data);
+#endif
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Unregisters a function that would be called when a plugin is unloaded.
  *
  * @param func The callback function.
+ * @deprecated Use the plugin-unload signal instead.
  */
 void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *,
 														   void *));
+#endif
 
 /**
  * Finds a plugin with the specified name.
--- a/libpurple/plugins/autoaccept.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/autoaccept.c	Sun Oct 28 17:16:42 2007 +0000
@@ -159,12 +159,12 @@
 
 	message = g_strdup_printf(_("When a file-transfer request arrives from %s"), 
 					purple_contact_get_alias((PurpleContact *)node));
-	purple_request_choice(plugin, _("Set Autoaccept Setting"), message,
+	purple_request_choice_with_hint(plugin, _("Set Autoaccept Setting"), message,
 						NULL, purple_blist_node_get_int(node, "autoaccept"),
 						_("_Save"), G_CALLBACK(save_cb),
 						_("_Cancel"), NULL,
 						NULL, NULL, NULL,
-						node,
+						PURPLE_REQUEST_UI_HINT_XFER, node,
 						_("Ask"), FT_ASK,
 						_("Auto Accept"), FT_ACCEPT,
 						_("Auto Reject"), FT_REJECT,
--- a/libpurple/plugins/buddynote.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/buddynote.c	Sun Oct 28 17:16:42 2007 +0000
@@ -43,14 +43,14 @@
 
 	note = purple_blist_node_get_string(node, "notes");
 
-	purple_request_input(node, _("Notes"),
+	purple_request_input_with_hint(node, _("Notes"),
 					   _("Enter your notes below..."),
 					   NULL,
 					   note, TRUE, FALSE, "html",
 					   _("Save"), G_CALLBACK(do_it_cb),
 					   _("Cancel"), G_CALLBACK(dont_do_it_cb),
 					   NULL, NULL, NULL,
-					   node);
+					   PURPLE_REQUEST_UI_HINT_BUDDY, node);
 }
 
 static void
--- a/libpurple/plugins/idle.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/idle.c	Sun Oct 28 17:16:42 2007 +0000
@@ -161,7 +161,7 @@
 	request = purple_request_fields_new();
 	purple_request_fields_add_group(request, group);
 
-	purple_request_fields(action->plugin,
+	purple_request_fields_with_hint(action->plugin,
 			N_("I'dle Mak'er"),
 			_("Set Account Idle Time"),
 			NULL,
@@ -169,7 +169,7 @@
 			_("_Set"), G_CALLBACK(idle_action_ok),
 			_("_Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, NULL);
 }
 
 static void
@@ -195,7 +195,7 @@
 	request = purple_request_fields_new();
 	purple_request_fields_add_group(request, group);
 
-	purple_request_fields(action->plugin,
+	purple_request_fields_with_hint(action->plugin,
 			N_("I'dle Mak'er"),
 			_("Unset Account Idle Time"),
 			NULL,
@@ -203,7 +203,7 @@
 			_("_Unset"), G_CALLBACK(unidle_action_ok),
 			_("_Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, NULL);
 }
 
 static void
@@ -221,7 +221,7 @@
 	request = purple_request_fields_new();
 	purple_request_fields_add_group(request, group);
 
-	purple_request_fields(action->plugin,
+	purple_request_fields_with_hint(action->plugin,
 			N_("I'dle Mak'er"),
 			_("Set Idle Time for All Accounts"),
 			NULL,
@@ -229,7 +229,7 @@
 			_("_Set"), G_CALLBACK(idle_all_action_ok),
 			_("_Cancel"), NULL,
 			NULL, NULL, NULL,
-			NULL);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, NULL);
 }
 
 static void
--- a/libpurple/plugins/log_reader.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Sun Oct 28 17:16:42 2007 +0000
@@ -28,6 +28,19 @@
 	NAME_GUESS_THEM
 };
 
+/* Some common functions. */
+static int get_month(const char *month)
+{
+	int iter;
+	const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+	for (iter = 0; months[iter]; iter++) {
+		if (strcmp(month, months[iter]) == 0)
+			break;
+	}
+	return iter;
+}
+
 
 /*****************************************************************************
  * Adium Logger                                                              *
@@ -103,9 +116,10 @@
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
-					char *contents;
+					char contents[57];   /* XXX: This is really inflexible. */
 					char *contents2;
 					struct adium_logger_data *data;
+					size_t rd;
 					PurpleLog *log;
 
 					if (!handle) {
@@ -113,11 +127,9 @@
 						continue;
 					}
 
-					/* XXX: This is really inflexible. */
-					contents = g_malloc(57);
-					fread(contents, 56, 1, handle);
+					rd = fread(contents, 1, 56, handle) == 0;
 					fclose(handle);
-					contents[56] = '\0';
+					contents[rd] = '\0';
 
 					/* XXX: This is fairly inflexible. */
 					contents2 = contents;
@@ -135,11 +147,9 @@
 
 						purple_debug_error("Adium log parse",
 						                   "Contents timestamp parsing error\n");
-						g_free(contents);
 						g_free(filename);
 						continue;
 					}
-					g_free(contents);
 
 					data = g_new0(struct adium_logger_data, 1);
 					data->path = filename;
@@ -168,21 +178,20 @@
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
-					char *contents;
+					char contents[14];   /* XXX: This is really inflexible. */
 					char *contents2;
 					struct adium_logger_data *data;
 					PurpleLog *log;
+					size_t rd;
 
 					if (!handle) {
 						g_free(filename);
 						continue;
 					}
 
-					/* XXX: This is really inflexible. */
-					contents = g_malloc(14);
-					fread(contents, 13, 1, handle);
+					rd = fread(contents, 1, 13, handle);
 					fclose(handle);
-					contents[13] = '\0';
+					contents[rd] = '\0';
 
 					contents2 = contents;
 					while (*contents2 && *contents2 != '(')
@@ -195,13 +204,10 @@
 
 						purple_debug_error("Adium log parse",
 						                   "Contents timestamp parsing error\n");
-						g_free(contents);
 						g_free(filename);
 						continue;
 					}
 
-					g_free(contents);
-
 					tm.tm_year -= 1900;
 					tm.tm_mon  -= 1;
 
@@ -1355,36 +1361,7 @@
 						 * daylight savings time.
 						 */
 						tm.tm_isdst = -1;
-
-						/* Ugly hack, in case current locale
-						 * is not English. This code is taken
-						 * from log.c.
-						 */
-						if (strcmp(month, "Jan") == 0) {
-							tm.tm_mon= 0;
-						} else if (strcmp(month, "Feb") == 0) {
-							tm.tm_mon = 1;
-						} else if (strcmp(month, "Mar") == 0) {
-							tm.tm_mon = 2;
-						} else if (strcmp(month, "Apr") == 0) {
-							tm.tm_mon = 3;
-						} else if (strcmp(month, "May") == 0) {
-							tm.tm_mon = 4;
-						} else if (strcmp(month, "Jun") == 0) {
-							tm.tm_mon = 5;
-						} else if (strcmp(month, "Jul") == 0) {
-							tm.tm_mon = 6;
-						} else if (strcmp(month, "Aug") == 0) {
-							tm.tm_mon = 7;
-						} else if (strcmp(month, "Sep") == 0) {
-							tm.tm_mon = 8;
-						} else if (strcmp(month, "Oct") == 0) {
-							tm.tm_mon = 9;
-						} else if (strcmp(month, "Nov") == 0) {
-							tm.tm_mon = 10;
-						} else if (strcmp(month, "Dec") == 0) {
-							tm.tm_mon = 11;
-						}
+						tm.tm_mon = get_month(month);
 
 						data = g_new0(
 							struct trillian_logger_data, 1);
@@ -1446,7 +1423,7 @@
 
 	file = g_fopen(data->path, "rb");
 	fseek(file, data->offset, SEEK_SET);
-	fread(read, data->length, 1, file);
+	data->length = fread(read, 1, data->length, file);
 	fclose(file);
 
 	if (read[data->length-1] == '\n') {
@@ -1945,7 +1922,7 @@
 	contents = g_malloc(data->length + 2);
 
 	fseek(file, data->offset, SEEK_SET);
-	fread(contents, data->length, 1, file);
+	data->length = fread(contents, 1, data->length, file);
 	fclose(file);
 
 	contents[data->length] = '\n';
@@ -2098,6 +2075,347 @@
 	g_free(data);
 }
 
+/*************************************************************************
+ * aMSN Logger                                                           *
+ *************************************************************************/
+
+/* The aMSN logger doesn't write logs, only reads them.  This is to include
+ * aMSN logs in the log viewer transparently.
+ */
+
+static PurpleLogLogger *amsn_logger;
+
+struct amsn_logger_data {
+	char *path;
+	int offset;
+	int length;
+};
+
+#define AMSN_LOG_CONV_START "|\"LRED[Conversation started on "
+#define AMSN_LOG_CONV_END "|\"LRED[You have closed the window on "
+#define AMSN_LOG_CONV_EXTRA "01 Aug 2001 00:00:00]"
+
+static GList *amsn_logger_parse_file(char *filename, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	GError *error;
+	char *contents;
+	struct amsn_logger_data *data;
+	PurpleLog *log;
+
+	purple_debug_info("aMSN logger", "Reading %s\n", filename);
+	error = NULL;
+	if (!g_file_get_contents(filename, &contents, NULL, &error)) {
+		purple_debug_error("aMSN logger",
+		                   "Couldn't read file %s: %s \n", filename,
+		                   (error && error->message) ?
+		                    error->message : "Unknown error");
+		if (error)
+			g_error_free(error);
+	} else {
+		char *c = contents;
+		gboolean found_start = FALSE;
+		char *start_log = c;
+		int offset = 0;
+		struct tm tm;
+		while (c && *c) {
+			if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) {
+				char month[4];
+				if (sscanf(c + strlen(AMSN_LOG_CONV_START),
+				           "%u %3s %u %u:%u:%u",
+				           &tm.tm_mday, (char*)&month, &tm.tm_year,
+				           &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+					found_start = FALSE;
+					purple_debug_error("aMSN logger",
+					                   "Error parsing start date for %s\n",
+					                   filename);
+				} else {
+					tm.tm_year -= 1900;
+
+					/* Let the C library deal with
+					 * daylight savings time.
+					 */
+					tm.tm_isdst = -1;
+					tm.tm_mon = get_month(month);
+
+					found_start = TRUE;
+					offset = c - contents;
+					start_log = c;
+				}
+			} else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
+				data = g_new0(struct amsn_logger_data, 1);
+				data->path = g_strdup(filename);
+				data->offset = offset;
+				data->length = c - start_log
+					             + strlen(AMSN_LOG_CONV_END)
+					             + strlen(AMSN_LOG_CONV_EXTRA);
+				log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
+				log->logger = amsn_logger;
+				log->logger_data = data;
+				list = g_list_prepend(list, log);
+				found_start = FALSE;
+
+				purple_debug_info("aMSN logger",
+				                  "Found log for %s:"
+				                  " path = (%s),"
+				                  " offset = (%d),"
+				                  " length = (%d)\n",
+				                  sn, data->path, data->offset, data->length);
+			}
+			c = strstr(c, "\n");
+			c++;
+		}
+
+		/* I've seen the file end without the AMSN_LOG_CONV_END bit */
+		if (found_start) {
+			data = g_new0(struct amsn_logger_data, 1);
+			data->path = g_strdup(filename);
+			data->offset = offset;
+			data->length = c - start_log
+				             + strlen(AMSN_LOG_CONV_END)
+				             + strlen(AMSN_LOG_CONV_EXTRA);
+			log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
+			log->logger = amsn_logger;
+			log->logger_data = data;
+			list = g_list_prepend(list, log);
+			found_start = FALSE;
+
+			purple_debug_info("aMSN logger",
+			                  "Found log for %s:"
+			                  " path = (%s),"
+			                  " offset = (%d),"
+			                  " length = (%d)\n",
+			                  sn, data->path, data->offset, data->length);
+		}
+		g_free(contents);
+	}
+
+	return list;
+}
+
+/* `log_dir`/username@hotmail.com/logs/buddyname@hotmail.com.log */
+/* `log_dir`/username@hotmail.com/logs/Month Year/buddyname@hotmail.com.log */
+static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	const char *logdir;
+	char *username;
+	char *log_path;
+	char *buddy_log;
+	char *filename;
+	GDir *dir;
+	const char *name;
+
+	logdir = purple_prefs_get_string("/plugins/core/log_reader/amsn/log_directory");
+
+	/* By clearing the log directory path, this logger can be (effectively) disabled. */
+	if (!logdir || !*logdir)
+		return NULL;
+
+	/* aMSN only works with MSN/WLM */
+	if (strcmp(account->protocol_id, "prpl-msn"))
+		return NULL;
+
+	username = g_strdup(purple_normalize(account, account->username));
+	buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
+	log_path = g_build_filename(logdir, username, "logs", NULL);
+
+	/* First check in the top-level */
+	filename = g_build_filename(log_path, buddy_log, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		list = amsn_logger_parse_file(filename, sn, account);
+	else
+		g_free(filename);
+
+	/* Check in previous months */
+	dir = g_dir_open(log_path, 0, NULL);
+	if (dir) {
+		while ((name = g_dir_read_name(dir)) != NULL) {
+			filename = g_build_filename(log_path, name, buddy_log, NULL);
+			if (g_file_test(filename, G_FILE_TEST_EXISTS))
+				list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+			g_free(filename);
+		}
+		g_dir_close(dir);
+	}
+
+	g_free(log_path);
+
+	/* New versions use 'friendlier' directory names */
+	purple_util_chrreplace(username, '@', '_');
+	purple_util_chrreplace(username, '.', '_');
+
+	log_path = g_build_filename(logdir, username, "logs", NULL);
+
+	/* First check in the top-level */
+	filename = g_build_filename(log_path, buddy_log, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+	g_free(filename);
+
+	/* Check in previous months */
+	dir = g_dir_open(log_path, 0, NULL);
+	if (dir) {
+		while ((name = g_dir_read_name(dir)) != NULL) {
+			filename = g_build_filename(log_path, name, buddy_log, NULL);
+			if (g_file_test(filename, G_FILE_TEST_EXISTS))
+				list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+			g_free(filename);
+		}
+		g_dir_close(dir);
+	}
+
+	g_free(log_path);
+	g_free(username);
+	g_free(buddy_log);
+
+	return list;
+}
+
+/* Really it's |"L, but the string's been escaped */
+#define AMSN_LOG_FORMAT_TAG "|&quot;L"
+
+static char *amsn_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	struct amsn_logger_data *data;
+	FILE *file;
+	char *contents;
+	char *escaped;
+	GString *formatted;
+	char *start;
+	gboolean in_span = FALSE;
+
+	if (flags != NULL)
+		*flags = PURPLE_LOG_READ_NO_NEWLINE;
+
+	g_return_val_if_fail(log != NULL, g_strdup(""));
+
+	data = log->logger_data;
+
+	g_return_val_if_fail(data->path != NULL, g_strdup(""));
+	g_return_val_if_fail(data->length > 0, g_strdup(""));
+
+	contents = g_malloc(data->length + 2);
+
+	file = g_fopen(data->path, "rb");
+	g_return_val_if_fail(file != NULL, g_strdup(""));
+	
+	fseek(file, data->offset, SEEK_SET);
+	data->length = fread(contents, 1, data->length, file);
+	fclose(file);
+
+	contents[data->length] = '\n';
+	contents[data->length + 1] = '\0';
+
+	escaped = g_markup_escape_text(contents, -1);
+	g_free(contents);
+	contents = escaped;
+
+	formatted = g_string_sized_new(data->length + 2);
+
+	start = contents;
+	while (start && *start) {
+		char *end;
+		char *old_tag;
+		char *tag;
+		end = strstr(start, "\n");
+		if (!end)
+			break;
+		*end = '\0';
+		if (purple_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) {
+			/* New format for this line */
+			g_string_append(formatted, "</span><br>");
+			in_span = FALSE;
+		} else if (start != contents) {
+			/* Continue format from previous line */
+			g_string_append(formatted, "<br>");
+		}
+		old_tag = start;
+		tag = strstr(start, AMSN_LOG_FORMAT_TAG);
+		while (tag) {
+			g_string_append_len(formatted, old_tag, tag - old_tag);
+			tag += strlen(AMSN_LOG_FORMAT_TAG);
+			if (in_span) {
+				g_string_append(formatted, "</span>");
+				in_span = FALSE;
+			}
+			if (*tag == 'C') {
+				/* |"LCxxxxxx is a hex colour */
+				char colour[7];
+				strncpy(colour, tag + 1, 6);
+				colour[6] = '\0';
+				g_string_append_printf(formatted, "<span style=\"color: #%s;\">", colour);
+				/* This doesn't appear to work? */
+				/* g_string_append_printf(formatted, "<span style=\"color: #%6s;\">", tag + 1); */
+				in_span = TRUE;
+				old_tag = tag + 7; /* C + xxxxxx */
+			} else {
+				/* |"Lxxx is a 3-digit colour code */
+				if (purple_str_has_prefix(tag, "RED")) {
+					g_string_append(formatted, "<span style=\"color: red;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "GRA")) {
+					g_string_append(formatted, "<span style=\"color: gray;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "NOR")) {
+					g_string_append(formatted, "<span style=\"color: black;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "ITA")) {
+					g_string_append(formatted, "<span style=\"color: blue;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "GRE")) {
+					g_string_append(formatted, "<span style=\"color: darkgreen;\">");
+					in_span = TRUE;
+				} else {
+					purple_debug_info("aMSN logger", "Unknown colour format: %3s\n", tag);
+				}
+				old_tag = tag + 3;
+			}
+			tag = strstr(tag, AMSN_LOG_FORMAT_TAG);
+		}
+		g_string_append(formatted, old_tag);
+		start = end + 1;
+	}
+	if (in_span)
+		g_string_append(formatted, "</span>");
+
+	g_free(contents);
+
+	return g_string_free(formatted, FALSE);
+}
+
+static int amsn_logger_size(PurpleLog *log)
+{
+	struct amsn_logger_data *data;
+	char *text;
+	int size;
+
+	g_return_val_if_fail(log != NULL, 0);
+
+	data = log->logger_data;
+	
+	if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
+		return data ? data->length : 0;
+	}
+
+	text = amsn_logger_read(log, NULL);
+	size = strlen(text);
+	g_free(text);
+
+	return size;
+}
+
+static void amsn_logger_finalize(PurpleLog *log)
+{
+	struct amsn_logger_data *data;
+
+	g_return_if_fail(log != NULL);
+
+	data = log->logger_data;
+	g_free(data->path);
+	g_free(data);
+}
+
 /*****************************************************************************
  * Plugin Code                                                               *
  *****************************************************************************/
@@ -2347,6 +2665,19 @@
 #endif
 	purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path ? path : "");
 	g_free(path);
+
+	/* Add aMSN Messenger log directory preference. */
+	purple_prefs_add_none("/plugins/core/log_reader/amsn");
+
+	/* Calculate default aMSN log directory. */
+#ifdef _WIN32
+	folder = wpurple_get_special_folder(CSIDL_PROFILE); /* Silly aMSN, not using CSIDL_APPDATA */
+	path = g_build_filename(folder, "amsn", NULL);
+#else
+	path = g_build_filename(purple_home_dir(), ".amsn", NULL);
+#endif
+	purple_prefs_add_string("/plugins/core/log_reader/amsn/log_directory", path);
+	g_free(path);
 }
 
 static gboolean
@@ -2429,6 +2760,18 @@
 										  trillian_logger_size);
 	purple_log_logger_add(trillian_logger);
 
+	/* The names of IM clients are marked for translation at the request of
+	   translators who wanted to transliterate them.  Many translators
+	   choose to leave them alone.  Choose what's best for your language. */
+	amsn_logger = purple_log_logger_new("amsn", _("aMSN"), 6,
+									   NULL,
+									   NULL,
+									   amsn_logger_finalize,
+									   amsn_logger_list,
+									   amsn_logger_read,
+									   amsn_logger_size);
+	purple_log_logger_add(amsn_logger);
+
 	return TRUE;
 }
 
@@ -2445,6 +2788,7 @@
 	purple_log_logger_remove(msn_logger);
 	purple_log_logger_remove(trillian_logger);
 	purple_log_logger_remove(qip_logger);
+	purple_log_logger_remove(amsn_logger);
 
 	return TRUE;
 }
@@ -2505,6 +2849,10 @@
 		"/plugins/core/log_reader/trillian/log_directory", _("Trillian"));
 	purple_plugin_pref_frame_add(frame, ppref);
 
+	ppref = purple_plugin_pref_new_with_name_and_label(
+		"/plugins/core/log_reader/amsn/log_directory", _("aMSN"));
+	purple_plugin_pref_frame_add(frame, ppref);
+
 	return frame;
 }
 
--- a/libpurple/plugins/offlinemsg.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/offlinemsg.c	Sun Oct 28 17:16:42 2007 +0000
@@ -157,11 +157,11 @@
 						"rest of the messages in a pounce and automatically send them "
 						"when \"%s\" logs back in?"), who, who);
 	
-		purple_request_action(handle, _("Offline Message"), ask,
+		purple_request_action_with_hint(handle, _("Offline Message"), ask,
 					_("You can edit/delete the pounce from the `Buddy Pounces' dialog"),
 					1,
 					offline->account, offline->who, offline->conv,
-					offline, 2,
+					PURPLE_REQUEST_UI_HINT_BUDDY, offline, 2,
 					_("Yes"), record_pounce,
 					_("No"), cancel_poune);
 		g_free(ask);
--- a/libpurple/plugins/perl/common/Request.xs	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/perl/common/Request.xs	Sun Oct 28 17:16:42 2007 +0000
@@ -141,7 +141,38 @@
 	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
 	g_free(basename);
 
-	RETVAL = purple_request_input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+	RETVAL = purple_request_input_with_hint(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gpr);
+OUTPUT:
+	RETVAL
+
+void *
+purple_request_input_with_hint(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, ok_cb, cancel_text, cancel_cb, ui_hint)
+	Purple::Plugin handle
+	const char * title
+	const char * primary
+	const char * secondary
+	const char * default_value
+	gboolean multiline
+	gboolean masked
+	gchar * hint
+	const char * ok_text
+	SV * ok_cb
+	const char * cancel_text
+	SV * cancel_cb
+	const char * ui_hint
+CODE:
+	PurplePerlRequestData *gpr;
+	STRLEN len;
+	char *basename;
+
+	basename = g_path_get_basename(handle->path);
+	purple_perl_normalize_script_name(basename);
+	gpr = g_new(PurplePerlRequestData, 1);
+	gpr->ok_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(ok_cb, len));
+	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
+	g_free(basename);
+
+	RETVAL = purple_request_input_with_hint(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, ui_hint, gpr);
 OUTPUT:
 	RETVAL
 
@@ -165,7 +196,32 @@
 	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
 	g_free(basename);
 
-	RETVAL = purple_request_file(handle, title, filename, savedialog, G_CALLBACK(purple_perl_request_ok_cb), G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+	RETVAL = purple_request_file_with_hint(handle, title, filename, savedialog, G_CALLBACK(purple_perl_request_ok_cb), G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gpr);
+OUTPUT:
+	RETVAL
+
+void *
+purple_request_file_with_hint(handle, title, filename, savedialog, ok_cb, cancel_cb, ui_hint)
+	Purple::Plugin handle
+	const char * title
+	const char * filename
+	gboolean savedialog
+	SV * ok_cb
+	SV * cancel_cb
+	const char * ui_hint
+CODE:
+	PurplePerlRequestData *gpr;
+	STRLEN len;
+	char *basename;
+
+	basename = g_path_get_basename(handle->path);
+	purple_perl_normalize_script_name(basename);
+	gpr = g_new(PurplePerlRequestData, 1);
+	gpr->ok_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(ok_cb, len));
+	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
+	g_free(basename);
+
+	RETVAL = purple_request_file_with_hint(handle, title, filename, savedialog, G_CALLBACK(purple_perl_request_ok_cb), G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, ui_hint, gpr);
 OUTPUT:
 	RETVAL
 
@@ -192,7 +248,35 @@
 	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
 	g_free(basename);
 
-	RETVAL = purple_request_fields(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+	RETVAL = purple_request_fields_with_hint(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gpr);
+OUTPUT:
+	RETVAL
+
+void *
+purple_request_fields_with_hint(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb, ui_hint)
+	Purple::Plugin handle
+	const char * title
+	const char * primary
+	const char * secondary
+	Purple::Request::Fields fields
+	const char * ok_text
+	SV * ok_cb
+	const char * cancel_text
+	SV * cancel_cb
+	const char *ui_hint
+CODE:
+	PurplePerlRequestData *gpr;
+	STRLEN len;
+	char *basename;
+
+	basename = g_path_get_basename(handle->path);
+	purple_perl_normalize_script_name(basename);
+	gpr = g_new(PurplePerlRequestData, 1);
+	gpr->ok_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(ok_cb, len));
+	gpr->cancel_cb = g_strdup_printf("Purple::Script::%s::%s", basename, SvPV(cancel_cb, len));
+	g_free(basename);
+
+	RETVAL = purple_request_fields_with_hint(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, ui_hint, gpr);
 OUTPUT:
 	RETVAL
 
--- a/libpurple/plugins/signals-test.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/plugins/signals-test.c	Sun Oct 28 17:16:42 2007 +0000
@@ -76,6 +76,28 @@
 					old, purple_account_get_alias(account));
 }
 
+static int
+account_authorization_requested_cb(PurpleAccount *account, const char *user, gpointer data)
+{
+	purple_debug_misc("signals test", "account-authorization-requested (%s, %s)\n",
+			purple_account_get_username(account), user);
+	return 0;
+}
+
+static void
+account_authorization_granted_cb(PurpleAccount *account, const char *user, gpointer data)
+{
+	purple_debug_misc("signals test", "account-authorization-granted (%s, %s)\n",
+			purple_account_get_username(account), user);
+}
+
+static void
+account_authorization_denied_cb(PurpleAccount *account, const char *user, gpointer data)
+{
+	purple_debug_misc("signals test", "account-authorization-denied (%s, %s)\n",
+			purple_account_get_username(account), user);
+}
+
 /**************************************************************************
  * Buddy Icons signal callbacks
  **************************************************************************/
@@ -568,6 +590,12 @@
 						plugin, PURPLE_CALLBACK(account_status_changed), NULL);
 	purple_signal_connect(accounts_handle, "account-alias-changed",
 						plugin, PURPLE_CALLBACK(account_alias_changed), NULL);
+	purple_signal_connect(accounts_handle, "account-authorization-requested",
+						plugin, PURPLE_CALLBACK(account_authorization_requested_cb), NULL);
+	purple_signal_connect(accounts_handle, "account-authorization-denied",
+						plugin, PURPLE_CALLBACK(account_authorization_denied_cb), NULL);
+	purple_signal_connect(accounts_handle, "account-authorization-granted",
+						plugin, PURPLE_CALLBACK(account_authorization_granted_cb), NULL);
 
 	/* Buddy List subsystem signals */
 	purple_signal_connect(blist_handle, "buddy-status-changed",
--- a/libpurple/protocols/bonjour/buddy.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Sun Oct 28 17:16:42 2007 +0000
@@ -62,9 +62,11 @@
 }
 
 void
-set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len){
+set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, guint32 len){
 	gchar **fld = NULL;
 
+	g_return_if_fail(record_key != NULL);
+
 	if (!strcmp(record_key, "1st"))
 		fld = &buddy->first;
 	else if(!strcmp(record_key, "email"))
--- a/libpurple/protocols/bonjour/buddy.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Sun Oct 28 17:16:42 2007 +0000
@@ -83,7 +83,7 @@
 /**
  * Sets a value in the BonjourBuddy struct, destroying the old value
  */
-void set_bonjour_buddy_value(BonjourBuddy *buddy, const char *record_key, const char *value, uint32_t len);
+void set_bonjour_buddy_value(BonjourBuddy *buddy, const char *record_key, const char *value, guint32 len);
 
 /**
  * Check if all the compulsory buddy data is present.
--- a/libpurple/protocols/gg/gg.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/gg/gg.c	Sun Oct 28 17:16:42 2007 +0000
@@ -333,10 +333,10 @@
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
-	purple_request_file(action, _("Save buddylist..."), NULL, TRUE,
+	purple_request_file_with_hint(action, _("Save buddylist..."), NULL, TRUE,
 			G_CALLBACK(ggp_callback_buddylist_save_ok), NULL,
 			purple_connection_get_account(gc), NULL, NULL,
-			gc);
+			PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 /* }}} */
 
@@ -347,10 +347,10 @@
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
-	purple_request_file(action, "Load buddylist from file...", NULL, FALSE,
+	purple_request_file_with_hint(action, "Load buddylist from file...", NULL, FALSE,
 			G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
 			purple_connection_get_account(gc), NULL, NULL,
-			gc);
+			PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 /* }}} */
 
@@ -498,7 +498,7 @@
 			_("Current token"), token->data, token->size);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(account,
+	purple_request_fields_with_hint(account,
 		_("Register New Gadu-Gadu Account"),
 		_("Register New Gadu-Gadu Account"),
 		_("Please, fill in the following fields"),
@@ -506,7 +506,7 @@
 		_("OK"), G_CALLBACK(ggp_callback_register_account_ok),
 		_("Cancel"), G_CALLBACK(ggp_callback_register_account_cancel),
 		purple_connection_get_account(gc), NULL, NULL,
-		gc);
+		PURPLE_REQUEST_UI_HINT_REGISTER, gc);
 }
 /* }}} */
 
@@ -657,7 +657,7 @@
 			_("Only online"), FALSE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(gc,
+	purple_request_fields_with_hint(gc,
 		_("Find buddies"),
 		_("Find buddies"),
 		_("Please, enter your search criteria below"),
@@ -665,7 +665,7 @@
 		_("OK"), G_CALLBACK(ggp_callback_find_buddies),
 		_("Cancel"), NULL,
 		purple_connection_get_account(gc), NULL, NULL,
-		gc);
+		PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 /* }}} */
 
@@ -795,14 +795,14 @@
 		_("Please, enter your current password and your new password for UIN: "),
 		ggp_get_uin(purple_connection_get_account(gc)));
 
-	purple_request_fields(gc,
+	purple_request_fields_with_hint(gc,
 		_("Change Gadu-Gadu Password"),
 		_("Change Gadu-Gadu Password"),
 		msg,
 		fields, _("OK"), G_CALLBACK(ggp_callback_change_passwd_ok),
 		_("Cancel"), NULL,
 		purple_connection_get_account(gc), NULL, NULL,
-		gc);
+		PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 
 	g_free(msg);
 }
@@ -875,7 +875,7 @@
 
 	msg = g_strdup_printf(_("Select a chat for buddy: %s"),
 			      purple_buddy_get_alias(buddy));
-	purple_request_fields(gc,
+	purple_request_fields_with_hint(gc,
 			_("Add to chat..."),
 			_("Add to chat..."),
 			msg,
@@ -883,7 +883,7 @@
 			_("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok),
 			_("Cancel"), NULL,
 			purple_connection_get_account(gc), NULL, NULL,			  
-			gc);
+			PURPLE_REQUEST_UI_HINT_BUDDY, gc);
 	g_free(msg);
 }
 /* }}} */
--- a/libpurple/protocols/jabber/auth.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Sun Oct 28 17:16:42 2007 +0000
@@ -318,10 +318,10 @@
 				} else if (!plaintext) {
 					char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"),
 							js->gc->account->username);
-					purple_request_yes_no(js->gc, _("Plaintext Authentication"),
+					purple_request_yes_no_with_hint(js->gc, _("Plaintext Authentication"),
 							_("Plaintext Authentication"),
 							msg,
-							2, js->gc->account, NULL, NULL, js->gc->account,
+							2, js->gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_ACCOUNT, js->gc->account,
 							allow_cyrus_plaintext_auth,
 							disallow_plaintext_auth);
 					g_free(msg);
@@ -507,13 +507,13 @@
 		if(js->gsc == NULL && !purple_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE)) {
 			char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"),
 					js->gc->account->username);
-			purple_request_yes_no(js->gc, _("Plaintext Authentication"),
+			purple_request_yes_no_with_hint(js->gc, _("Plaintext Authentication"),
 					_("Plaintext Authentication"),
 					msg,
 					2,
 					purple_connection_get_account(js->gc), NULL, NULL,
-					purple_connection_get_account(js->gc), allow_plaintext_auth,
-					disallow_plaintext_auth);
+					PURPLE_REQUEST_UI_HINT_ACCOUNT, purple_connection_get_account(js->gc),
+					allow_plaintext_auth, disallow_plaintext_auth);
 			g_free(msg);
 			return;
 		}
@@ -595,13 +595,13 @@
 		} else if(xmlnode_get_child(query, "password")) {
 			if(js->gsc == NULL && !purple_account_get_bool(js->gc->account,
 						"auth_plain_in_clear", FALSE)) {
-				purple_request_yes_no(js->gc, _("Plaintext Authentication"),
+				purple_request_yes_no_with_hint(js->gc, _("Plaintext Authentication"),
 						_("Plaintext Authentication"),
 						_("This server requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"),
 						2,
 						purple_connection_get_account(js->gc), NULL, NULL,
-						purple_connection_get_account(js->gc), allow_plaintext_auth,
-						disallow_plaintext_auth);
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, purple_connection_get_account(js->gc),
+						allow_plaintext_auth, disallow_plaintext_auth);
 				return;
 			}
 			finish_plaintext_authentication(js);
--- a/libpurple/protocols/jabber/buddy.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sun Oct 28 17:16:42 2007 +0000
@@ -719,7 +719,7 @@
 	if(x_vc_data != NULL)
 		xmlnode_free(x_vc_data);
 
-	purple_request_fields(gc, _("Edit XMPP vCard"),
+	purple_request_fields_with_hint(gc, _("Edit XMPP vCard"),
 						_("Edit XMPP vCard"),
 						_("All items below are optional. Enter only the "
 						  "information with which you feel comfortable."),
@@ -727,7 +727,7 @@
 						_("Save"), G_CALLBACK(jabber_format_info),
 						_("Cancel"), NULL,
 						purple_connection_get_account(gc), NULL, NULL,
-						gc);
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 /*---------------------------------------*/
@@ -2410,12 +2410,12 @@
 		usi->js = js;
 		usi->directory_server = g_strdup(from);
 
-		purple_request_fields(js->gc, _("Search for XMPP users"),
+		purple_request_fields_with_hint(js->gc, _("Search for XMPP users"),
 				_("Search for XMPP users"), instructions, fields,
 				_("Search"), G_CALLBACK(user_search_cb),
 				_("Cancel"), G_CALLBACK(user_search_cancel_cb),
 				purple_connection_get_account(js->gc), NULL, NULL,
-				usi);
+				PURPLE_REQUEST_UI_HINT_ACCOUNT, usi);
 
 		g_free(instructions);
 	}
@@ -2444,14 +2444,14 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	JabberStream *js = gc->proto_data;
 
-	purple_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"),
+	purple_request_input_with_hint(gc, _("Enter a User Directory"), _("Enter a User Directory"),
 			_("Select a user directory to search"),
 			js->user_directories ? js->user_directories->data : NULL,
 			FALSE, FALSE, NULL,
 			_("Search Directory"), PURPLE_CALLBACK(jabber_user_search),
 			_("Cancel"), NULL,
 			NULL, NULL, NULL,
-			js);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, js);
 }
 
 
--- a/libpurple/protocols/jabber/chat.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/chat.c	Sun Oct 28 17:16:42 2007 +0000
@@ -778,14 +778,14 @@
 	purple_roomlist_set_fields(js->roomlist, fields);
 
 
-	purple_request_input(gc, _("Enter a Conference Server"), _("Enter a Conference Server"),
+	purple_request_input_with_hint(gc, _("Enter a Conference Server"), _("Enter a Conference Server"),
 			_("Select a conference server to query"),
 			js->chat_servers ? js->chat_servers->data : NULL,
 			FALSE, FALSE, NULL,
 			_("Find Rooms"), PURPLE_CALLBACK(roomlist_ok_cb),
 			_("Cancel"), PURPLE_CALLBACK(roomlist_cancel_cb),
 			purple_connection_get_account(gc), NULL, NULL,			 
-			js);
+			PURPLE_REQUEST_UI_HINT_CONV, js);
 
 	return js->roomlist;
 }
--- a/libpurple/protocols/jabber/google.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/google.c	Sun Oct 28 17:16:42 2007 +0000
@@ -516,3 +516,22 @@
 	}
 	return g_string_free(str, FALSE);
 }
+
+void jabber_google_presence_incoming(JabberStream *js, const char *user, JabberBuddyResource *jbr)
+{
+	if (!js->googletalk)
+		return;
+	if (jbr->status && !strncmp(jbr->status, "♫ ", strlen("♫ "))) {
+		purple_prpl_got_user_status(js->gc->account, user, "tune",
+					    PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL);
+		jbr->status = NULL;
+	} else {
+		purple_prpl_got_user_status_deactive(js->gc->account, user, "tune");
+	}
+}
+
+char *jabber_google_presence_outgoing(PurpleStatus *tune)
+{
+	char *ret = g_strdup_printf("♫ %s", purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE));
+	return ret;
+}
--- a/libpurple/protocols/jabber/google.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/google.h	Sun Oct 28 17:16:42 2007 +0000
@@ -36,6 +36,10 @@
  * if this roster item should continue to be processed
  */
 gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item);
+
+void jabber_google_presence_incoming(JabberStream *js, const char *who, JabberBuddyResource *jbr);
+char *jabber_google_presence_outgoing(PurpleStatus *tune);
+
 void jabber_google_roster_add_deny(PurpleConnection *gc, const char *who);
 void jabber_google_roster_rem_deny(PurpleConnection *gc, const char *who);
 
--- a/libpurple/protocols/jabber/jabber.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1002,21 +1002,21 @@
 	cbdata->who = g_strdup(from);
 
 	if(js->registration)
-		purple_request_fields(js->gc, _("Register New XMPP Account"),
+		purple_request_fields_with_hint(js->gc, _("Register New XMPP Account"),
 				_("Register New XMPP Account"), instructions, fields,
 				_("Register"), G_CALLBACK(jabber_register_cb),
 				_("Cancel"), G_CALLBACK(jabber_register_cancel_cb),
 				purple_connection_get_account(js->gc), NULL, NULL,
-				cbdata);
+				PURPLE_REQUEST_UI_HINT_REGISTER, cbdata);
 	else {
 		char *title = registered?g_strdup_printf(_("Change Account Registration at %s"), from)
 								:g_strdup_printf(_("Register New Account at %s"), from);
-		purple_request_fields(js->gc, title,
+		purple_request_fields_with_hint(js->gc, title,
 			  title, instructions, fields,
 			  registered?_("Change Registration"):_("Register"), G_CALLBACK(jabber_register_cb),
 			  _("Cancel"), G_CALLBACK(jabber_register_cancel_cb),
 			  purple_connection_get_account(js->gc), NULL, NULL,
-			  cbdata);
+			  PURPLE_REQUEST_UI_HINT_REGISTER, cbdata);
 		g_free(title);
 	}
 
@@ -1400,10 +1400,11 @@
 		char *stripped;
 
 		if(!(stripped = purple_markup_strip_html(jabber_buddy_get_status_msg(jb)))) {
-			PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(b));
-
-			if(!purple_status_is_available(status))
-				stripped = g_strdup(purple_status_get_name(status));
+			PurplePresence *presence = purple_buddy_get_presence(b);
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
+				PurpleStatus *status = purple_presence_get_status(presence, "tune");
+				stripped = g_strdup(purple_status_get_attr_string(status, PURPLE_TUNE_TITLE));
+			}
 		}
 
 		if(stripped) {
@@ -1429,6 +1430,7 @@
 
 	if(jb) {
 		JabberBuddyResource *jbr = NULL;
+		PurplePresence *presence = purple_buddy_get_presence(b);
 		const char *sub;
 		GList *l;
 		const char *mood;
@@ -1455,7 +1457,7 @@
 			
 			purple_notify_user_info_add_pair(user_info, _("Subscription"), sub);
 			
-			status = purple_presence_get_active_status(purple_buddy_get_presence(b));
+			status = purple_presence_get_active_status(presence);
 			value = purple_status_get_attr_value(status, "mood");
 			if (value && purple_value_get_type(value) == PURPLE_TYPE_STRING && (mood = purple_value_get_string(value))) {
 				
@@ -1467,7 +1469,12 @@
 					g_free(moodplustext);
 				} else
 					purple_notify_user_info_add_pair(user_info, _("Mood"), mood);
-		}
+			}
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {	
+				PurpleStatus *tune = purple_presence_get_status(presence, "tune");
+				const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
+				purple_notify_user_info_add_pair(user_info, _("Current media"), title);
+			}
 		}
 
 		for(l=jb->resources; l; l = l->next) {
@@ -1532,15 +1539,6 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1555,15 +1553,6 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1578,15 +1567,6 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1601,15 +1581,6 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1624,15 +1595,6 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1650,6 +1612,20 @@
 			NULL);
 	types = g_list_append(types, type);
 
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
+			"tune", NULL, TRUE, TRUE, TRUE,
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+
 	return types;
 }
 
@@ -1731,12 +1707,12 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(js->gc, _("Change XMPP Password"),
+	purple_request_fields_with_hint(js->gc, _("Change XMPP Password"),
 			_("Change XMPP Password"), _("Please enter your new password"),
 			fields, _("OK"), G_CALLBACK(jabber_password_change_cb),
 			_("Cancel"), NULL,
 			purple_connection_get_account(gc), NULL, NULL,
-			js);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, js);
 }
 
 GList *jabber_actions(PurplePlugin *plugin, gpointer context)
--- a/libpurple/protocols/jabber/message.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/message.c	Sun Oct 28 17:16:42 2007 +0000
@@ -104,6 +104,7 @@
 
 					g_snprintf(buf, sizeof(buf),
 					           _("%s has left the conversation."), escaped);
+					g_free(escaped);
 
 					/* At some point when we restructure PurpleConversation,
 					 * this should be able to be implemented by removing the
--- a/libpurple/protocols/jabber/presence.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sun Oct 28 17:16:42 2007 +0000
@@ -33,6 +33,7 @@
 
 #include "buddy.h"
 #include "chat.h"
+#include "google.h"
 #include "presence.h"
 #include "iq.h"
 #include "jutil.h"
@@ -104,13 +105,14 @@
 	char *stripped = NULL;
 	JabberBuddyState state;
 	int priority;
-	const char *artist, *title, *source, *uri, *track;
-	int length;
+	const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL;
+	int length = -1;
 	gboolean allowBuzz;
+	PurplePresence *p = purple_account_get_presence(account);
+	PurpleStatus *tune;
 
-	if(NULL == status) {
-		PurplePresence *gpresence = purple_account_get_presence(account);
-		status = purple_presence_get_active_status(gpresence);
+	if (NULL == status) {
+		status = purple_presence_get_active_status(p);
 	}
 
 	if(!purple_status_is_active(status))
@@ -144,6 +146,12 @@
 	if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) ||
 		js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) {
 		js->allowBuzz = allowBuzz;
+
+		if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
+			tune = purple_presence_get_status(p, "tune");
+			stripped = jabber_google_presence_outgoing(tune);
+		}
+
 		presence = jabber_presence_create_js(js, state, stripped, priority);
 
 		if(js->avatar_hash) {
@@ -172,12 +180,16 @@
 	}
 					  	
 	/* next, check if there are any changes to the tune values */
-	artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
-	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-	source = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
-	uri = purple_status_get_attr_string(status, PURPLE_TUNE_URL);
-	track = purple_status_get_attr_string(status, PURPLE_TUNE_TRACK);
-	length = (!purple_status_get_attr_value(status, PURPLE_TUNE_TIME))?-1:purple_status_get_attr_int(status, PURPLE_TUNE_TIME);
+	tune = purple_presence_get_status(p, "tune");
+	if (tune && purple_status_is_active(tune)) {
+		artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+		title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
+		source = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+		uri = purple_status_get_attr_string(tune, PURPLE_TUNE_URL);
+		track = purple_status_get_attr_string(tune, PURPLE_TUNE_TRACK);
+		length = (!purple_status_get_attr_value(tune, PURPLE_TUNE_TIME)) ? -1 :
+				purple_status_get_attr_int(tune, PURPLE_TUNE_TIME);
+	}
 	
 	if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) ||
 	   CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) {
@@ -500,7 +512,7 @@
 						if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
 							chat->config_dialog_type = PURPLE_REQUEST_ACTION;
 							chat->config_dialog_handle =
-								purple_request_action(js->gc,
+								purple_request_action_with_hint(js->gc,
 										_("Create New Room"),
 										_("Create New Room"),
 										_("You are creating a new room.  Would"
@@ -508,7 +520,7 @@
 											" accept the default settings?"),
 										/* Default Action */ 1,
 										purple_connection_get_account(js->gc), NULL, chat->conv,
-										chat, 2,
+										PURPLE_REQUEST_UI_HINT_CONV, chat, 2,
 										_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
 										_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
 						}
@@ -731,7 +743,8 @@
 		}
 
 		if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
-			purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL);
+			jabber_google_presence_incoming(js, buddy_name, found_jbr);
+			purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL);
 		} else {
 			purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
 		}
--- a/libpurple/protocols/jabber/si.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/si.c	Sun Oct 28 17:16:42 2007 +0000
@@ -870,9 +870,9 @@
 
 			purple_request_fields_add_group(fields, group);
 
-			purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
+			purple_request_fields_with_hint(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
 					_("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb),
-					jsx->js->gc->account, xfer->who, NULL, xfer);
+					jsx->js->gc->account, xfer->who, NULL, PURPLE_REQUEST_UI_HINT_XFER, xfer);
 
 			g_free(msg);
 		}
--- a/libpurple/protocols/jabber/usermood.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Sun Oct 28 17:16:42 2007 +0000
@@ -194,14 +194,14 @@
 											FALSE);
 	purple_request_field_group_add_field(group, field);
 	
-	purple_request_fields(gc, _("Edit User Mood"),
+	purple_request_fields_with_hint(gc, _("Edit User Mood"),
 						  _("Edit User Mood"),
 						  _("Please select your mood from the list."),
 						  fields,
 						  _("Set"), G_CALLBACK(do_mood_set_from_fields),
 						  _("Cancel"), NULL,
 						  purple_connection_get_account(gc), NULL, NULL,
-						  gc);
+						  PURPLE_REQUEST_UI_HINT_BLIST, gc);
 	
 }
 
--- a/libpurple/protocols/jabber/usernick.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/usernick.c	Sun Oct 28 17:16:42 2007 +0000
@@ -73,10 +73,10 @@
 			oldnickname = xmlnode_get_data(nick);
 	}
 	
-	purple_request_input(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."),
+	purple_request_input_with_hint(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."),
 		_("This information is visible to all contacts on your contact list, so choose something appropriate."),
 		oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
-		purple_connection_get_account(js->gc), NULL, NULL, js);
+		purple_connection_get_account(js->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, js);
 	g_free(oldnickname);
 }
 
--- a/libpurple/protocols/jabber/usertune.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/usertune.c	Sun Oct 28 17:16:42 2007 +0000
@@ -35,7 +35,6 @@
 	xmlnode *tuneinfo, *tune;
 	PurpleJabberTuneInfo tuneinfodata;
 	JabberBuddyResource *resource;
-	const char *status_id;
 	
 	/* ignore the tune of people not on our buddy list */
 	if (!buddy || !item)
@@ -81,9 +80,8 @@
 			}
 		}
 	}
-	status_id = jabber_buddy_state_get_status_id(resource->state);
 
-	purple_prpl_got_user_status(js->gc->account, from, status_id,
+	purple_prpl_got_user_status(js->gc->account, from, "tune",
 			PURPLE_TUNE_ARTIST, tuneinfodata.artist,
 			PURPLE_TUNE_TITLE, tuneinfodata.title,
 			PURPLE_TUNE_ALBUM, tuneinfodata.album,
--- a/libpurple/protocols/jabber/xdata.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/jabber/xdata.c	Sun Oct 28 17:16:42 2007 +0000
@@ -395,11 +395,11 @@
 	if((x = xmlnode_get_child(packet, "instructions")))
 		instructions = xmlnode_get_data(x);
 
-	handle = purple_request_fields(js->gc, title, title, instructions, fields,
+	handle = purple_request_fields_with_hint(js->gc, title, title, instructions, fields,
 			_("OK"), G_CALLBACK(jabber_x_data_ok_cb),
 			_("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb),
 			purple_connection_get_account(js->gc), /* XXX Do we have a who here? */ NULL, NULL,
-			data);
+			PURPLE_REQUEST_UI_HINT_ACCOUNT, data);
 
 	g_free(title);
 	g_free(instructions);
--- a/libpurple/protocols/msn/dialog.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Sun Oct 28 17:16:42 2007 +0000
@@ -135,9 +135,9 @@
 								 passport);
 	}
 
-	purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, 
+	purple_request_action_with_hint(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, 
 						purple_connection_get_account(gc), data->who, NULL,
-						data, 2,
+						PURPLE_REQUEST_UI_HINT_BLIST, data, 2,
 						_("Yes"), G_CALLBACK(msn_add_cb),
 						_("No"), G_CALLBACK(msn_rem_cb));
 
--- a/libpurple/protocols/msn/msn.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sun Oct 28 17:16:42 2007 +0000
@@ -282,14 +282,14 @@
 
 	gc = (PurpleConnection *) action->context;
 
-	purple_request_input(gc, NULL, _("Set your friendly name."),
+	purple_request_input_with_hint(gc, NULL, _("Set your friendly name."),
 					   _("This is the name that other MSN buddies will "
 						 "see you as."),
 					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_act_id),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void
@@ -301,12 +301,12 @@
 	gc = (PurpleConnection *) action->context;
 	session = gc->proto_data;
 
-	purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Set your home phone number."), NULL,
 					   msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_set_home_phone_cb),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void
@@ -318,12 +318,12 @@
 	gc = (PurpleConnection *) action->context;
 	session = gc->proto_data;
 
-	purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Set your work phone number."), NULL,
 					   msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_set_work_phone_cb),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void
@@ -335,12 +335,12 @@
 	gc = (PurpleConnection *) action->context;
 	session = gc->proto_data;
 
-	purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Set your mobile phone number."), NULL,
 					   msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_set_mobile_phone_cb),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void
@@ -350,13 +350,13 @@
 
 	gc = (PurpleConnection *) action->context;
 
-	purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"),
+	purple_request_action_with_hint(gc, NULL, _("Allow MSN Mobile pages?"),
 						_("Do you want to allow or disallow people on "
 						  "your buddy list to send you MSN Mobile pages "
 						  "to your cell phone or other mobile device?"),
 						-1,
 						purple_connection_get_account(gc), NULL, NULL,
-						gc, 3,
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, gc, 3,
 						_("Allow"), G_CALLBACK(enable_msn_pages_cb),
 						_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
 						_("Cancel"), NULL);
@@ -400,12 +400,12 @@
 	data->gc = gc;
 	data->passport = buddy->name;
 
-	purple_request_input(gc, NULL, _("Send a mobile message."), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Send a mobile message."), NULL,
 					   NULL, TRUE, FALSE, NULL,
 					   _("Page"), G_CALLBACK(send_to_mobile_cb),
 					   _("Close"), G_CALLBACK(close_mobile_page_cb),
 					   purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL,
-					   data);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, data);
 }
 
 static gboolean
@@ -539,25 +539,36 @@
 
 /*
  * Set the User status text
- * Add the PSM String Using "Name - PSM String" format
  */
 static char *
 msn_status_text(PurpleBuddy *buddy)
 {
 	PurplePresence *presence;
 	PurpleStatus *status;
-	const char *msg, *cmedia;
+	const char *msg;
 
 	presence = purple_buddy_get_presence(buddy);
 	status = purple_presence_get_active_status(presence);
 
+	/* I think status message should take precedence over media */
 	msg = purple_status_get_attr_string(status, "message");
-	cmedia = purple_status_get_attr_string(status, "currentmedia");
-
-	if (cmedia)
-		return g_markup_escape_text(cmedia, -1);
-	else if (msg)
+	if (msg && *msg)
 		return g_markup_escape_text(msg, -1);
+
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
+		const char *title, *artist;
+		char *media, *esc;
+		status = purple_presence_get_status(presence, "tune");
+		title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
+		artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+
+		media = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
+				artist ? artist : "");
+		esc = g_markup_escape_text(media, -1);
+		g_free(media);
+		return esc;
+	}
+
 	return NULL;
 }
 
@@ -570,14 +581,21 @@
 
 	user = buddy->proto_data;
 
-
 	if (purple_presence_is_online(presence))
 	{
-		const char *psm, *currentmedia, *name;
+		const char *psm, *name;
+		char *currentmedia = NULL;
 		char *tmp;
 
 		psm = purple_status_get_attr_string(status, "message");
-		currentmedia = purple_status_get_attr_string(status, "currentmedia");
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
+			PurpleStatus *tune = purple_presence_get_status(presence, "tune");
+			const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
+			const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+			currentmedia = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
+					artist ? artist : "");
+			/* We could probably just use user->media.title etc. here */
+		}
 
 		if (!purple_presence_is_available(presence)) {
 			name = purple_status_get_name(status);
@@ -609,6 +627,7 @@
 			tmp = g_markup_escape_text(currentmedia, -1);
 			purple_notify_user_info_add_pair(user_info, _("Current media"), tmp);
 			g_free(tmp);
+			g_free(currentmedia);
 		}
 	}
 
@@ -632,40 +651,34 @@
 	status = purple_status_type_new_with_attrs(
 				PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
 				"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-				"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 				NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
@@ -681,6 +694,14 @@
 			"mobile", NULL, FALSE, FALSE, TRUE);
 	types = g_list_append(types, status);
 
+	status = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
+			"tune", NULL, TRUE, TRUE, TRUE,
+			PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, status);
+
 	return types;
 }
 
--- a/libpurple/protocols/msn/notification.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1616,10 +1616,8 @@
 	PurpleAccount *account;
 	MsnUser *user;
 	const char *passport;
-	char *psm_str, *currentmedia_str, *str;
-
-	/*get the payload content*/
-//	purple_debug_info("MSNP14","UBX {%s} payload{%s}\n",cmd->params[0], cmd->payload);
+	char *psm_str, *str;
+	CurrentMedia media = {NULL, NULL, NULL};
 
 	session = cmdproc->session;
 	account = session->account;
@@ -1628,16 +1626,17 @@
 	user = msn_userlist_find_user(session->userlist, passport);
 	
 	psm_str = msn_get_psm(cmd->payload,len);
-	currentmedia_str = msn_parse_currentmedia(
-	                                 str = msn_get_currentmedia(cmd->payload, len));
+	msn_user_set_statusline(user, psm_str);
+	g_free(psm_str);
+
+	str = msn_get_currentmedia(cmd->payload, len);
+	if (msn_parse_currentmedia(str, &media))
+		msn_user_set_currentmedia(user, &media);
+	else
+		msn_user_set_currentmedia(user, NULL);
 	g_free(str);
 
-	msn_user_set_statusline(user, psm_str);
-	msn_user_set_currentmedia(user, currentmedia_str);
 	msn_user_update(user);
-
-	g_free(psm_str);
-	g_free(currentmedia_str);
 }
 
 static void
--- a/libpurple/protocols/msn/state.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/state.c	Sun Oct 28 17:16:42 2007 +0000
@@ -83,60 +83,56 @@
 }
 
 /* parse CurrentMedia string */
-char *
-msn_parse_currentmedia(const char *cmedia)
+gboolean
+msn_parse_currentmedia(const char *cmedia, CurrentMedia *media)
 {
 	char **cmedia_array;
-	GString *buffer = NULL;
 	int strings;
 
 	if ((cmedia == NULL) || (*cmedia == '\0')) {
 		purple_debug_info("msn", "No currentmedia string\n");
-		return NULL;
+		return FALSE;
 	}
 
 	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
 
 	cmedia_array = g_strsplit(cmedia, "\\0", 0);
 
+	/*
+	 * 0: Media Player
+	 * 1: 'Music'
+	 * 2: '1' if enabled, '0' if not
+	 * 3: Format (eg. {0} by {1})
+	 * 4: Title
+	 * 5: Artist
+	 * 6: Album
+	 * 7: ?
+	 */
 	strings = 0;
-	/* Yes, we want to skip the first element here, as it is empty due to
-	 * the cmedia string starting with \0 -- see the examples below. */
 	while (cmedia_array[++strings] != NULL);
 
-	/* The cmedia_array[2] field contains a 1 if enabled. */
-	if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) {
-		char *inptr = cmedia_array[3];
-
-		buffer = g_string_new(NULL);
-
-		while (*inptr != '\0') {
-			if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}'))) {
-				char *tmpptr;
-				int tmp;
-
-				errno = 0;
-				tmp = strtol(inptr + 1, &tmpptr, 10);
+	if (strings < 4)
+		return FALSE;
+	if (strcmp(cmedia_array[2], "1"))
+		return FALSE;
 
-				if (errno == 0 && tmpptr != inptr + 1 &&
-				    tmp + 4 < strings) {
-					/* Replace {?} tag with appropriate text only when successful.
-					 * Skip otherwise. */
-					buffer = g_string_append(buffer, cmedia_array[tmp + 4]);
-				}
-				inptr += 3; /* Skip to the next char after '}' */
-			} else {
-				buffer = g_string_append_c(buffer, *inptr++);
-			}
-		}
-		purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n",
-		                  buffer->str);
+	if (strings == 4) {
+		media->title = g_strdup(cmedia_array[3]);
 	} else {
-		purple_debug_info("msn", "Current media marked disabled, not parsing.\n");
+		media->title = g_strdup(cmedia_array[4]);
 	}
 
-	g_strfreev(cmedia_array);
-	return buffer ? g_string_free(buffer, FALSE) : NULL;
+	if (strings > 5)
+		media->artist = g_strdup(cmedia_array[5]);
+	else
+		media->artist = NULL;
+
+	if (strings > 6)
+		media->album = g_strdup(cmedia_array[6]);
+	else
+		media->album = NULL;
+
+	return TRUE;
 }
 
 /* get the CurrentMedia info from the XML string */
@@ -191,6 +187,27 @@
 	return psm;
 }
 
+static char *
+create_media_string(PurplePresence *presence)
+{
+	const char *artist, *title, *album;
+	char *ret;
+	PurpleStatus *status = purple_presence_get_status(presence, "tune");
+	if (!status || !purple_status_is_active(status))
+		return g_strdup_printf("WMP\\0Music\\00\\0{0} - {1}\\0\\0\\0\\0\\0");
+
+	artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
+	album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+
+	ret = g_strdup_printf("WMP\\0Music\\0%c\\0{0} - {1}\\0%s\\0%s\\0%s\\0\\0",
+			(title && *title) ? '1' : '0',
+			title ? title : "",
+			artist ? artist : "",
+			album ? album : "");
+	return ret;
+}
+
 /* set the MSN's PSM info,Currently Read from the status Line 
  * Thanks for Cris Code
  */
@@ -204,7 +221,7 @@
 	MsnTransaction *trans;
 	char *payload;
 	const char *statusline;
-	gchar *unescapedstatusline;
+	gchar *unescapedstatusline, *media = NULL;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->notification != NULL);
@@ -219,8 +236,10 @@
 	status = purple_presence_get_active_status(presence);
 	statusline = purple_status_get_attr_string(status, "message");
 	unescapedstatusline = purple_unescape_html(statusline);
-	session->psm = msn_build_psm(unescapedstatusline, NULL, NULL);
+	media = create_media_string(presence);
+	session->psm = msn_build_psm(unescapedstatusline, media, NULL);
 	g_free(unescapedstatusline);
+	g_free(media);
 	payload = session->psm;
 
 	purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload);
--- a/libpurple/protocols/msn/state.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/state.h	Sun Oct 28 17:16:42 2007 +0000
@@ -62,7 +62,7 @@
 void msn_set_psm(MsnSession *session);
 
 /* Parse CurrentMedia string */
-char * msn_parse_currentmedia(const char *cmedia);
+gboolean msn_parse_currentmedia(const char *cmedia, CurrentMedia *media);
 
 /* Get the CurrentMedia info from the XML string */
 char * msn_get_currentmedia(char *xml_str,gsize len);
--- a/libpurple/protocols/msn/user.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Oct 28 17:16:42 2007 +0000
@@ -80,6 +80,9 @@
 	g_free(user->phone.home);
 	g_free(user->phone.work);
 	g_free(user->phone.mobile);
+	g_free(user->media.artist);
+	g_free(user->media.title);
+	g_free(user->media.album);
 
 	g_free(user);
 }
@@ -91,23 +94,24 @@
 
 	account = user->userlist->session->account;
 
-	if (user->statusline != NULL && user->currentmedia != NULL) {
+	if (user->status != NULL) {
+		gboolean offline = (strcmp(user->status, "offline") == 0);
 		purple_prpl_got_user_status(account, user->passport, user->status,
-		                          "message", user->statusline,
-		                          "currentmedia", user->currentmedia, NULL);
-	} else if (user->currentmedia != NULL) {
-		purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia",
-		                          user->currentmedia, NULL);
-	} else if (user->statusline != NULL) {
-		//char *status = g_strdup_printf("%s - %s", user->status, user->statusline);
-		purple_prpl_got_user_status(account, user->passport, user->status,
-		                          "message", user->statusline, NULL);
-	} else if (user->status != NULL) {
-		if (!strcmp(user->status, "offline") && user->mobile) {
-			purple_prpl_got_user_status(account, user->passport, "offline", NULL);
+				"message", user->statusline, NULL);
+
+		if (!offline && user->media.title) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+					PURPLE_TUNE_ARTIST, user->media.artist,
+					PURPLE_TUNE_ALBUM, user->media.album,
+					PURPLE_TUNE_TITLE, user->media.title,
+					NULL);
+		} else {
+			purple_prpl_got_user_status_deactive(account, user->passport, "tune");
+		}
+
+		if (!offline && user->mobile) {
 			purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
 		} else {
-			purple_prpl_got_user_status(account, user->passport, user->status, NULL);
 			purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
 		}
 	}
@@ -172,12 +176,17 @@
 }
 
 void
-msn_user_set_currentmedia(MsnUser *user, const char *currentmedia)
+msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *media)
 {
 	g_return_if_fail(user != NULL);
 
-	g_free(user->currentmedia);
-	user->currentmedia = g_strdup(currentmedia);
+	g_free(user->media.title);
+	g_free(user->media.album);
+	g_free(user->media.artist);
+
+	user->media.title  = media ? g_strdup(media->title) : NULL;
+	user->media.artist = media ? g_strdup(media->artist) : NULL;
+	user->media.album  = media ? g_strdup(media->album) : NULL;
 }
 
 void
--- a/libpurple/protocols/msn/user.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Sun Oct 28 17:16:42 2007 +0000
@@ -43,6 +43,16 @@
 } MsnUserType;
 
 /**
+ * Current media.
+ */
+typedef struct _CurrentMedia
+{
+	char *artist;   /**< Artist. */
+	char *album;    /**< Album.  */
+	char *title;    /**< Title.  */
+} CurrentMedia;
+
+/**
  * A user.
  */
 struct _MsnUser
@@ -60,7 +70,7 @@
 
 	const char *status;     /**< The state of the user.         */
 	char *statusline;       /**< The state of the user.         */	
-	char *currentmedia;     /**< The current media of the user. */
+	CurrentMedia media;     /**< Current media of the user.     */
 
 	gboolean idle;          /**< The idle state of the user.    */
 
@@ -134,10 +144,10 @@
  /**
   *  Sets the current media of user.
   * 
-  *  @param user The user.
-  *  @param state The statusline string.
+  *  @param user   The user.
+  *  @param cmedia Current media.
   */
-void msn_user_set_currentmedia(MsnUser *user, const char *currentmedia);
+void msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *cmedia);
 
 /**
  * Sets the new state of user.
--- a/libpurple/protocols/novell/novell.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/novell/novell.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1912,10 +1912,10 @@
 
 	/* Prompt the user */
 	gc = purple_account_get_connection(user->client_data);
-	purple_request_action(gc, title, primary, secondary,
+	purple_request_action_with_hint(gc, title, primary, secondary,
 						PURPLE_DEFAULT_ACTION_NONE,
 						purple_connection_get_account(gc), name, NULL,
-						parms, 2,
+						PURPLE_REQUEST_UI_HINT_CONV, parms, 2,
 						_("Yes"), G_CALLBACK(_join_conference_cb),
 						_("No"), G_CALLBACK(_reject_conference_cb));
 
@@ -2165,7 +2165,7 @@
 	if (server == NULL || *server == '\0') {
 
 		/* TODO: Would be nice to prompt if not set!
-		 *  purple_request_fields(gc, _("Server Address"),...);
+		 *  purple_request_fields_with_hint(gc, _("Server Address"),...);
 		 */
 
 		/* ...but for now just error out with a nice message. */
--- a/libpurple/protocols/oscar/oscar.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1448,13 +1448,13 @@
 	purple_debug_info("oscar", "Got SecurID request\n");
 
 	primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account));
-	purple_request_input(gc, NULL, _("Enter SecurID"), primary,
+	purple_request_input_with_hint(gc, NULL, _("Enter SecurID"), primary,
 					   _("Enter the 6 digit number from the digital display."),
 					   FALSE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb),
 					   _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb),
 					   account, NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 	g_free(primary);
 
 	return 1;
@@ -2241,12 +2241,12 @@
 	data->gc = gc;
 	data->name = g_strdup(name);
 
-	purple_request_input(data->gc, NULL, _("Authorization Request Message:"),
+	purple_request_input_with_hint(data->gc, NULL, _("Authorization Request Message:"),
 					   NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(purple_auth_request),
 					   _("_Cancel"), G_CALLBACK(purple_auth_dontrequest),
 					   purple_connection_get_account(gc), name, NULL,
-					   data);
+					   PURPLE_REQUEST_UI_HINT_BLIST, data);
 }
 
 
@@ -2290,12 +2290,12 @@
 purple_auth_dontgrant_msgprompt(gpointer cbdata)
 {
 	struct name_data *data = cbdata;
-	purple_request_input(data->gc, NULL, _("Authorization Denied Message:"),
+	purple_request_input_with_hint(data->gc, NULL, _("Authorization Denied Message:"),
 					   NULL, _("No reason given."), TRUE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(purple_auth_dontgrant),
 					   _("_Cancel"), G_CALLBACK(oscar_free_name_data),
 					   purple_connection_get_account(data->gc), data->name, NULL,
-					   data);
+					   PURPLE_REQUEST_UI_HINT_BLIST, data);
 }
 
 /* When someone sends you buddies */
@@ -2476,12 +2476,12 @@
 					data->name = g_strdup(text[i*2+1]);
 					data->nick = g_strdup(text[i*2+2]);
 
-					purple_request_action(gc, NULL, message,
+					purple_request_action_with_hint(gc, NULL, message,
 										_("Do you want to add this buddy "
 										  "to your buddy list?"),
 										PURPLE_DEFAULT_ACTION_NONE,
 										purple_connection_get_account(gc), data->name, NULL,
-										data, 2,
+										PURPLE_REQUEST_UI_HINT_BLIST, data, 2,
 										_("_Add"), G_CALLBACK(purple_icq_buddyadd),
 										_("_Decline"), G_CALLBACK(oscar_free_name_data));
 					g_free(message);
@@ -5233,10 +5233,10 @@
 	data->name = g_strdup(sn);
 	data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
 
-	purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
+	purple_request_yes_no_with_hint(gc, NULL, _("Authorization Given"), dialog_msg,
 						PURPLE_DEFAULT_ACTION_NONE,
 						purple_connection_get_account(gc), sn, NULL,
-						data,
+						PURPLE_REQUEST_UI_HINT_BLIST, data,
 						G_CALLBACK(purple_icq_buddyadd),
 						G_CALLBACK(oscar_free_name_data));
 	g_free(dialog_msg);
@@ -5932,12 +5932,12 @@
 	data->nick = g_strdup(purple_buddy_get_alias_only(buddy));
 
 	title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
-	purple_request_input(gc, title, _("Buddy Comment:"), NULL,
+	purple_request_input_with_hint(gc, title, _("Buddy Comment:"), NULL,
 					   comment_utf8, TRUE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
 					   _("_Cancel"), G_CALLBACK(oscar_free_name_data),
 					   purple_connection_get_account(gc), data->name, NULL,
-					   data);
+					   PURPLE_REQUEST_UI_HINT_BUDDY, data);
 	g_free(title);
 
 	g_free(comment);
@@ -5982,13 +5982,13 @@
 	buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
 			buddy->name);
 
-	purple_request_action(gc, NULL, buf,
+	purple_request_action_with_hint(gc, NULL, buf,
 			_("Because this reveals your IP address, it "
 			  "may be considered a security risk.  Do you "
 			  "wish to continue?"),
 			0,
 			purple_connection_get_account(gc), data->who, NULL,
-			data, 2,
+			PURPLE_REQUEST_UI_HINT_BUDDY, data, 2,
 			_("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
 			_("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
 	g_free(buf);
@@ -6140,12 +6140,12 @@
 
 	purple_request_fields_add_group(fields, g);
 
-	purple_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
+	purple_request_fields_with_hint(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
 						NULL, fields,
 						_("OK"), G_CALLBACK(oscar_icq_privacy_opts),
 						_("Cancel"), NULL,
 						purple_connection_get_account(gc), NULL, NULL,
-						gc);
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void oscar_format_screenname(PurpleConnection *gc, const char *nick) {
@@ -6213,12 +6213,12 @@
 static void oscar_show_change_email(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
+	purple_request_input_with_hint(gc, NULL, _("Change Address To:"), NULL, NULL,
 					   FALSE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(oscar_change_email),
 					   _("_Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void oscar_show_awaitingauth(PurplePluginAction *action)
@@ -6280,7 +6280,7 @@
 static void oscar_show_find_email(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, _("Find Buddy by E-Mail"),
+	purple_request_input_with_hint(gc, _("Find Buddy by E-Mail"),
 					   _("Search for a buddy by e-mail address"),
 					   _("Type the e-mail address of the buddy you are "
 						 "searching for."),
@@ -6288,7 +6288,7 @@
 					   _("_Search"), G_CALLBACK(search_by_email_cb),
 					   _("_Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void oscar_show_set_info(PurplePluginAction *action)
--- a/libpurple/protocols/oscar/peer.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/oscar/peer.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1033,7 +1033,7 @@
 		buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
 				sn, purple_account_get_username(account));
 
-		purple_request_action(conn, NULL, buf,
+		purple_request_action_with_hint(conn, NULL, buf,
 						_("This requires a direct connection between "
 						  "the two computers and is necessary for IM "
 						  "Images.  Because your IP address will be "
@@ -1041,7 +1041,7 @@
 						  "risk."),
 						PURPLE_DEFAULT_ACTION_NONE,
 						account, sn, NULL,
-						conn, 2,
+						PURPLE_REQUEST_UI_HINT_BUDDY, conn, 2,
 						_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
 						_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
 	}
--- a/libpurple/protocols/qq/buddy_info.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sun Oct 28 17:16:42 2007 +0000
@@ -519,12 +519,12 @@
 		mid->info->qq_show = g_strdup(info->qq_show);
 		mid->info->unknown6 = g_strdup(info->unknown6);
 
-		purple_request_fields(gc, _("Modify my information"),
+		purple_request_fields_with_hint(gc, _("Modify my information"),
 			_("Modify my information"), NULL, fields,
 			_("Update my information"), G_CALLBACK(modify_info_ok_cb),
 			_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
 			purple_connection_get_account(gc), NULL, NULL,
-			mid);
+			"account", mid);
 	}
 }
 
--- a/libpurple/protocols/qq/buddy_opt.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sun Oct 28 17:16:42 2007 +0000
@@ -209,11 +209,11 @@
 	msg2 = g_strdup(_("Input your reason:"));
 
 	nombre = uid_to_purple_name(uid);
-	purple_request_input(gc, _("Reject request"), msg1, msg2,
+	purple_request_input_with_hint(gc, _("Reject request"), msg1, msg2,
 			   _("Sorry, you are not my type..."), TRUE, FALSE,
 			   NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
 			   purple_connection_get_account(gc), nombre, NULL,
-			   g2);
+			   PURPLE_REQUEST_UI_HINT_BUDDY, g2);
 	g_free(nombre);
 }
 
@@ -402,7 +402,7 @@
 			g->gc = gc;
 			g->uid = for_uid;
 			msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
-			purple_request_input(gc, NULL, msg,
+			purple_request_input_with_hint(gc, NULL, msg,
 					   _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
 					   _("Would you be my friend?"),
 					   TRUE, FALSE, NULL, _("Send"),
@@ -410,7 +410,7 @@
 					   (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
 					   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
 					   purple_connection_get_account(gc), nombre, NULL,
-					   g);
+					   PURPLE_REQUEST_UI_HINT_BUDDY, g);
 			g_free(msg);
 			g_free(nombre);
 		} else {	/* add OK */
--- a/libpurple/protocols/qq/group.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/group.c	Sun Oct 28 17:16:42 2007 +0000
@@ -116,14 +116,14 @@
 	purple_roomlist_set_fields(rl, fields);
 	purple_roomlist_set_in_progress(qd->roomlist, TRUE);
 
-	purple_request_input(gc, _("QQ Qun"),
+	purple_request_input_with_hint(gc, _("QQ Qun"),
 			   _("Please enter external group ID"),
 			   _("You can only search for permanent QQ groups\n"),
 			   NULL, FALSE, FALSE, NULL, 
 			   _("Search"), G_CALLBACK(_qq_group_search_callback), 
 			   _("Cancel"), G_CALLBACK(_qq_group_search_cancel_callback), 
 			   purple_connection_get_account(gc), NULL, NULL,
-			   gc);
+			   PURPLE_REQUEST_UI_HINT_CONV, gc);
 
 	return qd->roomlist;
 }
--- a/libpurple/protocols/qq/group_im.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sun Oct 28 17:16:42 2007 +0000
@@ -133,11 +133,11 @@
 
 	nombre = uid_to_purple_name(user_uid);
 
-	purple_request_action(gc, _("QQ Qun Operation"),
+	purple_request_action_with_hint(gc, _("QQ Qun Operation"),
 			    msg, reason,
 			    2,
 				purple_connection_get_account(gc), nombre, NULL,
-				g, 3,
+				PURPLE_REQUEST_UI_HINT_CONV, g, 3,
 			    _("Approve"),
 			    G_CALLBACK
 			    (qq_group_approve_application_with_struct),
--- a/libpurple/protocols/qq/group_join.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/group_join.c	Sun Oct 28 17:16:42 2007 +0000
@@ -132,14 +132,14 @@
 	g = g_new0(gc_and_uid, 1);
 	g->gc = gc;
 	g->uid = group->internal_group_id;
-	purple_request_input(gc, NULL, msg,
+	purple_request_input_with_hint(gc, NULL, msg,
 			   _("Input request here"),
 			   _("Would you be my friend?"), TRUE, FALSE, NULL,
 			   _("Send"),
 			   G_CALLBACK(_qq_group_join_auth_with_gc_and_id),
 			   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
 			   purple_connection_get_account(gc), group->group_name_utf8, NULL,
-			   g);
+			   PURPLE_REQUEST_UI_HINT_CONV, g);
 	g_free(msg);
 }
 
@@ -356,13 +356,13 @@
 	g->gc = gc;
 	g->uid = internal_group_id;
 
-	purple_request_action(gc, _("QQ Qun Operation"),
+	purple_request_action_with_hint(gc, _("QQ Qun Operation"),
 			    _("Are you sure you want to leave this Qun?"),
 			    _
 			    ("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
-			    g, 2, _("Cancel"),
+			    PURPLE_REQUEST_UI_HINT_CONV, g, 2, _("Cancel"),
 			    G_CALLBACK(qq_do_nothing_with_gc_and_uid),
 			    _("Continue"), G_CALLBACK(_qq_group_exit_with_gc_and_id));
 }
--- a/libpurple/protocols/qq/group_opt.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Sun Oct 28 17:16:42 2007 +0000
@@ -121,9 +121,9 @@
 	g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0);
 
 	qq_send_packet_get_info(g->gc, g->member, TRUE);	/* we want to see window */
-	purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", 2,
+	purple_request_action_with_hint(g->gc, NULL, _("Do you want to approve the request?"), "", 2,
 					purple_connection_get_account(g->gc), NULL, NULL,
-					g, 2,
+					PURPLE_REQUEST_UI_HINT_CONV, g, 2,
 					_("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
 					_("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
 }
@@ -137,13 +137,13 @@
 	msg2 = g_strdup(_("Enter your reason:"));
 
 	nombre = uid_to_purple_name(g->member);
-	purple_request_input(g->gc, /* title */ NULL, msg1, msg2,
+	purple_request_input_with_hint(g->gc, /* title */ NULL, msg1, msg2,
 			   _("Sorry, you are not my type..."), /* multiline */ TRUE, /* masked */ FALSE,
 			   /* hint */ NULL,
 			   _("Send"), G_CALLBACK(_qq_group_reject_application_real),
 			   _("Cancel"), G_CALLBACK(_qq_group_do_nothing_with_struct),
 			   purple_connection_get_account(g->gc), nombre, NULL,
-			   g);
+			   PURPLE_REQUEST_UI_HINT_CONV, g);
 
 	g_free(msg1);
 	g_free(msg2);
@@ -388,13 +388,13 @@
 	g->gc = gc;
 	g->uid = internal_group_id;
 
-	purple_request_action(gc, _("QQ Qun Operation"),
+	purple_request_action_with_hint(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
 			    _
 			    ("Would you like to set up the Qun details now?"),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
-				g, 2,
+				PURPLE_REQUEST_UI_HINT_CONV, g, 2,
 				_("Setup"), G_CALLBACK(qq_group_setup_with_gc_and_uid),
 			    _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid));
 }
--- a/libpurple/protocols/qq/qq.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/qq.c	Sun Oct 28 17:16:42 2007 +0000
@@ -417,9 +417,9 @@
 	g->gc = gc;
 	g->uid = uid;
 
-	purple_request_action(gc, _("Block Buddy"),
+	purple_request_action_with_hint(gc, _("Block Buddy"),
 			    _("Are you sure you want to block this buddy?"), NULL,
-			    1, g, 2,
+			    1, g, "blist", 2,
 			    _("Cancel"),
 			    G_CALLBACK(qq_do_nothing_with_gc_and_uid),
 			    _("Block"), G_CALLBACK(qq_block_buddy_with_gc_and_uid));
@@ -470,11 +470,11 @@
 static void _qq_menu_create_permanent_group(PurplePluginAction * action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, _("Create QQ Qun"),
+	purple_request_input_with_hint(gc, _("Create QQ Qun"),
 			   _("Input Qun name here"),
 			   _("Only QQ members can create permanent Qun"),
 			   "OpenQ", FALSE, FALSE, NULL,
-			   _("Create"), G_CALLBACK(qq_group_create_with_name), _("Cancel"), NULL, gc);
+			   _("Create"), G_CALLBACK(qq_group_create_with_name), _("Cancel"), NULL, "blist", gc);
 }
 */
 
--- a/libpurple/protocols/qq/sys_msg.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Sun Oct 28 17:16:42 2007 +0000
@@ -83,10 +83,10 @@
 	qq_send_packet_get_info(gc, uid, TRUE);	/* we want to see window */
 
 	nombre = uid_to_purple_name(uid);
-	purple_request_action
+	purple_request_action_with_hint
 	    (gc, NULL, _("Do you want to approve the request?"), "", 2,
 		 purple_connection_get_account(gc), nombre, NULL,
-		 g, 2,
+		 PURPLE_REQUEST_UI_HINT_CONV, g, 2,
 	     _("Reject"), G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
 	     _("Approve"), G_CALLBACK(qq_approve_add_request_with_gc_and_uid));
 	g_free(nombre);
@@ -106,11 +106,11 @@
 
 	qq_send_packet_get_info(gc, uid, TRUE);	/* we want to see window */
 	nombre = uid_to_purple_name(uid);
-	purple_request_action
+	purple_request_action_with_hint
 	    (gc, NULL, _("Do you want to add this buddy?"), "", 2,
 		 purple_connection_get_account(gc), nombre, NULL,
-		 g, 2,
-	     _("Cancel"), NULL,
+		 PURPLE_REQUEST_UI_HINT_BUDDY, g, 2,
+		 _("Cancel"), NULL,
 		 _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid));
 	g_free(nombre);
 }
@@ -165,10 +165,10 @@
 		g->uid = uid;	/* only need to get value */
 		message = g_strdup_printf(_("You have been added by %s"), from);
 		_qq_sys_msg_log_write(gc, message, from);
-		purple_request_action(gc, NULL, message,
+		purple_request_action_with_hint(gc, NULL, message,
 				    _("Would you like to add him?"), 2,
 					purple_connection_get_account(gc), name, NULL,
-					g, 3,
+					PURPLE_REQUEST_UI_HINT_BUDDY, g, 3,
 				    _("Cancel"), NULL,
 					_("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
 				    _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
@@ -239,10 +239,10 @@
 	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
 	_qq_sys_msg_log_write(gc, message, from);
 
-	purple_request_action
+	purple_request_action_with_hint
 	    (gc, NULL, message, reason, 2,
 		purple_connection_get_account(gc), name, NULL,
-		 g, 3,
+		 PURPLE_REQUEST_UI_HINT_BUDDY, g, 3,
 	     _("Reject"),
 	     G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
 	     _("Approve"),
@@ -259,10 +259,10 @@
 		g2->gc = gc;
 		g2->uid = strtol(from, NULL, 10);
 		message = g_strdup_printf(_("%s is not in your buddy list"), from);
-		purple_request_action(gc, NULL, message,
+		purple_request_action_with_hint(gc, NULL, message,
 				    _("Would you like to add him?"), 2,
 					purple_connection_get_account(gc), name, NULL,
-					g2, 3,
+					PURPLE_REQUEST_UI_HINT_BLIST, g2, 3,
 					_("Cancel"), NULL,
 					_("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
 				    _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
--- a/libpurple/protocols/sametime/sametime.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Sun Oct 28 17:16:42 2007 +0000
@@ -3382,12 +3382,12 @@
 	   " message to be sent to %s");
   msg1 = g_strdup_printf(msgB, buddy->name);
 
-  purple_request_fields(gc, _("New Conference"),
+  purple_request_fields_with_hint(gc, _("New Conference"),
 		      msgA, msg1, fields,
 		      _("Create"), G_CALLBACK(conf_create_prompt_join),
 		      _("Cancel"), G_CALLBACK(conf_create_prompt_cancel),
 			  acct, purple_buddy_get_name(buddy), NULL,
-		      buddy);
+		      PURPLE_REQUEST_UI_HINT_CONV, buddy);
   g_free(msg1);
 }
 
@@ -3468,12 +3468,12 @@
 	   " create a new conference to invite this user to.");
   msg = g_strdup_printf(msgB, buddy->name);
 
-  purple_request_fields(gc, _("Invite to Conference"),
+  purple_request_fields_with_hint(gc, _("Invite to Conference"),
 		      msgA, msg, fields,
 		      _("Invite"), G_CALLBACK(conf_select_prompt_invite),
 		      _("Cancel"), G_CALLBACK(conf_select_prompt_cancel),
 			  acct, purple_buddy_get_name(buddy), NULL,
-		      buddy);
+		      PURPLE_REQUEST_UI_HINT_CONV, buddy);
   g_free(msg);
 }
 
@@ -3638,13 +3638,13 @@
 	  " continue logging in.");
   msg = g_strdup_printf(msgA, NSTR(purple_account_get_username(acct)));
   
-  purple_request_input(gc, _("Meanwhile Connection Setup"),
+  purple_request_input_with_hint(gc, _("Meanwhile Connection Setup"),
 		     _("No Sametime Community Server Specified"), msg,
 		     MW_PLUGIN_DEFAULT_HOST, FALSE, FALSE, NULL,
 		     _("Connect"), G_CALLBACK(prompt_host_ok_cb),
 		     _("Cancel"), G_CALLBACK(prompt_host_cancel_cb),
 			 acct, NULL, NULL,
-		     gc);
+		     PURPLE_REQUEST_UI_HINT_CONV, gc);
 
   g_free(msg);
 }
@@ -5215,10 +5215,10 @@
   title = g_strdup_printf(_("Import Sametime List for Account %s"),
 			  purple_account_get_username(account));
 
-  purple_request_file(gc, title, NULL, FALSE,
+  purple_request_file_with_hint(gc, title, NULL, FALSE,
 		    G_CALLBACK(st_import_action_cb), NULL,
 		    account, NULL, NULL,
-		    gc);
+		    PURPLE_REQUEST_UI_HINT_CONV, gc);
 
   g_free(title);
 }
@@ -5255,10 +5255,10 @@
   title = g_strdup_printf(_("Export Sametime List for Account %s"),
 			  purple_account_get_username(account));
 
-  purple_request_file(gc, title, NULL, TRUE,
+  purple_request_file_with_hint(gc, title, NULL, TRUE,
 		    G_CALLBACK(st_export_action_cb), NULL,
 			account, NULL, NULL,
-		    gc);
+		    PURPLE_REQUEST_UI_HINT_CONV, gc);
 
   g_free(title);
 }
@@ -5391,12 +5391,12 @@
 	  " the list below to add it to your buddy list.");
   msg = g_strdup_printf(msgB, result->name);
 
-  purple_request_fields(gc, _("Select Notes Address Book"),
+  purple_request_fields_with_hint(gc, _("Select Notes Address Book"),
 		      msgA, msg, fields,
 		      _("Add Group"), G_CALLBACK(remote_group_multi_cb),
 		      _("Cancel"), G_CALLBACK(remote_group_multi_cleanup),
 			  purple_connection_get_account(gc), result->name, NULL,
-		      pd);
+		      PURPLE_REQUEST_UI_HINT_BLIST, pd);
 
   g_free(msg);
 }
@@ -5481,12 +5481,12 @@
   msgB = _("Enter the name of a Notes Address Book group in the field below"
 	  " to add the group and its members to your buddy list.");
 
-  purple_request_input(gc, _("Add Group"), msgA, msgB, NULL,
+  purple_request_input_with_hint(gc, _("Add Group"), msgA, msgB, NULL,
 		     FALSE, FALSE, NULL,
 		     _("Add"), G_CALLBACK(remote_group_action_cb),
 		     _("Cancel"), NULL,
 			 purple_connection_get_account(gc), NULL, NULL,
-		     gc);
+		     PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 
 
@@ -5606,12 +5606,12 @@
   msgB = _("Enter a name or partial ID in the field below to search"
 	   " for matching users in your Sametime community.");
 
-  purple_request_input(gc, _("User Search"), msgA, msgB, NULL,
+  purple_request_input_with_hint(gc, _("User Search"), msgA, msgB, NULL,
 		     FALSE, FALSE, NULL,
 		     _("Search"), G_CALLBACK(search_action_cb),
 		     _("Cancel"), NULL,
 			 purple_connection_get_account(gc), NULL, NULL,
-			 gc);
+			 PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 
 
--- a/libpurple/protocols/silc/buddy.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/buddy.c	Sun Oct 28 17:16:42 2007 +0000
@@ -312,9 +312,9 @@
 		a->hostname = strdup(hostname);
 	a->port = port;
 
-	purple_request_action(client->application, _("Key Agreement Request"), tmp,
+	purple_request_action_with_hint(client->application, _("Key Agreement Request"), tmp,
 			      hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
-			      NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+			      NULL, PURPLE_REQUEST_UI_HINT_BLIST, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
 			      _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
 }
 
@@ -449,11 +449,11 @@
 	p->client = sg->client;
 	p->conn = sg->conn;
 	p->client_id = client_entry->id;
-	purple_request_input(gc, _("IM With Password"), NULL,
+	purple_request_input_with_hint(gc, _("IM With Password"), NULL,
 	                     _("Set IM Password"), NULL, FALSE, TRUE, NULL,
 	                     _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
 	                     _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
-	                     gc->account, NULL, NULL, p);
+	                     gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, p);
 
 	silc_client_list_free(sg->client, sg->conn, clients);
 }
@@ -1056,10 +1056,10 @@
 	}
 
 	/* Open file selector to select the public key. */
-	purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
+	purple_request_file_with_hint(r->client->application, _("Open..."), NULL, FALSE,
 			    G_CALLBACK(silcpurple_add_buddy_ask_import),
 			    G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
-			    purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+			    purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r);
 
 }
 
@@ -1069,10 +1069,10 @@
 	char tmp[512];
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
 		   r->b->name);
-	purple_request_action(r->client->application, _("Add Buddy"), tmp,
+	purple_request_action_with_hint(r->client->application, _("Add Buddy"), tmp,
 			      _("To add the buddy you must import his/her public key. "
 				"Press Import to import a public key."), 0,
-			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r, 2,
 			      _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
 			      _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
 }
@@ -1179,7 +1179,7 @@
 		silc_free(fingerprint);
 	}
 
-	purple_request_fields(r->client->application, _("Add Buddy"),
+	purple_request_fields_with_hint(r->client->application, _("Add Buddy"),
 			      _("Select correct user"),
 			      r->pubkey_search
 			      ? _("More than one user was found with the same public key. Select "
@@ -1189,7 +1189,7 @@
 			      fields,
 			      _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
 			      _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
-			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r);
 }
 
 static void
--- a/libpurple/protocols/silc/chat.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/chat.c	Sun Oct 28 17:16:42 2007 +0000
@@ -301,10 +301,10 @@
 	f = purple_request_fields_get_field(fields, "list");
 	if (!purple_request_field_list_get_selected(f)) {
 		/* Add new public key */
-		purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
+		purple_request_file_with_hint(sg->gc, _("Open Public Key..."), NULL, FALSE,
 				    G_CALLBACK(silcpurple_chat_chpk_add),
 				    G_CALLBACK(silcpurple_chat_chpk_cancel),
-				    purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				    purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 		return;
 	}
 
@@ -441,11 +441,11 @@
 	if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
 		f = purple_request_field_list_new("list", NULL);
 		purple_request_field_group_add_field(g, f);
-		purple_request_fields(sg->gc, _("Channel Authentication"),
+		purple_request_fields_with_hint(sg->gc, _("Channel Authentication"),
 				      _("Channel Authentication"), t, fields,
 				      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
 				      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-				      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				      purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 		if (channel_pubkeys)
 		  silc_dlist_uninit(channel_pubkeys);
 		return;
@@ -476,11 +476,11 @@
 	}
 
 	purple_request_field_list_set_multi_select(f, FALSE);
-	purple_request_fields(sg->gc, _("Channel Authentication"),
+	purple_request_fields_with_hint(sg->gc, _("Channel Authentication"),
 			      _("Channel Authentication"), t, fields,
 			      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
 			      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-			      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+			      purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 }
 
 static void
@@ -611,10 +611,10 @@
 	g_snprintf(tmp, sizeof(tmp),
 		   _("Please enter the %s channel private group name and passphrase."),
 		   p->channel);
-	purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
+	purple_request_fields_with_hint(gc, _("Add Channel Private Group"), NULL, tmp, fields,
 			      _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
 			      _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
-			      purple_connection_get_account(gc), NULL, NULL, p);
+			      purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, p);
 }
 
 
@@ -739,12 +739,12 @@
 	s->channel = ch;
 	s->sg = sg;
 	g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
-	purple_request_input(gc, _("User Limit"), NULL,
+	purple_request_input_with_hint(gc, _("User Limit"), NULL,
 			   _("Set user limit on channel. Set to zero to reset user limit."),
 			   tmp, FALSE, FALSE, NULL,
 			   _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
 			   _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
-			   purple_connection_get_account(gc), NULL, NULL, s);
+			   purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, s);
 }
 
 static void
--- a/libpurple/protocols/silc/ops.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/ops.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1810,11 +1810,11 @@
 		return;
 	internal->u.ask_pass = completion;
 	internal->context = context;
-	purple_request_input(gc, _("Passphrase"), NULL,
+	purple_request_input_with_hint(gc, _("Passphrase"), NULL,
 			     _("Passphrase required"), NULL, FALSE, TRUE, NULL,
 			     _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
 			     _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
-			     purple_connection_get_account(gc), NULL, NULL, internal);
+			     purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, internal);
 }
 
 
--- a/libpurple/protocols/silc/pk.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/pk.c	Sun Oct 28 17:16:42 2007 +0000
@@ -107,9 +107,9 @@
 		   _("Fingerprint and babbleprint for the %s key are:\n\n"
 		     "%s\n%s\n"), entity, fingerprint, babbleprint);
 
-	purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
+	purple_request_action_with_hint(gc, _("Verify Public Key"), tmp, tmp2,
 			      PURPLE_DEFAULT_ACTION_NONE,
-			      purple_connection_get_account(gc), entity, NULL, verify, 3,
+			      purple_connection_get_account(gc), entity, NULL, PURPLE_REQUEST_UI_HINT_BLIST, verify, 3,
 			      _("Yes"), G_CALLBACK(silcpurple_verify_cb),
 			      _("No"), G_CALLBACK(silcpurple_verify_cb),
 			      _("_View..."), G_CALLBACK(silcpurple_verify_details));
--- a/libpurple/protocols/silc/silc.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Sun Oct 28 17:16:42 2007 +0000
@@ -813,7 +813,7 @@
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
-	purple_request_fields(gc, _("User Online Status Attributes"),
+	purple_request_fields_with_hint(gc, _("User Online Status Attributes"),
 			    _("User Online Status Attributes"),
 			    _("You can let other users see your online status information "
 			      "and your personal information. Please fill the information "
@@ -821,7 +821,7 @@
 			    fields,
 			    _("OK"), G_CALLBACK(silcpurple_attrs_cb),
 			    _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
-				gc->account, NULL, NULL, gc);
+				gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 
 static void
@@ -1025,11 +1025,11 @@
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
-	purple_request_fields(gc, _("Create New SILC Key Pair"),
+	purple_request_fields_with_hint(gc, _("Create New SILC Key Pair"),
 			      _("Create New SILC Key Pair"), NULL, fields,
 			      _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
 			      _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
-			      gc->account, NULL, NULL, gc);
+			      gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gc);
 
 	g_strfreev(u);
 	silc_free(hostname);
--- a/libpurple/protocols/silc/util.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/util.c	Sun Oct 28 17:16:42 2007 +0000
@@ -376,10 +376,10 @@
 
 	buf = g_string_free(s, FALSE);
 
-	purple_request_action(sg->gc, _("Public Key Information"),
+	purple_request_action_with_hint(sg->gc, _("Public Key Information"),
 			      _("Public Key Information"),
 			      buf, 0, purple_connection_get_account(sg->gc),
-			      NULL, NULL, context, 1, _("Close"), callback);
+			      NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, context, 1, _("Close"), callback);
 
 	g_free(buf);
 	silc_free(fingerprint);
--- a/libpurple/protocols/silc/wb.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc/wb.c	Sun Oct 28 17:16:42 2007 +0000
@@ -299,8 +299,8 @@
 	req->channel = channel;
 	req->sg = sg;
 
-	purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1,
-				sg->account, sender->nickname, NULL, req, 2,
+	purple_request_action_with_hint(gc, _("Whiteboard"), tmp, NULL, 1,
+				sg->account, sender->nickname, NULL, PURPLE_REQUEST_UI_HINT_BLIST, req, 2,
 			    _("Yes"), G_CALLBACK(silcpurple_wb_request_cb),
 			    _("No"), G_CALLBACK(silcpurple_wb_request_cb));
 }
--- a/libpurple/protocols/silc10/buddy.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/buddy.c	Sun Oct 28 17:16:42 2007 +0000
@@ -309,9 +309,9 @@
 		a->hostname = strdup(hostname);
 	a->port = port;
 
-	purple_request_action(client->application, _("Key Agreement Request"), tmp,
+	purple_request_action_with_hint(client->application, _("Key Agreement Request"), tmp,
 			    hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
-				NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+				NULL, a, PURPLE_REQUEST_UI_HINT_BLIST, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
 			    _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
 }
 
@@ -462,11 +462,11 @@
 	p->client = sg->client;
 	p->conn = sg->conn;
 	p->client_id = *clients[0]->id;
-	purple_request_input(gc, _("IM With Password"), NULL,
+	purple_request_input_with_hint(gc, _("IM With Password"), NULL,
 	                     _("Set IM Password"), NULL, FALSE, TRUE, NULL,
 	                     _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
 	                     _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
-	                     gc->account, NULL, NULL, p);
+	                     gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, p);
 
 	silc_free(clients);
 	silc_free(nickname);
@@ -1064,10 +1064,10 @@
 	}
 
 	/* Open file selector to select the public key. */
-	purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
+	purple_request_file_with_hint(r->client->application, _("Open..."), NULL, FALSE,
 			  G_CALLBACK(silcpurple_add_buddy_ask_import),
 			  G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
-			  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+			  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r);
 
 }
 
@@ -1077,10 +1077,10 @@
 	char tmp[512];
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
 		   r->b->name);
-	purple_request_action(r->client->application, _("Add Buddy"), tmp,
+	purple_request_action_with_hint(r->client->application, _("Add Buddy"), tmp,
 			    _("To add the buddy you must import his/her public key. "
 			      "Press Import to import a public key."), 0,
-				  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+				  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r, 2,
 			    _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
 			    _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
 }
@@ -1177,7 +1177,7 @@
 		silc_free(fingerprint);
 	}
 
-	purple_request_fields(r->client->application, _("Add Buddy"),
+	purple_request_fields_with_hint(r->client->application, _("Add Buddy"),
 				_("Select correct user"),
 				r->pubkey_search
 					? _("More than one user was found with the same public key. Select "
@@ -1187,7 +1187,7 @@
 				fields,
 				_("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
 				_("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
-				purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+				purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, PURPLE_REQUEST_UI_HINT_BLIST, r);
 }
 
 static void
--- a/libpurple/protocols/silc10/chat.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/chat.c	Sun Oct 28 17:16:42 2007 +0000
@@ -288,10 +288,10 @@
 	f = purple_request_fields_get_field(fields, "list");
 	if (!purple_request_field_list_get_selected(f)) {
 		/* Add new public key */
-		purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
+		purple_request_file_with_hint(sg->gc, _("Open Public Key..."), NULL, FALSE,
 				  G_CALLBACK(silcpurple_chat_chpk_add),
 				  G_CALLBACK(silcpurple_chat_chpk_cancel),
-				  purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				  purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 		return;
 	}
 
@@ -419,11 +419,11 @@
 	if (!channel_pubkeys) {
 		f = purple_request_field_list_new("list", NULL);
 		purple_request_field_group_add_field(g, f);
-		purple_request_fields(sg->gc, _("Channel Authentication"),
+		purple_request_fields_with_hint(sg->gc, _("Channel Authentication"),
 				    _("Channel Authentication"), t, fields,
 				    _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
 				    _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-					purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+					purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 		return;
 	}
 	sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
@@ -458,11 +458,11 @@
 	}
 
 	purple_request_field_list_set_multi_select(f, FALSE);
-	purple_request_fields(sg->gc, _("Channel Authentication"),
+	purple_request_fields_with_hint(sg->gc, _("Channel Authentication"),
 			    _("Channel Authentication"), t, fields,
 			    _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
 			    _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-				purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				purple_connection_get_account(sg->gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, sgc);
 
 	silc_argument_payload_free(chpks);
 }
@@ -595,10 +595,10 @@
 	g_snprintf(tmp, sizeof(tmp),
 		   _("Please enter the %s channel private group name and passphrase."),
 		   p->channel);
-	purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
+	purple_request_fields_with_hint(gc, _("Add Channel Private Group"), NULL, tmp, fields,
 			    _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
 			    _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
-				purple_connection_get_account(gc), NULL, NULL, p);
+				purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, p);
 }
 
 
@@ -723,12 +723,12 @@
 	s->channel = ch;
 	s->sg = sg;
 	g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
-	purple_request_input(gc, _("User Limit"), NULL,
+	purple_request_input_with_hint(gc, _("User Limit"), NULL,
 			   _("Set user limit on channel. Set to zero to reset user limit."),
 			   tmp, FALSE, FALSE, NULL,
 			   _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
 			   _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
-			   purple_connection_get_account(gc), NULL, NULL, s);
+			   purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_CONV, s);
 }
 
 static void
--- a/libpurple/protocols/silc10/ops.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/ops.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1291,9 +1291,9 @@
 
 #if 0 /* XXX for now, let's not show attrs here */
 			if (client_entry->attrs)
-				purple_request_action(gc, _("User Information"),
+				purple_request_action_with_hint(gc, _("User Information"),
 						_("User Information"),
-						buf, 1, client_entry, 2,
+						buf, 1, PURPLE_REQUEST_UI_HINT_BLIST, client_entry, 2,
 						_("OK"), G_CALLBACK(silcpurple_whois_more),
 						_("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
 			else
@@ -1898,11 +1898,11 @@
 		return;
 	internal->completion = completion;
 	internal->context = context;
-	purple_request_input(gc, _("Passphrase"), NULL,
+	purple_request_input_with_hint(gc, _("Passphrase"), NULL,
 			   _("Passphrase required"), NULL, FALSE, TRUE, NULL,
 			   _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
 			   _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
-			   purple_connection_get_account(gc), NULL, NULL, internal);
+			   purple_connection_get_account(gc), NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, internal);
 }
 
 
--- a/libpurple/protocols/silc10/pk.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/pk.c	Sun Oct 28 17:16:42 2007 +0000
@@ -113,9 +113,9 @@
 		   _("Fingerprint and babbleprint for the %s key are:\n\n"
 		     "%s\n%s\n"), entity, fingerprint, babbleprint);
 
-	purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
+	purple_request_action_with_hint(gc, _("Verify Public Key"), tmp, tmp2,
 						PURPLE_DEFAULT_ACTION_NONE,
-						purple_connection_get_account(gc), entity, NULL, verify, 3,
+						purple_connection_get_account(gc), entity, NULL, verify, PURPLE_REQUEST_UI_HINT_BLIST, 3,
 			    _("Yes"), G_CALLBACK(silcpurple_verify_cb),
 			    _("No"), G_CALLBACK(silcpurple_verify_cb),
 			    _("_View..."), G_CALLBACK(silcpurple_verify_details));
--- a/libpurple/protocols/silc10/silc.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/silc.c	Sun Oct 28 17:16:42 2007 +0000
@@ -726,7 +726,7 @@
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
-	purple_request_fields(gc, _("User Online Status Attributes"),
+	purple_request_fields_with_hint(gc, _("User Online Status Attributes"),
 			    _("User Online Status Attributes"),
 			    _("You can let other users see your online status information "
 			      "and your personal information. Please fill the information "
@@ -734,7 +734,7 @@
 			    fields,
 			    _("OK"), G_CALLBACK(silcpurple_attrs_cb),
 			    _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
-				gc->account, NULL, NULL, gc);
+				gc->account, NULL, NULL, PURPLE_REQUEST_UI_HINT_BLIST, gc);
 }
 
 static void
@@ -936,11 +936,14 @@
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
-	purple_request_fields(gc, _("Create New SILC Key Pair"),
-			    _("Create New SILC Key Pair"), NULL, fields,
-			    _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
-			    _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
-				gc->account, NULL, NULL, gc);
+	purple_request_fields_with_hint(gc, _("Create New SILC Key Pair"),
+	                                _("Create New SILC Key Pair"), NULL,
+	                                fields, _("Generate Key Pair"),
+	                                G_CALLBACK(silcpurple_create_keypair_cb),
+	                                _("Cancel"),
+                                        G_CALLBACK(silcpurple_create_keypair_cancel),
+	                                gc->account, NULL, NULL,
+                                        PURPLE_REQUEST_UI_HINT_BLIST, gc);
 
 	g_strfreev(u);
 	silc_free(hostname);
--- a/libpurple/protocols/silc10/util.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/util.c	Sun Oct 28 17:16:42 2007 +0000
@@ -371,10 +371,10 @@
 
 	buf = g_string_free(s, FALSE);
 
-	purple_request_action(sg->gc, _("Public Key Information"),
+	purple_request_action_with_hint(sg->gc, _("Public Key Information"),
 			    _("Public Key Information"),
 			    buf, 0, purple_connection_get_account(sg->gc),
-				NULL, NULL, context, 1, _("Close"), callback);
+				NULL, NULL, context, PURPLE_REQUEST_UI_HINT_BLIST, 1, _("Close"), callback);
 
 	g_free(buf);
 	silc_free(fingerprint);
--- a/libpurple/protocols/silc10/wb.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/silc10/wb.c	Sun Oct 28 17:16:42 2007 +0000
@@ -299,8 +299,8 @@
 	req->channel = channel;
 	req->sg = sg;
 
-	purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1,
-				sg->account, sender->nickname, NULL, req, 2,
+	purple_request_action_with_hint(gc, _("Whiteboard"), tmp, NULL, 1,
+				sg->account, sender->nickname, NULL, PURPLE_REQUEST_UI_HINT_CONV, req, 2,
 			    _("Yes"), G_CALLBACK(silcpurple_wb_request_cb),
 			    _("No"), G_CALLBACK(silcpurple_wb_request_cb));
 }
--- a/libpurple/protocols/simple/simple.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Sun Oct 28 17:16:42 2007 +0000
@@ -80,14 +80,15 @@
 static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc);
 static void send_notify(struct simple_account_data *sip, struct simple_watcher *);
 
-static void send_publish(struct simple_account_data *sip);
+static void send_open_publish(struct simple_account_data *sip);
+static void send_closed_publish(struct simple_account_data *sip);
 
 static void do_notifies(struct simple_account_data *sip) {
 	GSList *tmp = sip->watcher;
 	purple_debug_info("simple", "do_notifies()\n");
 	if((sip->republish != -1) || sip->republish < time(NULL)) {
 		if(purple_account_get_bool(sip->account, "dopublish", TRUE)) {
-			send_publish(sip);
+			send_open_publish(sip);
 		}
 	}
 
@@ -1020,7 +1021,7 @@
 		case 200:
 			if(sip->registerstatus < SIMPLE_REGISTER_COMPLETE) { /* registered */
 				if(purple_account_get_bool(sip->account, "dopublish", TRUE)) {
-					send_publish(sip);
+					send_open_publish(sip);
 				}
 			}
 			sip->registerstatus = SIMPLE_REGISTER_COMPLETE;
@@ -1072,7 +1073,7 @@
 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) {
 	gchar *from;
 	gchar *fromhdr;
-	gchar *tmp2;
+	gchar *basicstatus_data;
 	xmlnode *pidf;
 	xmlnode *basicstatus = NULL, *tuple, *status;
 	gboolean isonline = FALSE;
@@ -1085,8 +1086,9 @@
 
 	if(!pidf) {
 		purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n");
+		purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		g_free(from);
-		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		return;
 	}
 
@@ -1101,27 +1103,28 @@
 		return;
 	}
 
-	tmp2 = xmlnode_get_data(basicstatus);
+	basicstatus_data = xmlnode_get_data(basicstatus);
 
-	if(!tmp2) {
+	if(!basicstatus_data) {
 		purple_debug_info("simple", "process_incoming_notify: no basic data found\n");
 		xmlnode_free(pidf);
 		g_free(from);
 		return;
 	}
 
-	if(strstr(tmp2, "open")) {
+	if(strstr(basicstatus_data, "open"))
 		isonline = TRUE;
-	}
+
 
-	g_free(tmp2);
-
-	if(isonline) purple_prpl_got_user_status(sip->account, from, "available", NULL);
-	else purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+	if(isonline) 
+		purple_prpl_got_user_status(sip->account, from, "available", NULL);
+	else 
+		purple_prpl_got_user_status(sip->account, from, "offline", NULL);
 
 	xmlnode_free(pidf);
+	g_free(from);
+	g_free(basicstatus_data);
 
-	g_free(from);
 	send_sip_response(sip->gc, msg, 200, "OK", NULL);
 }
 
@@ -1188,28 +1191,27 @@
 	return doc;
 }
 
-
-
-static gchar* gen_pidf(struct simple_account_data *sip) {
+static gchar* gen_pidf(struct simple_account_data *sip, gboolean open) {
 	gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
 			"<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
 			"xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"\n"
 			"entity=\"sip:%s@%s\">\n"
 			"<tuple id=\"bs35r9f\">\n"
 			"<status>\n"
-			"<basic>open</basic>\n"
+			"<basic>%s</basic>\n"
 			"</status>\n"
 			"<note>%s</note>\n"
 			"</tuple>\n"
 			"</presence>",
 			sip->username,
 			sip->servername,
-			sip->status);
+			(open == TRUE) ? "open" : "closed",
+			(open == TRUE) ? sip->status : "");
 	return doc;
 }
 
 static void send_notify(struct simple_account_data *sip, struct simple_watcher *watcher) {
-	gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
+	gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip, TRUE);
 	gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
 	send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
 	g_free(doc);
@@ -1223,9 +1225,9 @@
 	return TRUE;
 }
 
-static void send_publish(struct simple_account_data *sip) {
+static void send_open_publish(struct simple_account_data *sip) {
 	gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
-	gchar *doc = gen_pidf(sip);
+	gchar *doc = gen_pidf(sip, TRUE);
 	send_sip_request(sip->gc, "PUBLISH", uri, uri,
 		"Expires: 600\r\nEvent: presence\r\n"
 		"Content-Type: application/pidf+xml\r\n",
@@ -1235,6 +1237,18 @@
 	g_free(doc);
 }
 
+static void send_closed_publish(struct simple_account_data *sip) {
+	gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
+	gchar *doc = gen_pidf(sip, FALSE);
+	send_sip_request(sip->gc, "PUBLISH", uri, uri,
+		"Expires: 600\r\nEvent: presence\r\n"
+		"Content-Type: application/pidf+xml\r\n",
+		doc, NULL, process_publish_response);
+	/*sip->republish = time(NULL) + 500;*/
+	g_free(uri);
+	g_free(doc);
+}
+
 static void process_incoming_subscribe(struct simple_account_data *sip, struct sipmsg *msg) {
 	const char *from_hdr = sipmsg_find_header(msg, "From");
 	gchar *from = parse_from(from_hdr);
@@ -1738,7 +1752,14 @@
 	if(sip) {
 		/* unregister */
 		if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
+		{
+			if(purple_account_get_bool(sip->account, 
+				"dopublish", 
+				TRUE))
+				send_closed_publish(sip);
+			
 			do_register_exp(sip, 0);
+		}
 		connection_free_all(sip);
 
 		if (sip->query_data != NULL)
--- a/libpurple/protocols/toc/toc.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/toc/toc.c	Sun Oct 28 17:16:42 2007 +0000
@@ -2216,8 +2216,8 @@
 		g_snprintf(buf, sizeof(buf), _("%s requests you to send them a file"), ft->user);
 	}
 
-	purple_request_accept_cancel(ft->gc, NULL, buf, NULL, 
-							   PURPLE_DEFAULT_ACTION_NONE, ft,
+	purple_request_accept_cancel_with_hint(ft->gc, NULL, buf, NULL, 
+							   PURPLE_DEFAULT_ACTION_NONE, PURPLE_REQUEST_UI_HINT_XFER, ft,
 							   G_CALLBACK(toc_accept_ft),
 							   G_CALLBACK(toc_reject_ft));
 }
--- a/libpurple/protocols/yahoo/yahoo.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1028,12 +1028,12 @@
 static void
 yahoo_buddy_add_deny_reason_cb(gpointer data) {
 	struct yahoo_add_request *add_req = data;
-	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+	purple_request_input_with_hint(add_req->gc, NULL, _("Authorization denied message:"),
 			NULL, _("No reason given."), TRUE, FALSE, NULL,
 			_("OK"), G_CALLBACK(yahoo_buddy_add_deny_cb),
 			_("Cancel"), G_CALLBACK(yahoo_buddy_add_deny_noreason_cb),
 			purple_connection_get_account(add_req->gc), add_req->who, NULL,
-			add_req);
+			PURPLE_REQUEST_UI_HINT_BLIST, add_req);
 }
 
 static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
@@ -2065,9 +2065,9 @@
 		g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
 					"user is on your buddy list.  Clicking \"Yes\" "
 					"will remove and ignore the buddy."), who);
-		purple_request_yes_no(gc, NULL, _("Ignore buddy?"), buf, 0,
+		purple_request_yes_no_with_hint(gc, NULL, _("Ignore buddy?"), buf, 0,
 						gc->account, who, NULL,
-						b,
+						PURPLE_REQUEST_UI_HINT_BLIST, b,
 						G_CALLBACK(ignore_buddy),
 						G_CALLBACK(keep_buddy));
 		break;
@@ -3494,23 +3494,23 @@
 static void yahoo_show_act_id(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, NULL, _("Activate which ID?"), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Activate which ID?"), NULL,
 					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(yahoo_act_id),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_ACCOUNT, gc);
 }
 
 static void yahoo_show_chat_goto(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
+	purple_request_input_with_hint(gc, NULL, _("Join who in chat?"), NULL,
 					   "", FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(yahoo_chat_goto),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
+					   PURPLE_REQUEST_UI_HINT_CONV, gc);
 }
 
 static GList *yahoo_actions(PurplePlugin *plugin, gpointer context) {
--- a/libpurple/prpl.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/prpl.h	Sun Oct 28 17:16:42 2007 +0000
@@ -226,11 +226,17 @@
 	void (*tooltip_text)(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full);
 
 	/**
-	 * This must be implemented, and must add at least the offline
-	 * and online states.
+	 * Returns a list of #PurpleStatusType which exist for this account;
+	 * this must be implemented, and must add at least the offline and
+	 * online states.
 	 */
 	GList *(*status_types)(PurpleAccount *account);
 
+	/**
+	 * Returns a list of #PurpleMenuAction structs, which represent extra
+	 * actions to be shown in (for example) the right-click menu for @a
+	 * node.
+	 */
 	GList *(*blist_node_menu)(PurpleBlistNode *node);
 	GList *(*chat_info)(PurpleConnection *);
 	GHashTable *(*chat_info_defaults)(PurpleConnection *, const char *chat_name);
@@ -258,6 +264,10 @@
 
 	void (*set_info)(PurpleConnection *, const char *info);
 	unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleTypingState state);
+	/**
+	 * Should arrange for purple_notify_userinfo() to be called with
+	 * @a who's user info.
+	 */
 	void (*get_info)(PurpleConnection *, const char *who);
 	void (*set_status)(PurpleAccount *account, PurpleStatus *status);
 
@@ -287,8 +297,14 @@
 	/** new user registration */
 	void (*register_user)(PurpleAccount *);
 
-	/* get "chat buddy" info and away message */
+	/**
+	 * @deprecated Use #PurplePluginProtocolInfo.get_info instead.
+	 */
 	void (*get_cb_info)(PurpleConnection *, int, const char *who);
+	/**
+	 * @deprecated Use #PurplePluginProtocolInfo.get_cb_real_name and
+	 *             #PurplePluginProtocolInfo.status_text instead.
+	 */
 	void (*get_cb_away)(PurpleConnection *, int, const char *who);
 
 	/** save/store buddy's alias on server list/roster */
@@ -348,9 +364,12 @@
 	/* room list serialize */
 	char *(*roomlist_room_serialize)(PurpleRoomlistRoom *room);
 
-	/* Remove the user from the server. (This is only at the bottom to keep binary compatibility.)
-	 * The account can either be connected or disconnected. After the removal is finished,
-	 * the connection will stay open and has to be closed!
+	/** Remove the user from the server.  The account can either be
+	 * connected or disconnected. After the removal is finished, the
+	 * connection will stay open and has to be closed!
+	 */
+	/* This is here rather than next to register_user for API compatibility
+	 * reasons.
 	 */
 	void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data);
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purple.h	Sun Oct 28 17:16:42 2007 +0000
@@ -0,0 +1,92 @@
+/**
+ * @file purple.h  Header files and defines
+ * This file contains all the necessary preprocessor directives to include
+ * libpurple's headers and other preprocessor directives required for plugins
+ * or UIs to build.  Inlcuding this file eliminates the need to directly
+ * include any other libpurple files.  It will still be necessary for plugins
+ * to define @c PURPLE_PLUGINS before including this header.
+ *
+ * @ingroup core libpurple
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#ifndef _PURPLE_H
+#define _PURPLE_H
+
+#ifndef G_GNUC_NULL_TERMINATED
+#	if     __GNUC__ >= 4
+#		define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#	else
+#		define G_GNUC_NULL_TERMINATED
+#	endif
+#endif
+
+#include <account.h>
+#include <accountopt.h>
+#include <blist.h>
+#include <buddyicon.h>
+#include <certificate.h>
+#include <cipher.h>
+#include <circbuffer.h>
+#include <cmds.h>
+#include <connection.h>
+#include <conversation.h>
+#include <core.h>
+#include <debug.h>
+#include <desktopitem.h>
+#include <dnsquery.h>
+#include <dnssrv.h>
+#include <eventloop.h>
+#include <ft.h>
+#include <idle.h>
+#include <imgstore.h>
+#include <log.h>
+#include <mime.h>
+#include <nat-pmp.h>
+#include <network.h>
+#include <ntlm.h>
+#include <notify.h>
+#include <plugin.h>
+#include <pluginpref.h>
+#include <pounce.h>
+#include <prefs.h>
+#include <privacy.h>
+#include <proxy.h>
+#include <prpl.h>
+#include <request.h>
+#include <roomlist.h>
+#include <savedstatuses.h>
+#include <server.h>
+#include <signals.h>
+#include <status.h>
+#include <stringref.h>
+#include <stun.h>
+#include <sound.h>
+#include <sslconn.h>
+#include <upnp.h>
+#include <util.h>
+#include <value.h>
+#include <version.h>
+#include <xmlnode.h>
+#include <whiteboard.h>
+
+#endif
--- a/libpurple/request.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/request.c	Sun Oct 28 17:16:42 2007 +0000
@@ -377,6 +377,13 @@
 		g_hash_table_destroy(field->u.list.item_data);
 		g_hash_table_destroy(field->u.list.selected_table);
 	}
+	else if (field->type == PURPLE_REQUEST_FIELD_BLIST)
+	{
+		if (field->u.blist.default_nodes)
+			g_list_free(field->u.blist.default_nodes);
+		if (field->u.blist.selecteds)
+			g_list_free(field->u.blist.selecteds);
+	}
 
 	g_free(field);
 }
@@ -1133,6 +1140,85 @@
 
 /* -- */
 
+PurpleRequestField *purple_request_field_blist_nodes_new(const char *id,
+		const char *text, PurpleRequestBlistFlags flags, GList *selected)
+{
+	PurpleRequestField *field;
+
+	g_return_val_if_fail(id   != NULL, NULL);
+	g_return_val_if_fail(text != NULL, NULL);
+
+	field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_BLIST);
+
+	field->u.blist.flags = flags;
+	field->u.blist.default_nodes = selected;
+	purple_request_field_blist_set_selection_list(field, selected);
+
+	return field;
+}
+
+PurpleFilterBlistFunc
+purple_request_field_blist_set_filter(PurpleRequestField *field, PurpleFilterBlistFunc filter)
+{
+	PurpleFilterBlistFunc old;
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL);
+	old = field->u.blist.filter;
+	field->u.blist.filter = filter;
+	return old;
+}
+
+PurpleFilterBlistFunc
+purple_request_field_blist_get_filter(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL);
+	return field->u.blist.filter;
+}
+
+GList *purple_request_field_blist_get_selection_list(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL);
+	return field->u.blist.selecteds;
+}
+
+gboolean purple_request_field_blist_add(PurpleRequestField *field, PurpleBlistNode *node)
+{
+	g_return_val_if_fail(field != NULL, FALSE);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, FALSE);
+	if (!g_list_find(field->u.blist.selecteds, node)) {
+		field->u.blist.selecteds = g_list_append(field->u.blist.selecteds, node);
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+gboolean purple_request_field_blist_remove(PurpleRequestField *field, PurpleBlistNode *node)
+{
+	GList *search;
+	g_return_val_if_fail(field != NULL, FALSE);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, FALSE);
+	if ((search = g_list_find(field->u.blist.selecteds, node)) != NULL) {
+		field->u.blist.selecteds = g_list_delete_link(field->u.blist.selecteds, search);
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+void purple_request_field_blist_set_selection_list(PurpleRequestField *field, GList *selecteds)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST);
+	if (field->u.blist.selecteds)
+		g_list_free(field->u.blist.selecteds);
+	field->u.blist.selecteds = selecteds;
+}
+
+/* -- */
+
 void *
 purple_request_input(void *handle, const char *title, const char *primary,
 				   const char *secondary, const char *default_value,
@@ -1142,6 +1228,20 @@
 				   PurpleAccount *account, const char *who, PurpleConversation *conv,
 				   void *user_data)
 {
+	return purple_request_input_with_hint(
+		handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, ok_cb,
+		cancel_text, cancel_cb, account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data);
+}
+
+void *
+purple_request_input_with_hint(void *handle, const char *title, const char *primary,
+				   const char *secondary, const char *default_value,
+				   gboolean multiline, gboolean masked, gchar *hint,
+				   const char *ok_text, GCallback ok_cb,
+				   const char *cancel_text, GCallback cancel_cb,
+				   PurpleAccount *account, const char *who, PurpleConversation *conv,
+				   const char *ui_hint, void *user_data)
+{
 	PurpleRequestUiOps *ops;
 
 	g_return_val_if_fail(ok_text != NULL, NULL);
@@ -1161,7 +1261,7 @@
 											 ok_text, ok_cb,
 											 cancel_text, cancel_cb,
 											 account, who, conv,
-											 user_data);
+											 ui_hint, user_data);
 
 		handles = g_list_append(handles, info);
 
@@ -1186,10 +1286,34 @@
 	g_return_val_if_fail(ok_cb   != NULL,  NULL);
 
 	va_start(args, user_data);
-	ui_handle = purple_request_choice_varg(handle, title, primary, secondary,
+	ui_handle = purple_request_choice_varg_with_hint(handle, title, primary, secondary,
 					     default_value, ok_text, ok_cb,
 					     cancel_text, cancel_cb,
-					     account, who, conv, user_data, args);
+					     account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data, args);
+	va_end(args);
+
+	return ui_handle;
+}
+
+void *
+purple_request_choice_with_hint(void *handle, const char *title, const char *primary,
+					const char *secondary, int default_value,
+					const char *ok_text, GCallback ok_cb,
+					const char *cancel_text, GCallback cancel_cb,
+					PurpleAccount *account, const char *who, PurpleConversation *conv,
+					const char *ui_hint, void *user_data, ...)
+{
+	void *ui_handle;
+	va_list args;
+
+	g_return_val_if_fail(ok_text != NULL,  NULL);
+	g_return_val_if_fail(ok_cb   != NULL,  NULL);
+
+	va_start(args, user_data);
+	ui_handle = purple_request_choice_varg_with_hint(handle, title, primary, secondary,
+					     default_value, ok_text, ok_cb,
+					     cancel_text, cancel_cb,
+					     account, who, conv, ui_hint, user_data, args);
 	va_end(args);
 
 	return ui_handle;
@@ -1204,6 +1328,20 @@
 			 PurpleAccount *account, const char *who, PurpleConversation *conv,
 			 void *user_data, va_list choices)
 {
+	return purple_request_choice_varg_with_hint(
+		handle, title, primary, secondary, default_value, ok_text, ok_cb,
+		cancel_text, cancel_cb, account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data, choices);
+}
+
+void *
+purple_request_choice_varg_with_hint(void *handle, const char *title,
+			 const char *primary, const char *secondary,
+			 int default_value,
+			 const char *ok_text, GCallback ok_cb,
+			 const char *cancel_text, GCallback cancel_cb,
+			 PurpleAccount *account, const char *who, PurpleConversation *conv,
+			 const char *ui_hint, void *user_data, va_list choices)
+{
 	PurpleRequestUiOps *ops;
 
 	g_return_val_if_fail(ok_text != NULL,  NULL);
@@ -1222,7 +1360,7 @@
 						      ok_text, ok_cb,
 						      cancel_text, cancel_cb,
 							  account, who, conv,
-						      user_data, choices);
+						      ui_hint, user_data, choices);
 
 		handles = g_list_append(handles, info);
 
@@ -1244,9 +1382,29 @@
 	g_return_val_if_fail(action_count > 0, NULL);
 
 	va_start(args, action_count);
-	ui_handle = purple_request_action_varg(handle, title, primary, secondary,
+	ui_handle = purple_request_action_varg_with_hint(handle, title, primary, secondary,
 										 default_action, account, who, conv,
-										 user_data, action_count, args);
+										 PURPLE_REQUEST_UI_HINT_BLIST, user_data, action_count, args);
+	va_end(args);
+
+	return ui_handle;
+}
+
+void *
+purple_request_action_with_hint(void *handle, const char *title, const char *primary,
+					const char *secondary, int default_action,
+					PurpleAccount *account, const char *who, PurpleConversation *conv,
+					const char *ui_hint, void *user_data, size_t action_count, ...)
+{
+	void *ui_handle;
+	va_list args;
+
+	g_return_val_if_fail(action_count > 0, NULL);
+
+	va_start(args, action_count);
+	ui_handle = purple_request_action_varg_with_hint(handle, title, primary, secondary,
+										 default_action, account, who, conv,
+										 ui_hint, user_data, action_count, args);
 	va_end(args);
 
 	return ui_handle;
@@ -1257,7 +1415,19 @@
 						 const char *primary, const char *secondary,
 						 int default_action,
 						 PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data, size_t action_count, va_list actions)
+						 void *user_data, size_t action_count, va_list actions)
+{
+	return purple_request_action_varg_with_hint(
+		handle, title, primary, secondary, default_action, account, who, conv,
+		PURPLE_REQUEST_UI_HINT_BLIST, user_data, action_count, actions);
+}
+
+void *
+purple_request_action_varg_with_hint(void *handle, const char *title,
+						 const char *primary, const char *secondary,
+						 int default_action,
+						 PurpleAccount *account, const char *who, PurpleConversation *conv,
+						 const char *ui_hint, void *user_data, size_t action_count, va_list actions)
 {
 	PurpleRequestUiOps *ops;
 
@@ -1273,7 +1443,7 @@
 		info->handle    = handle;
 		info->ui_handle = ops->request_action(title, primary, secondary,
 											  default_action, account, who, conv,
-											  user_data, action_count, actions);
+											  ui_hint, user_data, action_count, actions);
 
 		handles = g_list_append(handles, info);
 
@@ -1291,6 +1461,19 @@
 					PurpleAccount *account, const char *who, PurpleConversation *conv,
 					void *user_data)
 {
+	return purple_request_fields_with_hint(
+		handle, title, primary, secondary, fields, ok_text, ok_cb,
+		cancel_text, cancel_cb, account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data);
+}
+
+void *
+purple_request_fields_with_hint(void *handle, const char *title, const char *primary,
+					const char *secondary, PurpleRequestFields *fields,
+					const char *ok_text, GCallback ok_cb,
+					const char *cancel_text, GCallback cancel_cb,
+					PurpleAccount *account, const char *who, PurpleConversation *conv,
+					const char *ui_hint, void *user_data)
+{
 	PurpleRequestUiOps *ops;
 
 	g_return_val_if_fail(fields  != NULL, NULL);
@@ -1309,7 +1492,7 @@
 											  fields, ok_text, ok_cb,
 											  cancel_text, cancel_cb,
 											  account, who, conv,
-											  user_data);
+											  ui_hint, user_data);
 
 		handles = g_list_append(handles, info);
 
@@ -1326,6 +1509,17 @@
 				  PurpleAccount *account, const char *who, PurpleConversation *conv,
 				  void *user_data)
 {
+	return purple_request_file_with_hint(
+		handle, title, filename, savedialog, ok_cb, cancel_cb, account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data);
+}
+
+void *
+purple_request_file_with_hint(void *handle, const char *title, const char *filename,
+				  gboolean savedialog,
+				  GCallback ok_cb, GCallback cancel_cb,
+				  PurpleAccount *account, const char *who, PurpleConversation *conv,
+				  const char *ui_hint, void *user_data)
+{
 	PurpleRequestUiOps *ops;
 
 	ops = purple_request_get_ui_ops();
@@ -1338,7 +1532,7 @@
 		info->handle    = handle;
 		info->ui_handle = ops->request_file(title, filename, savedialog,
 											ok_cb, cancel_cb,
-											account, who, conv, user_data);
+											account, who, conv, ui_hint, user_data);
 		handles = g_list_append(handles, info);
 		return info->ui_handle;
 	}
@@ -1352,6 +1546,16 @@
 				  PurpleAccount *account, const char *who, PurpleConversation *conv,
 				  void *user_data)
 {
+	return purple_request_folder_with_hint(
+		handle, title, dirname, ok_cb, cancel_cb, account, who, conv, PURPLE_REQUEST_UI_HINT_BLIST, user_data);
+}
+
+void *
+purple_request_folder_with_hint(void *handle, const char *title, const char *dirname,
+				  GCallback ok_cb, GCallback cancel_cb,
+				  PurpleAccount *account, const char *who, PurpleConversation *conv,
+				  const char *ui_hint, void *user_data)
+{
 	PurpleRequestUiOps *ops;
 
 	ops = purple_request_get_ui_ops();
@@ -1365,7 +1569,7 @@
 		info->ui_handle = ops->request_folder(title, dirname,
 											ok_cb, cancel_cb,
 											account, who, conv,
-											user_data);
+											ui_hint, user_data);
 		handles = g_list_append(handles, info);
 		return info->ui_handle;
 	}
--- a/libpurple/request.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/request.h	Sun Oct 28 17:16:42 2007 +0000
@@ -61,7 +61,8 @@
 	PURPLE_REQUEST_FIELD_LIST,
 	PURPLE_REQUEST_FIELD_LABEL,
 	PURPLE_REQUEST_FIELD_IMAGE,
-	PURPLE_REQUEST_FIELD_ACCOUNT
+	PURPLE_REQUEST_FIELD_ACCOUNT,
+	PURPLE_REQUEST_FIELD_BLIST,
 
 } PurpleRequestFieldType;
 
@@ -94,6 +95,17 @@
 } PurpleRequestFieldGroup;
 
 /**
+ * Flags that can be used for Buddylist Fields.
+ */
+typedef enum
+{
+	PURPLE_REQUEST_BLIST_FLAG_BUDDY         = 0x01,  /**< Include buddies in the list. */
+	PURPLE_REQUEST_BLIST_FLAG_CHAT          = 0x02,  /**< Include chats in the list. */
+	PURPLE_REQUEST_BLIST_FLAG_GROUP         = 0x04,  /**< Include groups in the list. */
+	PURPLE_REQUEST_BLIST_FLAG_ALLOW_OFFLINE = 0x08,  /**< Include offline buddies in the list. */
+} PurpleRequestBlistFlags;
+
+/**
  * A request field.
  */
 typedef struct
@@ -172,12 +184,91 @@
 			gsize size;
 		} image;
 
+		struct
+		{
+			GList *default_nodes;
+			PurpleRequestBlistFlags flags;
+			GList *selecteds;
+			PurpleFilterBlistFunc filter;
+		} blist;
+
 	} u;
 
 	void *ui_data;
 
 } PurpleRequestField;
 
+/*
+ * UI hints for the request functions.  These are used by libpurple.
+ */
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_ACCOUNT  "account"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_BLIST    "blist"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_BUDDY    "buddy"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_CONV     "conversation"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_REGISTER "register-account"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_XFER     "xfer"
+
+
+/*
+ * UI hints for the request functions.  These are common hints used by UIs.
+ * UIs can still define any hints they like, but standardization is good.
+ */
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_ACCOUNTMGR "accountmgr"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_CERTMGR    "certmgr"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_PLUGINMGR  "pluginmgr"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_POUNCEMGR  "pouncemgr"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_PREFSMGR   "preferences"
+
+/**
+ * @since 2.3.0
+ */
+#define PURPLE_REQUEST_UI_HINT_STATUSMGR  "statusmgr"
+
+
 /**
  * Request UI operations.
  */
@@ -189,33 +280,33 @@
 						   const char *ok_text, GCallback ok_cb,
 						   const char *cancel_text, GCallback cancel_cb,
 						   PurpleAccount *account, const char *who, PurpleConversation *conv,
-						   void *user_data);
+						   const char *ui_hint, void *user_data);
 	void *(*request_choice)(const char *title, const char *primary,
 							const char *secondary, int default_value,
 							const char *ok_text, GCallback ok_cb,
 							const char *cancel_text, GCallback cancel_cb,
 							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data, va_list choices);
+							const char *ui_hint, void *user_data, va_list choices);
 	void *(*request_action)(const char *title, const char *primary,
 							const char *secondary, int default_action,
 							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data, size_t action_count,
+							const char *ui_hint, void *user_data, size_t action_count,
 							va_list actions);
 	void *(*request_fields)(const char *title, const char *primary,
 							const char *secondary, PurpleRequestFields *fields,
 							const char *ok_text, GCallback ok_cb,
 							const char *cancel_text, GCallback cancel_cb,
 							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data);
+							const char *ui_hint, void *user_data);
 	void *(*request_file)(const char *title, const char *filename,
 						  gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
 						  PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data);
+						  const char *ui_hint, void *user_data);
 	void (*close_request)(PurpleRequestType type, void *ui_handle);
 	void *(*request_folder)(const char *title, const char *dirname,
 							GCallback ok_cb, GCallback cancel_cb,
 							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data);
+							const char *ui_hint, void *user_data);
 
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
@@ -1151,6 +1242,98 @@
 /*@}*/
 
 /**************************************************************************/
+/** @name Buddylist Field API                                             */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates a buddylist field.
+ *
+ * @param id       The field ID.
+ * @param text     The label for the field.
+ * @param flag     Flags dictating what kind of blist nodes should be
+ *                 included in the request field.
+ * @param selected A list of PurpleBlistNode's to select by default, or @c NULL.
+ *
+ * @return  The new field.
+ *
+ * @since 2.3.0
+ */
+PurpleRequestField *purple_request_field_blist_nodes_new(const char *id, const char *text,
+		PurpleRequestBlistFlags flag, GList *selected);
+
+/**
+ * Set a filter for the request field.
+ *
+ * @param field   The request field.
+ * @param filter  The filter function.
+ *
+ * @return  The old filter function, or @c NULL if there was none.
+ *
+ * @since 2.3.0
+ */
+PurpleFilterBlistFunc purple_request_field_blist_set_filter(PurpleRequestField *field, PurpleFilterBlistFunc filter);
+
+/**
+ * Get the filter function for the request field.
+ *
+ * @param field  The request field.
+ *
+ * @return  The filter function, or @c NULL if there isn't any.
+ *
+ * @since 2.3.0
+ */
+PurpleFilterBlistFunc purple_request_field_blist_get_filter(const PurpleRequestField *field);
+
+/**
+ * Add a PurpleBlistNode to the selected list.
+ *
+ * @param field  The request field.
+ * @param node   The buddylist node to add to the list.
+ *
+ * @return  @c TRUE if the node is added to the list, @c FALSE if it was already in the list.
+ *
+ * @since 2.3.0
+ */
+gboolean purple_request_field_blist_add(PurpleRequestField *field, PurpleBlistNode *node);
+
+/**
+ * Remove a PurpleBlistNode from the selected list.
+ *
+ * @param field   The request field.
+ * @param node    The buddylist node to remove from the list.
+ *
+ * @return @c TRUE if the node is removed from the list, @c FALSE if the node is not in the list.
+ *
+ * @since 2.3.0
+ */
+gboolean purple_request_field_blist_remove(PurpleRequestField *field, PurpleBlistNode *node);
+
+/**
+ * Set the list of selected nodes in the request field.
+ *
+ * @param field      The request field.
+ * @param selecteds  The list of selected PurpleBlistNode's. Note that the request field
+ *                   becomes the owner of the list, and so the caller should not modify it.
+ *
+ * @since 2.3.0
+ */
+void purple_request_field_blist_set_selection_list(PurpleRequestField *field, GList *selecteds);
+
+/**
+ * Get a list of the selected buddylist nodes.
+ *
+ * @param field  The request field.
+ *
+ * @return A GList of PurpleBlistNode's.
+ *
+ * @since 2.3.0
+ */
+GList *purple_request_field_blist_get_selection_list(const PurpleRequestField *field);
+
+/*@}*/
+
+/**************************************************************************/
 /** @name Request API                                                     */
 /**************************************************************************/
 /*@{*/
@@ -1187,9 +1370,60 @@
  * @param account		The PurpleAccount associated with this request, or NULL if none is
  * @param who			The username of the buddy assocaited with this request, or NULL if none is
  * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint       UI hint
  * @param user_data     The data to pass to the callback.
  *
  * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_input_with_hint(void *handle, const char *title,
+						 const char *primary, const char *secondary,
+						 const char *default_value,
+						 gboolean multiline, gboolean masked, gchar *hint,
+						 const char *ok_text, GCallback ok_cb,
+						 const char *cancel_text, GCallback cancel_cb,
+						 PurpleAccount *account, const char *who, PurpleConversation *conv,
+						 const char *ui_hint, void *user_data);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Prompts the user for text input.
+ *
+ * @param handle        The plugin or connection handle.  For some
+ *                      things this is EXTREMELY important.  The
+ *                      handle is used to programmatically close
+ *                      the request dialog when it is no longer
+ *                      needed.  For PRPLs this is often a pointer
+ *                      to the PurpleConnection instance.  For plugins
+ *                      this should be a similar, unique memory
+ *                      location.  This value is important because
+ *                      it allows a request to be closed, say, when
+ *                      you sign offline.  If the request is NOT
+ *                      closed it is VERY likely to cause a crash
+ *                      whenever the callback handler functions are
+ *                      triggered.
+ * @param title         The title of the message.
+ * @param primary       The main point of the message.
+ * @param secondary     The secondary information.
+ * @param default_value The default value.
+ * @param multiline     TRUE if the inputted text can span multiple lines.
+ * @param masked        TRUE if the inputted text should be masked in some way.
+ * @param hint          Optionally suggest how the input box should appear.
+ *                      Use "html," for example, to allow the user to enter
+ *                      HTML.
+ * @param ok_text       The text for the @c OK button.
+ * @param ok_cb         The callback for the @c OK button.
+ * @param cancel_text   The text for the @c Cancel button.
+ * @param cancel_cb     The callback for the @c Cancel button.
+ * @param account		The PurpleAccount associated with this request, or NULL if none is
+ * @param who			The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param user_data     The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_input_with_hint() instead.
  */
 void *purple_request_input(void *handle, const char *title,
 						 const char *primary, const char *secondary,
@@ -1199,6 +1433,7 @@
 						 const char *cancel_text, GCallback cancel_cb,
 						 PurpleAccount *account, const char *who, PurpleConversation *conv,
 						 void *user_data);
+#endif
 
 /**
  * Prompts the user for multiple-choice input.
@@ -1217,11 +1452,48 @@
  * @param account		The PurpleAccount associated with this request, or NULL if none is
  * @param who			The username of the buddy assocaited with this request, or NULL if none is
  * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint       UI hint
+ * @param user_data     The data to pass to the callback.
+ * @param ...           The choices.  This argument list should be
+ *                      terminated with a NULL parameter.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_choice_with_hint(void *handle, const char *title,
+						  const char *primary, const char *secondary,
+						  int default_value,
+						  const char *ok_text, GCallback ok_cb,
+						  const char *cancel_text, GCallback cancel_cb,
+						  PurpleAccount *account, const char *who, PurpleConversation *conv,
+						  const char *ui_hint, void *user_data, ...) G_GNUC_NULL_TERMINATED;
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Prompts the user for multiple-choice input.
+ *
+ * @param handle        The plugin or connection handle.  For some
+ *                      things this is EXTREMELY important.  See
+ *                      the comments on purple_request_input.
+ * @param title         The title of the message.
+ * @param primary       The main point of the message.
+ * @param secondary     The secondary information.
+ * @param default_value The default value.
+ * @param ok_text       The text for the @c OK button.
+ * @param ok_cb         The callback for the @c OK button.
+ * @param cancel_text   The text for the @c Cancel button.
+ * @param cancel_cb     The callback for the @c Cancel button.
+ * @param account		The PurpleAccount associated with this request, or NULL if none is
+ * @param who			The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv			The PurpleConversation associated with this request, or NULL if none is
  * @param user_data     The data to pass to the callback.
  * @param ...           The choices.  This argument list should be
  *                      terminated with a NULL parameter.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_choice_with_hint() instead.
  */
 void *purple_request_choice(void *handle, const char *title,
 						  const char *primary, const char *secondary,
@@ -1230,6 +1502,7 @@
 						  const char *cancel_text, GCallback cancel_cb,
 						  PurpleAccount *account, const char *who, PurpleConversation *conv,
 						  void *user_data, ...) G_GNUC_NULL_TERMINATED;
+#endif
 
 /**
  * Prompts the user for multiple-choice input.
@@ -1248,11 +1521,48 @@
  * @param account		The PurpleAccount associated with this request, or NULL if none is
  * @param who			The username of the buddy assocaited with this request, or NULL if none is
  * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint       UI hint
+ * @param user_data     The data to pass to the callback.
+ * @param choices       The choices.  This argument list should be
+ *                      terminated with a @c NULL parameter.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_choice_varg_with_hint(void *handle, const char *title,
+							   const char *primary, const char *secondary,
+							   int default_value,
+							   const char *ok_text, GCallback ok_cb,
+							   const char *cancel_text, GCallback cancel_cb,
+							   PurpleAccount *account, const char *who, PurpleConversation *conv,
+							   const char *ui_hint, void *user_data, va_list choices);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Prompts the user for multiple-choice input.
+ *
+ * @param handle        The plugin or connection handle.  For some
+ *                      things this is EXTREMELY important.  See
+ *                      the comments on purple_request_input.
+ * @param title         The title of the message.
+ * @param primary       The main point of the message.
+ * @param secondary     The secondary information.
+ * @param default_value The default value.
+ * @param ok_text       The text for the @c OK button.
+ * @param ok_cb         The callback for the @c OK button.
+ * @param cancel_text   The text for the @c Cancel button.
+ * @param cancel_cb     The callback for the @c Cancel button.
+ * @param account		The PurpleAccount associated with this request, or NULL if none is
+ * @param who			The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv			The PurpleConversation associated with this request, or NULL if none is
  * @param user_data     The data to pass to the callback.
  * @param choices       The choices.  This argument list should be
  *                      terminated with a @c NULL parameter.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_choice_varg_with_hint() instead.
  */
 void *purple_request_choice_varg(void *handle, const char *title,
 							   const char *primary, const char *secondary,
@@ -1261,6 +1571,7 @@
 							   const char *cancel_text, GCallback cancel_cb,
 							   PurpleAccount *account, const char *who, PurpleConversation *conv,
 							   void *user_data, va_list choices);
+#endif
 
 /**
  * Prompts the user for an action.
@@ -1277,6 +1588,43 @@
  * @param account		 The PurpleAccount associated with this request, or NULL if none is
  * @param who			 The username of the buddy assocaited with this request, or NULL if none is
  * @param conv			 The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint        UI hint
+ * @param user_data      The data to pass to the callback.
+ * @param action_count   The number of actions.
+ * @param ...            A list of actions.  These are pairs of
+ *                       arguments.  The first of each pair is the
+ *                       string that appears on the button.  It should
+ *                       have an underscore before the letter you want
+ *                       to use as the accelerator key for the button.
+ *                       The second of each pair is the callback
+ *                       function to use when the button is clicked.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_action_with_hint(void *handle, const char *title,
+						  const char *primary, const char *secondary,
+						  int default_action,
+						  PurpleAccount *account, const char *who, PurpleConversation *conv,
+						  const char *ui_hint, void *user_data, size_t action_count, ...);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Prompts the user for an action.
+ *
+ * This is often represented as a dialog with a button for each action.
+ *
+ * @param handle         The plugin or connection handle.  For some
+ *                       things this is EXTREMELY important.  See
+ *                       the comments on purple_request_input.
+ * @param title          The title of the message.
+ * @param primary        The main point of the message.
+ * @param secondary      The secondary information.
+ * @param default_action The default value.
+ * @param account		 The PurpleAccount associated with this request, or NULL if none is
+ * @param who			 The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv			 The PurpleConversation associated with this request, or NULL if none is
  * @param user_data      The data to pass to the callback.
  * @param action_count   The number of actions.
  * @param ...            A list of actions.  These are pairs of
@@ -1288,12 +1636,15 @@
  *                       function to use when the button is clicked.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_action_with_hint() instead.
  */
 void *purple_request_action(void *handle, const char *title,
 						  const char *primary, const char *secondary,
 						  int default_action,
 						  PurpleAccount *account, const char *who, PurpleConversation *conv,
 						  void *user_data, size_t action_count, ...);
+#endif
 
 /**
  * Prompts the user for an action.
@@ -1310,11 +1661,45 @@
  * @param account		 The PurpleAccount associated with this request, or NULL if none is
  * @param who			 The username of the buddy assocaited with this request, or NULL if none is
  * @param conv			 The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint        UI hint
+ * @param user_data      The data to pass to the callback.
+ * @param action_count   The number of actions.
+ * @param actions        A list of actions and callbacks.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_action_varg_with_hint(void *handle, const char *title,
+							   const char *primary, const char *secondary,
+							   int default_action,
+							   PurpleAccount *account, const char *who, PurpleConversation *conv,
+							   const char *ui_hint, void *user_data, size_t action_count,
+							   va_list actions);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Prompts the user for an action.
+ *
+ * This is often represented as a dialog with a button for each action.
+ *
+ * @param handle         The plugin or connection handle.  For some
+ *                       things this is EXTREMELY important.  See
+ *                       the comments on purple_request_input.
+ * @param title          The title of the message.
+ * @param primary        The main point of the message.
+ * @param secondary      The secondary information.
+ * @param default_action The default value.
+ * @param account		 The PurpleAccount associated with this request, or NULL if none is
+ * @param who			 The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv			 The PurpleConversation associated with this request, or NULL if none is
  * @param user_data      The data to pass to the callback.
  * @param action_count   The number of actions.
  * @param actions        A list of actions and callbacks.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_action_varg_with_hint() instead.
  */
 void *purple_request_action_varg(void *handle, const char *title,
 							   const char *primary, const char *secondary,
@@ -1322,6 +1707,7 @@
 							   PurpleAccount *account, const char *who, PurpleConversation *conv,
 							   void *user_data, size_t action_count,
 							   va_list actions);
+#endif
 
 /**
  * Displays groups of fields for the user to fill in.
@@ -1340,9 +1726,44 @@
  * @param account	  The PurpleAccount associated with this request, or NULL if none is
  * @param who		  The username of the buddy associated with this request, or NULL if none is
  * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint     UI hint
+ * @param user_data   The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_fields_with_hint(void *handle, const char *title,
+						  const char *primary, const char *secondary,
+						  PurpleRequestFields *fields,
+						  const char *ok_text, GCallback ok_cb,
+						  const char *cancel_text, GCallback cancel_cb,
+						  PurpleAccount *account, const char *who, PurpleConversation *conv,
+						  const char *ui_hint, void *user_data);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Displays groups of fields for the user to fill in.
+ *
+ * @param handle      The plugin or connection handle.  For some
+ *                    things this is EXTREMELY important.  See
+ *                    the comments on purple_request_input.
+ * @param title       The title of the message.
+ * @param primary     The main point of the message.
+ * @param secondary   The secondary information.
+ * @param fields      The list of fields.
+ * @param ok_text     The text for the @c OK button.
+ * @param ok_cb       The callback for the @c OK button.
+ * @param cancel_text The text for the @c Cancel button.
+ * @param cancel_cb   The callback for the @c Cancel button.
+ * @param account	  The PurpleAccount associated with this request, or NULL if none is
+ * @param who		  The username of the buddy associated with this request, or NULL if none is
+ * @param conv		  The PurpleConversation associated with this request, or NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_fields_with_hint() instead.
  */
 void *purple_request_fields(void *handle, const char *title,
 						  const char *primary, const char *secondary,
@@ -1351,6 +1772,7 @@
 						  const char *cancel_text, GCallback cancel_cb,
 						  PurpleAccount *account, const char *who, PurpleConversation *conv,
 						  void *user_data);
+#endif
 
 /**
  * Closes a request.
@@ -1369,6 +1791,21 @@
 
 /**
  * A wrapper for purple_request_action() that uses @c Yes and @c No buttons.
+ *
+ * @since 2.3.0
+ */
+#define purple_request_yes_no_with_hint(handle, title, primary, secondary, \
+							default_action, account, who, conv, \
+							ui_hint, user_data, yes_cb, no_cb) \
+	purple_request_action_with_hint((handle), (title), (primary), (secondary), \
+						(default_action), account, who, conv, (ui_hint), (user_data), 2, \
+						_("_Yes"), (yes_cb), _("_No"), (no_cb))
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * A wrapper for purple_request_action() that uses @c Yes and @c No buttons.
+ *
+ * @deprecated Please use purple_request_yes_no_with_hint instead.
  */
 #define purple_request_yes_no(handle, title, primary, secondary, \
 							default_action, account, who, conv, \
@@ -1376,9 +1813,25 @@
 	purple_request_action((handle), (title), (primary), (secondary), \
 						(default_action), account, who, conv, (user_data), 2, \
 						_("_Yes"), (yes_cb), _("_No"), (no_cb))
+#endif
 
 /**
  * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
+ *
+ * @since 2.3.0
+ */
+#define purple_request_ok_cancel_with_hint(handle, title, primary, secondary, \
+							default_action, account, who, conv, \
+						    ui_hint, user_data, ok_cb, cancel_cb) \
+	purple_request_action_with_hint((handle), (title), (primary), (secondary), \
+						(default_action), account, who, conv, (ui_hint), (user_data), 2, \
+						_("_OK"), (ok_cb), _("_Cancel"), (cancel_cb))
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
+ *
+ * @deprecated Please use purple_request_ok_cancel_with_hint instead.
  */
 #define purple_request_ok_cancel(handle, title, primary, secondary, \
 							default_action, account, who, conv, \
@@ -1386,9 +1839,25 @@
 	purple_request_action((handle), (title), (primary), (secondary), \
 						(default_action), account, who, conv, (user_data), 2, \
 						_("_OK"), (ok_cb), _("_Cancel"), (cancel_cb))
+#endif
 
 /**
  * A wrapper for purple_request_action() that uses Accept and Cancel buttons.
+ *
+ * @since 2.3.0
+ */
+#define purple_request_accept_cancel_with_hint(handle, title, primary, secondary, \
+								   default_action, account, who, conv, \
+								   ui_hint, user_data, accept_cb, cancel_cb) \
+	purple_request_action_with_hint((handle), (title), (primary), (secondary), \
+						(default_action), account, who, conv, (ui_hint), (user_data), 2, \
+						_("_Accept"), (accept_cb), _("_Cancel"), (cancel_cb))
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * A wrapper for purple_request_action() that uses Accept and Cancel buttons.
+ *
+ * @deprecated Please use purple_request_accept_cancel_with_hint instead.
  */
 #define purple_request_accept_cancel(handle, title, primary, secondary, \
 								   default_action, account, who, conv, \
@@ -1396,6 +1865,7 @@
 	purple_request_action((handle), (title), (primary), (secondary), \
 						(default_action), account, who, conv, (user_data), 2, \
 						_("_Accept"), (accept_cb), _("_Cancel"), (cancel_cb))
+#endif
 
 /**
  * Displays a file selector request dialog.  Returns the selected filename to
@@ -1413,15 +1883,48 @@
  * @param account	  The PurpleAccount associated with this request, or NULL if none is
  * @param who		  The username of the buddy assocaited with this request, or NULL if none is
  * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint     UI hint
+ * @param user_data   The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_file_with_hint(void *handle, const char *title, const char *filename,
+						gboolean savedialog,
+						GCallback ok_cb, GCallback cancel_cb,
+						PurpleAccount *account, const char *who, PurpleConversation *conv,
+						const char *ui_hint, void *user_data);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Displays a file selector request dialog.  Returns the selected filename to
+ * the callback.  Can be used for either opening a file or saving a file.
+ *
+ * @param handle      The plugin or connection handle.  For some
+ *                    things this is EXTREMELY important.  See
+ *                    the comments on purple_request_input.
+ * @param title       The title for the dialog (may be @c NULL)
+ * @param filename    The default filename (may be @c NULL)
+ * @param savedialog  True if this dialog is being used to save a file.
+ *                    False if it is being used to open a file.
+ * @param ok_cb       The callback for the @c OK button.
+ * @param cancel_cb   The callback for the @c Cancel button.
+ * @param account	  The PurpleAccount associated with this request, or NULL if none is
+ * @param who		  The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv		  The PurpleConversation associated with this request, or NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_file_with_hint() instead.
  */
 void *purple_request_file(void *handle, const char *title, const char *filename,
 						gboolean savedialog,
 						GCallback ok_cb, GCallback cancel_cb,
 						PurpleAccount *account, const char *who, PurpleConversation *conv,
 						void *user_data);
+#endif
 
 /**
  * Displays a folder select dialog. Returns the selected filename to
@@ -1437,14 +1940,44 @@
  * @param account	  The PurpleAccount associated with this request, or NULL if none is
  * @param who		  The username of the buddy assocaited with this request, or NULL if none is
  * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param ui_hint     UI hint
+ * @param user_data   The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ *
+ * @since 2.3.0
+ */
+void *purple_request_folder_with_hint(void *handle, const char *title, const char *dirname,
+						GCallback ok_cb, GCallback cancel_cb,
+						PurpleAccount *account, const char *who, PurpleConversation *conv,
+						const char *ui_hint, void *user_data);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Displays a folder select dialog. Returns the selected filename to
+ * the callback.
+ *
+ * @param handle      The plugin or connection handle.  For some
+ *                    things this is EXTREMELY important.  See
+ *                    the comments on purple_request_input.
+ * @param title       The title for the dialog (may be @c NULL)
+ * @param dirname     The default directory name (may be @c NULL)
+ * @param ok_cb       The callback for the @c OK button.
+ * @param cancel_cb   The callback for the @c Cancel button.
+ * @param account	  The PurpleAccount associated with this request, or NULL if none is
+ * @param who		  The username of the buddy assocaited with this request, or NULL if none is
+ * @param conv		  The PurpleConversation associated with this request, or NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
+ *
+ * @deprecated Please use purple_request_folder_with_hint() instead.
  */
 void *purple_request_folder(void *handle, const char *title, const char *dirname,
 						GCallback ok_cb, GCallback cancel_cb,
 						PurpleAccount *account, const char *who, PurpleConversation *conv,
 						void *user_data);
+#endif
 
 /*@}*/
 
--- a/libpurple/server.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/server.c	Sun Oct 28 17:16:42 2007 +0000
@@ -799,9 +799,9 @@
 				   who, purple_account_get_username(account), name);
 
 
-		purple_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2,
+		purple_request_accept_cancel_with_hint(gc, NULL, _("Accept chat invitation?"), buf2,
 							   PURPLE_DEFAULT_ACTION_NONE, account, who, NULL,
-							   cid, G_CALLBACK(chat_invite_accept),
+							   "chat", cid, G_CALLBACK(chat_invite_accept),
 							   G_CALLBACK(chat_invite_reject));
 	}
 	else if (plugin_return > 0)
--- a/libpurple/signals.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/signals.c	Sun Oct 28 17:16:42 2007 +0000
@@ -794,6 +794,19 @@
 		*return_val = GINT_TO_POINTER(ret_val);
 }
 
+void
+purple_marshal_INT__POINTER_POINTER(PurpleCallback cb, va_list args, void *data,
+                                      void **return_val)
+{
+	gint ret_val;
+	void *arg1 = va_arg(args, void *);
+	void *arg2 = va_arg(args, void *);
+
+	ret_val = ((gint (*)(void *, void *, void *))cb)(arg1, arg2, data);
+
+	if (return_val != NULL)
+		*return_val = GINT_TO_POINTER(ret_val);
+}
 
 void
 purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER(
--- a/libpurple/signals.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/signals.h	Sun Oct 28 17:16:42 2007 +0000
@@ -307,6 +307,8 @@
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_INT__INT_INT(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
+void purple_marshal_INT__POINTER_POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 
--- a/libpurple/sslconn.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/sslconn.h	Sun Oct 28 17:16:42 2007 +0000
@@ -185,6 +185,7 @@
 									PurpleSslErrorFunction error_func,
 									void *data);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Makes a SSL connection using an already open file descriptor.
  *
@@ -202,6 +203,7 @@
 									   PurpleSslInputFunction func,
 									   PurpleSslErrorFunction error_func,
  									   void *data);
+#endif
 
 /**
  * Makes a SSL connection using an already open file descriptor.
--- a/libpurple/status.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/status.c	Sun Oct 28 17:16:42 2007 +0000
@@ -157,7 +157,8 @@
 	{ PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")       },
 	{ PURPLE_STATUS_AWAY,            "away",            N_("Away")            },
 	{ PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")   },
-	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")          }
+	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")          },
+	{ PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music") }
 };
 
 const char *
@@ -903,6 +904,8 @@
 	}
 	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING);
 
+	/* XXX: Check if the value has actually changed. If it has, and the status
+	 * is active, should this trigger 'status_has_changed'? */
 	purple_value_set_string(attr_value, value);
 }
 
--- a/libpurple/status.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/status.h	Sun Oct 28 17:16:42 2007 +0000
@@ -94,6 +94,10 @@
 /**
  * A primitive defining the basic structure of a status type.
  */
+/*
+ * If you add a value to this enum, make sure you update
+ * the status_primitive_map array in status.c.
+ */
 typedef enum
 {
 	PURPLE_STATUS_UNSET = 0,
@@ -104,6 +108,7 @@
 	PURPLE_STATUS_AWAY,
 	PURPLE_STATUS_EXTENDED_AWAY,
 	PURPLE_STATUS_MOBILE,
+	PURPLE_STATUS_TUNE,
 	PURPLE_STATUS_NUM_PRIMITIVES
 
 } PurpleStatusPrimitive;
--- a/libpurple/util.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/util.c	Sun Oct 28 17:16:42 2007 +0000
@@ -70,6 +70,7 @@
 static char *custom_user_dir = NULL;
 static char *user_dir = NULL;
 
+
 PurpleMenuAction *
 purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
                      GList *children)
@@ -91,6 +92,25 @@
 	g_free(act);
 }
 
+void
+purple_util_init(void)
+{
+	/* This does nothing right now.  It exists for symmetry with 
+	 * purple_util_uninit() and forwards compatibility. */
+}
+
+void
+purple_util_uninit(void)
+{
+	/* Free these so we don't have leaks at shutdown. */
+
+	g_free(custom_user_dir);
+	custom_user_dir = NULL;
+
+	g_free(user_dir);
+	user_dir = NULL;
+}
+
 /**************************************************************************
  * Base16 Functions
  **************************************************************************/
@@ -2565,6 +2585,8 @@
 	purple_debug_info("util", "Writing file %s\n",
 					filename_full);
 
+	g_return_val_if_fail((size >= -1), FALSE);
+
 	filename_temp = g_strdup_printf("%s.save", filename_full);
 
 	/* Remove an old temporary file, if one exists */
@@ -2590,7 +2612,7 @@
 	}
 
 	/* Write to file */
-	real_size = (size == -1) ? strlen(data) : size;
+	real_size = (size == -1) ? strlen(data) : (size_t) size;
 	byteswritten = fwrite(data, 1, real_size, file);
 
 	/* Close file */
--- a/libpurple/util.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/libpurple/util.h	Sun Oct 28 17:16:42 2007 +0000
@@ -86,6 +86,27 @@
 void purple_menu_action_free(PurpleMenuAction *act);
 
 /**************************************************************************/
+/** @name Utility Subsystem                                               */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Initializes the utility subsystem.
+ *
+ * @since 2.3.0
+ */
+void purple_util_init(void);
+
+/**
+ * Uninitializes the util subsystem.
+ *
+ * @since 2.3.0
+ */
+void purple_util_uninit(void);
+
+/*@}*/
+
+/**************************************************************************/
 /** @name Base16 Functions                                                */
 /**************************************************************************/
 /*@{*/
--- a/pidgin/gtkaccount.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkaccount.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1902,9 +1902,9 @@
 							  purple_account_get_username(account));
 
 		purple_request_close_with_handle(account);
-		purple_request_action(account, NULL, buf, NULL, 0,
+		purple_request_action_with_hint(account, NULL, buf, NULL, 0,
 							account, NULL, NULL,
-							account, 2,
+							PURPLE_REQUEST_UI_HINT_ACCOUNTMGR, account, 2,
 							_("Delete"), delete_account_cb,
 							_("Cancel"), NULL);
 		g_free(buf);
--- a/pidgin/gtkblist.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkblist.c	Sun Oct 28 17:16:42 2007 +0000
@@ -522,9 +522,9 @@
 	if (i > 1)
 	{
 		char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias);
-		purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
+		purple_request_action_with_hint(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
 							 "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, NULL, NULL,
-				      merges, 2, _("_Merge"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_Cancel"), PURPLE_CALLBACK(g_list_free));
+				      PURPLE_REQUEST_UI_HINT_BLIST, merges, 2, _("_Merge"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_Cancel"), PURPLE_CALLBACK(g_list_free));
 		g_free(msg);
 	} else
 		g_list_free(merges);
@@ -617,7 +617,7 @@
 
 static void gtk_blist_menu_bp_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	pidgin_pounce_editor_show(b->account, b->name, NULL);
+	pidgin_pounce_editor_show_with_parent(GTK_WINDOW(gtkblist->window), b->account, b->name, NULL);
 }
 
 static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node)
@@ -643,7 +643,7 @@
 			name = prpl_info->get_chat_name(c->components);
 		}
 	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		pidgin_log_show_contact((PurpleContact *)node);
+		pidgin_log_show_contact_with_parent(GTK_WINDOW(gtkblist->window), (PurpleContact *)node);
 		pidgin_clear_cursor(gtkblist->window);
 		return;
 	} else {
@@ -655,7 +655,7 @@
 	}
 
 	if (name && account) {
-		pidgin_log_show(type, name, account);
+		pidgin_log_show_with_parent(GTK_WINDOW(gtkblist->window), type, name, account);
 		g_free(name);
 
 		pidgin_clear_cursor(gtkblist->window);
@@ -682,11 +682,6 @@
 	pidgin_blist_update(purple_get_blist(), node);
 }
 
-static void gtk_blist_show_systemlog_cb()
-{
-	pidgin_syslog_show();
-}
-
 static void gtk_blist_show_onlinehelp_cb()
 {
 	purple_notify_uri(NULL, PURPLE_WEBSITE "documentation");
@@ -3057,6 +3052,11 @@
 			!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"));
 }
 
+static void
+pidgin_blist_show_with_parent(gpointer data1, void (*callback)(GtkWindow *parent), gpointer data3)
+{
+	callback(GTK_WINDOW(gtkblist->window));
+}
 
 /***************************************************
  *            Crap                                 *
@@ -3090,15 +3090,15 @@
 
 	/* Tools */
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
-	{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 0, "<Item>", NULL },
+	{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_blist_show_with_parent, (int)pidgin_pounces_manager_show_with_parent, "<Item>", NULL },
 	{ N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
-	{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
+	{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_blist_show_with_parent, (int)pidgin_plugin_dialog_show_with_parent, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
 	{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
 	{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
 	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<Item>", NULL },
 	{ N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
-	{ N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, "<Item>", NULL },
+	{ N_("/Tools/System _Log"), NULL, pidgin_blist_show_with_parent, (int)pidgin_syslog_show_with_parent, "<Item>", NULL },
 	{ "/Tools/sep3", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Tools/Mute _Sounds"), "<CTL>S", pidgin_blist_mute_sounds_cb, 0, "<CheckItem>", NULL },
 	/* Help */
@@ -3106,9 +3106,9 @@
 	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
 	{ N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL },
 #if GTK_CHECK_VERSION(2,6,0)
-	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<StockItem>", GTK_STOCK_ABOUT },
+	{ N_("/Help/_About"), NULL, pidgin_blist_show_with_parent, (int)pidgin_dialogs_about_with_parent, "<StockItem>", GTK_STOCK_ABOUT },
 #else
-	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<Item>", NULL },
+	{ N_("/Help/_About"), NULL, pidgin_blist_show_with_parent, (int)pidgin_dialogs_about_with_parent, "<Item>", NULL },
 #endif
 };
 
@@ -3384,7 +3384,7 @@
 		return ret;
 	}
 
-	if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) {
+	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
 		ret = gdk_pixbuf_new_from_file(path, NULL);
 		g_free(path);
@@ -4323,9 +4323,9 @@
 	text = g_hash_table_lookup(gtkblist->connection_errors, account);
 
 	enabled = purple_account_get_enabled(account, purple_core_get_ui());
-	purple_request_action(account, _("Connection Error"), primary, text, 2,
+	purple_request_action_with_hint(account, _("Connection Error"), primary, text, 2,
 						account, NULL, NULL,
-						account, 3,
+						PURPLE_REQUEST_UI_HINT_ACCOUNT, account, 3,
 						_("OK"), NULL,
 						_("Modify Account"), PURPLE_CALLBACK(ce_modify_account_cb),
 						enabled ? _("Connect") : _("Re-enable Account"),
@@ -5787,11 +5787,13 @@
 	gtkblist = PIDGIN_BLIST(purple_get_blist());
 
 	data->window = gtk_dialog_new_with_buttons(_("Add Buddy"),
-			NULL, GTK_DIALOG_NO_SEPARATOR,
+			gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR,
 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 			GTK_STOCK_ADD, GTK_RESPONSE_OK,
 			NULL);
 
+	if (gtkblist)
+		gtk_window_set_transient_for(GTK_WINDOW(data->window), GTK_WINDOW(gtkblist->window));
 	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
 	gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
 	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
@@ -6169,11 +6171,13 @@
 	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	data->window = gtk_dialog_new_with_buttons(_("Add Chat"),
-		NULL, GTK_DIALOG_NO_SEPARATOR,
+		gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		GTK_STOCK_ADD, GTK_RESPONSE_OK,
 		NULL);
 
+	if (gtkblist)
+		gtk_window_set_transient_for(GTK_WINDOW(data->window), GTK_WINDOW(gtkblist->window));
 	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
 	gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
 	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
@@ -6277,13 +6281,13 @@
 static void
 pidgin_blist_request_add_group(void)
 {
-	purple_request_input(NULL, _("Add Group"), NULL,
+	purple_request_input_with_hint(NULL, _("Add Group"), NULL,
 					   _("Please enter the name of the group to be added."),
 					   NULL, FALSE, FALSE, NULL,
 					   _("Add"), G_CALLBACK(add_group_cb),
 					   _("Cancel"), NULL,
 					   NULL, NULL, NULL,
-					   NULL);
+					   PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 void
--- a/pidgin/gtkcertmgr.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Sun Oct 28 17:16:42 2007 +0000
@@ -184,7 +184,7 @@
 		   if the window gets closed unusually, such as by handle
 		   deletion */
 		/* TODO: Display some more information on the certificate? */
-		purple_request_input(tpm_dat,
+		purple_request_input_with_hint(tpm_dat,
 				     _("Certificate Import"),
 				     _("Specify a hostname"),
 				     _("Type the host name this certificate is for."),
@@ -197,7 +197,7 @@
 				     _("Cancel"),
 				     G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
 				     NULL, NULL, NULL, /* No account/who/conv*/
-				     crt    /* Pass cert instance to callback*/
+				     PURPLE_REQUEST_UI_HINT_CERTMGR, crt    /* Pass cert instance to callback*/
 				     );
 		
 		g_free(default_hostname);
@@ -220,13 +220,13 @@
 tls_peers_mgmt_import_cb(GtkWidget *button, gpointer data)
 {
 	/* TODO: need to tell the user that we want a .PEM file! */
-	purple_request_file(tpm_dat,
+	purple_request_file_with_hint(tpm_dat,
 			    _("Select a PEM certificate"),
 			    "certificate.pem",
 			    FALSE, /* Not a save dialog */
 			    G_CALLBACK(tls_peers_mgmt_import_ok_cb),
 			    NULL,  /* Do nothing if cancelled */
-			    NULL, NULL, NULL, NULL );/* No account,conv,etc. */
+			    NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_CERTMGR, NULL );/* No account,conv,etc. */
 }
 
 static void
@@ -295,14 +295,14 @@
 
 	
 	/* TODO: inform user that it will be a PEM? */
-	purple_request_file(tpm_dat,
+	purple_request_file_with_hint(tpm_dat,
 			    _("PEM X.509 Certificate Export"),
 			    "certificate.pem",
 			    TRUE, /* Is a save dialog */
 			    G_CALLBACK(tls_peers_mgmt_export_ok_cb),
 			    G_CALLBACK(tls_peers_mgmt_export_cancel_cb),
 			    NULL, NULL, NULL, /* No account,conv,etc. */
-			    crt); /* Pass the certificate on to the callback */
+			    PURPLE_REQUEST_UI_HINT_CERTMGR, crt); /* Pass the certificate on to the callback */
 }
 
 static void
@@ -371,10 +371,11 @@
 		primary = g_strdup_printf(
 			_("Really delete certificate for %s?"), id );
 		
-		purple_request_yes_no(tpm_dat, _("Confirm certificate delete"),
+		purple_request_yes_no_with_hint(tpm_dat, _("Confirm certificate delete"),
 				      primary, NULL, /* Can this be NULL? */
 				      2, /* NO is default action */
 				      NULL, NULL, NULL,
+				      PURPLE_REQUEST_UI_HINT_CERTMGR,
 				      id, /* id ownership passed to callback */
 				      tls_peers_mgmt_delete_confirm_cb,
 				      tls_peers_mgmt_delete_confirm_cb );
--- a/pidgin/gtkconv.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1029,11 +1029,11 @@
 		if (*c == '/' || *c == '\\')
 			*c = ' ';
 	}
-	purple_request_file(PIDGIN_CONVERSATION(conv), _("Save Conversation"),
+	purple_request_file_with_hint(PIDGIN_CONVERSATION(conv), _("Save Conversation"),
 					  buf,
 					  TRUE, G_CALLBACK(savelog_writefile_cb), NULL,
 					  NULL, NULL, conv,
-					  conv);
+					  PURPLE_REQUEST_UI_HINT_BLIST, conv);
 
 	g_free(buf);
 }
@@ -1081,7 +1081,7 @@
 		PurpleBlistNode *node = cur->data;
 		if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL)))
 		{
-			pidgin_log_show_contact((PurpleContact *)node->parent);
+			pidgin_log_show_contact_with_parent(GTK_WINDOW(win->window), (PurpleContact *)node->parent);
 			g_slist_free(buddies);
 			gdk_window_set_cursor(gtkblist->window->window, NULL);
 			gdk_window_set_cursor(win->window->window, NULL);
@@ -1090,7 +1090,7 @@
 	}
 	g_slist_free(buddies);
 
-	pidgin_log_show(type, name, account);
+	pidgin_log_show_with_parent(GTK_WINDOW(win->window), type, name, account);
 
 	gdk_window_set_cursor(gtkblist->window->window, NULL);
 	gdk_window_set_cursor(win->window->window, NULL);
@@ -1236,7 +1236,7 @@
 
 	conv = pidgin_conv_window_get_active_gtkconv(win)->active_conv;
 
-	pidgin_pounce_editor_show(purple_conversation_get_account(conv),
+	pidgin_pounce_editor_show_with_parent(GTK_WINDOW(win->window), purple_conversation_get_account(conv),
 								purple_conversation_get_name(conv), NULL);
 }
 
@@ -2718,10 +2718,10 @@
 
 	buf = g_strdup_printf("%s.%s", purple_normalize(conv->account, conv->name), ext);
 
-	purple_request_file(gtkconv, _("Save Icon"), buf, TRUE,
+	purple_request_file_with_hint(gtkconv, _("Save Icon"), buf, TRUE,
 					 G_CALLBACK(saveicon_writefile_cb), NULL,
 					conv->account, NULL, conv,
-					gtkconv);
+					PURPLE_REQUEST_UI_HINT_BLIST, gtkconv);
 
 	g_free(buf);
 }
@@ -6519,6 +6519,7 @@
 		AtkObject *accessibility_obj;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
 		char *style;
+		gboolean bold = FALSE;
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 			im = PURPLE_CONV_IM(conv);
@@ -6552,7 +6553,7 @@
 		gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
 				CONV_TEXT_COLUMN, markup, -1);
 	        /* XXX seanegan Why do I have to do this? */
-        	gtk_widget_queue_draw(gtkconv->infopane);
+		gtk_widget_queue_draw(gtkconv->infopane);
 	
 		if (title != markup)
 			g_free(markup);
@@ -6571,31 +6572,41 @@
 			style = "color=\"#c4a000\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK)	{
 			atk_object_set_description(accessibility_obj, _("Nick Said"));
-			style = "color=\"#204a87\" weight=\"bold\"";
+			style = "color=\"#cc0000\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT)	{
 			atk_object_set_description(accessibility_obj, _("Unread Messages"));
-			style = "color=\"#cc0000\" weight=\"bold\"";
+			if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT)
+				style = "color=\"#204a87\" weight=\"bold\"";
+			else
+				style = "color=\"#cc0000\" weight=\"bold\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
 			atk_object_set_description(accessibility_obj, _("New Event"));
-			style = "color=\"#888a85\" weight=\"bold\"";
+			style = "color=\"#888a85\"";
 		} else {
-			style = "";
-		}
+			style = NULL;
+		}
+
+		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_NICK ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT)
+			bold = TRUE;
 		
-		if (*style != '\0')
+		if (style || bold)
 		{
 			char *html_title,*label;
 
 			html_title = g_markup_escape_text(title, -1);
-			label = g_strdup_printf("<span %s>%s</span>",
-			                        style, html_title);
+			label = g_strdup_printf("<span %s %s>%s</span>",
+			                        style ? style : "",
+			                        bold ? "weight=\"bold\"" : "",
+			                        html_title);
 			g_free(html_title);
 			gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label);
 			g_free(label);
 		}
 		else
 			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
-		
+
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
 
@@ -7397,6 +7408,17 @@
 	pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
 }
 
+/* Message history stuff */
+
+/* Compare two PurpleConvMessage's, according to time in ascending order. */
+static int
+message_compare(gconstpointer p1, gconstpointer p2)
+{
+	const PurpleConvMessage *m1 = p1, *m2 = p2;
+	return (m1->when > m2->when);
+}
+
+/* Adds some message history to the gtkconv. This happens in a idle-callback. */
 static gboolean
 add_message_history_to_gtkconv(gpointer data)
 {
@@ -7404,49 +7426,106 @@
 	int count = 0;
 	int timer = gtkconv->attach.timer;
 	time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time"));
+	gboolean im = (gtkconv->active_conv->type == PURPLE_CONV_TYPE_IM);
 
 	gtkconv->attach.timer = 0;
 	while (gtkconv->attach.current && count < 100) {  /* XXX: 100 is a random value here */
 		PurpleConvMessage *msg = gtkconv->attach.current->data;
-		if (when && when < msg->when) {
+		if (!im && when && when < msg->when) {
 			gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
 			g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 		}
-		pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when);
-		gtkconv->attach.current = gtkconv->attach.current->prev;
+		pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
+		if (im) {
+			gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current);
+		} else {
+			gtkconv->attach.current = gtkconv->attach.current->prev;
+		}
 		count++;
 	}
 	gtkconv->attach.timer = timer;
 	if (gtkconv->attach.current)
 		return TRUE;
 
+	g_source_remove(gtkconv->attach.timer);
+	gtkconv->attach.timer = 0;
+	if (im) {
+		/* Print any message that was sent while the old history was being added back. */
+		GList *msgs = NULL;
+		GList *iter = gtkconv->convs;
+		for (; iter; iter = iter->next) {
+			PurpleConversation *conv = iter->data;
+			GList *history = purple_conversation_get_message_history(conv);
+			for (; history; history = history->next) {
+				PurpleConvMessage *msg = history->data;
+				if (msg->when > when)
+					msgs = g_list_prepend(msgs, msg);
+			}
+		}
+		msgs = g_list_sort(msgs, message_compare);
+		for (; msgs; msgs = g_list_delete_link(msgs, msgs)) {
+			PurpleConvMessage *msg = msgs->data;
+			pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
+		}
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+		g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
+	}
+
 	g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 	purple_signal_emit(pidgin_conversations_get_handle(),
 			"conversation-displayed", gtkconv);
-	g_source_remove(gtkconv->attach.timer);
-	gtkconv->attach.timer = 0;
 	return FALSE;
 }
 
+static void
+pidgin_conv_attach(PurpleConversation *conv)
+{
+	int timer;
+	purple_conversation_set_data(conv, "unseen-count", NULL);
+	purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
+	private_gtkconv_new(conv, FALSE);
+	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+	if (timer)
+		purple_timeout_remove(timer);
+}
+
 gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv)
 {
 	GList *list;
 	PidginConversation *gtkconv;
-	int timer;
 
 	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 		return FALSE;
 
-	purple_conversation_set_data(conv, "unseen-count", NULL);
-	purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
-	private_gtkconv_new(conv, FALSE);
+	pidgin_conv_attach(conv);
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
 	list = purple_conversation_get_message_history(conv);
 	if (list) {
+		switch (purple_conversation_get_type(conv)) {
+			case PURPLE_CONV_TYPE_IM: 
+			{
+				GList *convs;
+				list = g_list_copy(list);
+				for (convs = purple_get_ims(); convs; convs = convs->next)
+					if (convs->data != conv &&
+							pidgin_conv_find_gtkconv(convs->data) == gtkconv) {
+						pidgin_conv_attach(convs->data);
+						list = g_list_concat(list, g_list_copy(purple_conversation_get_message_history(convs->data)));
+					}
+				list = g_list_sort(list, message_compare);
+				gtkconv->attach.current = list;
+				list = g_list_last(list);
+				break;
+			}
+			case PURPLE_CONV_TYPE_CHAT:
+				gtkconv->attach.current = g_list_last(list);
+				break;
+			default:
+				g_return_val_if_reached(TRUE);
+		}
 		g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time",
 				GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when));
-		gtkconv->attach.current = g_list_last(list);
 		gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
 	} else {
 		purple_signal_emit(pidgin_conversations_get_handle(),
@@ -7458,9 +7537,6 @@
 		pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
 	}
 
-	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
-	if (timer)
-		purple_timeout_remove(timer);
 	return TRUE;
 }
 
@@ -9791,3 +9867,4 @@
 	return colors;
 }
 
+
--- a/pidgin/gtkdebug.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkdebug.c	Sun Oct 28 17:16:42 2007 +0000
@@ -36,6 +36,7 @@
 #include "gtkimhtml.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
+#include "gtkblist.h"
 
 #ifdef HAVE_REGEX_H
 # include <regex.h>
@@ -231,10 +232,10 @@
 static void
 save_cb(GtkWidget *w, DebugWindow *win)
 {
-	purple_request_file(win, _("Save Debug Log"), "purple-debug.log", TRUE,
+	purple_request_file_with_hint(win, _("Save Debug Log"), "purple-debug.log", TRUE,
 					  G_CALLBACK(save_writefile_cb), NULL,
 					  NULL, NULL, NULL,
-					  win);
+					  PURPLE_REQUEST_UI_HINT_BLIST, win);
 }
 
 static void
@@ -673,6 +674,7 @@
 static DebugWindow *
 debug_window_new(void)
 {
+	PidginBuddyList *blist;
 	DebugWindow *win;
 	GtkWidget *vbox;
 	GtkWidget *toolbar;
@@ -687,6 +689,9 @@
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/height");
 
 	PIDGIN_DIALOG(win->window);
+	if ((blist = pidgin_blist_get_default_gtk_blist()) != NULL)
+		if (blist->window)
+			gtk_window_set_transient_for(GTK_WINDOW(win->window), GTK_WINDOW(blist->window));
 	purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
 					width, height);
 
--- a/pidgin/gtkdialogs.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkdialogs.c	Sun Oct 28 17:16:42 2007 +0000
@@ -334,6 +334,13 @@
 
 void pidgin_dialogs_about()
 {
+	PidginBuddyList *blist = pidgin_blist_get_default_gtk_blist();
+
+	pidgin_dialogs_about_with_parent(blist ? GTK_WINDOW(blist->window) : NULL);
+}
+
+void pidgin_dialogs_about_with_parent(GtkWindow *parent)
+{
 	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *logo;
@@ -349,11 +356,15 @@
 	GdkPixbuf *pixbuf;
 
 	if (about != NULL) {
+		if (parent)
+			gtk_window_set_transient_for(GTK_WINDOW(about), parent);
 		gtk_window_present(GTK_WINDOW(about));
 		return;
 	}
 
 	PIDGIN_DIALOG(about);
+	if (parent)
+		gtk_window_set_transient_for(GTK_WINDOW(about), parent);
 	tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
 	gtk_window_set_title(GTK_WINDOW(about), tmp);
 	g_free(tmp);
@@ -756,6 +767,10 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
+	field = purple_request_field_blist_nodes_new("blistnodes", _("Buddy"),
+			PURPLE_REQUEST_BLIST_FLAG_BUDDY, NULL);
+	purple_request_field_group_add_field(group, field);
+
 	field = purple_request_field_account_new("account", _("_Account"), NULL);
 	purple_request_field_set_type_hint(field, "account");
 	purple_request_field_set_visible(field,
@@ -764,7 +779,7 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(purple_get_blist(), _("New Instant Message"),
+	purple_request_fields_with_hint(purple_get_blist(), _("New Instant Message"),
 						NULL,
 						_("Please enter the screen name or alias of the person "
 						  "you would like to IM."),
@@ -772,7 +787,7 @@
 						_("OK"), G_CALLBACK(pidgin_dialogs_im_cb),
 						_("Cancel"), NULL,
 						NULL, NULL, NULL,
-						NULL);
+						PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 void
@@ -903,7 +918,7 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(purple_get_blist(), _("Get User Info"),
+	purple_request_fields_with_hint(purple_get_blist(), _("Get User Info"),
 						NULL,
 						_("Please enter the screen name or alias of the person "
 						  "whose info you would like to view."),
@@ -911,7 +926,7 @@
 						_("OK"), G_CALLBACK(pidgin_dialogs_info_cb),
 						_("Cancel"), NULL,
 						NULL, NULL, NULL,
-						NULL);
+						PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -939,7 +954,7 @@
 			PurpleBlistNode *node = cur->data;
 			if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL)))
 			{
-				pidgin_log_show_contact((PurpleContact *)node->parent);
+				pidgin_log_show_contact_with_parent(GTK_WINDOW(gtkblist->window), (PurpleContact *)node->parent);
 				g_slist_free(buddies);
 				pidgin_clear_cursor(gtkblist->window);
 				g_free(username);
@@ -948,7 +963,7 @@
 		}
 		g_slist_free(buddies);
 
-		pidgin_log_show(PURPLE_LOG_IM, username, account);
+		pidgin_log_show_with_parent(GTK_WINDOW(gtkblist->window), PURPLE_LOG_IM, username, account);
 
 		pidgin_clear_cursor(gtkblist->window);
 	}
@@ -995,7 +1010,7 @@
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
-	purple_request_fields(purple_get_blist(), _("View User Log"),
+	purple_request_fields_with_hint(purple_get_blist(), _("View User Log"),
 						NULL,
 						_("Please enter the screen name or alias of the person "
 						  "whose log you would like to view."),
@@ -1003,7 +1018,7 @@
 						_("OK"), G_CALLBACK(pidgin_dialogs_log_cb),
 						_("Cancel"), NULL,
 						NULL, NULL, NULL,
-						NULL);
+						PURPLE_REQUEST_UI_HINT_BLIST, NULL);
 }
 
 static void
@@ -1017,13 +1032,13 @@
 {
 	g_return_if_fail(contact != NULL);
 
-	purple_request_input(NULL, _("Alias Contact"), NULL,
+	purple_request_input_with_hint(NULL, _("Alias Contact"), NULL,
 					   _("Enter an alias for this contact."),
 					   contact->alias, FALSE, FALSE, NULL,
 					   _("Alias"), G_CALLBACK(pidgin_dialogs_alias_contact_cb),
 					   _("Cancel"), NULL,
 					   NULL, purple_contact_get_alias(contact), NULL,
-					   contact);
+					   PURPLE_REQUEST_UI_HINT_BLIST, contact);
 }
 
 static void
@@ -1042,12 +1057,12 @@
 
 	secondary = g_strdup_printf(_("Enter an alias for %s."), buddy->name);
 
-	purple_request_input(NULL, _("Alias Buddy"), NULL,
+	purple_request_input_with_hint(NULL, _("Alias Buddy"), NULL,
 					   secondary, buddy->alias, FALSE, FALSE, NULL,
 					   _("Alias"), G_CALLBACK(pidgin_dialogs_alias_buddy_cb),
 					   _("Cancel"), NULL,
 					   purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), NULL,
-					   buddy);
+					   PURPLE_REQUEST_UI_HINT_BLIST, buddy);
 
 	g_free(secondary);
 }
@@ -1063,13 +1078,13 @@
 {
 	g_return_if_fail(chat != NULL);
 
-	purple_request_input(NULL, _("Alias Chat"), NULL,
+	purple_request_input_with_hint(NULL, _("Alias Chat"), NULL,
 					   _("Enter an alias for this chat."),
 					   chat->alias, FALSE, FALSE, NULL,
 					   _("Alias"), G_CALLBACK(pidgin_dialogs_alias_chat_cb),
 					   _("Cancel"), NULL,
 					   chat->account, NULL, NULL,
-					   chat);
+					   PURPLE_REQUEST_UI_HINT_BLIST, chat);
 }
 
 static void
@@ -1111,9 +1126,9 @@
 						"want to continue?", contact->totalsize - 1),
 					buddy->name, contact->totalsize - 1);
 
-		purple_request_action(contact, NULL, _("Remove Contact"), text, 0,
+		purple_request_action_with_hint(contact, NULL, _("Remove Contact"), text, 0,
 				NULL, purple_contact_get_alias(contact), NULL,
-				contact, 2,
+				PURPLE_REQUEST_UI_HINT_BLIST, contact, 2,
 				_("_Remove Contact"), G_CALLBACK(pidgin_dialogs_remove_contact_cb),
 				_("Cancel"),
 				NULL);
@@ -1152,9 +1167,9 @@
 	ggp->parent = source;
 	ggp->new_name = g_strdup(new_name);
 	
-	purple_request_action(source, NULL, _("Merge Groups"), text, 0,
+	purple_request_action_with_hint(source, NULL, _("Merge Groups"), text, 0,
 			NULL, NULL, NULL,
-			ggp, 2,
+			PURPLE_REQUEST_UI_HINT_BLIST, ggp, 2,
 			_("_Merge Groups"), G_CALLBACK(pidgin_dialogs_merge_groups_cb),
 			_("Cancel"), G_CALLBACK(free_ggmo));
 
@@ -1208,9 +1223,9 @@
 	text = g_strdup_printf(_("You are about to remove the group %s and all its members from your buddy list.  Do you want to continue?"),
 						   group->name);
 
-	purple_request_action(group, NULL, _("Remove Group"), text, 0,
+	purple_request_action_with_hint(group, NULL, _("Remove Group"), text, 0,
 						NULL, NULL, NULL,
-						group, 2,
+						PURPLE_REQUEST_UI_HINT_BLIST, group, 2,
 						_("_Remove Group"), G_CALLBACK(pidgin_dialogs_remove_group_cb),
 						_("Cancel"), NULL);
 
@@ -1247,9 +1262,9 @@
 	text = g_strdup_printf(_("You are about to remove %s from your buddy list.  Do you want to continue?"),
 						   buddy->name);
 
-	purple_request_action(buddy, NULL, _("Remove Buddy"), text, 0,
+	purple_request_action_with_hint(buddy, NULL, _("Remove Buddy"), text, 0,
 						purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), NULL,
-						buddy, 2,
+						PURPLE_REQUEST_UI_HINT_BLIST, buddy, 2,
 						_("_Remove Buddy"), G_CALLBACK(pidgin_dialogs_remove_buddy_cb),
 						_("Cancel"), NULL);
 
@@ -1274,9 +1289,9 @@
 	text = g_strdup_printf(_("You are about to remove the chat %s from your buddy list.  Do you want to continue?"),
 			name ? name : "");
 
-	purple_request_action(chat, NULL, _("Remove Chat"), text, 0,
+	purple_request_action_with_hint(chat, NULL, _("Remove Chat"), text, 0,
 						chat->account, NULL, NULL,
-						chat, 2,
+						PURPLE_REQUEST_UI_HINT_BLIST, chat, 2,
 						_("_Remove Chat"), G_CALLBACK(pidgin_dialogs_remove_chat_cb),
 						_("Cancel"), NULL);
 
--- a/pidgin/gtkdialogs.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkdialogs.h	Sun Oct 28 17:16:42 2007 +0000
@@ -31,18 +31,33 @@
 #include "conversation.h"
 
 /* Functions in gtkdialogs.c (these should actually stay in this file) */
+
 void pidgin_dialogs_destroy_all(void);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * @deprecated Use pidgin_dialogs_about_with_parent() instead.
+ */
 void pidgin_dialogs_about(void);
+#endif
+
+/**
+ * @since 2.3.0
+ */
+void pidgin_dialogs_about_with_parent(GtkWindow *parent);
+
 void pidgin_dialogs_im(void);
 void pidgin_dialogs_im_with_user(PurpleAccount *, const char *);
 void pidgin_dialogs_info(void);
 void pidgin_dialogs_log(void);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * @deprecated This function is no longer used and will be removed in
  *             Pidgin 3.0.0 unless there is sufficient demand to keep it.
  */
 void pidgin_dialogs_alias_contact(PurpleContact *);
+#endif
 
 void pidgin_dialogs_alias_buddy(PurpleBuddy *);
 void pidgin_dialogs_alias_chat(PurpleChat *);
--- a/pidgin/gtkdocklet.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkdocklet.c	Sun Oct 28 17:16:42 2007 +0000
@@ -407,7 +407,7 @@
 	GdkPixbuf *pixbuf;
 	GtkWidget *image;
 
-	menuitem = gtk_image_menu_item_new_with_mnemonic(str);
+	menuitem = gtk_image_menu_item_new_with_label(str);
 
 	if (menu)
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
@@ -474,6 +474,7 @@
 }
 
 
+
 static void
 plugin_act(GtkObject *obj, PurplePluginAction *pam)
 {
@@ -553,7 +554,6 @@
 		pidgin_separator(menu);
 }
 
-
 static void
 docklet_menu() {
 	static GtkWidget *menu = NULL;
--- a/pidgin/gtkimhtmltoolbar.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sun Oct 28 17:16:42 2007 +0000
@@ -184,6 +184,8 @@
 			g_signal_connect_after(G_OBJECT(toolbar->font_dialog), "realize",
 							 G_CALLBACK(realize_toolbar_font), toolbar);
 		}
+		gtk_window_set_transient_for(GTK_WINDOW(toolbar->font_dialog),
+		    GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
 		gtk_window_present(GTK_WINDOW(toolbar->font_dialog));
 	} else {
 		cancel_toolbar_font(font, toolbar);
@@ -427,14 +429,14 @@
 		}
 
 		toolbar->link_dialog =
-			purple_request_fields(toolbar, _("Insert Link"),
+			purple_request_fields_with_hint(toolbar, _("Insert Link"),
 					    NULL,
-						msg,
+					    msg,
 					    fields,
 					    _("_Insert"), G_CALLBACK(do_insert_link_cb),
 					    _("Cancel"), G_CALLBACK(cancel_link_cb),
-						NULL, NULL, NULL,
-					    toolbar);
+					    NULL, NULL, NULL,
+					    PURPLE_REQUEST_UI_HINT_CONV, toolbar);
 		g_free(msg);
 		g_free(desc);
 	} else {
@@ -709,6 +711,8 @@
 	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
 	gtk_window_set_role(GTK_WINDOW(dialog), "smiley_dialog");
 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
+	gtk_window_set_transient_for(GTK_WINDOW(dialog),
+	    GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
 
 	if (unique_smileys != NULL) {
 		struct smiley_button_list *ls, *it, *it_tmp;
--- a/pidgin/gtklog.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtklog.c	Sun Oct 28 17:16:42 2007 +0000
@@ -321,9 +321,9 @@
 	data2[0] = lv->treestore;
 	data2[1] = data[3]; /* iter */
 	data2[2] = log;
-	purple_request_action(lv, NULL, "Delete Log?", tmp, 0, 
+	purple_request_action_with_hint(lv, NULL, "Delete Log?", tmp, 0, 
 						NULL, NULL, NULL,
-						data2, 2,
+						"log_viewer", data2, 2,
 						_("Delete"), delete_log_cb,
 						_("Cancel"), delete_log_cleanup_cb);
 	g_free(tmp);
@@ -523,7 +523,7 @@
 	}
 }
 
-static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
+static PidginLogViewer *display_log_viewer(GtkWindow *parent, struct log_viewer_hash_t *ht, GList *logs,
 						const char *title, GtkWidget *icon, int log_size)
 {
 	PidginLogViewer *lv;
@@ -569,7 +569,7 @@
 		g_hash_table_insert(log_viewers, ht, lv);
 
 	/* Window ***********/
-	lv->window = gtk_dialog_new_with_buttons(title, NULL, 0,
+	lv->window = gtk_dialog_new_with_buttons(title, parent, 0,
 					     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
 #ifdef _WIN32
 	/* Steal the "HELP" response and use it to trigger browsing to the logs folder */
@@ -676,6 +676,10 @@
 }
 
 void pidgin_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account) {
+	pidgin_log_show_with_parent(NULL, type, screenname, account);
+}
+
+void pidgin_log_show_with_parent(GtkWindow *parent, PurpleLogType type, const char *screenname, PurpleAccount *account) {
 	struct log_viewer_hash_t *ht;
 	PidginLogViewer *lv = NULL;
 	const char *name = screenname;
@@ -720,7 +724,7 @@
 
 	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 
-	display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
+	display_log_viewer(parent, ht, purple_log_get_logs(type, screenname, account),
 			title, gtk_image_new_from_pixbuf(prpl_icon),
 			purple_log_get_total_size(type, screenname, account));
 
@@ -730,6 +734,10 @@
 }
 
 void pidgin_log_show_contact(PurpleContact *contact) {
+	pidgin_log_show_contact_with_parent(NULL, contact);
+}
+
+void pidgin_log_show_contact_with_parent(GtkWindow *parent, PurpleContact *contact) {
 	struct log_viewer_hash_t *ht = g_new0(struct log_viewer_hash_t, 1);
 	PurpleBlistNode *child;
 	PidginLogViewer *lv = NULL;
@@ -783,11 +791,16 @@
 	}
 
 	title = g_strdup_printf(_("Conversations with %s"), name);
-	display_log_viewer(ht, logs, title, image, total_log_size);
+	display_log_viewer(parent, ht, logs, title, image, total_log_size);
 	g_free(title);
 }
 
-void pidgin_syslog_show()
+void pidgin_syslog_show(void)
+{
+	pidgin_syslog_show_with_parent(NULL);
+}
+
+void pidgin_syslog_show_with_parent(GtkWindow *parent)
 {
 	GList *accounts = NULL;
 	GList *logs = NULL;
@@ -807,7 +820,7 @@
 	}
 	logs = g_list_sort(logs, purple_log_compare);
 
-	syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), NULL, 0);
+	syslog_viewer = display_log_viewer(parent, NULL, logs, _("System Log"), NULL, 0);
 }
 
 /****************************************************************************
--- a/pidgin/gtklog.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtklog.h	Sun Oct 28 17:16:42 2007 +0000
@@ -52,11 +52,41 @@
 };
 
 
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * @deprecated Use pidgin_log_show_with_parent() instead.
+ */
+void pidgin_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account);
+#endif
 
-void pidgin_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account);
+/**
+ * @since 2.3.0
+ */
+void pidgin_log_show_with_parent(GtkWindow *parent, PurpleLogType type, const char *screenname, PurpleAccount *account);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * @deprecated pidgin_log_show_contact_with_parent() instead.
+ */
 void pidgin_log_show_contact(PurpleContact *contact);
+#endif
 
+/**
+ * @since 2.3.0
+ */
+void pidgin_log_show_contact_with_parent(GtkWindow *parent, PurpleContact *contact);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * @deprecated Use pidgin_syslog_show_with_parent() instead.
+ */
 void pidgin_syslog_show(void);
+#endif
+
+/**
+ * @since 2.3.0
+ */
+void pidgin_syslog_show_with_parent(GtkWindow *parent);
 
 /**************************************************************************/
 /** @name GTK+ Log Subsystem                                              */
--- a/pidgin/gtknotify.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtknotify.c	Sun Oct 28 17:16:42 2007 +0000
@@ -270,8 +270,9 @@
 	primary_esc = g_markup_escape_text(primary, -1);
 	secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
 	g_snprintf(label_text, sizeof(label_text),
-			   "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
-			   primary_esc, (secondary ? secondary_esc : ""));
+			   "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
+			   primary_esc, (secondary ? "\n\n" : ""),
+			   (secondary ? secondary_esc : ""));
 	g_free(primary_esc);
 	g_free(secondary_esc);
 
@@ -740,7 +741,7 @@
 	GtkListStore *model;
 	GtkCellRenderer *renderer;
 	guint col_num;
-	GList *column;
+	GList *columniter;
 	guint i;
 
 	GtkWidget *vbox;
@@ -824,11 +825,12 @@
 					-1, "", renderer, "pixbuf", 0, NULL);
 
 	i = 1;
-	for (column = results->columns; column != NULL; column = column->next) {
+	for (columniter = results->columns; columniter != NULL; columniter = columniter->next) {
+		PurpleNotifySearchColumn *column = columniter->data;
 		renderer = gtk_cell_renderer_text_new();
 
 		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1,
-				column->data, renderer, "text", i, NULL);
+				column->title, renderer, "text", i, NULL);
 		i++;
 	}
 
--- a/pidgin/gtkplugin.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkplugin.c	Sun Oct 28 17:16:42 2007 +0000
@@ -280,11 +280,11 @@
 			cb_data[1] = model;
 			cb_data[2] = iter;
 
-			purple_request_action(plugin_dialog, NULL,
+			purple_request_action_with_hint(plugin_dialog, NULL,
 			                    _("Multiple plugins will be unloaded."),
 			                    tmp->str, 0,
-								NULL, NULL, NULL,
-								cb_data, 2,
+			                    NULL, NULL, NULL,
+			                    PURPLE_REQUEST_UI_HINT_PLUGINMGR, cb_data, 2,
 			                    _("Unload Plugins"), G_CALLBACK(plugin_unload_confirm_cb),
 			                    _("Cancel"), g_free);
 			g_string_free(tmp, TRUE);
@@ -303,7 +303,24 @@
 	{
 		pidgin_set_cursor(plugin_dialog, GDK_WATCH);
 
-		purple_plugin_unload(plug);
+		if (!purple_plugin_unload(plug))
+		{
+			const char *primary = _("Could not unload plugin");
+			const char *reload = _("The plugin could not be unloaded now, but will be disabled at the next startup.");
+
+			if (!plug->error)
+			{
+				purple_notify_warning(NULL, NULL, primary, reload);
+			}
+			else
+			{
+				char *tmp = g_strdup_printf("%s\n\n%s", reload, plug->error);
+				purple_notify_warning(NULL, NULL, primary, tmp);
+				g_free(tmp);
+			}
+
+			purple_plugin_disable(plug);
+		}
 
 		pidgin_clear_cursor(plugin_dialog);
 	}
@@ -516,6 +533,11 @@
 
 void pidgin_plugin_dialog_show()
 {
+	pidgin_plugin_dialog_show_with_parent(NULL);
+}
+
+void pidgin_plugin_dialog_show_with_parent(GtkWindow *parent)
+{
 	GtkWidget *sw;
 	GtkWidget *event_view;
 	GtkListStore *ls;
@@ -524,6 +546,8 @@
 	GtkTreeSelection *sel;
 
 	if (plugin_dialog != NULL) {
+		if (parent)
+			gtk_window_set_transient_for(GTK_WINDOW(plugin_dialog), parent);
 		gtk_window_present(GTK_WINDOW(plugin_dialog));
 		return;
 	}
@@ -532,6 +556,8 @@
 						    NULL,
 						    GTK_DIALOG_NO_SEPARATOR,
 						    NULL);
+	if (parent)
+		gtk_window_set_transient_for(GTK_WINDOW(plugin_dialog), parent);
 	pref_button = gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
 						_("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE);
 	gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
--- a/pidgin/gtkplugin.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkplugin.h	Sun Oct 28 17:16:42 2007 +0000
@@ -72,9 +72,20 @@
  */
 void pidgin_plugins_save(void);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Shows the Plugins dialog
+ *
+ * @deprecated Use pidgin_plugin_dialog_show_with_parent() instead.
  */
 void pidgin_plugin_dialog_show(void);
+#endif
+
+/**
+ * Shows the Plugins dialog, transient to a parent window
+ *
+ * @since 2.3.0
+ */
+void pidgin_plugin_dialog_show_with_parent(GtkWindow *parent);
 
 #endif /* _PIDGINPLUGIN_H_ */
--- a/pidgin/gtkpounce.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkpounce.c	Sun Oct 28 17:16:42 2007 +0000
@@ -154,10 +154,10 @@
 	entry = (GtkWidget *)data;
 	name = gtk_entry_get_text(GTK_ENTRY(entry));
 
-	purple_request_file(entry, _("Select a file"), name, FALSE,
+	purple_request_file_with_hint(entry, _("Select a file"), name, FALSE,
 					  G_CALLBACK(pounce_update_entry_fields), NULL,
 					  NULL, NULL, NULL,
-					  entry);
+					  "buddy_pounce", entry);
 	g_signal_connect_swapped(G_OBJECT(entry), "destroy",
 			G_CALLBACK(purple_request_close_with_handle), entry);
 }
@@ -472,6 +472,13 @@
 pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
 							PurplePounce *cur_pounce)
 {
+	pidgin_pounce_editor_show_with_parent(NULL, account, name, cur_pounce);
+}
+
+void
+pidgin_pounce_editor_show_with_parent(GtkWindow *parent, PurpleAccount *account, const char *name,
+							PurplePounce *cur_pounce)
+{
 	PidginPounceDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *label;
@@ -1050,7 +1057,7 @@
 static void
 pounces_manager_add_cb(GtkButton *button, gpointer user_data)
 {
-	pidgin_pounce_editor_show(NULL, NULL, NULL);
+	pidgin_pounce_editor_show_with_parent(GTK_WINDOW(pounces_manager->window), NULL, NULL, NULL);
 }
 
 static void
@@ -1060,7 +1067,7 @@
 	PurplePounce *pounce;
 
 	gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
-	pidgin_pounce_editor_show(NULL, NULL, pounce);
+	pidgin_pounce_editor_show_with_parent(GTK_WINDOW(pounces_manager->window), NULL, NULL, pounce);
 }
 
 static void
@@ -1101,9 +1108,9 @@
 	pouncee = purple_pounce_get_pouncee(pounce);
 
 	buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
-	purple_request_action(pounce, NULL, buf, NULL, 0,
+	purple_request_action_with_hint(pounce, NULL, buf, NULL, 0,
 						account, pouncee, NULL,
-						pounce, 2,
+						PURPLE_REQUEST_UI_HINT_POUNCEMGR, pounce, 2,
 						_("Delete"), pounces_manager_delete_confirm_cb,
 						_("Cancel"), NULL);
 	g_free(buf);
@@ -1160,7 +1167,7 @@
 	if ((pounce != NULL) && (event->button == 1) &&
 		(event->type == GDK_2BUTTON_PRESS))
 	{
-		pidgin_pounce_editor_show(NULL, NULL, pounce);
+		pidgin_pounce_editor_show_with_parent(GTK_WINDOW(pounces_manager->window), NULL, NULL, pounce);
 		return TRUE;
 	}
 
@@ -1311,6 +1318,12 @@
 void
 pidgin_pounces_manager_show(void)
 {
+	pidgin_pounces_manager_show_with_parent(NULL);
+}
+
+void
+pidgin_pounces_manager_show_with_parent(GtkWindow *parent)
+{
 	PouncesManager *dialog;
 	GtkWidget *bbox;
 	GtkWidget *button;
@@ -1321,6 +1334,7 @@
 
 	if (pounces_manager != NULL) {
 		gtk_window_present(GTK_WINDOW(pounces_manager->window));
+		gtk_window_set_transient_for(GTK_WINDOW(pounces_manager->window), parent);
 		return;
 	}
 
@@ -1331,6 +1345,7 @@
 
 	dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
+	gtk_window_set_transient_for(GTK_WINDOW(win), parent);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(pounces_manager_destroy_cb), dialog);
--- a/pidgin/gtkpounce.h	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkpounce.h	Sun Oct 28 17:16:42 2007 +0000
@@ -28,20 +28,48 @@
 
 #include "pounce.h"
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
  *
  * @param account    The optional account to use.
  * @param name       The optional name to pounce on.
  * @param cur_pounce The current buddy pounce, if editing an existing one.
+ *
+ * @deprecated Use pidgin_pounce_editor_show_with_parent() instead.
  */
 void pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
 								PurplePounce *cur_pounce);
+#endif
+
+/**
+ * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
+ *
+ * @param parent     The parent window.
+ * @param account    The optional account to use.
+ * @param name       The optional name to pounce on.
+ * @param cur_pounce The current buddy pounce, if editing an existing one.
+ *
+ * @since 2.3.0
+ */
+void pidgin_pounce_editor_show_with_parent(GtkWindow *parent, PurpleAccount *account, const char *name,
+								PurplePounce *cur_pounce);
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Shows the pounces manager window.
+ *
+ * @deprecated Use pidgin_pounces_manager_show_with_parent() instead.
+ */
+void pidgin_pounces_manager_show(void);
+#endif
 
 /**
  * Shows the pounces manager window.
+ *
+ * @since 2.3.0
  */
-void pidgin_pounces_manager_show(void);
+void pidgin_pounces_manager_show_with_parent(GtkWindow *parent);
 
 /**
  * Hides the pounces manager window.
--- a/pidgin/gtkprefs.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkprefs.c	Sun Oct 28 17:16:42 2007 +0000
@@ -641,7 +641,8 @@
 static void
 add_theme_button_clicked_cb(GtkWidget *widget, gpointer null)
 {
-	purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ;
+	purple_request_file_with_hint(NULL, _("Install Theme"), NULL, FALSE,
+	    (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_PREFSMGR, NULL) ;
 }
 
 static void
@@ -1624,9 +1625,9 @@
 	if (*filename == '\0')
 		filename = NULL;
 
-	purple_request_file(prefs, _("Sound Selection"), filename, FALSE,
+	purple_request_file_with_hint(prefs, _("Sound Selection"), filename, FALSE,
 					  G_CALLBACK(sound_chosen_cb), NULL,
-					  NULL, NULL, NULL,
+					  NULL, NULL, NULL, PURPLE_REQUEST_UI_HINT_PREFSMGR,
 					  GINT_TO_POINTER(sound_row_sel));
 }
 
--- a/pidgin/gtkprivacy.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkprivacy.c	Sun Oct 28 17:16:42 2007 +0000
@@ -88,6 +88,8 @@
 
 static PidginPrivacyDialog *privacy_dialog = NULL;
 
+#define PIDGIN_REQUEST_HINT_PRIVACY "privacy"
+
 static void
 rebuild_allow_list(PidginPrivacyDialog *dialog)
 {
@@ -367,7 +369,7 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
+	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, PIDGIN_REQUEST_HINT_PRIVACY, TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
@@ -554,7 +556,7 @@
 	data->block   = FALSE;
 
 	if (name == NULL) {
-		purple_request_input(account, _("Permit User"),
+		purple_request_input_with_hint(account, _("Permit User"),
 			_("Type a user you permit to contact you."),
 			_("Please enter the name of the user you wish to be "
 			  "able to contact you."),
@@ -562,7 +564,7 @@
 			_("_Permit"), G_CALLBACK(add_permit_block_cb),
 			_("Cancel"), G_CALLBACK(destroy_request_data),
 			account, name, NULL,
-			data);
+			PIDGIN_REQUEST_HINT_PRIVACY, data);
 	}
 	else {
 		char *primary = g_strdup_printf(_("Allow %s to contact you?"), name);
@@ -571,10 +573,10 @@
 							  "%s to contact you?"), name);
 
 
-		purple_request_action(account, _("Permit User"), primary, secondary,
+		purple_request_action_with_hint(account, _("Permit User"), primary, secondary,
 							0,
 							account, name, NULL,
-							data, 2,
+							PIDGIN_REQUEST_HINT_PRIVACY, data, 2,
 							_("_Permit"), G_CALLBACK(confirm_permit_block_cb),
 							_("Cancel"), G_CALLBACK(destroy_request_data));
 
@@ -596,24 +598,24 @@
 	data->block   = TRUE;
 
 	if (name == NULL) {
-		purple_request_input(account, _("Block User"),
+		purple_request_input_with_hint(account, _("Block User"),
 			_("Type a user to block."),
 			_("Please enter the name of the user you wish to block."),
 			NULL, FALSE, FALSE, NULL,
 			_("_Block"), G_CALLBACK(add_permit_block_cb),
 			_("Cancel"), G_CALLBACK(destroy_request_data),
 			account, name, NULL,
-			data);
+			PIDGIN_REQUEST_HINT_PRIVACY, data);
 	}
 	else {
 		char *primary = g_strdup_printf(_("Block %s?"), name);
 		char *secondary =
 			g_strdup_printf(_("Are you sure you want to block %s?"), name);
 
-		purple_request_action(account, _("Block User"), primary, secondary,
+		purple_request_action_with_hint(account, _("Block User"), primary, secondary,
 							0,
 							account, name, NULL,
-							data, 2,
+							PIDGIN_REQUEST_HINT_PRIVACY, data, 2,
 							_("_Block"), G_CALLBACK(confirm_permit_block_cb),
 							_("Cancel"), G_CALLBACK(destroy_request_data));
 
--- a/pidgin/gtkrequest.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkrequest.c	Sun Oct 28 17:16:42 2007 +0000
@@ -29,11 +29,13 @@
 #include "prefs.h"
 #include "util.h"
 
+#include "gtkblist.h"
 #include "gtkimhtml.h"
 #include "gtkimhtmltoolbar.h"
 #include "gtkrequest.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
+#include "gtkblist.h"
 
 #include <gdk/gdkkeysyms.h>
 
@@ -79,6 +81,55 @@
 
 } PidginRequestData;
 
+static GtkWindow *
+find_toplevel(GList *ll_toplevels, const char *role)
+{
+	const char *window_role = NULL;
+	GList *ll_itr = NULL;
+
+	for (ll_itr = ll_toplevels ; ll_itr ; ll_itr = ll_itr->next) {
+		if ((window_role = gtk_window_get_role(GTK_WINDOW(ll_itr->data))) != NULL) {
+			if (!strcmp(window_role, role))
+				return GTK_WINDOW(ll_itr->data);
+		}
+	}
+
+	return NULL;
+}
+
+static GtkWindow *
+get_request_parent(const char *ui_hint, PidginConversation *convo)
+{
+	GtkWindow *toplevel = NULL;
+	PidginBuddyList *blist = NULL;
+
+	if (convo)
+		return GTK_WINDOW(convo->win->window);
+
+	if (strcmp(ui_hint, PURPLE_REQUEST_UI_HINT_BLIST)) {
+		GList *ll_toplevels = NULL;
+
+		ll_toplevels = gtk_window_list_toplevels();
+
+		if (!(toplevel = find_toplevel(ll_toplevels, ui_hint))) {
+			if (!strcmp(ui_hint, PURPLE_REQUEST_UI_HINT_REGISTER))
+				toplevel = find_toplevel(ll_toplevels, "account");
+			else
+			if (!strcmp(ui_hint, PURPLE_REQUEST_UI_HINT_XFER))
+				toplevel = find_toplevel(ll_toplevels, "file transfer");
+		}
+		
+		g_list_free(ll_toplevels);
+	}
+
+	/* Takes care of "pidgin-statusbox" as well */
+	if (!toplevel)
+		if ((blist = pidgin_blist_get_default_gtk_blist()) != NULL)
+			return GTK_WINDOW(blist->window);
+
+	return toplevel;
+}
+
 static void
 generic_response_start(PidginRequestData *data)
 {
@@ -287,7 +338,7 @@
 					   const char *ok_text, GCallback ok_cb,
 					   const char *cancel_text, GCallback cancel_cb,
 					   PurpleAccount *account, const char *who, PurpleConversation *conv,
-					   void *user_data)
+					   const char *ui_hint, void *user_data)
 {
 	PidginRequestData *data;
 	GtkWidget *dialog;
@@ -312,7 +363,7 @@
 
 	/* Create the dialog. */
 	dialog = gtk_dialog_new_with_buttons(title ? title : PIDGIN_ALERT_TITLE,
-					     NULL, 0,
+					     get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL), 0,
 					     text_to_stock(cancel_text), 1,
 					     text_to_stock(ok_text),     0,
 					     NULL);
@@ -450,7 +501,7 @@
 			const char *ok_text, GCallback ok_cb,
 			const char *cancel_text, GCallback cancel_cb,
 			PurpleAccount *account, const char *who, PurpleConversation *conv,
-			void *user_data, va_list args)
+			const char *ui_hint, void *user_data, va_list args)
 {
 	PidginRequestData *data;
 	GtkWidget *dialog;
@@ -474,6 +525,8 @@
 
 	/* Create the dialog. */
 	data->dialog = dialog = gtk_dialog_new();
+	gtk_window_set_transient_for(GTK_WINDOW(dialog),
+		get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL));
 
 	if (title != NULL)
 		gtk_window_set_title(GTK_WINDOW(dialog), title);
@@ -554,7 +607,7 @@
 pidgin_request_action(const char *title, const char *primary,
 						const char *secondary, int default_action,
 					    PurpleAccount *account, const char *who, PurpleConversation *conv,
-						void *user_data, size_t action_count, va_list actions)
+						const char *ui_hint, void *user_data, size_t action_count, va_list actions)
 {
 	PidginRequestData *data;
 	GtkWidget *dialog;
@@ -584,6 +637,8 @@
 
 	/* Create the dialog. */
 	data->dialog = dialog = gtk_dialog_new();
+	gtk_window_set_transient_for(GTK_WINDOW(dialog),
+		get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL));
 
 #if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE);
@@ -952,6 +1007,92 @@
 	return widget;
 }
 
+static GtkWidget *
+create_blist_field(PurpleRequestField *field)
+{
+	GtkTreeStore *model;
+	GtkWidget *tree, *sw;
+	PurpleBlistNode *node;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *column;
+	GtkTreeIter parent = {0, NULL, NULL, NULL}, iter;
+	PurpleRequestBlistFlags flags = field->u.blist.flags;
+	gboolean offline = !!(field->u.blist.flags & PURPLE_REQUEST_BLIST_FLAG_ALLOW_OFFLINE);
+
+	/* Create the treeview. Populate the blistnodes.
+	 * Hook to signed-on, signed-off signals to update the list when account goes online/offline.
+	 * Hook to buddy-signed-on/off, -status-changed signals to update the status pixbuf.
+	 */
+
+	model = gtk_tree_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF);
+	node = purple_blist_get_root();
+	while (node) {
+		GdkPixbuf *status = NULL, *prpl = NULL;
+		const char *name = NULL;
+		if ((PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node))
+				&& (flags & PURPLE_REQUEST_BLIST_FLAG_BUDDY)) {
+			PurpleBuddy *buddy; 
+			GtkTreeIter *p = NULL;
+			if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+				buddy = (PurpleBuddy*)node;
+				p = &parent;
+			} else {
+				buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
+				parent = iter;
+			}
+			if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+				gtk_tree_store_append(model, &iter, p);
+				name = purple_buddy_get_name(buddy);
+				status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL);
+			}
+		} else if (PURPLE_BLIST_NODE_IS_CHAT(node) && (flags & PURPLE_REQUEST_BLIST_FLAG_CHAT)) {
+			gtk_tree_store_append(model, &iter, NULL);
+			name = purple_chat_get_name((PurpleChat*)node);
+			status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL);
+		}
+		if (name)
+			gtk_tree_store_set(model, &iter,
+					0, status,
+					1, name,
+					2, prpl,
+					-1);
+		if (prpl)
+			gdk_pixbuf_unref(prpl);
+		if (status)
+			gdk_pixbuf_unref(status);
+		node = purple_blist_node_next(node, offline);
+	}
+
+	tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
+	gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), 1);
+	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree), pidgin_tree_view_search_equal_func, NULL, NULL);
+	gtk_widget_show(tree);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	rend = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", 0, NULL);
+
+	rend = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column, rend, TRUE);
+	gtk_tree_view_column_set_attributes(column, rend, "markup", 1, NULL);
+
+	rend = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", 2, NULL);
+
+	sw = gtk_scrolled_window_new(NULL,NULL);
+	gtk_widget_show(sw);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_NONE);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_container_add(GTK_CONTAINER(sw), tree);
+
+	return sw;
+}
+
 static void
 select_field_list_item(GtkTreeModel *model, GtkTreePath *path,
 					   GtkTreeIter *iter, gpointer data)
@@ -1051,7 +1192,7 @@
 						const char *ok_text, GCallback ok_cb,
 						const char *cancel_text, GCallback cancel_cb,
 					    PurpleAccount *account, const char *who, PurpleConversation *conv,
-						void *user_data)
+						const char *ui_hint, void *user_data)
 {
 	PidginRequestData *data;
 	GtkWidget *win;
@@ -1092,6 +1233,8 @@
 #else /* !_WIN32 */
 	data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #endif /* _WIN32 */
+	gtk_window_set_transient_for(GTK_WINDOW(win),
+		get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL));
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(destroy_multifield_cb), data);
@@ -1328,6 +1471,8 @@
 					widget = create_image_field(field);
 				else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 					widget = create_account_field(field);
+				else if (type == PURPLE_REQUEST_FIELD_BLIST)
+					widget = create_blist_field(field);
 				else
 					continue;
 
@@ -1504,10 +1649,10 @@
 
 	if ((data->u.file.savedialog == TRUE) &&
 		(g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
-		purple_request_action(data, NULL, _("That file already exists"),
+		purple_request_action_with_hint(data, NULL, _("That file already exists"),
 							_("Would you like to overwrite it?"), 0,
 							NULL, NULL, NULL,
-							data, 2,
+							"pidgin-request-file", data, 2,
 							_("Overwrite"), G_CALLBACK(file_yes_no_cb),
 							_("Choose New Name"), G_CALLBACK(file_yes_no_cb));
 	} else
@@ -1532,7 +1677,7 @@
 					  gboolean savedialog,
 					  GCallback ok_cb, GCallback cancel_cb,
 					  PurpleAccount *account, const char *who, PurpleConversation *conv,
-					  void *user_data)
+					  const char *ui_hint, void *user_data)
 {
 	PidginRequestData *data;
 	GtkWidget *filesel;
@@ -1620,6 +1765,9 @@
 	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
 #endif /* FILECHOOSER */
+	gtk_window_set_role(GTK_WINDOW(filesel), "pidgin-request-file");
+	gtk_window_set_transient_for(GTK_WINDOW(filesel),
+		get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL));
 
 	data->dialog = filesel;
 	gtk_widget_show(filesel);
@@ -1631,7 +1779,7 @@
 pidgin_request_folder(const char *title, const char *dirname,
 					  GCallback ok_cb, GCallback cancel_cb,
 					  PurpleAccount *account, const char *who, PurpleConversation *conv,
-					  void *user_data)
+					  const char *ui_hint, void *user_data)
 {
 	PidginRequestData *data;
 	GtkWidget *dirsel;
@@ -1670,6 +1818,9 @@
 	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dirsel)->ok_button), "clicked",
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
 #endif
+	gtk_window_set_role(GTK_WINDOW(dirsel), "pidgin-request-dir");
+	gtk_window_set_transient_for(GTK_WINDOW(dirsel),
+		get_request_parent(ui_hint, conv ? PIDGIN_CONVERSATION(conv) : NULL));
 
 	data->dialog = dirsel;
 	gtk_widget_show(dirsel);
--- a/pidgin/gtksavedstatuses.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Sun Oct 28 17:16:42 2007 +0000
@@ -63,7 +63,7 @@
 };
 
 /**
- * These is used for the GtkTreeView containing the list of accounts
+ * These are used for the GtkTreeView containing the list of accounts
  * at the bottom of the window when you're editing a particular
  * saved status.
  */
@@ -338,9 +338,9 @@
 		handle = dialog;
 	}
 
-	purple_request_action(handle, NULL, title, NULL, 0,
+	purple_request_action_with_hint(handle, NULL, title, NULL, 0,
 		 NULL, NULL, NULL,
-		 sel_titles, 2,
+		 PURPLE_REQUEST_UI_HINT_STATUSMGR, sel_titles, 2,
 		_("Delete"), status_window_delete_confirm_cb,
 		_("Cancel"), status_window_delete_cancel_cb);
 
@@ -898,6 +898,12 @@
 
 	for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
 	{
+		if (i == PURPLE_STATUS_MOBILE || i == PURPLE_STATUS_TUNE)
+			/*
+			 * Special-case these.  They're intended to be independent
+			 * status types, so don't show them in the list.
+			 */
+			continue;
 		item = gtk_menu_item_new_with_label(purple_primitive_get_name_from_type(i));
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 	}
@@ -1588,8 +1594,12 @@
 
 		status_type = list->data;
 
-		/* Only allow users to select statuses that are flagged as "user settable" */
-		if (!purple_status_type_is_user_settable(status_type))
+		/*
+		 * Only allow users to select statuses that are flagged as
+		 * "user settable" and that aren't independent.
+		 */
+		if (!purple_status_type_is_user_settable(status_type) ||
+				purple_status_type_is_independent(status_type))
 			continue;
 
 		id = purple_status_type_get_id(status_type);
--- a/pidgin/gtksound.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtksound.c	Sun Oct 28 17:16:42 2007 +0000
@@ -118,12 +118,9 @@
 	if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 	{
 		PidginConversation *gtkconv;
-		PidginWindow *win;
 		gboolean has_focus;
 
 		gtkconv = PIDGIN_CONVERSATION(conv);
-		win = gtkconv->win;
-
 		has_focus = purple_conversation_has_focus(conv);
 
 		if (!gtkconv->make_sound ||
--- a/pidgin/gtkstatusbox.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Sun Oct 28 17:16:42 2007 +0000
@@ -276,7 +276,7 @@
 			return FALSE;
 		}
 
-		box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(NULL, icon_choose_cb, box);
+		box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), icon_choose_cb, box);
 		gtk_widget_show_all(box->buddy_icon_sel);
 	}
 	return FALSE;
@@ -1586,9 +1586,9 @@
 
 	msg = g_strdup_printf(_("Are you sure you want to delete %s?"), purple_savedstatus_get_title(saved));
 
-	purple_request_action(saved, NULL, msg, NULL, 0,
+	purple_request_action_with_hint(saved, NULL, msg, NULL, 0,
 		NULL, NULL, NULL,
-		data, 2,
+		"pidgin-statusbox", data, 2,
 		_("Delete"), tree_view_delete_current_selection_cb,
 		_("Cancel"), NULL);
 
--- a/pidgin/gtkutils.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/gtkutils.c	Sun Oct 28 17:16:42 2007 +0000
@@ -1526,28 +1526,30 @@
 
 			if (prpl_info && prpl_info->can_receive_file)
 				ft = prpl_info->can_receive_file(gc, who);
+			else if (prpl_info && prpl_info->send_file)
+				ft = TRUE;
 
 			if (im && ft)
-				purple_request_choice(NULL, NULL,
+				purple_request_choice_with_hint(NULL, NULL,
 						    _("You have dragged an image"),
 						    _("You can send this image as a file transfer, "
 						      "embed it into this message, or use it as the buddy icon for this user."),
 						    DND_FILE_TRANSFER, "OK", (GCallback)dnd_image_ok_callback,
 						    "Cancel", (GCallback)dnd_image_cancel_callback,
 							account, who, NULL,
-							data,
+							PURPLE_REQUEST_UI_HINT_CONV, data,
 							_("Set as buddy icon"), DND_BUDDY_ICON,
 						    _("Send image file"), DND_FILE_TRANSFER,
 						    _("Insert in message"), DND_IM_IMAGE,
 							NULL);
 			else if (!(im || ft))
-				purple_request_yes_no(NULL, NULL, _("You have dragged an image"),
+				purple_request_yes_no_with_hint(NULL, NULL, _("You have dragged an image"),
 							_("Would you like to set it as the buddy icon for this user?"),
 							0,
 							account, who, NULL,
-							data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb);
+							PURPLE_REQUEST_UI_HINT_CONV, data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb);
 			else
-				purple_request_choice(NULL, NULL,
+				purple_request_choice_with_hint(NULL, NULL,
 						    _("You have dragged an image"),
 						    (ft ? _("You can send this image as a file transfer, or use it as the buddy icon for this user.") :
 						    _("You can insert this image into this message, or use it as the buddy icon for this user")),
@@ -1555,10 +1557,11 @@
 							"OK", (GCallback)dnd_image_ok_callback,
 						    "Cancel", (GCallback)dnd_image_cancel_callback,
 							account, who, NULL,
-							data,
+							PURPLE_REQUEST_UI_HINT_CONV, data,
 						    _("Set as buddy icon"), DND_BUDDY_ICON,
 						    (ft ? _("Send image file") : _("Insert in message")), (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
 							NULL);
+			gdk_pixbuf_unref(pb);
 			return;
 		}
 
--- a/pidgin/plugins/history.c	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/plugins/history.c	Sun Oct 28 17:16:42 2007 +0000
@@ -42,6 +42,7 @@
 	GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
 	char *header;
 	char *protocol;
+	char *escaped_alias;
 
 	convtype = purple_conversation_get_type(c);
 	gtkconv = PIDGIN_CONVERSATION(c);
@@ -120,10 +121,12 @@
 	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
 
-	header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias,
+	escaped_alias = g_markup_escape_text(alias, -1);
+	header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), escaped_alias,
 							 purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time)));
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
 	g_free(header);
+	g_free(escaped_alias);
 
 	g_strchomp(history);
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), history, options);
--- a/pidgin/plugins/perl/common/GtkDialogs.xs	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkDialogs.xs	Sun Oct 28 17:16:42 2007 +0000
@@ -10,6 +10,10 @@
 pidgin_dialogs_about()
 
 void
+pidgin_dialogs_about_with_parent(parent)
+	void * parent
+
+void
 pidgin_dialogs_im()
 
 void
--- a/pidgin/plugins/perl/common/GtkLog.xs	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkLog.xs	Sun Oct 28 17:16:42 2007 +0000
@@ -13,11 +13,27 @@
 	Purple::Account account
 
 void
+pidgin_log_show_with_parent(parent, type, screenname, account)
+	void * parent
+	Purple::LogType type
+	const char * screenname
+	Purple::Account account
+
+void
 pidgin_log_show_contact(contact)
 	Purple::BuddyList::Contact contact
 
+void
+pidgin_log_show_contact_with_parent(parent, contact)
+	void * parent
+	Purple::BuddyList::Contact contact
+
 MODULE = Pidgin::Log  PACKAGE = Pidgin::SysLog  PREFIX = pidgin_syslog_
 PROTOTYPES: ENABLE
 
 void
 pidgin_syslog_show()
+
+void
+pidgin_syslog_show_with_parent(parent)
+	void * parent
--- a/pidgin/plugins/perl/common/GtkPlugin.xs	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkPlugin.xs	Sun Oct 28 17:16:42 2007 +0000
@@ -11,3 +11,7 @@
 
 void
 pidgin_plugin_dialog_show()
+
+void
+pidgin_plugin_dialog_show_with_parent(parent)
+	void * parent
--- a/pidgin/plugins/perl/common/GtkPounce.xs	Sun Oct 28 17:08:49 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkPounce.xs	Sun Oct 28 17:16:42 2007 +0000
@@ -9,6 +9,13 @@
 	const char * name
 	Purple::Pounce cur_pounce
 
+void
+pidgin_pounce_editor_show_with_parent(parent, account, name, cur_pounce)
+	void * parent
+	Purple::Account account
+	const char * name
+	Purple::Pounce cur_pounce
+
 MODULE = Pidgin::Pounce  PACKAGE = Pidgin::Pounces  PREFIX = pidgin_pounces_
 PROTOTYPES: ENABLE
 
@@ -22,4 +29,8 @@
 pidgin_pounces_manager_show()
 
 void
+pidgin_pounces_manager_show_with_parent(parent)
+	void * parent
+
+void
 pidgin_pounces_manager_hide()