changeset 32711:dc44bb639e8e

propagate from branch 'im.pidgin.pidgin' (head 069350831810b6f4bd534a26fed03260a9b4adcd) to branch 'im.pidgin.cpw.qulogic.gtk3' (head cf49f6cfd58e227b28dc1822e5ab543bbabd80b7)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 04 Jan 2012 02:37:25 +0000
parents 8436fb68930e (diff) a677e7b1497f (current diff)
children 7df79e0d1150
files pidgin/gtkblist.h pidgin/gtkconv.c pidgin/gtkconv.h pidgin/gtkconvwin.h pidgin/gtkimhtmltoolbar.h
diffstat 97 files changed, 1028 insertions(+), 860 deletions(-) [+]
line wrap: on
line diff
--- a/finch/finch.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/finch/finch.c	Wed Jan 04 02:37:25 2012 +0000
@@ -339,7 +339,17 @@
 
 	/* set a user-specified config directory */
 	if (opt_config_dir_arg != NULL) {
-		purple_util_set_user_dir(opt_config_dir_arg);
+		if (g_path_is_absolute(opt_config_dir_arg)) {
+			purple_util_set_user_dir(opt_config_dir_arg);
+		} else {
+			/* Make an absolute (if not canonical) path */
+			char *cwd = g_get_current_dir();
+			char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
+			purple_util_set_user_dir(path);
+			g_free(path);
+			g_free(cwd);
+		}
+
 		g_free(opt_config_dir_arg);
 	}
 
--- a/libpurple/account.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/account.h	Wed Jan 04 02:37:25 2012 +0000
@@ -171,9 +171,7 @@
 	PurpleConnectionErrorInfo *current_error;	/**< Errors */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Account API                                                     */
@@ -1191,8 +1189,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_ACCOUNT_H_ */
--- a/libpurple/accountopt.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/accountopt.h	Wed Jan 04 02:37:25 2012 +0000
@@ -37,10 +37,7 @@
 /** @copydoc _PurpleAccountUserSplit */
 typedef struct _PurpleAccountUserSplit	PurpleAccountUserSplit;
 
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Account Option API                                              */
@@ -353,8 +350,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_ACCOUNTOPT_H_ */
--- a/libpurple/blist.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/blist.h	Wed Jan 04 02:37:25 2012 +0000
@@ -244,9 +244,7 @@
 	void (*_purple_reserved1)(void);
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy List API                                                  */
@@ -1202,8 +1200,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_BLIST_H_ */
--- a/libpurple/buddyicon.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/buddyicon.h	Wed Jan 04 02:37:25 2012 +0000
@@ -39,10 +39,7 @@
 #include "prpl.h"
 #include "util.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy Icon API                                                  */
@@ -397,8 +394,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_BUDDYICON_H_ */
--- a/libpurple/certificate.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/certificate.h	Wed Jan 04 02:37:25 2012 +0000
@@ -34,11 +34,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
 typedef enum
 {
 	PURPLE_CERTIFICATE_INVALID = 0,
@@ -357,6 +352,8 @@
 	gpointer cb_data;
 };
 
+G_BEGIN_DECLS
+
 /*****************************************************************************/
 /** @name Certificate Verification Functions                                 */
 /*****************************************************************************/
@@ -833,8 +830,6 @@
  */
 void purple_certificate_add_ca_search_path(const char *path);
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* _PURPLE_CERTIFICATE_H */
--- a/libpurple/circbuffer.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/circbuffer.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,10 +26,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct _PurpleCircBuffer {
 
 	/** A pointer to the starting address of our chunk of memory. */
@@ -55,6 +51,8 @@
 
 } PurpleCircBuffer;
 
+G_BEGIN_DECLS
+
 /**
  * Creates a new circular buffer.  This will not allocate any memory for the
  * actual buffer until data is appended to it.
@@ -111,8 +109,6 @@
  */
 gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _CIRCBUFFER_H */
--- a/libpurple/cmds.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/cmds.h	Wed Jan 04 02:37:25 2012 +0000
@@ -99,9 +99,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Commands API                                                    */
@@ -240,8 +238,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CMDS_H_ */
--- a/libpurple/connection.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/connection.h	Wed Jan 04 02:37:25 2012 +0000
@@ -258,9 +258,7 @@
 					  prpl to avoid sending unneeded keepalives */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Connection API                                                  */
@@ -543,8 +541,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CONNECTION_H_ */
--- a/libpurple/conversation.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/conversation.h	Wed Jan 04 02:37:25 2012 +0000
@@ -249,9 +249,7 @@
 	void (*_purple_reserved4)(void);
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Conversation API                                                */
@@ -1402,8 +1400,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CONVERSATION_H_ */
--- a/libpurple/core.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/core.h	Wed Jan 04 02:37:25 2012 +0000
@@ -76,9 +76,7 @@
 	void (*_purple_reserved3)(void);
 } PurpleCoreUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Initializes the core of purple.
@@ -197,9 +195,7 @@
  */
 GHashTable* purple_core_get_ui_info(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CORE_H_ */
 
--- a/libpurple/dbus-bindings.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/dbus-bindings.h	Wed Jan 04 02:37:25 2012 +0000
@@ -32,9 +32,7 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 gint purple_dbus_pointer_to_id(gconstpointer node);
 gpointer purple_dbus_id_to_pointer(gint id, PurpleDBusType *type);
@@ -103,8 +101,6 @@
 
 DBusConnection *purple_dbus_get_connection(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif
--- a/libpurple/dbus-useful.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/dbus-useful.h	Wed Jan 04 02:37:25 2012 +0000
@@ -1,5 +1,7 @@
 #include "conversation.h"
 
+G_BEGIN_DECLS
+
 PurpleAccount *purple_accounts_find_ext(const char *name, const char *protocol_id,
 				    gboolean (*account_test)(const PurpleAccount *account));
 
@@ -7,7 +9,5 @@
 
 PurpleAccount *purple_accounts_find_connected(const char *name, const char *protocol);
 
-
+G_END_DECLS
 
-
-
--- a/libpurple/debug.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/debug.h	Wed Jan 04 02:37:25 2012 +0000
@@ -59,9 +59,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleDebugUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Debug API                                                       */
@@ -228,8 +226,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_DEBUG_H_ */
--- a/libpurple/dnsquery.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/dnsquery.h	Wed Jan 04 02:37:25 2012 +0000
@@ -76,9 +76,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleDnsQueryUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name DNS query API                                                   */
@@ -154,8 +152,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_DNSQUERY_H_ */
--- a/libpurple/dnssrv.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/dnssrv.h	Wed Jan 04 02:37:25 2012 +0000
@@ -24,10 +24,6 @@
 #ifndef _PURPLE_DNSSRV_H
 #define _PURPLE_DNSSRV_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct _PurpleSrvTxtQueryData PurpleSrvTxtQueryData;
 typedef struct _PurpleSrvResponse PurpleSrvResponse;
 typedef struct _PurpleTxtResponse PurpleTxtResponse;
@@ -96,6 +92,8 @@
  */
 typedef void (*PurpleTxtCallback)(GList *responses, gpointer data);
 
+G_BEGIN_DECLS
+
 /**
  * Queries an SRV record.
  *
@@ -184,9 +182,7 @@
  */
 int purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_DNSSRV_H */
 
--- a/libpurple/eventloop.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/eventloop.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,10 +28,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * An input condition.
  */
@@ -154,6 +150,8 @@
 	void (*_purple_reserved4)(void);
 };
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name Event Loop API                                                  */
 /**************************************************************************/
@@ -266,8 +264,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_EVENTLOOP_H_ */
--- a/libpurple/ft.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/ft.h	Wed Jan 04 02:37:25 2012 +0000
@@ -184,9 +184,7 @@
 	void *proto_data;                 /**< prpl-specific data.     */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name File Transfer API                                               */
@@ -843,9 +841,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_FT_H_ */
 
--- a/libpurple/idle.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/idle.h	Wed Jan 04 02:37:25 2012 +0000
@@ -41,9 +41,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleIdleUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Idle API                                                        */
@@ -97,8 +95,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_IDLE_H_ */
--- a/libpurple/imgstore.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/imgstore.h	Wed Jan 04 02:37:25 2012 +0000
@@ -34,9 +34,7 @@
  */
 typedef struct _PurpleStoredImage PurpleStoredImage;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Add an image to the store.
@@ -206,8 +204,6 @@
  */
 void purple_imgstore_uninit(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_IMGSTORE_H_ */
--- a/libpurple/log.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/log.h	Wed Jan 04 02:37:25 2012 +0000
@@ -181,9 +181,7 @@
 	 * IMPORTANT: Update that code if you add members here. */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /***************************************/
 /** @name Log Functions                */
@@ -574,8 +572,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_LOG_H_ */
--- a/libpurple/media-gst.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/media-gst.h	Wed Jan 04 02:37:25 2012 +0000
@@ -32,8 +32,6 @@
 
 #include <gst/gst.h>
 
-G_BEGIN_DECLS
-
 #define PURPLE_TYPE_MEDIA_ELEMENT_TYPE           (purple_media_element_type_get_type())
 #define PURPLE_TYPE_MEDIA_ELEMENT_INFO           (purple_media_element_info_get_type())
 #define PURPLE_MEDIA_ELEMENT_INFO(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfo))
@@ -73,9 +71,7 @@
 	PURPLE_MEDIA_ELEMENT_SINK = 1 << 10,		/** can be set as an active sink */
 } PurpleMediaElementType;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Gets the element type's GType.
@@ -176,10 +172,6 @@
 		PurpleMediaElementInfo *info, PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_GST_H_ */
--- a/libpurple/media.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/media.h	Wed Jan 04 02:37:25 2012 +0000
@@ -27,15 +27,13 @@
 #ifndef _PURPLE_MEDIA_H_
 #define _PURPLE_MEDIA_H_
 
+#include <glib.h>
+#include <glib-object.h>
+
 #include "media/candidate.h"
 #include "media/codec.h"
 #include "media/enum-types.h"
 
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
 #define PURPLE_TYPE_MEDIA            (purple_media_get_type())
 #define PURPLE_MEDIA(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia))
 #define PURPLE_MEDIA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA, PurpleMediaClass))
@@ -49,9 +47,7 @@
 #include "signals.h"
 #include "util.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Gets the media class's GType
@@ -379,10 +375,6 @@
  */
 void purple_media_remove_output_windows(PurpleMedia *media);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_H_ */
--- a/libpurple/mediamanager.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/mediamanager.h	Wed Jan 04 02:37:25 2012 +0000
@@ -38,8 +38,6 @@
 #include "account.h"
 #include "media.h"
 
-G_BEGIN_DECLS
-
 #define PURPLE_TYPE_MEDIA_MANAGER            (purple_media_manager_get_type())
 #define PURPLE_MEDIA_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManager))
 #define PURPLE_MEDIA_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
@@ -47,9 +45,7 @@
 #define PURPLE_IS_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_MANAGER))
 #define PURPLE_MEDIA_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Media Manager API                                              */
@@ -209,10 +205,6 @@
 
 /*}@*/
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_MANAGER_H_ */
--- a/libpurple/mime.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/mime.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,10 +26,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * @file mime.h
  * @ingroup core
@@ -48,6 +44,8 @@
  */
 typedef struct _PurpleMimePart PurpleMimePart;
 
+G_BEGIN_DECLS
+
 /**
  * Allocate an empty MIME document.
  */
@@ -211,8 +209,6 @@
 
 void purple_mime_part_set_data(PurpleMimePart *part, const char *data);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif
--- a/libpurple/nat-pmp.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/nat-pmp.h	Wed Jan 04 02:37:25 2012 +0000
@@ -41,6 +41,8 @@
 	PURPLE_PMP_TYPE_TCP
 } PurplePmpType;
 
+G_BEGIN_DECLS
+
 /**
  * Initialize nat-pmp
  */
@@ -54,24 +56,28 @@
 /**
  * Remove the NAT-PMP mapping for a specified type on a specified port
  *
- * @param type The PurplePmpType
+ * @param type        The PurplePmpType
  * @param privateport The private port on which we are listening locally
- * @param publicport The public port on which we are expecting a response
- * @param lifetime The lifetime of the mapping. It is recommended that this be PURPLE_PMP_LIFETIME.
+ * @param publicport  The public port on which we are expecting a response
+ * @param lifetime    The lifetime of the mapping. It is recommended that this
+ *                    be PURPLE_PMP_LIFETIME.
  *
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * @returns TRUE if successful; FALSE if unsuccessful
  */
-gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport, unsigned short publicport, int lifetime);
+gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport,
+                               unsigned short publicport, int lifetime);
 
 /**
  * Remove the NAT-PMP mapping for a specified type on a specified port
  *
- * @param type The PurplePmpType
+ * @param type        The PurplePmpType
  * @param privateport The private port on which the mapping was previously made
  *
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * @returns TRUE if successful; FALSE if unsuccessful
  */
 gboolean purple_pmp_destroy_map(PurplePmpType type, unsigned short privateport);
 
+G_END_DECLS
+
 #endif
 
--- a/libpurple/network.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/network.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,9 +26,9 @@
 #ifndef _PURPLE_NETWORK_H_
 #define _PURPLE_NETWORK_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <glib.h>
+
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Network API                                                     */
@@ -316,8 +316,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NETWORK_H_ */
--- a/libpurple/notify.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/notify.h	Wed Jan 04 02:37:25 2012 +0000
@@ -175,9 +175,7 @@
 } PurpleNotifyUiOps;
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 
 /**************************************************************************/
@@ -734,8 +732,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NOTIFY_H_ */
--- a/libpurple/ntlm.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/ntlm.h	Wed Jan 04 02:37:25 2012 +0000
@@ -27,9 +27,7 @@
 #ifndef _PURPLE_NTLM_H
 #define _PURPLE_NTLM_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Generates the base64 encoded type 1 message needed for NTLM authentication
@@ -66,8 +64,6 @@
  */
 gchar *purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NTLM_H */
--- a/libpurple/plugin.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/plugin.h	Wed Jan 04 02:37:25 2012 +0000
@@ -239,9 +239,7 @@
 #endif
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Plugin API                                                      */
@@ -663,8 +661,6 @@
  */
 void purple_plugin_action_free(PurplePluginAction *action);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PLUGIN_H_ */
--- a/libpurple/pluginpref.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/pluginpref.h	Wed Jan 04 02:37:25 2012 +0000
@@ -50,9 +50,7 @@
 #include <glib.h>
 #include "prefs.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Plugin Preference API                                           */
@@ -262,8 +260,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PLUGINPREF_H_ */
--- a/libpurple/pounce.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/pounce.h	Wed Jan 04 02:37:25 2012 +0000
@@ -59,9 +59,7 @@
 /** A pounce callback. */
 typedef void (*PurplePounceCb)(PurplePounce *, PurplePounceEvent, void *);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy Pounce API                                                */
@@ -354,8 +352,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_POUNCE_H_ */
--- a/libpurple/prefs.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/prefs.h	Wed Jan 04 02:37:25 2012 +0000
@@ -62,9 +62,7 @@
 typedef void (*PurplePrefCallback) (const char *name, PurplePrefType type,
 		gconstpointer val, gpointer data);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Prefs API
@@ -362,8 +360,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PREFS_H_ */
--- a/libpurple/privacy.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/privacy.h	Wed Jan 04 02:37:25 2012 +0000
@@ -40,10 +40,6 @@
 
 #include "account.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Privacy core/UI operations.
  */
@@ -60,6 +56,8 @@
 	void (*_purple_reserved4)(void);
 } PurplePrivacyUiOps;
 
+G_BEGIN_DECLS
+
 /**
  * Adds a user to the account's permit list.
  *
@@ -187,8 +185,6 @@
  */
 void purple_privacy_init(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PRIVACY_H_ */
--- a/libpurple/protocols/jabber/auth_cyrus.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/protocols/jabber/auth_cyrus.c	Wed Jan 04 02:37:25 2012 +0000
@@ -167,7 +167,6 @@
 auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
-	JabberStream *js;
 
 	/* The password prompt dialog doesn't get disposed if the account disconnects */
 	if (!PURPLE_CONNECTION_IS_VALID(gc))
@@ -361,30 +360,30 @@
 
 	id = 0;
 	js->sasl_cb[id].id = SASL_CB_GETREALM;
-	js->sasl_cb[id].proc = jabber_sasl_cb_realm;
+	js->sasl_cb[id].proc = (void *)jabber_sasl_cb_realm;
 	js->sasl_cb[id].context = (void *)js;
 	id++;
 
 	js->sasl_cb[id].id = SASL_CB_AUTHNAME;
-	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+	js->sasl_cb[id].proc = (void *)jabber_sasl_cb_simple;
 	js->sasl_cb[id].context = (void *)js;
 	id++;
 
 	js->sasl_cb[id].id = SASL_CB_USER;
-	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+	js->sasl_cb[id].proc = (void *)jabber_sasl_cb_simple;
 	js->sasl_cb[id].context = (void *)js;
 	id++;
 
 	account = purple_connection_get_account(js->gc);
 	if (purple_account_get_password(account) != NULL ) {
 		js->sasl_cb[id].id = SASL_CB_PASS;
-		js->sasl_cb[id].proc = jabber_sasl_cb_secret;
+		js->sasl_cb[id].proc = (void *)jabber_sasl_cb_secret;
 		js->sasl_cb[id].context = (void *)js;
 		id++;
 	}
 
 	js->sasl_cb[id].id = SASL_CB_LOG;
-	js->sasl_cb[id].proc = jabber_sasl_cb_log;
+	js->sasl_cb[id].proc = (void *)jabber_sasl_cb_log;
 	js->sasl_cb[id].context = (void*)js;
 	id++;
 
--- a/libpurple/proxy.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/proxy.h	Wed Jan 04 02:37:25 2012 +0000
@@ -56,9 +56,7 @@
 
 #include "account.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Proxy structure API                                             */
@@ -326,8 +324,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PROXY_H_ */
--- a/libpurple/prpl.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/prpl.h	Wed Jan 04 02:37:25 2012 +0000
@@ -621,9 +621,7 @@
 #define PURPLE_PLUGIN_PROTOCOL_INFO(plugin) \
 	((PurplePluginProtocolInfo *)(plugin)->info->extra_info)
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Attention Type API                                              */
@@ -951,8 +949,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PRPL_H_ */
--- a/libpurple/request.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/request.h	Wed Jan 04 02:37:25 2012 +0000
@@ -158,9 +158,7 @@
 typedef void (*PurpleRequestFieldsCb)(void *, PurpleRequestFields *fields);
 typedef void (*PurpleRequestFileCb)(void *, const char *filename);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Field List API                                                  */
@@ -1527,8 +1525,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_REQUEST_H_ */
--- a/libpurple/roomlist.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/roomlist.h	Wed Jan 04 02:37:25 2012 +0000
@@ -81,9 +81,7 @@
 };
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Room List API                                                   */
@@ -412,8 +410,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_ROOMLIST_H_ */
--- a/libpurple/savedstatuses.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/savedstatuses.h	Wed Jan 04 02:37:25 2012 +0000
@@ -60,9 +60,7 @@
 
 #include "status.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Saved status subsystem                                          */
@@ -418,8 +416,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SAVEDSTATUSES_H_ */
+
--- a/libpurple/server.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/server.h	Wed Jan 04 02:37:25 2012 +0000
@@ -30,9 +30,7 @@
 #include "conversation.h"
 #include "prpl.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Send a typing message to a given user over a given connection.
@@ -185,8 +183,7 @@
 					  PurpleMessageFlags flags, const char *message, time_t mtime);
 void serv_send_file(PurpleConnection *gc, const char *who, const char *file);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SERVER_H_ */
+
--- a/libpurple/signals.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/signals.h	Wed Jan 04 02:37:25 2012 +0000
@@ -35,9 +35,7 @@
 typedef void (*PurpleSignalMarshalFunc)(PurpleCallback cb, va_list args,
 									  void *data, void **return_val);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Signal API                                                      */
@@ -373,8 +371,7 @@
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SIGNALS_H_ */
+
--- a/libpurple/smiley.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/smiley.h	Wed Jan 04 02:37:25 2012 +0000
@@ -50,9 +50,7 @@
 #define PURPLE_IS_SMILEY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SMILEY))
 #define PURPLE_SMILEY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Custom Smiley API                                               */
@@ -253,9 +251,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SMILEY_H_ */
 
--- a/libpurple/sound.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/sound.h	Wed Jan 04 02:37:25 2012 +0000
@@ -72,9 +72,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleSoundUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Sound API                                                       */
@@ -136,8 +134,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SOUND_H_ */
--- a/libpurple/sslconn.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/sslconn.h	Wed Jan 04 02:37:25 2012 +0000
@@ -140,9 +140,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleSslOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name SSL API                                                         */
@@ -311,8 +309,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_SSLCONN_H_ */
--- a/libpurple/status.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/status.h	Wed Jan 04 02:37:25 2012 +0000
@@ -148,9 +148,7 @@
 #define PURPLE_MOOD_NAME	"mood"
 #define PURPLE_MOOD_COMMENT	"moodtext"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name PurpleStatusPrimitive API                                       */
@@ -945,8 +943,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_STATUS_H_ */
+
--- a/libpurple/stringref.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/stringref.h	Wed Jan 04 02:37:25 2012 +0000
@@ -29,11 +29,9 @@
 #ifndef _PURPLE_STRINGREF_H_
 #define _PURPLE_STRINGREF_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+typedef struct _PurpleStringref PurpleStringref;
 
-typedef struct _PurpleStringref PurpleStringref;
+G_BEGIN_DECLS
 
 /**
  * Creates an immutable reference-counted string object.  The newly
@@ -131,8 +129,6 @@
  */
 size_t purple_stringref_len(const PurpleStringref *stringref);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_STRINGREF_H_ */
--- a/libpurple/stun.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/stun.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,10 +26,6 @@
 #ifndef _PURPLE_STUN_H_
 #define _PURPLE_STUN_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**************************************************************************/
 /** @name STUN API                                                        */
 /**************************************************************************/
@@ -63,6 +59,8 @@
 
 typedef void (*StunCallback) (PurpleStunNatDiscovery *);
 
+G_BEGIN_DECLS
+
 /**
  * Starts a NAT discovery. It returns a PurpleStunNatDiscovery if the discovery
  * is already done. Otherwise the callback is called when the discovery is over
@@ -81,8 +79,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_STUN_H_ */
--- a/libpurple/upnp.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/upnp.h	Wed Jan 04 02:37:25 2012 +0000
@@ -29,9 +29,7 @@
 
 typedef struct _UPnPMappingAddRemove UPnPMappingAddRemove;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name UPnP API                                                        */
@@ -125,8 +123,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_UPNP_H_ */
--- a/libpurple/util.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/util.h	Wed Jan 04 02:37:25 2012 +0000
@@ -58,10 +58,6 @@
 #include "notify.h"
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef char *(*PurpleInfoFieldFormatCallback)(const char *field, size_t len);
 
 struct _PurpleKeyValuePair
@@ -71,6 +67,8 @@
 
 };
 
+G_BEGIN_DECLS
+
 /**
  * Creates a new PurpleMenuAction.
  *
@@ -1459,8 +1457,6 @@
  */
 gchar *purple_uuid_random(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_UTIL_H_ */
--- a/libpurple/value.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/value.h	Wed Jan 04 02:37:25 2012 +0000
@@ -88,9 +88,7 @@
  */
 typedef struct _PurpleValue PurpleValue;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Creates a new PurpleValue.
@@ -464,8 +462,6 @@
  */
 void *purple_value_get_boxed(const PurpleValue *value);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_VALUE_H_ */
--- a/libpurple/whiteboard.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/whiteboard.h	Wed Jan 04 02:37:25 2012 +0000
@@ -79,9 +79,7 @@
 	void (*_purple_reserved4)(void);
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
 
 /******************************************************************************/
 /** @name PurpleWhiteboard API                                                  */
@@ -330,8 +328,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* _PURPLE_WHITEBOARD_H_ */
--- a/libpurple/xmlnode.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/libpurple/xmlnode.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,10 +28,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * The valid types for an xmlnode
  */
@@ -61,6 +57,8 @@
 	GHashTable *namespace_map;  /**< The namespace map. */
 };
 
+G_BEGIN_DECLS
+
 /**
  * Creates a new xmlnode.
  *
@@ -360,9 +358,7 @@
 xmlnode *xmlnode_from_file(const char *dir, const char *filename,
 			   const char *description, const char *process);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_XMLNODE_H_ */
 
--- a/pidgin/gtkaccount.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkaccount.h	Wed Jan 04 02:37:25 2012 +0000
@@ -37,6 +37,8 @@
 } PidginAccountDialogType;
 
 
+G_BEGIN_DECLS
+
 /**
  * Shows the accounts window.
  */
@@ -80,4 +82,6 @@
  */
 void pidgin_account_uninit(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINACCOUNT_H_ */
--- a/pidgin/gtkblist-theme.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkblist-theme.h	Wed Jan 04 02:37:25 2012 +0000
@@ -79,6 +79,8 @@
 
 } PidginBlistLayout;
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name PidginThemeFont API                                               */
 /**************************************************************************/
@@ -146,7 +148,6 @@
 /**************************************************************************/
 /** @name Purple Buddy List Theme API                                     */
 /**************************************************************************/
-G_BEGIN_DECLS
 
 /**
  * GObject foo.
@@ -182,7 +183,7 @@
  *
  * @returns The buddy list layout.
  */
- PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme);
+PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme);
 
 /**
  * Returns the background color to be used with expanded groups.
@@ -191,7 +192,7 @@
  *
  * @returns A gdk color.
  */
- GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used with expanded groups.
@@ -200,7 +201,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the background color to be used with collapsed groups.
@@ -209,7 +210,7 @@
  *
  * @returns A gdk color.
  */
- GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used with collapsed groups.
@@ -218,7 +219,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the colors to be used for contacts and chats.
@@ -227,7 +228,7 @@
  *
  * @returns A gdkcolor for contacts and chats.
  */
- GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for expanded contacts.
@@ -236,7 +237,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for online buddies.
@@ -245,7 +246,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for away and idle buddies.
@@ -254,7 +255,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for offline buddies.
@@ -263,7 +264,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for idle buddies.
@@ -272,7 +273,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for buddies with unread messages.
@@ -281,7 +282,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for chats with unread messages
@@ -291,7 +292,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme);
 
 /**
  * Returns the text font and color to be used for a buddy's status message.
@@ -300,7 +301,7 @@
  *
  * @returns A font and color pair.
  */
- PidginThemeFont *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme);
 
 /* Set Methods */
 
@@ -434,4 +435,5 @@
 void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
 
 G_END_DECLS
+
 #endif /* PIDGIN_BLIST_THEME_H */
--- a/pidgin/gtkblist.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkblist.h	Wed Jan 04 02:37:25 2012 +0000
@@ -127,6 +127,8 @@
 #define PIDGIN_IS_PIDGIN_BLIST(list) \
 	(purple_blist_get_ui_ops() == pidgin_blist_get_ui_ops())
 
+G_BEGIN_DECLS
+
 /**************************************************************************
  * @name GTK+ Buddy List API
  **************************************************************************/
@@ -397,5 +399,6 @@
  */
 void pidgin_blist_tooltip_destroy(void);
 
+G_END_DECLS
 
 #endif /* _PIDGINBLIST_H_ */
--- a/pidgin/gtkcellrendererexpander.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkcellrendererexpander.h	Wed Jan 04 02:37:25 2012 +0000
@@ -23,11 +23,6 @@
 
 #include <gtk/gtk.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
 #define PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER         (pidgin_cell_renderer_expander_get_type())
 #define PIDGIN_CELL_RENDERER_EXPANDER(obj)         (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER, PidginCellRendererExpander))
 #define PIDGIN_CELL_RENDERER_EXPANDER_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_GTK_CELL_RENDERER_EXPANDER, PidginCellRendererExpanderClass))
@@ -48,12 +43,11 @@
 	GtkCellRendererClass parent_class;
 };
 
+G_BEGIN_DECLS
+
 GType            pidgin_cell_renderer_expander_get_type     (void);
 GtkCellRenderer  *pidgin_cell_renderer_expander_new          (void);
 
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* _PIDGINCELLRENDEREREXPANDER_H_ */
--- a/pidgin/gtkcertmgr.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkcertmgr.h	Wed Jan 04 02:37:25 2012 +0000
@@ -43,6 +43,8 @@
 	gchar *label;
 };
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name Certificate Manager API                                         */
 /**************************************************************************/
@@ -59,4 +61,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGINCERTMGR_H_ */
--- a/pidgin/gtkconn.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkconn.h	Wed Jan 04 02:37:25 2012 +0000
@@ -25,6 +25,8 @@
 #ifndef _PIDGINCONN_H_
 #define _PIDGINCONN_H_
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name GTK+ Connection API                                             */
 /**************************************************************************/
@@ -56,4 +58,6 @@
  */
 void pidgin_connection_uninit(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINCONN_H_ */
--- a/pidgin/gtkconv-theme.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkconv-theme.c	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,7 @@
 #include "xmlnode.h"
 
 #include "pidgin.h"
+#include "internal.h"
 #include "gtkconv.h"
 #include "gtkwebview.h"
 
--- a/pidgin/gtkconv.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkconv.c	Wed Jan 04 02:37:25 2012 +0000
@@ -196,6 +196,7 @@
 static void generate_send_to_items(PidginWindow *win);
 
 /* Prototypes. <-- because Paco-Paco hates this comment. */
+static void load_conv_theme(PidginConversation *gtkconv);
 static gboolean infopane_entry_activate(PidginConversation *gtkconv);
 static void got_typing_keypress(PidginConversation *gtkconv, gboolean first);
 static void gray_stuff_out(PidginConversation *gtkconv);
@@ -448,12 +449,12 @@
 static void clear_conversation_scrollback_cb(PurpleConversation *conv,
                                              void *data)
 {
-	PidginConversation *gtkconv = NULL;
-
-	gtkconv = PIDGIN_CONVERSATION(conv);
-
-	if (PIDGIN_CONVERSATION(conv))
-		webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(gtkconv->webview), "", "");
+	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+
+	if (PIDGIN_CONVERSATION(conv)) {
+		load_conv_theme(gtkconv);
+		gtkconv->last_flags = 0;
+	}
 }
 
 static PurpleCmdRet
@@ -2195,19 +2196,13 @@
 
 		case GDK_Page_Up:
 		case GDK_KP_Page_Up:
-/* TODO WEBKIT: Write this. */
-#if 0
-			gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
-#endif /* if 0 */
+			gtk_webview_page_up(GTK_WEBVIEW(gtkconv->webview));
 			return TRUE;
 			break;
 
 		case GDK_Page_Down:
 		case GDK_KP_Page_Down:
-/* TODO WEBKIT: Write this. */
-#if 0
-			gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
-#endif /* if 0 */
+			gtk_webview_page_down(GTK_WEBVIEW(gtkconv->webview));
 			return TRUE;
 			break;
 
@@ -5086,6 +5081,8 @@
 	GString *str;
 	const char *cur = text;
 	const char *prev = cur;
+	time_t mtime;
+	struct tm *tm = NULL;
 
 	if (text == NULL || *text == '\0')
 		return NULL;
@@ -5122,6 +5119,7 @@
 		} else if (g_str_has_prefix(cur, "%timeOpened")) {
 			const char *tmp = cur + strlen("%timeOpened");
 			char *format = NULL;
+
 			if (*tmp == '{') {
 				const char *end;
 				tmp++;
@@ -5131,11 +5129,22 @@
 				format = g_strndup(tmp, end - tmp);
 				fin = end + 1;
 			}
-			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+
+			if (!tm) {
+				mtime = time(NULL);
+				tm = localtime(&mtime);
+			}
+
+			replace = purple_utf8_strftime(format ? format : "%X", tm);
 			g_free(format);
 
 		} else if (g_str_has_prefix(cur, "%dateOpened%")) {
-			replace = purple_date_format_short(NULL);
+			if (!tm) {
+				mtime = time(NULL);
+				tm = localtime(&mtime);
+			}
+
+			replace = purple_date_format_short(tm);
 
 		} else {
 			cur++;
@@ -5237,15 +5246,52 @@
 conv_variant_changed_cb(GObject *gobject, GParamSpec *pspec, gpointer user_data)
 {
 	PidginConversation *gtkconv = user_data;
-	const char *path;
-	char *js;
+	char *path, *js;
 
 	path = pidgin_conversation_theme_get_css_path(PIDGIN_CONV_THEME(gobject));
 	js = g_strdup_printf("setStylesheet(\"mainStyle\", \"file://%s\");", path);
+	g_free(path);
 	gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview), js);
 	g_free(js);
 }
 
+static void
+load_conv_theme(PidginConversation *gtkconv)
+{
+	char *header, *footer;
+	char *template;
+	char *basedir, *baseuri;
+
+	header = replace_header_tokens(gtkconv->active_conv,
+		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER));
+	footer = replace_header_tokens(gtkconv->active_conv,
+		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER));
+	template = replace_template_tokens(gtkconv->theme, header, footer);
+	g_free(header);
+	g_free(footer);
+
+	if (template == NULL)
+		return;
+
+	set_theme_webkit_settings(WEBKIT_WEB_VIEW(gtkconv->webview), gtkconv->theme);
+
+	basedir = pidgin_conversation_theme_get_template_path(gtkconv->theme);
+	baseuri = g_strdup_printf("file://%s", basedir);
+	webkit_web_view_load_string(WEBKIT_WEB_VIEW(gtkconv->webview), template,
+	                            "text/html", "UTF-8", baseuri);
+
+	if (purple_conversation_get_type(gtkconv->active_conv) == PURPLE_CONV_TYPE_CHAT)
+		gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview),
+			"document.getElementById('Chat').className = 'groupchat'");
+
+	g_signal_connect(G_OBJECT(gtkconv->theme), "notify::variant",
+	                 G_CALLBACK(conv_variant_changed_cb), gtkconv);
+
+	g_free(basedir);
+	g_free(baseuri);
+	g_free(template);
+}
+
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
@@ -5256,8 +5302,6 @@
 	PurpleBuddy *buddy;
 	gboolean chat = (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT);
 	int buddyicon_size = 0;
-	char *header, *footer;
-	char *template;
 
 	/* Setup the top part of the pane */
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -5351,36 +5395,7 @@
 	frame = pidgin_create_webview(FALSE, &gtkconv->webview, NULL, &webview_sw);
 	gtk_widget_set_size_request(gtkconv->webview, -1, 0);
 
-	header = replace_header_tokens(conv,
-		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER));
-	footer = replace_header_tokens(conv,
-		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER));
-	template = replace_template_tokens(gtkconv->theme, header, footer);
-	g_free(header);
-	g_free(footer);
-
-	if (template != NULL) {
-		char *basedir;
-		char *baseuri;
-
-		purple_debug_info("webkit", "template: %s\n", template);
-
-		set_theme_webkit_settings(WEBKIT_WEB_VIEW(gtkconv->webview), gtkconv->theme);
-
-		basedir = pidgin_conversation_theme_get_template_path(gtkconv->theme);
-		baseuri = g_strdup_printf("file://%s", basedir);
-		webkit_web_view_load_string(WEBKIT_WEB_VIEW(gtkconv->webview), template, "text/html", "UTF-8", baseuri);
-
-		if (chat)
-			gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview), "document.getElementById('Chat').className = 'groupchat'");
-
-		g_signal_connect(G_OBJECT(gtkconv->theme), "notify::variant",
-		                 G_CALLBACK(conv_variant_changed_cb), gtkconv);
-
-		g_free(basedir);
-		g_free(baseuri);
-		g_free(template);
-	}
+	load_conv_theme(gtkconv);
 
 	if (chat) {
 		GtkWidget *hpaned;
@@ -6142,14 +6157,16 @@
 	GString *str;
 	const char *cur = text;
 	const char *prev = cur;
-
-	if (text == NULL)
-		return g_strdup("");
+	struct tm *tm = NULL;
+
+	if (text == NULL || *text == '\0')
+		return NULL;
 
 	str = g_string_new(NULL);
 	while ((cur = strchr(cur, '%'))) {
 		const char *replace = NULL;
 		const char *fin = NULL;
+		gpointer freeval = NULL;
 
 		if (g_str_has_prefix(cur, "%message%")) {
 			replace = message;
@@ -6161,6 +6178,7 @@
 		} else if (g_str_has_prefix(cur, "%time")) {
 			const char *tmp = cur + strlen("%time");
 			char *format = NULL;
+
 			if (*tmp == '{') {
 				char *end;
 				tmp++;
@@ -6170,11 +6188,18 @@
 				format = g_strndup(tmp, end - tmp);
 				fin = end + 1;
 			}
-			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+
+			if (!tm)
+				tm = localtime(&mtime);
+
+			replace = purple_utf8_strftime(format ? format : "%X", tm);
 			g_free(format);
 
 		} else if (g_str_has_prefix(cur, "%shortTime%")) {
-			replace = purple_utf8_strftime("%H:%M", NULL);
+			if (!tm)
+				tm = localtime(&mtime);
+
+			replace = purple_utf8_strftime("%H:%M", tm);
 
 		} else if (g_str_has_prefix(cur, "%userIconPath%")) {
 			if (flags & PURPLE_MESSAGE_SEND) {
@@ -6185,14 +6210,14 @@
 					replace = purple_imgstore_get_filename(img);
 				}
 				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
-					replace = g_build_filename("Outgoing", "buddy_icon.png", NULL);
+					replace = freeval = g_build_filename("Outgoing", "buddy_icon.png", NULL);
 				}
 			} else if (flags & PURPLE_MESSAGE_RECV) {
 				PurpleBuddyIcon *icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
 				if (icon)
 					replace = purple_buddy_icon_get_full_path(icon);
 				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
-					replace = g_build_filename("Incoming", "buddy_icon.png", NULL);
+					replace = freeval = g_build_filename("Incoming", "buddy_icon.png", NULL);
 				}
 			}
 
@@ -6217,6 +6242,8 @@
 		g_string_append_len(str, prev, cur - prev);
 		if (replace)
 			g_string_append(str, replace);
+		g_free(freeval);
+		replace = freeval = NULL;
 
 		/* And update the pointers */
 		if (fin) {
@@ -6324,23 +6351,40 @@
 
 	old_flags = gtkconv->last_flags;
 	if ((flags & PURPLE_MESSAGE_SEND) && (old_flags & PURPLE_MESSAGE_SEND)) {
-		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT);
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+			PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT);
 		func = "appendNextMessage";
+
 	} else if (flags & PURPLE_MESSAGE_SEND) {
-		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT);
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+			PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT);
+
 	} else if ((flags & PURPLE_MESSAGE_RECV) && (old_flags & PURPLE_MESSAGE_RECV)) {
-		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT);
-		func = "appendNextMessage";
+		GList *history = purple_conversation_get_message_history(conv);
+		PurpleConvMessage *last_msg = (PurpleConvMessage *)history->data;
+
+		/* If the senders are the same, use appendNextMessage */
+		if (purple_strequal(purple_conversation_message_get_sender(last_msg), name)) {
+			message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+				PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT);
+			func = "appendNextMessage";
+		} else {
+			message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+				PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
+		}
 	} else if (flags & PURPLE_MESSAGE_RECV) {
-		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+			PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
+
 	} else {
-		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS);
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+			PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS);
 	}
 	gtkconv->last_flags = flags;
 
 	smileyed = smiley_parse_markup(message, purple_account_get_protocol_id(account));
 	msg = replace_message_tokens(message_html, conv, name, alias, smileyed, flags, mtime);
-	escape = gtk_webview_quote_js_string(msg);
+	escape = gtk_webview_quote_js_string(msg ? msg : "");
 	script = g_strdup_printf("%s(%s)", func, escape);
 
 	purple_debug_info("webkit", "JS: %s\n", script);
--- a/pidgin/gtkconv.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkconv.h	Wed Jan 04 02:37:25 2012 +0000
@@ -157,6 +157,8 @@
 
 /*@}*/
 
+G_BEGIN_DECLS
+
 /**************************************************************************
  * @name GTK+ Conversation API
  **************************************************************************/
@@ -269,4 +271,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGIN_CONVERSATION_H_ */
--- a/pidgin/gtkconvwin.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkconvwin.h	Wed Jan 04 02:37:25 2012 +0000
@@ -107,6 +107,8 @@
 
 /*@}*/
 
+G_BEGIN_DECLS
+
 /**************************************************************************
  * @name GTK+ Conversation Window API
  **************************************************************************/
@@ -153,4 +155,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGIN_CONVERSATION_WINDOW_H_ */
--- a/pidgin/gtkdebug.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkdebug.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "debug.h"
 
+G_BEGIN_DECLS
+
 /**
  * Initializes the GTK+ debug system.
  */
@@ -62,4 +64,6 @@
  */
 PurpleDebugUiOps *pidgin_debug_get_ui_ops(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINDEBUG_H_ */
--- a/pidgin/gtkdialogs.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkdialogs.h	Wed Jan 04 02:37:25 2012 +0000
@@ -30,6 +30,8 @@
 #include "account.h"
 #include "conversation.h"
 
+G_BEGIN_DECLS
+
 /* Functions in gtkdialogs.c (these should actually stay in this file) */
 void pidgin_dialogs_destroy_all(void);
 void pidgin_dialogs_about(void);
@@ -53,4 +55,6 @@
 /* This macro should probably be moved elsewhere */
 #define PIDGIN_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED)
 
+G_END_DECLS
+
 #endif /* _PIDGINDIALOGS_H_ */
--- a/pidgin/gtkdnd-hints.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkdnd-hints.h	Wed Jan 04 02:37:25 2012 +0000
@@ -52,6 +52,8 @@
 
 } DndHintPosition;
 
+G_BEGIN_DECLS
+
 /**
  * Shows a drag-and-drop hint at the specified location.
  *
@@ -84,4 +86,6 @@
 void dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
 							 DndHintPosition horiz, DndHintPosition vert);
 
+G_END_DECLS
+
 #endif /* _PIDGIN_DND_HINTS_H_ */
--- a/pidgin/gtkdocklet.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkdocklet.h	Wed Jan 04 02:37:25 2012 +0000
@@ -25,9 +25,13 @@
 #ifndef _GTKDOCKLET_H_
 #define _GTKDOCKLET_H_
 
+G_BEGIN_DECLS
+
 void pidgin_docklet_init(void);
 void pidgin_docklet_uninit(void);
 void*pidgin_docklet_get_handle(void);
 
+G_END_DECLS
+
 #endif /* _GTKDOCKLET_H_ */
 
--- a/pidgin/gtkeventloop.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkeventloop.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "eventloop.h"
 
+G_BEGIN_DECLS
+
 /**
  * Returns the GTK+ event loop UI operations structure.
  *
@@ -35,4 +37,6 @@
  */
 PurpleEventLoopUiOps *pidgin_eventloop_get_ui_ops(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINEVENTLOOP_H_ */
--- a/pidgin/gtkft.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkft.h	Wed Jan 04 02:37:25 2012 +0000
@@ -36,6 +36,8 @@
  */
 typedef struct _PidginXferDialog PidginXferDialog;
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name GTK+ File Transfer Dialog API                                   */
 /**************************************************************************/
@@ -145,4 +147,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGINFT_H_ */
--- a/pidgin/gtkidle.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkidle.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "idle.h"
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name GTK+ Idle API                                                  */
 /**************************************************************************/
@@ -42,4 +44,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGIN_IDLE_H_ */
--- a/pidgin/gtkimhtml.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkimhtml.h	Wed Jan 04 02:37:25 2012 +0000
@@ -31,9 +31,7 @@
 
 #include "connection.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************
  * @name Structures
@@ -828,8 +826,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PIDGINIMHTML_H_ */
--- a/pidgin/gtkimhtmltoolbar.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkimhtmltoolbar.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,9 +26,7 @@
 #include <gtk/gtk.h>
 #include "gtkimhtml.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 #define DEFAULT_FONT_FACE "Helvetica 12"
 
@@ -98,8 +96,6 @@
 void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
 	PurpleConversation *conv);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PIDGINIMHTMLTOOLBAR_H_ */
--- a/pidgin/gtklog.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtklog.h	Wed Jan 04 02:37:25 2012 +0000
@@ -52,6 +52,7 @@
 };
 
 
+G_BEGIN_DECLS
 
 void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account);
 void pidgin_log_show_contact(PurpleContact *contact);
@@ -82,4 +83,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif
--- a/pidgin/gtkmain.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkmain.c	Wed Jan 04 02:37:25 2012 +0000
@@ -719,7 +719,16 @@
 
 	/* set a user-specified config directory */
 	if (opt_config_dir_arg != NULL) {
-		purple_util_set_user_dir(opt_config_dir_arg);
+		if (g_path_is_absolute(opt_config_dir_arg)) {
+			purple_util_set_user_dir(opt_config_dir_arg);
+		} else {
+			/* Make an absolute (if not canonical) path */
+			char *cwd = g_get_current_dir();
+			char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
+			purple_util_set_user_dir(path);
+			g_free(path);
+			g_free(cwd);
+		}
 	}
 
 	/*
--- a/pidgin/gtknotify.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtknotify.h	Wed Jan 04 02:37:25 2012 +0000
@@ -29,6 +29,8 @@
 #include "notify.h"
 #include "pounce.h"
 
+G_BEGIN_DECLS
+
 /**
  * Adds a buddy pounce to the buddy pounce dialog
  *
@@ -59,4 +61,6 @@
  */
 void pidgin_notify_uninit(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINNOTIFY_H_ */
--- a/pidgin/gtkplugin.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkplugin.h	Wed Jan 04 02:37:25 2012 +0000
@@ -56,6 +56,8 @@
 #define PIDGIN_PLUGIN_UI_INFO(plugin) \
 	((PidginPluginUiInfo *)(plugin)->info->ui_info)
 
+G_BEGIN_DECLS
+
 /**
  * Returns the configuration frame widget for a GTK+ plugin, if one
  * exists.
@@ -77,4 +79,6 @@
  */
 void pidgin_plugin_dialog_show(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINPLUGIN_H_ */
--- a/pidgin/gtkpluginpref.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkpluginpref.h	Wed Jan 04 02:37:25 2012 +0000
@@ -31,9 +31,7 @@
 
 #include "pidgin.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Creates a Gtk Preference frame for a PurplePluginPrefFrame
@@ -43,8 +41,6 @@
  */
 GtkWidget *pidgin_plugin_pref_create_frame(PurplePluginPrefFrame *frame);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PIDGINPLUGINPREF_H_ */
--- a/pidgin/gtkpounce.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkpounce.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "pounce.h"
 
+G_BEGIN_DECLS
+
 /**
  * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
  *
@@ -60,4 +62,6 @@
  */
 void pidgin_pounces_init(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINPOUNCE_H_ */
--- a/pidgin/gtkprefs.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkprefs.h	Wed Jan 04 02:37:25 2012 +0000
@@ -29,6 +29,8 @@
 
 #include "prefs.h"
 
+G_BEGIN_DECLS
+
 /**
  * Initializes all UI-specific preferences.
  */
@@ -128,4 +130,6 @@
  */
 void pidgin_prefs_update_old(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINPREFS_H_ */
--- a/pidgin/gtkprivacy.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkprivacy.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "privacy.h"
 
+G_BEGIN_DECLS
+
 /**
  * Initializes the GTK+ privacy subsystem.
  */
@@ -72,4 +74,6 @@
  */
 PurplePrivacyUiOps *pidgin_privacy_get_ui_ops(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINPRIVACY_H_ */
--- a/pidgin/gtkrequest.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkrequest.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "request.h"
 
+G_BEGIN_DECLS
+
 /**
  * Returns the UI operations structure for GTK+ request functions.
  *
@@ -35,4 +37,6 @@
  */
 PurpleRequestUiOps *pidgin_request_get_ui_ops(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINREQUEST_H_ */
--- a/pidgin/gtkroomlist.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkroomlist.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "roomlist.h"
 
+G_BEGIN_DECLS
+
 /**
  * Initializes the room list subsystem.
  */
@@ -53,4 +55,6 @@
  */
 void pidgin_roomlist_dialog_show_with_account(PurpleAccount *account);
 
+G_END_DECLS
+
 #endif /* _PIDGINROOMLIST_H_ */
--- a/pidgin/gtksavedstatuses.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtksavedstatuses.h	Wed Jan 04 02:37:25 2012 +0000
@@ -29,6 +29,8 @@
 #include "savedstatuses.h"
 #include "status.h"
 
+G_BEGIN_DECLS
+
 /**
  * Shows the status window.
  */
@@ -85,4 +87,6 @@
  */
 void pidgin_status_uninit(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINSAVEDSTATUSES_H_ */
--- a/pidgin/gtksession.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtksession.h	Wed Jan 04 02:37:25 2012 +0000
@@ -26,6 +26,8 @@
 #ifndef _PIDGINSESSION_H_
 #define _PIDGINSESSION_H_
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name X Windows session subsystem                                     */
 /**************************************************************************/
@@ -51,4 +53,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGINSESSION_H_ */
--- a/pidgin/gtksmiley.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtksmiley.h	Wed Jan 04 02:37:25 2012 +0000
@@ -31,6 +31,8 @@
 
 typedef struct _PidginSmiley PidginSmiley;
 
+G_BEGIN_DECLS
+
 /**
  * Add a PurpleSmiley to the GtkIMHtmlSmiley's list to be able to use it
  * in pidgin
@@ -108,4 +110,6 @@
  */
 void pidgin_smiley_editor_set_data(PidginSmiley *editor, gpointer data, gsize datasize);
 
+G_END_DECLS
+
 #endif /* PIDGIN_GTKSMILEY_H */
--- a/pidgin/gtksound.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtksound.h	Wed Jan 04 02:37:25 2012 +0000
@@ -28,6 +28,8 @@
 
 #include "sound.h"
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name GTK+ Sound API                                                  */
 /**************************************************************************/
@@ -72,4 +74,6 @@
 
 /*@}*/
 
+G_END_DECLS
+
 #endif /* _PIDGINSOUND_H_ */
--- a/pidgin/gtksourceundomanager.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtksourceundomanager.h	Wed Jan 04 02:37:25 2012 +0000
@@ -57,6 +57,8 @@
     	void (*can_redo) (GtkSourceUndoManager *um, gboolean can_redo);
 };
 
+G_BEGIN_DECLS
+
 GType        		gtk_source_undo_manager_get_type	(void) G_GNUC_CONST;
 
 GtkSourceUndoManager* 	gtk_source_undo_manager_new 		(GtkTextBuffer 		*buffer);
@@ -78,6 +80,8 @@
 								(GtkSourceUndoManager 	*um,
 				  	     			 gint		 	 undo_levels);
 
+G_END_DECLS
+
 #endif /* __GTK_SOURCE_UNDO_MANAGER_H__ */
 
 
--- a/pidgin/gtkthemes.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkthemes.h	Wed Jan 04 02:37:25 2012 +0000
@@ -46,6 +46,8 @@
 extern struct smiley_theme *current_smiley_theme;
 extern GSList *smiley_themes;
 
+G_BEGIN_DECLS
+
 void pidgin_themes_init(void);
 
 gboolean pidgin_themes_smileys_disabled(void);
@@ -62,4 +64,6 @@
 
 GSList *pidgin_themes_get_proto_smileys(const char *id);
 
+G_END_DECLS
+
 #endif /* _PIDGINTHEMES_H_ */
--- a/pidgin/gtkutils.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkutils.h	Wed Jan 04 02:37:25 2012 +0000
@@ -31,10 +31,6 @@
 #include "prpl.h"
 #include "util.h"
 
-
-
-
-
 typedef enum
 {
 	PIDGIN_BUTTON_HORIZONTAL,
@@ -80,6 +76,8 @@
 typedef gboolean (*PidginFilterBuddyCompletionEntryFunc) (const PidginBuddyCompletionEntry *completion_entry, gpointer user_data);
 
 
+G_BEGIN_DECLS
+
 /**
  * Sets up a gtkimhtml widget, loads it with smileys, and sets the
  * default signal handlers.
@@ -903,5 +901,7 @@
  */
 void pidgin_utils_uninit(void);
 
+G_END_DECLS
+
 #endif /* _PIDGINUTILS_H_ */
 
--- a/pidgin/gtkwebview.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkwebview.c	Wed Jan 04 02:37:25 2012 +0000
@@ -25,79 +25,78 @@
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "internal.h"
+#include "pidgin.h"
 
-#include <ctype.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <JavaScriptCore/JavaScript.h>
+#include "gtkwebview.h"
+
+#define MAX_SCROLL_TIME 0.4 /* seconds */
+#define SCROLL_DELAY 33 /* milliseconds */
 
-#include "util.h"
-#include "gtkwebview.h"
-#include "imgstore.h"
+#define GTK_WEBVIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_WEBVIEW, GtkWebViewPriv))
 
-static WebKitWebViewClass *parent_class = NULL;
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
 
-struct GtkWebViewPriv {
+typedef struct _GtkWebViewPriv {
 	GHashTable *images; /**< a map from id to temporary file for the image */
-	gboolean empty;  /**< whether anything has been appended **/
+	gboolean empty;     /**< whether anything has been appended **/
 
 	/* JS execute queue */
 	GQueue *js_queue;
 	gboolean is_loading;
+
+	/* Scroll adjustments */
 	GtkAdjustment *vadj;
 	guint scroll_src;
 	GTimer *scroll_time;
-};
+} GtkWebViewPriv;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
 
-GtkWidget *
-gtk_webview_new(void)
-{
-	GtkWebView* ret = GTK_WEBVIEW(g_object_new(gtk_webview_get_type(), NULL));
-	return GTK_WIDGET(ret);
-}
+static WebKitWebViewClass *parent_class = NULL;
 
-static char *
-get_image_filename_from_id(GtkWebView* view, int id)
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+static const char *
+get_image_src_from_id(GtkWebViewPriv *priv, int id)
 {
-	char *filename = NULL;
-	FILE *file;
-	PurpleStoredImage* img;
-
-	if (!view->priv->images)
-		view->priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+	char *src;
+	PurpleStoredImage *img;
 
-	filename = (char *)g_hash_table_lookup(view->priv->images, GINT_TO_POINTER(id));
-	if (filename)
-		return filename;
+	if (priv->images) {
+		/* Check for already loaded image */
+		src = (char *)g_hash_table_lookup(priv->images, GINT_TO_POINTER(id));
+		if (src)
+			return src;
+	} else {
+		priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+		                                     NULL, g_free);
+	}
 
-	/* else get from img store */
-	file = purple_mkstemp(&filename, TRUE);
-
+	/* Find image in store */
 	img = purple_imgstore_find_by_id(id);
 
-	fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file);
-	g_hash_table_insert(view->priv->images, GINT_TO_POINTER(id), filename);
-	fclose(file);
-	return filename;
-}
+	src = (char *)purple_imgstore_get_filename(img);
+	if (src) {
+		src = g_strdup_printf("file://%s", src);
+	} else {
+		char *tmp;
+		tmp = purple_base64_encode(purple_imgstore_get_data(img),
+		                           purple_imgstore_get_size(img));
+		src = g_strdup_printf("data:base64,%s", tmp);
+		g_free(tmp);
+	}
 
-static void
-clear_single_image(gpointer key, gpointer value, gpointer userdata)
-{
-	g_unlink((char *)value);
-}
+	g_hash_table_insert(priv->images, GINT_TO_POINTER(id), src);
 
-static void
-clear_images(GtkWebView *view)
-{
-	if (!view->priv->images)
-		return;
-	g_hash_table_foreach(view->priv->images, clear_single_image, NULL);
-	g_hash_table_unref(view->priv->images);
+	return src;
 }
 
 /*
@@ -106,10 +105,10 @@
  * purple changes the way it works.
  */
 static char *
-replace_img_id_with_src(GtkWebView *view, const char *html)
+replace_img_id_with_src(GtkWebViewPriv *priv, const char *html)
 {
-	GString *buffer = g_string_sized_new(strlen(html));
-	const char* cur = html;
+	GString *buffer = g_string_new(NULL);
+	const char *cur = html;
 	char *id;
 	int nid;
 
@@ -152,39 +151,57 @@
 		/* let's dump this, tag and then dump the src information */
 		g_string_append_len(buffer, img, cur - img);
 
-		g_string_append_printf(buffer, " src='file://%s' ", get_image_filename_from_id(view, nid));
+		g_string_append_printf(buffer, " src='%s' ", get_image_src_from_id(priv, nid));
 	}
 
 	return g_string_free(buffer, FALSE);
 }
 
-static void
-gtk_webview_finalize(GObject *view)
+static gboolean
+process_js_script_queue(GtkWebView *webview)
 {
-	gpointer temp;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	char *script;
 
-	while ((temp = g_queue_pop_head(GTK_WEBVIEW(view)->priv->js_queue)))
-		g_free(temp);
-	g_queue_free(GTK_WEBVIEW(view)->priv->js_queue);
+	if (priv->is_loading)
+		return FALSE; /* we will be called when loaded */
+	if (!priv->js_queue || g_queue_is_empty(priv->js_queue))
+		return FALSE; /* nothing to do! */
 
-	clear_images(GTK_WEBVIEW(view));
-	g_free(GTK_WEBVIEW(view)->priv);
-	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(view));
+	script = g_queue_pop_head(priv->js_queue);
+	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(webview), script);
+	g_free(script);
+
+	return TRUE; /* there may be more for now */
 }
 
 static void
-gtk_webview_class_init(GtkWebViewClass *klass, gpointer userdata)
+webview_load_started(WebKitWebView *webview, WebKitWebFrame *frame,
+                     gpointer userdata)
 {
-	parent_class = g_type_class_ref(webkit_web_view_get_type());
-	G_OBJECT_CLASS(klass)->finalize = gtk_webview_finalize;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	/* is there a better way to test for is_loading? */
+	priv->is_loading = TRUE;
+}
+
+static void
+webview_load_finished(WebKitWebView *webview, WebKitWebFrame *frame,
+                      gpointer userdata)
+{
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	priv->is_loading = FALSE;
+	g_idle_add((GSourceFunc)process_js_script_queue, webview);
 }
 
 static gboolean
-webview_link_clicked(WebKitWebView *view,
-		      WebKitWebFrame *frame,
-		      WebKitNetworkRequest *request,
-		      WebKitWebNavigationAction *navigation_action,
-		      WebKitWebPolicyDecision *policy_decision)
+webview_link_clicked(WebKitWebView *webview,
+                     WebKitWebFrame *frame,
+                     WebKitNetworkRequest *request,
+                     WebKitWebNavigationAction *navigation_action,
+                     WebKitWebPolicyDecision *policy_decision,
+                     gpointer userdata)
 {
 	const gchar *uri;
 	WebKitWebNavigationReason reason;
@@ -202,77 +219,155 @@
 	return TRUE;
 }
 
+/*
+ * Smoothly scroll a WebView.
+ *
+ * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
+ */
 static gboolean
-process_js_script_queue(GtkWebView *view)
+smooth_scroll_cb(gpointer data)
 {
-	char *script;
-	if (view->priv->is_loading)
-		return FALSE; /* we will be called when loaded */
-	if (!view->priv->js_queue || g_queue_is_empty(view->priv->js_queue))
-		return FALSE; /* nothing to do! */
+	GtkWebViewPriv *priv = data;
+	GtkAdjustment *adj;
+	gdouble max_val;
+	gdouble scroll_val;
+
+	g_return_val_if_fail(priv->scroll_time != NULL, FALSE);
 
-	script = g_queue_pop_head(view->priv->js_queue);
-	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script);
-	g_free(script);
+	adj = priv->vadj;
+#if GTK_CHECK_VERSION(2,14,0)
+	max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
+#else
+	max_val = adj->upper - adj->page_size;
+#endif
+	scroll_val = gtk_adjustment_get_value(adj) +
+	             ((max_val - gtk_adjustment_get_value(adj)) / 3);
 
-	return TRUE; /* there may be more for now */
+	if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME
+	 || scroll_val >= max_val) {
+		/* time's up. jump to the end and kill the timer */
+		gtk_adjustment_set_value(adj, max_val);
+		g_timer_destroy(priv->scroll_time);
+		priv->scroll_time = NULL;
+		g_source_remove(priv->scroll_src);
+		priv->scroll_src = 0;
+		return FALSE;
+	}
+
+	/* scroll by 1/3rd the remaining distance */
+	gtk_adjustment_set_value(adj, scroll_val);
+	return TRUE;
 }
 
-static void
-webview_load_started(WebKitWebView *view,
-		      WebKitWebFrame *frame,
-		      gpointer userdata)
+static gboolean
+scroll_idle_cb(gpointer data)
 {
-	/* is there a better way to test for is_loading? */
-	GTK_WEBVIEW(view)->priv->is_loading = TRUE;
+	GtkWebViewPriv *priv = data;
+	GtkAdjustment *adj = priv->vadj;
+	gdouble max_val;
+
+	if (adj) {
+#if GTK_CHECK_VERSION(2,14,0)
+		max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
+#else
+		max_val = adj->upper - adj->page_size;
+#endif
+		gtk_adjustment_set_value(adj, max_val);
+	}
+
+	priv->scroll_src = 0;
+	return FALSE;
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+GtkWidget *
+gtk_webview_new(void)
+{
+	return GTK_WIDGET(g_object_new(gtk_webview_get_type(), NULL));
 }
 
 static void
-webview_load_finished(WebKitWebView *view,
-		       WebKitWebFrame *frame,
-		       gpointer userdata)
+gtk_webview_finalize(GObject *webview)
 {
-	GTK_WEBVIEW(view)->priv->is_loading = FALSE;
-	g_idle_add((GSourceFunc)process_js_script_queue, view);
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	gpointer temp;
+
+	while ((temp = g_queue_pop_head(priv->js_queue)))
+		g_free(temp);
+	g_queue_free(priv->js_queue);
+
+	if (priv->images)
+		g_hash_table_unref(priv->images);
+
+	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
 }
 
-void
-gtk_webview_safe_execute_script(GtkWebView *view, const char *script)
+static void
+gtk_webview_class_init(GtkWebViewClass *klass, gpointer userdata)
 {
-	g_queue_push_tail(view->priv->js_queue, g_strdup(script));
-	g_idle_add((GSourceFunc)process_js_script_queue, view);
+	parent_class = g_type_class_ref(webkit_web_view_get_type());
+
+	g_type_class_add_private(klass, sizeof(GtkWebViewPriv));
+
+	G_OBJECT_CLASS(klass)->finalize = gtk_webview_finalize;
 }
 
 static void
-gtk_webview_init(GtkWebView *view, gpointer userdata)
+gtk_webview_init(GtkWebView *webview, gpointer userdata)
 {
-	view->priv = g_new0(struct GtkWebViewPriv, 1);
-	g_signal_connect(view, "navigation-policy-decision-requested",
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	priv->empty = TRUE;
+	priv->js_queue = g_queue_new();
+
+	g_signal_connect(webview, "navigation-policy-decision-requested",
 			  G_CALLBACK(webview_link_clicked),
-			  view);
+			  webview);
 
-	g_signal_connect(view, "load-started",
+	g_signal_connect(webview, "load-started",
 			  G_CALLBACK(webview_load_started),
-			  view);
+			  webview);
 
-	g_signal_connect(view, "load-finished",
+	g_signal_connect(webview, "load-finished",
 			  G_CALLBACK(webview_load_finished),
-			  view);
-
-	view->priv->empty = TRUE;
-	view->priv->js_queue = g_queue_new();
+			  webview);
 }
 
-
-void
-gtk_webview_load_html_string_with_imgstore(GtkWebView *view, const char *html)
+GType
+gtk_webview_get_type(void)
 {
-	char *html_imged;
+	static GType mview_type = 0;
+	if (G_UNLIKELY(mview_type == 0)) {
+		static const GTypeInfo mview_info = {
+			sizeof(GtkWebViewClass),
+			NULL,
+			NULL,
+			(GClassInitFunc)gtk_webview_class_init,
+			NULL,
+			NULL,
+			sizeof(GtkWebView),
+			0,
+			(GInstanceInitFunc)gtk_webview_init,
+			NULL
+		};
+		mview_type = g_type_register_static(webkit_web_view_get_type(),
+				"GtkWebView", &mview_info, 0);
+	}
+	return mview_type;
+}
 
-	clear_images(view);
-	html_imged = replace_img_id_with_src(view, html);
-	webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(view), html_imged, "file:///");
-	g_free(html_imged);
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+gboolean
+gtk_webview_is_empty(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	return priv->empty;
 }
 
 char *
@@ -299,14 +394,34 @@
 		}
 		cur++;
 	}
+
 	g_string_append_c(str, '"');
+
 	return g_string_free(str, FALSE);
 }
+void
+gtk_webview_safe_execute_script(GtkWebView *webview, const char *script)
+{
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	g_queue_push_tail(priv->js_queue, g_strdup(script));
+	g_idle_add((GSourceFunc)process_js_script_queue, webview);
+}
 
 void
-gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj)
+gtk_webview_load_html_string_with_imgstore(GtkWebView *webview, const char *html)
 {
-	webview->priv->vadj = vadj;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	char *html_imged;
+
+	if (priv->images) {
+		g_hash_table_unref(priv->images);
+		priv->images = NULL;
+	}
+
+	html_imged = replace_img_id_with_src(priv, html);
+	webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), html_imged, NULL,
+	                            NULL, "file:///");
+	g_free(html_imged);
 }
 
 /* this is a "hack", my plan is to eventually handle this
@@ -315,77 +430,34 @@
  * wrong to hardcode that here.
  */
 void
-gtk_webview_append_html(GtkWebView *view, const char *html)
+gtk_webview_append_html(GtkWebView *webview, const char *html)
 {
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
 	char *escaped = gtk_webview_quote_js_string(html);
 	char *script = g_strdup_printf("document.write(%s)", escaped);
-	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script);
-	view->priv->empty = FALSE;
-	gtk_webview_scroll_to_end(view, TRUE);
+	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(webview), script);
+	priv->empty = FALSE;
+	gtk_webview_scroll_to_end(webview, TRUE);
 	g_free(script);
 	g_free(escaped);
 }
 
-gboolean
-gtk_webview_is_empty(GtkWebView *view)
-{
-	return view->priv->empty;
-}
-
-#define MAX_SCROLL_TIME 0.4 /* seconds */
-#define SCROLL_DELAY 33 /* milliseconds */
-
-/*
- * Smoothly scroll a WebView.
- *
- * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
- */
-static gboolean
-smooth_scroll_cb(gpointer data)
+void
+gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj)
 {
-	struct GtkWebViewPriv *priv = data;
-	GtkAdjustment *adj = priv->vadj;
-	gdouble max_val = adj->upper - adj->page_size;
-	gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3);
-
-	g_return_val_if_fail(priv->scroll_time != NULL, FALSE);
-
-	if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) {
-		/* time's up. jump to the end and kill the timer */
-		gtk_adjustment_set_value(adj, max_val);
-		g_timer_destroy(priv->scroll_time);
-		priv->scroll_time = NULL;
-		g_source_remove(priv->scroll_src);
-		priv->scroll_src = 0;
-		return FALSE;
-	}
-
-	/* scroll by 1/3rd the remaining distance */
-	gtk_adjustment_set_value(adj, scroll_val);
-	return TRUE;
-}
-
-static gboolean
-scroll_idle_cb(gpointer data)
-{
-	struct GtkWebViewPriv *priv = data;
-	GtkAdjustment *adj = priv->vadj;
-	if (adj) {
-		gtk_adjustment_set_value(adj, adj->upper - adj->page_size);
-	}
-	priv->scroll_src = 0;
-	return FALSE;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	priv->vadj = vadj;
 }
 
 void
 gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth)
 {
-	struct GtkWebViewPriv *priv = webview->priv;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
 	if (priv->scroll_time)
 		g_timer_destroy(priv->scroll_time);
 	if (priv->scroll_src)
 		g_source_remove(priv->scroll_src);
-	if(smooth) {
+	if (smooth) {
 		priv->scroll_time = g_timer_new();
 		priv->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, priv, NULL);
 	} else {
@@ -394,26 +466,40 @@
 	}
 }
 
-GType
-gtk_webview_get_type(void)
+void gtk_webview_page_up(GtkWebView *webview)
 {
-	static GType mview_type = 0;
-	if (G_UNLIKELY(mview_type == 0)) {
-		static const GTypeInfo mview_info = {
-			sizeof(GtkWebViewClass),
-			NULL,
-			NULL,
-			(GClassInitFunc) gtk_webview_class_init,
-			NULL,
-			NULL,
-			sizeof(GtkWebView),
-			0,
-			(GInstanceInitFunc) gtk_webview_init,
-			NULL
-		};
-		mview_type = g_type_register_static(webkit_web_view_get_type(),
-				"GtkWebView", &mview_info, 0);
-	}
-	return mview_type;
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	GtkAdjustment *vadj = priv->vadj;
+	gdouble scroll_val;
+
+#if GTK_CHECK_VERSION(2,14,0)
+	scroll_val = gtk_adjustment_get_value(vadj) - gtk_adjustment_get_page_size(vadj);
+	scroll_val = MAX(scroll_val, gtk_adjustment_get_lower(vadj));
+#else
+	scroll_val = gtk_adjustment_get_value(vadj) - vadj->page_size;
+	scroll_val = MAX(scroll_val, vadj->lower);
+#endif
+
+	gtk_adjustment_set_value(vadj, scroll_val);
 }
 
+void gtk_webview_page_down(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	GtkAdjustment *vadj = priv->vadj;
+	gdouble scroll_val;
+	gdouble page_size;
+
+#if GTK_CHECK_VERSION(2,14,0)
+	page_size = gtk_adjustment_get_page_size(vadj);
+	scroll_val = gtk_adjustment_get_value(vadj) + page_size;
+	scroll_val = MIN(scroll_val, gtk_adjustment_get_upper(vadj) - page_size);
+#else
+	page_size = vadj->page_size;
+	scroll_val = gtk_adjustment_get_value(vadj) + page_size;
+	scroll_val = MIN(scroll_val, vadj->upper - page_size);
+#endif
+
+	gtk_adjustment_set_value(vadj, scroll_val);
+}
+
--- a/pidgin/gtkwebview.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkwebview.h	Wed Jan 04 02:37:25 2012 +0000
@@ -32,34 +32,27 @@
 #include <gtk/gtk.h>
 #include <webkit/webkit.h>
 
-#include "notify.h"
-
 #define GTK_TYPE_WEBVIEW            (gtk_webview_get_type())
 #define GTK_WEBVIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_WEBVIEW, GtkWebView))
 #define GTK_WEBVIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_WEBVIEW, GtkWebViewClass))
 #define GTK_IS_WEBVIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_WEBVIEW))
-#define GTK_IS_WEBVIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW))
+#define GTK_IS_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW))
+#define GTK_WEBVIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_WEBVIEW, GtkWebViewClass))
 
-
-struct GtkWebViewPriv;
+typedef struct _GtkWebView GtkWebView;
+typedef struct _GtkWebViewClass GtkWebViewClass;
 
 struct _GtkWebView
 {
-	WebKitWebView webkit_web_view;
-
-	/*< private >*/
-	struct GtkWebViewPriv *priv;
+	WebKitWebView parent;
 };
 
-typedef struct _GtkWebView GtkWebView;
-
 struct _GtkWebViewClass
 {
 	WebKitWebViewClass parent;
 };
 
-typedef struct _GtkWebViewClass GtkWebViewClass;
-
+G_BEGIN_DECLS
 
 /**
  * Returns the GType for a GtkWebView widget
@@ -76,12 +69,15 @@
 GtkWidget *gtk_webview_new(void);
 
 /**
- * Set the vertical adjustment for the GtkWebView.
+ * TODO WEBKIT: Right now this just tests whether an append has been called
+ * since the last clear or since the Widget was created.  So it does not
+ * test for load_string's called in between.
  *
- * @param webview  The GtkWebView object
- * @param vadj     The GtkAdjustment that control the webview
+ * @param webview The GtkWebView object
+ *
+ * @return gboolean indicating whether the webview is empty
  */
-void gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj);
+gboolean gtk_webview_is_empty(GtkWebView *webview);
 
 /**
  * A very basic routine to append html, which can be considered
@@ -103,17 +99,6 @@
 void gtk_webview_load_html_string_with_imgstore(GtkWebView *webview, const char *html);
 
 /**
- * TODO WEBKIT: Right now this just tests whether an append has been called
- * since the last clear or since the Widget was created.  So it does not
- * test for load_string's called in between.
- *
- * @param webview The GtkWebView object
- *
- * @return gboolean indicating whether the webview is empty
- */
-gboolean gtk_webview_is_empty(GtkWebView *webview);
-
-/**
  * Execute the JavaScript only after the webkit_webview_load_string
  * loads completely. We also guarantee that the scripts are executed
  * in the order they are called here. This is useful to avoid race
@@ -136,6 +121,14 @@
 char *gtk_webview_quote_js_string(const char *str);
 
 /**
+ * Set the vertical adjustment for the GtkWebView.
+ *
+ * @param webview  The GtkWebView object
+ * @param vadj     The GtkAdjustment that control the webview
+ */
+void gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj);
+
+/**
  * Scrolls the Webview to the end of its contents.
  *
  * @param webview The GtkWebView object
@@ -143,5 +136,21 @@
  */
 void gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth);
 
+/**
+ * Scrolls a GtkWebView up by one page.
+ *
+ * @param webview The GtkWebView.
+ */
+void gtk_webview_page_up(GtkWebView *webview);
+
+/**
+ * Scrolls a GtkWebView down by one page.
+ *
+ * @param webview The GtkWebView.
+ */
+void gtk_webview_page_down(GtkWebView *webview);
+
+G_END_DECLS
+
 #endif /* _PIDGIN_WEBVIEW_H_ */
 
--- a/pidgin/gtkwhiteboard.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/gtkwhiteboard.h	Wed Jan 04 02:37:25 2012 +0000
@@ -59,9 +59,7 @@
 	int brush_size;          /**< Brush size */
 } PidginWhiteboard;
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
 
 /*****************************************************************************/
 /** @name PidginWhiteboard API                                              */
@@ -77,8 +75,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* _PIDGINWHITEBOARD_H_ */
--- a/pidgin/pidgintooltip.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/pidgintooltip.h	Wed Jan 04 02:37:25 2012 +0000
@@ -59,6 +59,8 @@
  */
 typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, gpointer userdata);
 
+G_BEGIN_DECLS
+
 /**
  * Setup tooltip drawing functions for a treeview.
  *
@@ -101,4 +103,6 @@
 void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
 		PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
 
+G_END_DECLS
+
 #endif
--- a/pidgin/plugins/Makefile.am	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/plugins/Makefile.am	Wed Jan 04 02:37:25 2012 +0000
@@ -38,6 +38,7 @@
 gtkbuddynote_la_LDFLAGS     = -module -avoid-version
 history_la_LDFLAGS          = -module -avoid-version
 iconaway_la_LDFLAGS         = -module -avoid-version
+markerline_la_LDFLAGS       = -module -avoid-version
 notify_la_LDFLAGS           = -module -avoid-version
 pidginrc_la_LDFLAGS         = -module -avoid-version
 relnot_la_LDFLAGS           = -module -avoid-version
@@ -56,6 +57,7 @@
 	gtkbuddynote.la     \
 	history.la          \
 	iconaway.la         \
+	markerline.la       \
 	notify.la           \
 	pidginrc.la         \
 	relnot.la           \
@@ -80,6 +82,7 @@
 gtkbuddynote_la_SOURCES     = gtkbuddynote.c
 history_la_SOURCES          = history.c
 iconaway_la_SOURCES         = iconaway.c
+markerline_la_SOURCES       = markerline.c
 notify_la_SOURCES           = notify.c
 pidginrc_la_SOURCES         = pidginrc.c
 relnot_la_SOURCES           = relnot.c
@@ -96,6 +99,7 @@
 gtkbuddynote_la_LIBADD      = $(GTK_LIBS)
 history_la_LIBADD           = $(GTK_LIBS)
 iconaway_la_LIBADD          = $(GTK_LIBS)
+markerline_la_LIBADD        = $(GTK_LIBS) $(WEBKIT_LIBS)
 notify_la_LIBADD            = $(GTK_LIBS)
 pidginrc_la_LIBADD          = $(GTK_LIBS)
 relnot_la_LIBADD            = $(GLIB_LIBS)
--- a/pidgin/plugins/markerline.c	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/plugins/markerline.c	Wed Jan 04 02:37:25 2012 +0000
@@ -33,81 +33,38 @@
 
 /* Purple headers */
 #include <gtkconv.h>
-#include <gtkimhtml.h>
 #include <gtkplugin.h>
+#include <gtkwebview.h>
 #include <version.h>
 
 #define PREF_PREFIX     "/plugins/gtk/" PLUGIN_ID
 #define PREF_IMS        PREF_PREFIX "/ims"
 #define PREF_CHATS      PREF_PREFIX "/chats"
 
-static int
-imhtml_expose_cb(GtkWidget *widget, GdkEventExpose *event, PidginConversation *gtkconv)
-{
-	int y, last_y, offset;
-	GdkRectangle visible_rect;
-	GtkTextIter iter;
-	GdkRectangle buf;
-	int pad;
-	PurpleConversation *conv = gtkconv->active_conv;
-	PurpleConversationType type = purple_conversation_get_type(conv);
-
-	if ((type == PURPLE_CONV_TYPE_CHAT && !purple_prefs_get_bool(PREF_CHATS)) ||
-			(type == PURPLE_CONV_TYPE_IM && !purple_prefs_get_bool(PREF_IMS)))
-		return FALSE;
-
-	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &visible_rect);
-
-	offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "markerline"));
-	if (offset)
-	{
-		gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)),
-							&iter, offset);
-
-		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &iter, &buf);
-		last_y = buf.y + buf.height;
-		pad = (gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(widget)) +
-				gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(widget))) / 2;
-		last_y += pad;
-	}
-	else
-		last_y = 0;
-
-	gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT,
-										0, last_y, 0, &y);
-
-	if (y >= event->area.y)
-	{
-		GdkColor red = {0, 0xffff, 0, 0};
-		cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
-
-		gdk_cairo_set_source_color(cr, &red);
-		cairo_move_to(cr, 0.0, y + 0.5);
-		cairo_rel_line_to(cr, visible_rect.width, 0.0);
-		cairo_set_line_width(cr, 1.0);
-		cairo_stroke(cr);
-		cairo_destroy(cr);
-	}
-	return FALSE;
-}
-
 static void
 update_marker_for_gtkconv(PidginConversation *gtkconv)
 {
-	GtkTextIter iter;
-	GtkTextBuffer *buffer;
+	PurpleConversation *conv;
+	PurpleConversationType type;
+
 	g_return_if_fail(gtkconv != NULL);
 
-	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
+	conv = gtkconv->active_conv;
+	type = purple_conversation_get_type(conv);
 
-	if (!gtk_text_buffer_get_char_count(buffer))
+	if ((type == PURPLE_CONV_TYPE_CHAT && !purple_prefs_get_bool(PREF_CHATS)) ||
+	    (type == PURPLE_CONV_TYPE_IM && !purple_prefs_get_bool(PREF_IMS)))
 		return;
 
-	gtk_text_buffer_get_end_iter(buffer, &iter);
-
-	g_object_set_data(G_OBJECT(gtkconv->imhtml), "markerline",
-						GINT_TO_POINTER(gtk_text_iter_get_offset(&iter)));
-	gtk_widget_queue_draw(gtkconv->imhtml);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview),
+		"var mhr = document.getElementById(\"markerhr\");"
+		"if (!mhr) {"
+			"mhr = document.createElement(\"hr\");"
+			"mhr.setAttribute(\"id\", \"markerhr\");"
+			"mhr.setAttribute(\"color\", \"#ff0000\");"
+			"mhr.setAttribute(\"size\", \"1\");"
+		"}"
+		"document.getElementById(\"Chat\").appendChild(mhr);");
 }
 
 static gboolean
@@ -125,28 +82,6 @@
 	return FALSE;
 }
 
-#if 0
-static gboolean
-window_resized(GtkWidget *w, GdkEventConfigure *event, PidginWindow *win)
-{
-	GList *list;
-
-	list = pidgin_conv_window_get_gtkconvs(win);
-
-	for (; list; list = list->next)
-		update_marker_for_gtkconv(list->data);
-
-	return FALSE;
-}
-
-static gboolean
-imhtml_resize_cb(GtkWidget *w, GtkAllocation *allocation, PidginConversation *gtkconv)
-{
-	gtk_widget_queue_draw(w);
-	return FALSE;
-}
-#endif
-
 static void
 page_switched(GtkWidget *widget, GtkWidget *page, gint num, PidginWindow *win)
 {
@@ -156,7 +91,9 @@
 static void
 detach_from_gtkconv(PidginConversation *gtkconv, gpointer null)
 {
-	g_signal_handlers_disconnect_by_func(G_OBJECT(gtkconv->imhtml), imhtml_expose_cb, gtkconv);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview),
+		"var mhr = document.getElementById(\"markerhr\");"
+		"if (mhr) mhr.parentNode.removeChild(mhr);");
 }
 
 static void
@@ -165,16 +102,13 @@
 	g_list_foreach(pidgin_conv_window_get_gtkconvs(win), (GFunc)detach_from_gtkconv, NULL);
 	g_signal_handlers_disconnect_by_func(G_OBJECT(win->notebook), page_switched, win);
 	g_signal_handlers_disconnect_by_func(G_OBJECT(win->window), focus_removed, win);
-
-	gtk_widget_queue_draw(win->window);
 }
 
 static void
 attach_to_gtkconv(PidginConversation *gtkconv, gpointer null)
 {
 	detach_from_gtkconv(gtkconv, NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "expose_event",
-					 G_CALLBACK(imhtml_expose_cb), gtkconv);
+	update_marker_for_gtkconv(gtkconv);
 }
 
 static void
@@ -187,8 +121,6 @@
 
 	g_signal_connect(G_OBJECT(win->notebook), "switch_page",
 					G_CALLBACK(page_switched), win);
-
-	gtk_widget_queue_draw(win->window);
 }
 
 static void
@@ -220,15 +152,15 @@
 jump_to_markerline(PurpleConversation *conv, gpointer null)
 {
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-	int offset;
-	GtkTextIter iter;
 
 	if (!gtkconv)
 		return;
 
-	offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->imhtml), "markerline"));
-	gtk_text_buffer_get_iter_at_offset(GTK_IMHTML(gtkconv->imhtml)->text_buffer, &iter, offset);
-	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(gtkconv->imhtml), &iter, 0, TRUE, 0, 0);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview),
+		"var mhr = document.getElementById(\"markerhr\");"
+		"if (mhr) {"
+			"window.scroll(0, mhr.offsetTop);"
+		"}");
 }
 
 static void
--- a/pidgin/smileyparser.h	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/smileyparser.h	Wed Jan 04 02:37:25 2012 +0000
@@ -20,6 +20,10 @@
  *
  */
 
+G_BEGIN_DECLS
+
 char *
 smiley_parse_markup(const char *markup, const char *sml);
 
+G_END_DECLS
+
--- a/pidgin/themes/Template.html	Thu Dec 29 07:33:57 2011 +0000
+++ b/pidgin/themes/Template.html	Wed Jan 04 02:37:25 2012 +0000
@@ -3,66 +3,215 @@
 <head>
 	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
 	<base href="%@">
-	<script type="text/ecmascript" defer="defer">
-	
+	<script type="text/javascript" defer="defer">
+		// NOTE:
+		// Any percent signs in this file must be escaped!
+		// Use two escape signs (%%) to display it, this is passed through a format call!
+
+		function appendHTML(html) {
+			var node = document.getElementById("Chat");
+			var range = document.createRange();
+			range.selectNode(node);
+			var documentFragment = range.createContextualFragment(html);
+			node.appendChild(documentFragment);
+		}
+
+		// a coalesced HTML object buffers and outputs DOM objects en masse.
+		// saves A LOT of CSS recalculation time when loading many messages.
+		// (ex. a long twitter timeline)
+		function CoalescedHTML() {
+			var self = this;
+			this.fragment = document.createDocumentFragment();
+			this.timeoutID = 0;
+			this.coalesceRounds = 0;
+			this.isCoalescing = false;
+			this.isConsecutive = undefined;
+			this.shouldScroll = undefined;
+
+			var appendElement = function (elem) {
+				document.getElementById("Chat").appendChild(elem);
+			};
+
+			function outputHTML() {
+				var insert = document.getElementById("insert");
+				if(!!insert && self.isConsecutive) {
+					insert.parentNode.replaceChild(self.fragment, insert);
+				} else {
+					if(insert)
+						insert.parentNode.removeChild(insert);
+					// insert the documentFragment into the live DOM
+					appendElement(self.fragment);
+				}
+				alignChat(self.shouldScroll);
+
+				// reset state to empty/non-coalescing
+				self.shouldScroll = undefined;
+				self.isConsecutive = undefined;
+				self.isCoalescing = false;
+				self.coalesceRounds = 0;
+			}
+
+			// creates and returns a new documentFragment, containing all content nodes
+			// which can be inserted as a single node.
+			function createHTMLNode(html) {
+				var range = document.createRange();
+				range.selectNode(document.getElementById("Chat"));
+				return range.createContextualFragment(html);
+			}
+
+			// removes first insert node from the internal fragment.
+			function rmInsertNode() {
+				var insert = self.fragment.querySelector("#insert");
+				if(insert)
+					insert.parentNode.removeChild(insert);
+			}
+
+			function setShouldScroll(flag) {
+				if(flag && undefined === self.shouldScroll)
+					self.shouldScroll = flag;
+			}
+
+			// hook in a custom method to append new data
+			// to the chat.
+			this.setAppendElementMethod = function (func) {
+				if(typeof func === 'function')
+					appendElement = func;
+			}
+
+			// (re)start the coalescing timer.
+			//   we wait 25ms for a new message to come in.
+			//   If we get one, restart the timer and wait another 10ms.
+			//   If not, run outputHTML()
+			//  We do this a maximum of 400 times, for 10s max that can be spent
+			//  coalescing input, since this will block display.
+			this.coalesce = function() {
+				window.clearTimeout(self.timeoutID);
+				self.timeoutID = window.setTimeout(outputHTML, 25);
+				self.isCoalescing = true;
+				self.coalesceRounds += 1;
+				if(400 < self.coalesceRounds)
+					self.cancel();
+			}
+
+			// if we need to append content into an insertion div,
+			// we need to clear the buffer and cancel the timeout.
+			this.cancel = function() {
+				if(self.isCoalescing) {
+					window.clearTimeout(self.timeoutID);
+					outputHTML();
+				}
+			}
+
+
+			// coalased analogs to the global functions
+
+			this.append = function(html, shouldScroll) {
+				// if we started this fragment with a consecuative message,
+				// cancel and output before we continue
+				if(self.isConsecutive) {
+					self.cancel();
+				}
+				self.isConsecutive = false;
+				rmInsertNode();
+				var node = createHTMLNode(html);
+				self.fragment.appendChild(node);
+
+				node = null;
+
+				setShouldScroll(shouldScroll);
+				self.coalesce();
+			}
+
+			this.appendNext = function(html, shouldScroll) {
+				if(undefined === self.isConsecutive)
+					self.isConsecutive = true;
+				var node = createHTMLNode(html);
+				var insert = self.fragment.querySelector("#insert");
+				if(insert) {
+					insert.parentNode.replaceChild(node, insert);
+				} else {
+					self.fragment.appendChild(node);
+				}
+				node = null;
+				setShouldScroll(shouldScroll);
+				self.coalesce();
+			}
+
+			this.replaceLast = function (html, shouldScroll) {
+				rmInsertNode();
+				var node = createHTMLNode(html);
+				var lastMessage = self.fragment.lastChild;
+				lastMessage.parentNode.replaceChild(node, lastMessage);
+				node = null;
+				setShouldScroll(shouldScroll);
+			}
+		}
+		var coalescedHTML;
+
 		//Appending new content to the message view
 		function appendMessage(html) {
-			shouldScroll = nearBottom();
-		
-			//Remove any existing insertion point
-			insert = document.getElementById("insert");
-			if(insert) insert.parentNode.removeChild(insert);
+			var shouldScroll;
 
-			//Append the new message to the bottom of our chat block
-			chat = document.getElementById("Chat");
-			range = document.createRange();
-			range.selectNode(chat);
-			documentFragment = range.createContextualFragment(html);
-			chat.appendChild(documentFragment);
-			
-			alignChat(shouldScroll);
+			// Only call nearBottom() if should scroll is undefined.
+			if(undefined === coalescedHTML.shouldScroll) {
+				shouldScroll = nearBottom();
+			} else {
+				shouldScroll = coalescedHTML.shouldScroll;
+			}
+			appendMessageNoScroll(html, shouldScroll);
 		}
-		function appendMessageNoScroll(html) {
-			//Remove any existing insertion point
-			insert = document.getElementById("insert");
-			if(insert) insert.parentNode.removeChild(insert);
 
-			//Append the new message to the bottom of our chat block
-			chat = document.getElementById("Chat");
-			range = document.createRange();
-			range.selectNode(chat);
-			documentFragment = range.createContextualFragment(html);
-			chat.appendChild(documentFragment);
+		function appendMessageNoScroll(html, shouldScroll) {
+			shouldScroll = shouldScroll || false;
+			// always try to coalesce new, non-griuped, messages
+			coalescedHTML.append(html, shouldScroll)
 		}
+
 		function appendNextMessage(html){
-			shouldScroll = nearBottom();
+			var shouldScroll;
+			if(undefined === coalescedHTML.shouldScroll) {
+				shouldScroll = nearBottom();
+			} else {
+				shouldScroll = coalescedHTML.shouldScroll;
+			}
+			appendNextMessageNoScroll(html, shouldScroll);
+		}
 
-			//Locate the insertion point
-			insert = document.getElementById("insert");
-		
-			//make new node
-			range = document.createRange();
-			range.selectNode(insert.parentNode);
-			newNode = range.createContextualFragment(html);
+		function appendNextMessageNoScroll(html, shouldScroll){
+			shouldScroll = shouldScroll || false;
+			// only group next messages if we're already coalescing input
+			coalescedHTML.appendNext(html, shouldScroll);
+		}
 
-			//swap
-			insert.parentNode.replaceChild(newNode,insert);
-			
-			alignChat(shouldScroll);
+		function replaceLastMessage(html){
+			var shouldScroll;
+			// only replace messages if we're already coalescing
+			if(coalescedHTML.isCoalescing){
+				if(undefined === coalescedHTML.shouldScroll) {
+					shouldScroll = nearBottom();
+				} else {
+					shouldScroll = coalescedHTML.shouldScroll;
+				}
+				coalescedHTML.replaceLast(html, shouldScroll);
+			} else {
+				shouldScroll = nearBottom();
+				//Retrieve the current insertion point, then remove it
+				//This requires that there have been an insertion point... is there a better way to retrieve the last element? -evands
+				var insert = document.getElementById("insert");
+				if(insert){
+					var parentNode = insert.parentNode;
+					parentNode.removeChild(insert);
+					var lastMessage = document.getElementById("Chat").lastChild;
+					document.getElementById("Chat").removeChild(lastMessage);
+				}
+
+				//Now append the message itself
+				appendHTML(html);
+
+				alignChat(shouldScroll);
+			}
 		}
-		function appendNextMessageNoScroll(html){
-			//Locate the insertion point
-			insert = document.getElementById("insert");
-		
-			//make new node
-			range = document.createRange();
-			range.selectNode(insert.parentNode);
-			newNode = range.createContextualFragment(html);
 
-			//swap
-			insert.parentNode.replaceChild(newNode,insert);
-		}
-		
 		//Auto-scroll to bottom.  Use nearBottom to determine if a scrollToBottom is desired.
 		function nearBottom() {
 			return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) );
@@ -73,89 +222,119 @@
 
 		//Dynamically exchange the active stylesheet
 		function setStylesheet( id, url ) {
-			code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
-			if( url.length ) code += "@import url( \"" + url + "\" );";
+			var code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
+			if( url.length )
+				code += "@import url( \"" + url + "\" );";
 			code += "</style>";
-			range = document.createRange();
-			head = document.getElementsByTagName( "head" ).item(0);
+			var range = document.createRange();
+			var head = document.getElementsByTagName( "head" ).item(0);
 			range.selectNode( head );
-			documentFragment = range.createContextualFragment( code );
+			var documentFragment = range.createContextualFragment( code );
 			head.removeChild( document.getElementById( id ) );
 			head.appendChild( documentFragment );
 		}
-		
-		//Swap an image with its alt-tag text on click, or expand/unexpand an attached image
-		document.onclick = imageCheck;
-		function imageCheck() {		
-			node = event.target;
-			if(node.tagName == 'IMG' && !client.zoomImage(node) && node.alt) {
-				a = document.createElement('a');
-				a.setAttribute('onclick', 'imageSwap(this)');
-				a.setAttribute('src', node.getAttribute('src'));
-				a.className = node.className;
-				text = document.createTextNode(node.alt);
-				a.appendChild(text);
-				node.parentNode.replaceChild(a, node);
-			}
+
+		/* Converts emoticon images to textual emoticons; all emoticons in message if alt is held */
+		document.onclick = function imageCheck() {
+			var node = event.target;
+			if (node.tagName.toLowerCase() != 'img')
+				return;
+
+			imageSwap(node, false);
 		}
 
-		function imageSwap(node) {
-			shouldScroll = nearBottom();
+		/* Converts textual emoticons to images if textToImagesFlag is true, otherwise vice versa */
+		function imageSwap(node, textToImagesFlag) {
+			var shouldScroll = nearBottom();
+
+			var images = [node];
+			if (event.altKey) {
+				while (node.id != "Chat" && node.parentNode.id != "Chat")
+					node = node.parentNode;
+				images = node.querySelectorAll(textToImagesFlag ? "a" : "img");
+			}
 
+			for (var i = 0; i < images.length; i++) {
+				textToImagesFlag ? textToImage(images[i]) : imageToText(images[i]);
+			}
+
+			alignChat(shouldScroll);
+		}
+
+		function textToImage(node) {
+			if (!node.getAttribute("isEmoticon"))
+				return;
 			//Swap the image/text
-			img = document.createElement('img');
+			var img = document.createElement('img');
 			img.setAttribute('src', node.getAttribute('src'));
 			img.setAttribute('alt', node.firstChild.nodeValue);
 			img.className = node.className;
 			node.parentNode.replaceChild(img, node);
-			
-			alignChat(shouldScroll);
 		}
-		
+
+		function imageToText(node)
+		{
+			if (client.zoomImage(node) || !node.alt)
+				return;
+			var a = document.createElement('a');
+			a.setAttribute('onclick', 'imageSwap(this, true)');
+			a.setAttribute('src', node.getAttribute('src'));
+			a.setAttribute('isEmoticon', true);
+			a.className = node.className;
+			var text = document.createTextNode(node.alt);
+			a.appendChild(text);
+			node.parentNode.replaceChild(a, node);
+		}
+
 		//Align our chat to the bottom of the window.  If true is passed, view will also be scrolled down
 		function alignChat(shouldScroll) {
 			var windowHeight = window.innerHeight;
-			
+
 			if (windowHeight > 0) {
 				var contentElement = document.getElementById('Chat');
-				var contentHeight = contentElement.offsetHeight;
-				if (windowHeight - contentHeight > 0) {
+				var heightDifference = (windowHeight - contentElement.offsetHeight);
+				if (heightDifference > 0) {
 					contentElement.style.position = 'relative';
-					contentElement.style.top = (windowHeight - contentHeight) + 'px';
+					contentElement.style.top = heightDifference + 'px';
 				} else {
 					contentElement.style.position = 'static';
 				}
 			}
-			
+
 			if (shouldScroll) scrollToBottom();
 		}
-		
-		function windowDidResize(){
+
+		window.onresize = function windowDidResize(){
 			alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs
 		}
-		
-		window.onresize = windowDidResize;
+
+		function initStyle() {
+			alignChat(true);
+			if(!coalescedHTML)
+				coalescedHTML = new CoalescedHTML();
+		}
 	</script>
-	
+
 	<style type="text/css">
-		.actionMessageUserName:before { content:"*"; }
+		.actionMessageUserName { display:none; }
+		.actionMessageBody:before { content:"*"; }
 		.actionMessageBody:after { content:"*"; }
-		*{ word-wrap:break-word; }
-		img.scaledToFitImage { height:auto; width:100%; }
+		* { word-wrap:break-word; text-rendering: optimizelegibility; }
+		img.scaledToFitImage { height: auto; max-width: 100%%; }
 	</style>
-	
+
 	<!-- This style is shared by all variants. !-->
-	<style id="baseStyle" type="text/css" media="screen,print">	
+	<style id="baseStyle" type="text/css" media="screen,print">
 		%@
 	</style>
-	
+
 	<!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
-	<style id="mainStyle" type="text/css" media="screen,print">	
+	<style id="mainStyle" type="text/css" media="screen,print">
 		@import url( "%@" );
 	</style>
 
 </head>
-<body onload="alignChat(true);" style="==bodyBackground==">
+<body onload="initStyle();" style="==bodyBackground==">
 %@
 <div id="Chat">
 </div>