changeset 31835:d66112347eb4

merge of '1577331230de94bf1d081cb81f03ae1271cf8960' and '85490424ef0ad56f257b7f2cc39e24e51d2fe160'
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Mon, 14 Mar 2011 04:34:34 +0000
parents 539b7170c7df (current diff) dc1b3cb85f51 (diff)
children 9710b3226f11
files
diffstat 51 files changed, 662 insertions(+), 405 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Mar 14 04:15:33 2011 +0000
+++ b/ChangeLog	Mon Mar 14 04:34:34 2011 +0000
@@ -1,6 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.7.12 (??/??/????):
+version 2.8.0 (??/??/????):
 	General:
 	* Implement simple silence suppression for voice calls, preventing wasted
 	  bandwidth for silent periods during a call. (Jakub Adam) (half of #13180)
@@ -8,9 +8,19 @@
 	Gadu-Gadu:
 	* Allow showing your status only to buddies. (Mateusz Piękos) (#13358)
 
+	libpurple:
+	* media: Allow obtaining active local and remote candidates. (#11830)
+	* media: Allow getting/setting video capabilities. (Jakub Adam) (half of
+	  #13095)
+	* Simple Silence Suppression is optional per-account. (Jakub Adam) (half
+	  of #13180)
+
+	Pidgin:
+	* Duplicate code cleanup.  (Gabriel Schulhof) (#10599)
+
 	Windows-Specific Changes:
 	* Fix building libpurple with Visual C++ .NET 2005. This was accidentally
-	  broken in 2.7.11.  Florian Quèze)
+	  broken in 2.7.11. (Florian Quèze)
 
 version 2.7.11 (03/10/2011):
 	General:
--- a/ChangeLog.API	Mon Mar 14 04:15:33 2011 +0000
+++ b/ChangeLog.API	Mon Mar 14 04:34:34 2011 +0000
@@ -1,6 +1,19 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.7.12 (??/??/????):
+version 2.8.0 (??/??/????):
+	libpurple:
+		Added:
+		* account-authorization-requested-with-message signal (Stefan Ott)
+		  (#8690)
+		* purple_notify_user_info_add_pair_plaintext
+		* purple_media_get_active_local_candidates
+		* purple_media_get_active_remote_candidates
+		* purple_media_manager_get_video_caps (Jakub Adam) (#13095)
+		* purple_media_manager_set_video_caps (Jakub Adam) (#13095)
+
+	Pidgin:
+		Added:
+		* pidgin_make_scrollable (Gabriel Schulhof) (#10599)
 
 version 2.7.11 (03/10/2011):
 	* libpurple:
--- a/NEWS	Mon Mar 14 04:15:33 2011 +0000
+++ b/NEWS	Mon Mar 14 04:34:34 2011 +0000
@@ -2,7 +2,7 @@
 
 Our development blog is available at: http://planet.pidgin.im
 
-2.7.12 (??/??/????):
+2.8.0 (??/??/????):
 
 2.7.11 (03/10/2011):
 	John: Yet another release.  This time around we finally fixed that
--- a/configure.ac	Mon Mar 14 04:15:33 2011 +0000
+++ b/configure.ac	Mon Mar 14 04:34:34 2011 +0000
@@ -43,10 +43,10 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [7])
+m4_define([purple_lt_current], [8])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [7])
-m4_define([purple_micro_version], [12])
+m4_define([purple_minor_version], [8])
+m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
--- a/doc/account-signals.dox	Mon Mar 14 04:15:33 2011 +0000
+++ b/doc/account-signals.dox	Mon Mar 14 04:34:34 2011 +0000
@@ -14,6 +14,7 @@
   @signal account-actions-changed
   @signal account-alias-changed
   @signal account-authorization-requested
+  @signal account-authorization-requested-with-message
   @signal account-authorization-denied
   @signal account-authorization-granted
   @signal account-error-changed
@@ -158,6 +159,23 @@
   @since 2.3.0
  @endsignaldef
 
+ @signaldef account-authorization-requested-with-message
+  @signalproto
+int (*account_authorization_requested)(PurpleAccount *account, const char *user, const char *message);
+  @endsignalproto
+  @signaldesc
+   Emitted when a user requests authorization.
+  @param account The account.
+  @param user    The name of the user requesting authorization.
+  @param message The authorization request message
+  @return PURPLE_ACCOUNT_RESPONSE_IGNORE to silently ignore the request,
+          PURPLE_ACCOUNT_RESPONSE_DENY to block the request (the sender might
+          get informed, PURPLE_ACCOUNT_RESPONSE_ACCEPT if the request should be
+          granted. If PURPLE_ACCOUNT_RESPONSE_PASS is returned, then the user
+          will be prompted with the request.
+  @since 2.8.0
+ @endsignaldef
+
  @signaldef account-authorization-denied
   @signalproto
 void (*account_authorization_denied)(PurpleAccount *account, const char *user);
--- a/libpurple/account.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/account.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1441,6 +1441,27 @@
 		return NULL;
 	}
 
+	plugin_return = GPOINTER_TO_INT(
+			purple_signal_emit_return_1(
+				purple_accounts_get_handle(),
+				"account-authorization-requested-with-message",
+				account, remote_user, message
+			));
+
+	switch (plugin_return)
+	{
+		case PURPLE_ACCOUNT_RESPONSE_IGNORE:
+			return NULL;
+		case PURPLE_ACCOUNT_RESPONSE_ACCEPT:
+			if (auth_cb != NULL)
+				auth_cb(user_data);
+			return NULL;
+		case PURPLE_ACCOUNT_RESPONSE_DENY:
+			if (deny_cb != NULL)
+				deny_cb(user_data);
+			return NULL;
+	}
+
 	if (ui_ops != NULL && ui_ops->request_authorize != NULL) {
 		info            = g_new0(PurpleAccountRequestInfo, 1);
 		info->type      = PURPLE_ACCOUNT_REQUEST_AUTHORIZATION;
@@ -1915,6 +1936,20 @@
 	}
 }
 
+gboolean
+purple_account_get_silence_suppression(const PurpleAccount *account)
+{
+	return purple_account_get_bool(account, "silence-suppression", FALSE);
+}
+
+void
+purple_account_set_silence_suppression(PurpleAccount *account, gboolean value)
+{
+	g_return_if_fail(account != NULL);
+
+	purple_account_set_bool(account, "silence-suppression", value);
+}
+
 void
 purple_account_clear_settings(PurpleAccount *account)
 {
@@ -3046,6 +3081,13 @@
 										PURPLE_SUBTYPE_ACCOUNT),
 						purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "account-authorization-requested-with-message",
+						purple_marshal_INT__POINTER_POINTER_POINTER,
+						purple_value_new(PURPLE_TYPE_INT), 3,
+						purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						purple_value_new(PURPLE_TYPE_STRING),
+						purple_value_new(PURPLE_TYPE_STRING));
 	purple_signal_register(handle, "account-authorization-denied",
 						purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 						purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/account.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/account.h	Mon Mar 14 04:34:34 2011 +0000
@@ -59,6 +59,16 @@
 	PURPLE_ACCOUNT_REQUEST_AUTHORIZATION = 0 /* Account authorization request */
 } PurpleAccountRequestType;
 
+/**
+ * Account request response types
+ */
+typedef enum
+{
+	PURPLE_ACCOUNT_RESPONSE_IGNORE = -2,
+	PURPLE_ACCOUNT_RESPONSE_DENY = -1,
+	PURPLE_ACCOUNT_RESPONSE_PASS = 0,
+	PURPLE_ACCOUNT_RESPONSE_ACCEPT = 1
+} PurpleAccountRequestResponse;
 
 /**  Account UI operations, used to notify the user of status changes and when
  *   buddies add this account to their buddy lists.
@@ -503,6 +513,24 @@
 	PurpleGetPublicAliasFailureCallback failure_cb);
 
 /**
+ * Return whether silence suppression is used during voice call.
+ *
+ * @param account The account.
+ *
+ * @return @c TRUE if suppression is used, or @c FALSE if not.
+ */
+gboolean purple_account_get_silence_suppression(const PurpleAccount *account);
+
+/**
+ * Sets whether silence suppression is used during voice call.
+ *
+ * @param account The account.
+ * @param value   @c TRUE if suppression should be used.
+ */
+void purple_account_set_silence_suppression(PurpleAccount *account,
+											gboolean value);
+
+/**
  * Clears all protocol-specific settings on an account.
  *
  * @param account The account.
--- a/libpurple/conversation.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/conversation.c	Mon Mar 14 04:34:34 2011 +0000
@@ -2135,6 +2135,8 @@
 	cb->name = g_strdup(name);
 	cb->flags = flags;
 	cb->alias = g_strdup(alias);
+	cb->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
+										   g_free, g_free);
 
 	PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
 	return cb;
@@ -2167,6 +2169,7 @@
 	g_free(cb->alias);
 	g_free(cb->alias_key);
 	g_free(cb->name);
+	g_hash_table_destroy(cb->attributes);
 
 	PURPLE_DBUS_UNREGISTER_POINTER(cb);
 	g_free(cb);
@@ -2180,6 +2183,76 @@
 	return cb->name;
 }
 
+const char *
+purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key)
+{
+	g_return_val_if_fail(cb != NULL, NULL);
+	g_return_val_if_fail(key != NULL, NULL);
+	
+	return g_hash_table_lookup(cb->attributes, key);
+}
+
+static void
+append_attribute_key(gpointer key, gpointer value, gpointer user_data)
+{
+	GList **list = user_data;
+	*list = g_list_prepend(*list, key);
+}
+
+GList *
+purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb)
+{
+	GList *keys = NULL;
+	
+	g_return_val_if_fail(cb != NULL, NULL);
+	
+	g_hash_table_foreach(cb->attributes, (GHFunc)append_attribute_key, &keys);
+	
+	return keys;
+}
+
+void
+purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value)
+{
+	PurpleConversation *conv;
+	PurpleConversationUiOps *ops;
+	
+	g_return_if_fail(cb != NULL);
+	g_return_if_fail(key != NULL);
+	g_return_if_fail(value != NULL);
+	
+	g_hash_table_replace(cb->attributes, g_strdup(key), g_strdup(value));
+	
+	conv = purple_conv_chat_get_conversation(chat);
+	ops = purple_conversation_get_ui_ops(conv);
+	
+	if (ops != NULL && ops->chat_update_user != NULL)
+		ops->chat_update_user(conv, cb->name);
+}
+
+void
+purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values)
+{
+	PurpleConversation *conv;
+	PurpleConversationUiOps *ops;
+	
+	g_return_if_fail(cb != NULL);
+	g_return_if_fail(keys != NULL);
+	g_return_if_fail(values != NULL);
+	
+	while (keys != NULL && values != NULL) {
+		g_hash_table_replace(cb->attributes, g_strdup(keys->data), g_strdup(values->data));
+		keys = g_list_next(keys);
+		values = g_list_next(values);
+	}
+	
+	conv = purple_conv_chat_get_conversation(chat);
+	ops = purple_conversation_get_ui_ops(conv);
+	
+	if (ops != NULL && ops->chat_update_user != NULL)
+		ops->chat_update_user(conv, cb->name);
+}
+
 GList *
 purple_conversation_get_extended_menu(PurpleConversation *conv)
 {
--- a/libpurple/conversation.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/conversation.h	Mon Mar 14 04:34:34 2011 +0000
@@ -139,7 +139,8 @@
 	PURPLE_CBFLAGS_HALFOP        = 0x0002, /**< Half-op                      */
 	PURPLE_CBFLAGS_OP            = 0x0004, /**< Channel Op or Moderator      */
 	PURPLE_CBFLAGS_FOUNDER       = 0x0008, /**< Channel Founder              */
-	PURPLE_CBFLAGS_TYPING        = 0x0010  /**< Currently typing             */
+	PURPLE_CBFLAGS_TYPING        = 0x0010, /**< Currently typing             */
+	PURPLE_CBFLAGS_AWAY          = 0x0020  /**< Currently away. @since 2.8.0 */
 
 } PurpleConvChatBuddyFlags;
 
@@ -300,6 +301,9 @@
 	PurpleConvChatBuddyFlags flags;  /**< A bitwise OR of flags for this participant,
 	                                  *   such as whether they are a channel operator.
 	                                  */
+	GHashTable *attributes;          /**< A hash table of attributes about the user, such as
+                                    *   real name, user@host, etc.
+                                    */
 };
 
 /**
@@ -513,6 +517,46 @@
 const char *purple_conversation_get_name(const PurpleConversation *conv);
 
 /**
+ * Get an attribute of a chat buddy
+ *
+ * @param cb	The chat buddy.
+ * @param key	The key of the attribute.
+ *
+ * @return The value of the attribute key.
+ */
+const char *purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key);
+
+/**
+ * Get the keys of all atributes of a chat buddy
+ *
+ * @param cb	The chat buddy.
+ *
+ * @return A list of the attributes of a chat buddy.
+ */
+GList *purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb);
+	
+/**
+ * Set an attribute of a chat buddy
+ *
+ * @param chat	The chat.
+ * @param cb	The chat buddy.
+ * @param key	The key of the attribute.
+ * @param value	The value of the attribute.
+ */
+void purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value);
+
+/**
+ * Set attributes of a chat buddy
+ *
+ * @param chat	The chat.
+ * @param cb	The chat buddy.
+ * @param keys	A GList of the keys.
+ * @param values A GList of the values.
+ */
+void
+purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values);
+
+/**
  * Enables or disables logging for this conversation.
  *
  * @param conv The conversation.
--- a/libpurple/media-gst.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/media-gst.h	Mon Mar 14 04:34:34 2011 +0000
@@ -159,6 +159,31 @@
 PurpleMediaElementInfo *purple_media_manager_get_active_element(
 		PurpleMediaManager *manager, PurpleMediaElementType type);
 
+/**
+ * Reduces media formats supported by the video source to given set.
+ *
+ * Useful to force negotiation of smaller picture resolution more suitable for
+ * use with particular codec and communication protocol without rescaling.
+ *
+ * @param manager The media manager to set the media formats.
+ * @param caps Set of allowed media formats.
+ *
+ * @since 2.8.0
+ */
+void purple_media_manager_set_video_caps(PurpleMediaManager *manager,
+		GstCaps *caps);
+
+/**
+ * Returns current set of media formats limiting the output from video source.
+ *
+ * @param manager The media manager to get the media formats from.
+ *
+ * @return @c GstCaps limiting the video source's formats.
+ *
+ * @since 2.8.0
+ */
+GstCaps *purple_media_manager_get_video_caps(PurpleMediaManager *manager);
+
 gchar *purple_media_element_info_get_id(PurpleMediaElementInfo *info);
 gchar *purple_media_element_info_get_name(PurpleMediaElementInfo *info);
 PurpleMediaElementType purple_media_element_info_get_element_type(
--- a/libpurple/media.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/media.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1151,12 +1151,6 @@
 #endif
 }
 
-#if 0
-/*
- * These two functions aren't being used and I'd rather not lock in the API
- * until they are needed. If they ever are.
- */
-
 GList *
 purple_media_get_active_local_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant)
@@ -1186,7 +1180,6 @@
 	return NULL;
 #endif
 }
-#endif
 
 gboolean
 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
--- a/libpurple/media.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/media.h	Mon Mar 14 04:34:34 2011 +0000
@@ -230,12 +230,6 @@
 					 const gchar *sess_id,
 					 const gchar *participant);
 
-#if 0
-/*
- * These two functions aren't being used and I'd rather not lock in the API
- * until they are needed. If they ever are.
- */
-
 /**
  * Gets the active local candidates for the stream.
  *
@@ -245,6 +239,8 @@
  *                    from.
  *
  * @return The active candidates retrieved.
+ *
+ * @since 2.8.0
  */
 GList *purple_media_get_active_local_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
@@ -258,10 +254,11 @@
  *                    from.
  *
  * @return The remote candidates retrieved.
+ *
+ * @since 2.8.0
  */
 GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
-#endif
 
 /**
  * Sets remote candidates from the stream.
--- a/libpurple/media/backend-fs2.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/media/backend-fs2.c	Mon Mar 14 04:34:34 2011 +0000
@@ -155,13 +155,7 @@
 
 static void
 purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self)
-{
-	PurpleMediaBackendFs2Private *priv =
-			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
-
-	priv->silence_threshold = purple_prefs_get_int(
-			"/purple/media/audio/silence_threshold") / 100.0;
-}
+{}
 
 static gboolean
 event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad)
@@ -817,9 +811,11 @@
 
 		if (!strncmp(name, "sendlevel_", 10)) {
 			session = get_session(self, name+10);
-			percent = gst_msg_db_to_percent(msg, "decay");
-			g_object_set(session->srcvalve,
-					"drop", (percent < priv->silence_threshold), NULL);
+			if (priv->silence_threshold > 0) {
+				percent = gst_msg_db_to_percent(msg, "decay");
+				g_object_set(session->srcvalve,
+						"drop", (percent < priv->silence_threshold), NULL);
+			}
 		}
 
 		g_free(name);
@@ -1255,6 +1251,13 @@
 		return FALSE;
 	}
 
+	if (purple_account_get_silence_suppression(
+				purple_media_get_account(priv->media)))
+		priv->silence_threshold = purple_prefs_get_int(
+				"/purple/media/audio/silence_threshold") / 100.0;
+	else
+		priv->silence_threshold = 0;
+
 	pipeline = purple_media_manager_get_pipeline(
 			purple_media_get_manager(priv->media));
 
--- a/libpurple/mediamanager.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/mediamanager.c	Mon Mar 14 04:34:34 2011 +0000
@@ -420,7 +420,30 @@
 
 #ifdef USE_GSTREAMER
 
-static GstCaps *
+void
+purple_media_manager_set_video_caps(PurpleMediaManager *manager, GstCaps *caps)
+{
+#ifdef USE_VV
+	if (manager->priv->video_caps)
+		gst_caps_unref(manager->priv->video_caps);
+
+	manager->priv->video_caps = caps;
+
+	if (manager->priv->pipeline && manager->priv->video_src) {
+		gchar *id = purple_media_element_info_get_id(manager->priv->video_src);
+		GstElement *src = gst_bin_get_by_name(GST_BIN(manager->priv->pipeline), id);
+
+		if (src) {
+			GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps");
+			g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+		}
+
+		g_free(id);
+	}
+#endif
+}
+
+GstCaps *
 purple_media_manager_get_video_caps(PurpleMediaManager *manager)
 {
 #ifdef USE_VV
--- a/libpurple/notify.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/notify.c	Mon Mar 14 04:34:34 2011 +0000
@@ -602,6 +602,18 @@
 }
 
 void
+purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+{
+	gchar *escaped;
+	PurpleNotifyUserInfoEntry *entry;
+
+	escaped = g_markup_escape_text(value, -1);
+	entry = purple_notify_user_info_entry_new(label, escaped);
+	g_free(escaped);
+	user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+}
+
+void
 purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
 {
 	PurpleNotifyUserInfoEntry *entry;
--- a/libpurple/notify.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/notify.h	Mon Mar 14 04:34:34 2011 +0000
@@ -540,13 +540,28 @@
  *                   a colon.  If NULL, value will be displayed without a
  *                   label.
  * @param value      The value, which might be displayed by a UI after
- *                   the label.  If NULL, label will still be displayed;
- *                   the UI should then treat label as independent and not
+ *                   the label.  This should be valid HTML.  If you want
+ *                   to insert plaintext then use
+ *                   purple_notify_user_info_add_pair_plaintext(), instead.
+ *                   If this is NULL the label will still be displayed;
+ *                   the UI should treat label as independent and not
  *                   include a colon if it would otherwise.
  */
+/*
+ * TODO: In 3.0.0 this function should be renamed to
+ *       purple_notify_user_info_add_pair_html().  And optionally
+ *       purple_notify_user_info_add_pair_plaintext() could be renamed to
+ *       purple_notify_user_info_add_pair().
+ */
 void purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 /**
+ * Like purple_notify_user_info_add_pair, but value should be plaintext
+ * and will be escaped using g_markup_escape_text().
+ */
+void purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+
+/**
  * Prepend a label/value pair to a PurpleNotifyUserInfo object
  *
  * @param user_info  The PurpleNotifyUserInfo
--- a/libpurple/protocols/irc/irc.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/irc/irc.c	Mon Mar 14 04:34:34 2011 +0000
@@ -41,6 +41,8 @@
 
 static void irc_ison_buddy_init(char *name, struct irc_buddy *ib, GList **list);
 
+static void irc_who_channel(PurpleConversation *conv, struct irc_conn *irc);
+
 static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b);
 static GList *irc_status_types(PurpleAccount *account);
 static GList *irc_actions(PurplePlugin *plugin, gpointer context);
@@ -232,6 +234,26 @@
 	*list = g_list_append(*list, ib);
 }
 
+
+gboolean irc_who_channel_timeout(struct irc_conn *irc)
+{
+	// WHO all of our channels.
+	g_list_foreach(purple_get_conversations(), (GFunc)irc_who_channel, (gpointer)irc);
+	
+	return TRUE;
+}
+
+static void irc_who_channel(PurpleConversation *conv, struct irc_conn *irc)
+{
+	if (purple_conversation_get_account(conv) == irc->account && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+		char *buf = irc_format(irc, "vc", "WHO", purple_conversation_get_name(conv));
+		
+		purple_debug(PURPLE_DEBUG_INFO, "irc", "Performing periodic who on %s", purple_conversation_get_name(conv));
+		irc_send(irc, buf);
+		g_free(buf);
+	}
+}
+
 static void irc_ison_one(struct irc_conn *irc, struct irc_buddy *ib)
 {
 	char *buf;
@@ -517,6 +539,8 @@
 	}
 	if (irc->timer)
 		purple_timeout_remove(irc->timer);
+	if (irc->who_channel_timer)
+		purple_timeout_remove(irc->who_channel_timer);
 	g_hash_table_destroy(irc->cmds);
 	g_hash_table_destroy(irc->msgs);
 	g_hash_table_destroy(irc->buddies);
--- a/libpurple/protocols/irc/irc.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/irc/irc.h	Mon Mar 14 04:34:34 2011 +0000
@@ -55,6 +55,7 @@
 	char *server;
 	int fd;
 	guint timer;
+	guint who_channel_timer;
 	GHashTable *buddies;
 
 	gboolean ison_outstanding;
@@ -106,6 +107,7 @@
 
 int irc_send(struct irc_conn *irc, const char *buf);
 gboolean irc_blist_timeout(struct irc_conn *irc);
+gboolean irc_who_channel_timeout(struct irc_conn *irc);
 void irc_buddy_query(struct irc_conn *irc);
 
 char *irc_escape_privmsg(const char *text, gssize length);
@@ -164,6 +166,7 @@
 void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args);
+void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args);
 
 void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args);
 
--- a/libpurple/protocols/irc/msgs.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/irc/msgs.c	Mon Mar 14 04:34:34 2011 +0000
@@ -110,6 +110,8 @@
 	irc_blist_timeout(irc);
 	if (!irc->timer)
 		irc->timer = purple_timeout_add_seconds(45, (GSourceFunc)irc_blist_timeout, (gpointer)irc);
+    if (!irc->who_channel_timer)
+        irc->who_channel_timer = purple_timeout_add_seconds(300, (GSourceFunc)irc_who_channel_timeout, (gpointer)irc);
 }
 
 void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -400,6 +402,59 @@
 	memset(&irc->whois, 0, sizeof(irc->whois));
 }
 
+void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	if (!strcmp(name, "352")) {
+		PurpleConversation *conv;
+		PurpleConvChat *chat;
+		PurpleConvChatBuddy *cb;
+		
+		char *userhost, *realname;
+		
+		PurpleConvChatBuddyFlags flags;
+		GList *keys = NULL, *values = NULL;
+		
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
+		if (!conv) {
+			purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a WHO response for %s, which doesn't exist\n", args[1]);
+			return;
+		}
+
+		cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), args[5]);
+		if (!cb) {
+			purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a WHO response for %s who isn't a buddy.\n", args[5]);
+			return;
+		}
+
+		chat = PURPLE_CONV_CHAT(conv);
+		
+		userhost = g_strdup_printf("%s@%s", args[2], args[3]);
+		realname = g_strdup(args[8]);
+		
+		keys = g_list_prepend(keys, "userhost");
+		values = g_list_prepend(values, userhost);
+		
+		keys = g_list_prepend(keys, "realname");
+		values = g_list_prepend(values, realname);
+		
+		purple_conv_chat_cb_set_attributes(chat, cb, keys, values);
+		
+		g_list_free(keys);
+		g_list_free(values);
+		
+		g_free(userhost);
+		g_free(realname);
+		
+		flags = purple_conv_chat_user_get_flags(chat, cb->name);
+
+		if (args[6][0] == 'G' && !(flags & PURPLE_CBFLAGS_AWAY)) {
+			purple_conv_chat_user_set_flags(chat, cb->name, flags | PURPLE_CBFLAGS_AWAY);
+		} else if(args[6][0] == 'H' && (flags & PURPLE_CBFLAGS_AWAY)) {
+			purple_conv_chat_user_set_flags(chat, cb->name, flags & ~PURPLE_CBFLAGS_AWAY);
+		}
+	}
+}
+
 void irc_msg_list(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	if (!irc->roomlist)
@@ -797,7 +852,10 @@
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
 	PurpleConversation *convo;
-	char *nick = irc_mask_nick(from), *userhost;
+	PurpleConvChat *chat;
+	PurpleConvChatBuddy *cb;
+
+	char *nick = irc_mask_nick(from), *userhost, *buf;
 	struct irc_buddy *ib;
 	static int id = 1;
 
@@ -820,6 +878,12 @@
 		}
 		purple_conversation_set_data(convo, IRC_NAMES_FLAG,
 					   GINT_TO_POINTER(FALSE));
+		
+		// Get the real name and user host for all participants.
+		buf = irc_format(irc, "vc", "WHO", args[0]);
+		irc_send(irc, buf);
+		g_free(buf);
+		
 		/* Until purple_conversation_present does something that
 		 * one would expect in Pidgin, this call produces buggy
 		 * behavior both for the /join and auto-join cases. */
@@ -835,8 +899,16 @@
 	}
 
 	userhost = irc_mask_userhost(from);
-	purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), nick, userhost, PURPLE_CBFLAGS_NONE, TRUE);
-
+	chat = PURPLE_CONV_CHAT(convo);
+	
+	purple_conv_chat_add_user(chat, nick, userhost, PURPLE_CBFLAGS_NONE, TRUE);
+	
+	cb = purple_conv_chat_cb_find(chat, nick);
+	
+	if (cb) {
+		purple_conv_chat_cb_set_attribute(chat, cb, "userhost", userhost);		
+	}
+	
 	if ((ib = g_hash_table_lookup(irc->buddies, nick)) != NULL) {
 		ib->new_online_status = TRUE;
 		irc_buddy_status(nick, ib, irc);
--- a/libpurple/protocols/irc/parse.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/irc/parse.c	Mon Mar 14 04:34:34 2011 +0000
@@ -65,6 +65,7 @@
 	{ "319", "nn:", irc_msg_whois },	/* Whois channels		*/
 	{ "320", "nn:", irc_msg_whois },	/* Whois (fn ident)		*/
 	{ "314", "nnnvv:", irc_msg_whois },	/* Whowas user			*/
+	{ "315", "nt:", irc_msg_who },      /* end of WHO channel   */
 	{ "369", "nt:", irc_msg_endwhois },	/* End of WHOWAS		*/
 	{ "321", "*", irc_msg_list },		/* Start of list		*/
 	{ "322", "ncv:", irc_msg_list },	/* List.			*/
@@ -73,6 +74,7 @@
 	{ "331", "nc:",	irc_msg_topic },	/* No channel topic		*/
 	{ "332", "nc:", irc_msg_topic },	/* Channel topic		*/
 	{ "333", "*", irc_msg_ignore },		/* Topic setter stuff		*/
+	{ "352", "nvcvnvvv:", irc_msg_who },/* Channel WHO			*/
 	{ "353", "nvc:", irc_msg_names },	/* Names list			*/
 	{ "366", "nc:", irc_msg_names },	/* End of names			*/
 	{ "367", "ncnnv", irc_msg_ban },	/* Ban list			*/
--- a/libpurple/protocols/jabber/buddy.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1021,7 +1021,7 @@
 				if (!serverside_alias)
 					serverside_alias = g_strdup(text);
 
-				purple_notify_user_info_add_pair(user_info, _("Full Name"), text);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Full Name"), text);
 			} else if(!strcmp(child->name, "N")) {
 				for(child2 = child->child; child2; child2 = child2->next)
 				{
@@ -1032,11 +1032,11 @@
 
 					text2 = xmlnode_get_data(child2);
 					if(text2 && !strcmp(child2->name, "FAMILY")) {
-						purple_notify_user_info_add_pair(user_info, _("Family Name"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Family Name"), text2);
 					} else if(text2 && !strcmp(child2->name, "GIVEN")) {
-						purple_notify_user_info_add_pair(user_info, _("Given Name"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Given Name"), text2);
 					} else if(text2 && !strcmp(child2->name, "MIDDLE")) {
-						purple_notify_user_info_add_pair(user_info, _("Middle Name"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Middle Name"), text2);
 					}
 					g_free(text2);
 				}
@@ -1047,10 +1047,10 @@
 					g_free(serverside_alias);
 					serverside_alias = g_strdup(text);
 
-					purple_notify_user_info_add_pair(user_info, _("Nickname"), text);
+					purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), text);
 				}
 			} else if(text && !strcmp(child->name, "BDAY")) {
-				purple_notify_user_info_add_pair(user_info, _("Birthday"), text);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Birthday"), text);
 			} else if(!strcmp(child->name, "ADR")) {
 				gboolean address_line_added = FALSE;
 
@@ -1074,25 +1074,25 @@
 					}
 
 					if(!strcmp(child2->name, "POBOX")) {
-						purple_notify_user_info_add_pair(user_info, _("P.O. Box"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("P.O. Box"), text2);
 					} else if (g_str_equal(child2->name, "EXTADD") || g_str_equal(child2->name, "EXTADR")) {
 						/*
 						 * EXTADD is correct, EXTADR is generated by other
 						 * clients. The next time someone reads this, remove
 						 * EXTADR.
 						 */
-						purple_notify_user_info_add_pair(user_info, _("Extended Address"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Extended Address"), text2);
 					} else if(!strcmp(child2->name, "STREET")) {
-						purple_notify_user_info_add_pair(user_info, _("Street Address"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Street Address"), text2);
 					} else if(!strcmp(child2->name, "LOCALITY")) {
-						purple_notify_user_info_add_pair(user_info, _("Locality"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Locality"), text2);
 					} else if(!strcmp(child2->name, "REGION")) {
-						purple_notify_user_info_add_pair(user_info, _("Region"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Region"), text2);
 					} else if(!strcmp(child2->name, "PCODE")) {
-						purple_notify_user_info_add_pair(user_info, _("Postal Code"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Postal Code"), text2);
 					} else if(!strcmp(child2->name, "CTRY")
 								|| !strcmp(child2->name, "COUNTRY")) {
-						purple_notify_user_info_add_pair(user_info, _("Country"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Country"), text2);
 					}
 					g_free(text2);
 				}
@@ -1106,13 +1106,13 @@
 					/* show what kind of number it is */
 					number = xmlnode_get_data(child2);
 					if(number) {
-						purple_notify_user_info_add_pair(user_info, _("Telephone"), number);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number);
 						g_free(number);
 					}
 				} else if((number = xmlnode_get_data(child))) {
 					/* lots of clients (including purple) do this, but it's
 					 * out of spec */
-					purple_notify_user_info_add_pair(user_info, _("Telephone"), number);
+					purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number);
 					g_free(number);
 				}
 			} else if(!strcmp(child->name, "EMAIL")) {
@@ -1153,18 +1153,18 @@
 
 					text2 = xmlnode_get_data(child2);
 					if(text2 && !strcmp(child2->name, "ORGNAME")) {
-						purple_notify_user_info_add_pair(user_info, _("Organization Name"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Organization Name"), text2);
 					} else if(text2 && !strcmp(child2->name, "ORGUNIT")) {
-						purple_notify_user_info_add_pair(user_info, _("Organization Unit"), text2);
+						purple_notify_user_info_add_pair_plaintext(user_info, _("Organization Unit"), text2);
 					}
 					g_free(text2);
 				}
 			} else if(text && !strcmp(child->name, "TITLE")) {
-				purple_notify_user_info_add_pair(user_info, _("Job Title"), text);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Job Title"), text);
 			} else if(text && !strcmp(child->name, "ROLE")) {
-				purple_notify_user_info_add_pair(user_info, _("Role"), text);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Role"), text);
 			} else if(text && !strcmp(child->name, "DESC")) {
-				purple_notify_user_info_add_pair(user_info, _("Description"), text);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Description"), text);
 			} else if(!strcmp(child->name, "PHOTO") ||
 					!strcmp(child->name, "LOGO")) {
 				char *bintext = NULL;
--- a/libpurple/protocols/msn/msn.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1040,7 +1040,6 @@
 		const char *psm, *name;
 		const char *mediatype = NULL;
 		char *currentmedia = NULL;
-		char *tmp;
 
 		psm = purple_status_get_attr_string(status, "message");
 		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
@@ -1084,9 +1083,7 @@
 			}
 
 			if (psm != NULL && *psm) {
-				tmp = g_markup_escape_text(psm, -1);
-				purple_notify_user_info_add_pair(user_info, tmp2, tmp);
-				g_free(tmp);
+				purple_notify_user_info_add_pair_plaintext(user_info, tmp2, psm);
 			} else {
 				purple_notify_user_info_add_pair(user_info, _("Status"), tmp2);
 			}
@@ -1094,13 +1091,11 @@
 			g_free(tmp2);
 		} else {
 			if (psm != NULL && *psm) {
-				tmp = g_markup_escape_text(psm, -1);
 				if (purple_presence_is_idle(presence)) {
-					purple_notify_user_info_add_pair(user_info, _("Idle"), tmp);
+					purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), psm);
 				} else {
-					purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
+					purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), psm);
 				}
-				g_free(tmp);
 			} else {
 				if (purple_presence_is_idle(presence)) {
 					purple_notify_user_info_add_pair(user_info, _("Status"),
@@ -2304,9 +2299,7 @@
 		alias = purple_buddy_get_local_buddy_alias(b);
 		if (alias && alias[0])
 		{
-			char *aliastext = g_markup_escape_text(alias, -1);
-			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
-			g_free(aliastext);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
 		}
 
 		if ((alias = purple_buddy_get_server_alias(b)) != NULL)
--- a/libpurple/protocols/yahoo/libymsg.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/yahoo/libymsg.c	Mon Mar 14 04:34:34 2011 +0000
@@ -3990,7 +3990,6 @@
 void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	YahooFriend *f;
-	char *escaped;
 	char *status = NULL;
 	const char *presence = NULL;
 	PurpleAccount *account;
@@ -4029,14 +4028,12 @@
 	}
 
 	if (status != NULL) {
-		escaped = g_markup_escape_text(status, strlen(status));
-		purple_notify_user_info_add_pair(user_info, _("Status"), escaped);
+		purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status);
 		g_free(status);
-		g_free(escaped);
 	}
 
 	if (presence != NULL)
-		purple_notify_user_info_add_pair(user_info, _("Presence"), presence);
+		purple_notify_user_info_add_pair_plaintext(user_info, _("Presence"), presence);
 
 	if (f && full) {
 		YahooPersonalDetails *ypd = &f->ypd;
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Mon Mar 14 04:34:34 2011 +0000
@@ -701,14 +701,12 @@
 	if (b) {
 		const char *balias = purple_buddy_get_local_buddy_alias(b);
 		if(balias && balias[0]) {
-			char *aliastext = g_markup_escape_text(balias, -1);
-			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
-			g_free(aliastext);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
 		}
 		#if 0
 		if (b->idle > 0) {
 			char *idletime = purple_str_seconds_to_string(time(NULL) - b->idle);
-			purple_notify_user_info_add_pair(user_info, _("Idle"), idletime);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), idletime);
 			g_free(idletime);
 		}
 		#endif
@@ -719,7 +717,7 @@
 		if ((f = yahoo_friend_find(info_data->gc, purple_buddy_get_name(b)))) {
 			const char *ip;
 			if ((ip = yahoo_friend_get_ip(f)))
-				purple_notify_user_info_add_pair(user_info, _("IP Address"), ip);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), ip);
 		}
 	}
 }
--- a/libpurple/signals.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/signals.c	Mon Mar 14 04:34:34 2011 +0000
@@ -818,6 +818,21 @@
 		*return_val = GINT_TO_POINTER(ret_val);
 }
 
+	void
+purple_marshal_INT__POINTER_POINTER_POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val)
+{
+	gint ret_val;
+	void *arg1 = va_arg(args, void *);
+	void *arg2 = va_arg(args, void *);
+	void *arg3 = va_arg(args, void *);
+
+	ret_val = ((gint (*)(void *, void *, void *, void *))cb)(arg1, arg2, arg3, data);
+
+	if (return_val != NULL)
+		*return_val = GINT_TO_POINTER(ret_val);
+}
+
 void
 purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val)
--- a/libpurple/signals.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/libpurple/signals.h	Mon Mar 14 04:34:34 2011 +0000
@@ -330,6 +330,8 @@
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_INT__POINTER_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
+void purple_marshal_INT__POINTER_POINTER_POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 
--- a/pidgin/gtkaccount.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkaccount.c	Mon Mar 14 04:34:34 2011 +0000
@@ -143,6 +143,10 @@
 	GtkWidget *proxy_user_entry;
 	GtkWidget *proxy_pass_entry;
 
+	/* Voice & Video Options*/
+	GtkWidget *voice_frame;
+	GtkWidget *suppression_check;
+
 } AccountPrefsDialog;
 
 static AccountsWindow *accounts_window = NULL;
@@ -159,6 +163,7 @@
 static void add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent);
 static void add_protocol_options(AccountPrefsDialog *dialog);
 static void add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent);
+static void add_voice_options(AccountPrefsDialog *dialog);
 
 static GtkWidget *
 add_pref_box(AccountPrefsDialog *dialog, GtkWidget *parent,
@@ -237,6 +242,7 @@
 	add_login_options(dialog,    dialog->top_vbox);
 	add_user_options(dialog,     dialog->top_vbox);
 	add_protocol_options(dialog);
+	add_voice_options(dialog);
 
 	gtk_widget_grab_focus(dialog->protocol_menu);
 
@@ -1163,6 +1169,39 @@
 					 G_CALLBACK(proxy_type_changed_cb), dialog);
 }
 
+static void
+add_voice_options(AccountPrefsDialog *dialog)
+{
+#ifdef USE_VV
+	if (!dialog->prpl_info || !dialog->prpl_info->initiate_media) {
+		if (dialog->voice_frame) {
+			gtk_widget_destroy(dialog->voice_frame);
+			dialog->voice_frame = NULL;
+			dialog->suppression_check = NULL;
+		}
+		return;
+	}
+
+	if (!dialog->voice_frame) {
+		dialog->voice_frame = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+		gtk_container_set_border_width(GTK_CONTAINER(dialog->voice_frame),
+										PIDGIN_HIG_BORDER);
+
+		dialog->suppression_check =
+				gtk_check_button_new_with_mnemonic(_("Use _silence suppression"));
+		gtk_box_pack_start(GTK_BOX(dialog->voice_frame), dialog->suppression_check,
+				FALSE, FALSE, 0);
+
+		gtk_notebook_append_page(GTK_NOTEBOOK(dialog->notebook),
+				dialog->voice_frame, gtk_label_new_with_mnemonic(_("_Voice and Video")));
+		gtk_widget_show_all(dialog->voice_frame);
+	}
+
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->suppression_check),
+								purple_account_get_silence_suppression(dialog->account));
+#endif
+}
+
 static gboolean
 account_win_destroy_cb(GtkWidget *w, GdkEvent *event,
 					   AccountPrefsDialog *dialog)
@@ -1437,6 +1476,12 @@
 		proxy_info = NULL;
 	}
 
+	/* Voice and Video settings */
+	if (dialog->voice_frame) {
+		purple_account_set_silence_suppression(account,
+				gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->suppression_check)));
+	}
+
 	/* If this is a new account, add it to our list */
 	if (new_acct)
 		purple_accounts_add(account);
@@ -1558,6 +1603,8 @@
 	gtk_widget_show(dbox);
 	add_proxy_options(dialog, dbox);
 
+	add_voice_options(dialog);
+
 	/* Cancel button */
 	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog);
 
@@ -2141,7 +2188,6 @@
 create_accounts_list(AccountsWindow *dialog)
 {
 	GtkWidget *frame;
-	GtkWidget *sw;
 	GtkWidget *label;
 	GtkWidget *treeview;
 	GtkTreeSelection *sel;
@@ -2181,16 +2227,6 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
 	gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook), label, NULL);
 
-	/* Create the scrolled window. */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-					GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-					GTK_SHADOW_NONE);
-	gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook), sw, NULL);
-	gtk_widget_show(sw);
-
 	/* Create the list model. */
 	dialog->model = gtk_list_store_new(NUM_COLUMNS,
 					GDK_TYPE_PIXBUF,   /* COLUMN_ICON */
@@ -2216,7 +2252,9 @@
 	g_signal_connect(G_OBJECT(treeview), "button_press_event",
 					 G_CALLBACK(account_treeview_double_click_cb), dialog);
 
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	gtk_notebook_append_page(GTK_NOTEBOOK(accounts_window->notebook),
+		pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1), 
+		NULL);
 
 	add_columns(treeview, dialog);
 	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(treeview));
--- a/pidgin/gtkblist.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkblist.c	Mon Mar 14 04:34:34 2011 +0000
@@ -5730,7 +5730,6 @@
 	GtkTreeViewColumn *column;
 	GtkWidget *menu;
 	GtkWidget *ebox;
-	GtkWidget *sw;
 	GtkWidget *sep;
 	GtkWidget *label;
 	GtkWidget *close;
@@ -5881,11 +5880,6 @@
 	g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(headline_box_press_cb), gtkblist);
 
 	/****************************** GtkTreeView **********************************/
-	sw = gtk_scrolled_window_new(NULL,NULL);
-	gtk_widget_show(sw);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_NONE);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
 	gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS,
 						 GDK_TYPE_PIXBUF, /* Status icon */
 						 G_TYPE_BOOLEAN,  /* Status icon visible */
@@ -5965,8 +5959,9 @@
 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview),
 			pidgin_blist_search_equal_func, NULL, NULL);
 
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0);
-	gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), 
+		pidgin_make_scrollable(gtkblist->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1), 
+		TRUE, TRUE, 0);
 
 	sep = gtk_hseparator_new();
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sep, FALSE, FALSE, 0);
--- a/pidgin/gtkcertmgr.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkcertmgr.c	Mon Mar 14 04:34:34 2011 +0000
@@ -390,7 +390,6 @@
 {
 	GtkWidget *bbox;
 	GtkListStore *store;
-	GtkWidget *sw;
 
 	/* This block of variables will end up in tpm_dat */
 	GtkTreeView *listview;
@@ -417,16 +416,6 @@
 	g_signal_connect(G_OBJECT(mgmt_widget), "destroy",
 			 G_CALLBACK(tls_peers_mgmt_destroy), NULL);
 
-	/* Scrolled window */
-	sw = gtk_scrolled_window_new(NULL,NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-			GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(mgmt_widget), GTK_WIDGET(sw),
-			TRUE, TRUE, /* Take up lots of space */
-			0);
-	gtk_widget_show(GTK_WIDGET(sw));
-
 	/* List view */
 	store = gtk_list_store_new(TPM_N_COLUMNS, G_TYPE_STRING);
 
@@ -463,7 +452,10 @@
 	g_signal_connect(G_OBJECT(select), "changed",
 			 G_CALLBACK(tls_peers_mgmt_select_chg_cb), NULL);
 
-	gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(listview));
+	gtk_box_pack_start(GTK_BOX(mgmt_widget), 
+			pidgin_make_scrollable(GTK_WIDGET(listview), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
+			TRUE, TRUE, /* Take up lots of space */
+			0);
 	gtk_widget_show(GTK_WIDGET(listview));
 
 	/* Fill the list for the first time */
--- a/pidgin/gtkconv.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkconv.c	Mon Mar 14 04:34:34 2011 +0000
@@ -4693,7 +4693,7 @@
 setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
 {
 	PidginChatPane *gtkchat = gtkconv->u.chat;
-	GtkWidget *lbox, *sw, *list;
+	GtkWidget *lbox, *list;
 	GtkListStore *ls;
 	GtkCellRenderer *rend;
 	GtkTreeViewColumn *col;
@@ -4713,12 +4713,6 @@
 	gtk_widget_show(gtkchat->count);
 
 	/* Setup the list of users. */
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(lbox), sw, TRUE, TRUE, 0);
-	gtk_widget_show(sw);
 
 	ls = gtk_list_store_new(CHAT_USERS_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
 							G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT,
@@ -4788,7 +4782,9 @@
 
 	gtkchat->list = list;
 
-	gtk_container_add(GTK_CONTAINER(sw), list);
+	gtk_box_pack_start(GTK_BOX(lbox), 
+		pidgin_make_scrollable(list, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
+		TRUE, TRUE, 0);
 }
 
 static gboolean
@@ -4895,7 +4891,6 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	PurpleBuddy *buddy;
 	gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
-	GtkPolicyType imhtml_sw_hscroll;
 	int buddyicon_size = 0;
 
 	/* Setup the top part of the pane */
@@ -5012,10 +5007,7 @@
 	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
 	g_object_set_data(G_OBJECT(gtkconv->imhtml), "gtkconv", gtkconv);
 
-	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               &imhtml_sw_hscroll, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
+	g_object_set(G_OBJECT(imhtml_sw), "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
 
 	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
--- a/pidgin/gtkft.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkft.c	Mon Mar 14 04:34:34 2011 +0000
@@ -557,22 +557,12 @@
 static GtkWidget *
 setup_tree(PidginXferDialog *dialog)
 {
-	GtkWidget *sw;
 	GtkWidget *tree;
 	GtkListStore *model;
 	GtkCellRenderer *renderer;
 	GtkTreeViewColumn *column;
 	GtkTreeSelection *selection;
 
-	/* Create the scrolled window. */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-						GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-					GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_widget_show(sw);
-
 	/* Build the tree model */
 	/* Transfer type, Progress Bar, Filename, Size, Remaining */
 	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_INT,
@@ -636,10 +626,9 @@
 
 	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree));
 
-	gtk_container_add(GTK_CONTAINER(sw), tree);
 	gtk_widget_show(tree);
 
-	return sw;
+	return tree;
 }
 
 static GtkWidget *
@@ -713,7 +702,6 @@
 	PidginXferDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *vbox1, *vbox2;
-	GtkWidget *sw;
 	GtkWidget *expander;
 	GtkWidget *alignment;
 	GtkWidget *table;
@@ -744,9 +732,9 @@
 	gtk_widget_show(vbox2);
 
 	/* Setup the listbox */
-	sw = setup_tree(dialog);
-	gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0);
-	gtk_widget_set_size_request(sw,-1, 140);
+	gtk_box_pack_start(GTK_BOX(vbox2), 
+		pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140), 
+		TRUE, TRUE, 0);
 
 	/* "Close this window when all transfers finish" */
 	checkbox = gtk_check_button_new_with_mnemonic(
--- a/pidgin/gtkimhtmltoolbar.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Mon Mar 14 04:34:34 2011 +0000
@@ -858,14 +858,9 @@
 		g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
 	}
 
-	scrolled = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_NONE);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
-			GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+	scrolled = pidgin_make_scrollable(smiley_table, GTK_POLICY_NEVER, GTK_POLICY_NEVER, GTK_SHADOW_NONE, -1, -1);
 	gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
-	gtk_widget_show(scrolled);
-
-	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), smiley_table);
 	gtk_widget_show(smiley_table);
 
 	viewport = gtk_widget_get_parent(smiley_table);
@@ -890,8 +885,10 @@
 	 * makes one or both scrollbars visible (sometimes).
 	 * I too think this hack is gross. But I couldn't find a better way -- sadrul */
 	gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
-			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	g_object_set(G_OBJECT(scrolled),
+		"hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		"vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		NULL);
 
 #ifdef _WIN32
 	winpidgin_ensure_onscreen(dialog);
--- a/pidgin/gtklog.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtklog.c	Mon Mar 14 04:34:34 2011 +0000
@@ -531,7 +531,6 @@
 	GtkWidget *title_box;
 	char *text;
 	GtkWidget *pane;
-	GtkWidget *sw;
 	GtkCellRenderer *rend;
 	GtkTreeViewColumn *col;
 	GtkTreeSelection *sel;
@@ -614,10 +613,6 @@
 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv->window)->vbox), pane, TRUE, TRUE, 0);
 
 	/* List *************/
-	sw = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-	gtk_paned_add1(GTK_PANED(pane), sw);
 	lv->treestore = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
 	lv->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (lv->treestore));
 	g_object_unref(G_OBJECT(lv->treestore));
@@ -625,7 +620,8 @@
 	col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col);
 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv->treeview), FALSE);
-	gtk_container_add (GTK_CONTAINER (sw), lv->treeview);
+	gtk_paned_add1(GTK_PANED(pane), 
+		pidgin_make_scrollable(lv->treeview, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1));
 
 	populate_log_tree(lv);
 
--- a/pidgin/gtknotify.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtknotify.c	Mon Mar 14 04:34:34 2011 +0000
@@ -956,7 +956,6 @@
 
 	GtkWidget *vbox;
 	GtkWidget *label;
-	GtkWidget *sw;
 	PidginNotifySearchResultsData *data;
 	char *label_text;
 	char *primary_esc, *secondary_esc;
@@ -1012,15 +1011,6 @@
 	model = gtk_list_store_newv(col_num, col_types);
 	g_free(col_types);
 
-	/* Setup the scrolled window containing the treeview */
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	gtk_widget_show(sw);
-
 	/* Setup the treeview */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
 	g_object_unref(G_OBJECT(model));
@@ -1029,7 +1019,9 @@
 	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
 								GTK_SELECTION_SINGLE);
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	gtk_box_pack_start(GTK_BOX(vbox),
+		pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
+		TRUE, TRUE, 0);
 	gtk_widget_show(treeview);
 
 	renderer = gtk_cell_renderer_pixbuf_new();
@@ -1488,7 +1480,6 @@
 	GtkTreeStore *model = NULL;
 	GtkWidget *dialog = NULL;
 	GtkWidget *label = NULL;
-	GtkWidget *sw;
 	GtkCellRenderer *rend;
 	GtkTreeViewColumn *column;
 	GtkWidget *button = NULL;
@@ -1526,10 +1517,6 @@
 	/* Golden ratio it up! */
 	gtk_widget_set_size_request(dialog, 550, 400);
 
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-
 	spec_dialog = g_new0(PidginNotifyDialog, 1);
 	spec_dialog->dialog = dialog;
 
@@ -1538,7 +1525,6 @@
 	g_object_unref(G_OBJECT(model));
 
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog->treeview), TRUE);
-	gtk_container_add(GTK_CONTAINER(sw), spec_dialog->treeview);
 
 	if (type == PIDGIN_NOTIFY_MAIL) {
 		gtk_window_set_title(GTK_WINDOW(dialog), _("New Mail"));
@@ -1660,7 +1646,9 @@
 	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 2);
+	gtk_box_pack_start(GTK_BOX(vbox), 
+		pidgin_make_scrollable(spec_dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
+		TRUE, TRUE, 2);
 
 	return spec_dialog;
 }
--- a/pidgin/gtkplugin.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkplugin.c	Mon Mar 14 04:34:34 2011 +0000
@@ -706,7 +706,6 @@
 
 void pidgin_plugin_dialog_show()
 {
-	GtkWidget *sw;
 	GtkWidget *event_view;
 	GtkListStore *ls;
 	GtkCellRenderer *rend, *rendt;
@@ -729,12 +728,6 @@
 	gtk_widget_set_sensitive(pref_button, FALSE);
 	gtk_window_set_role(GTK_WINDOW(plugin_dialog), "plugins");
 
-	sw = gtk_scrolled_window_new(NULL,NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-
-	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), sw, TRUE, TRUE, 0);
-
 	ls = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),
 					     1, GTK_SORT_ASCENDING);
@@ -779,7 +772,9 @@
 	gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
 	gtk_tree_view_column_set_sort_column_id(col, 1);
 	g_object_unref(G_OBJECT(ls));
-	gtk_container_add(GTK_CONTAINER(sw), event_view);
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), 
+		pidgin_make_scrollable(event_view, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1), 
+		TRUE, TRUE, 0);
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view), 1);
 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view),
 				pidgin_tree_view_search_equal_func, NULL, NULL);
--- a/pidgin/gtkpounce.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkpounce.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1221,21 +1221,11 @@
 static GtkWidget *
 create_pounces_list(PouncesManager *dialog)
 {
-	GtkWidget *sw;
 	GtkWidget *treeview;
 	GtkTreeSelection *sel;
 	GtkTreeViewColumn *column;
 	GtkCellRenderer *renderer;
 
-	/* Create the scrolled window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-					GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-						GTK_SHADOW_IN);
-	gtk_widget_show(sw);
-
 	/* Create the list model */
 	dialog->model = gtk_list_store_new(POUNCES_MANAGER_NUM_COLUMNS,
 									   G_TYPE_POINTER,
@@ -1259,7 +1249,6 @@
 	/* Handle double-clicking */
 	g_signal_connect(G_OBJECT(treeview), "button_press_event",
 					 G_CALLBACK(pounce_double_click_cb), dialog);
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
 	gtk_widget_show(treeview);
 
 	/* Pouncee Column */
@@ -1319,7 +1308,7 @@
 	/* Populate list */
 	populate_pounces_list(dialog);
 
-	return sw;
+	return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
 }
 
 void
--- a/pidgin/gtkprefs.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkprefs.c	Mon Mar 14 04:34:34 2011 +0000
@@ -2513,12 +2513,6 @@
 			vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);
 
 	/* SOUND SELECTION */
-	sw = gtk_scrolled_window_new(NULL,NULL);
-	gtk_widget_set_size_request(sw, -1, 100);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	event_store = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
 
 	for (j=0; j < PURPLE_NUM_SOUNDS; j++) {
@@ -2567,7 +2561,9 @@
 							NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
 	g_object_unref(G_OBJECT(event_store));
-	gtk_container_add(GTK_CONTAINER(sw), event_view);
+	gtk_box_pack_start(GTK_BOX(vbox),
+		pidgin_make_scrollable(event_view, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 100),
+		TRUE, TRUE, 0);
 
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
--- a/pidgin/gtkprivacy.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkprivacy.c	Mon Mar 14 04:34:34 2011 +0000
@@ -133,12 +133,6 @@
 	GtkTreeViewColumn *column;
 	GtkTreeSelection *sel;
 
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-					GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
 	*ret_treeview = treeview;
 
@@ -150,7 +144,7 @@
 	gtk_tree_view_column_set_clickable(GTK_TREE_VIEW_COLUMN(column), TRUE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	sw = pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 200);
 
 	gtk_widget_show(treeview);
 
@@ -159,8 +153,6 @@
 	g_signal_connect(G_OBJECT(sel), "changed",
 					 G_CALLBACK(user_selected_cb), dialog);
 
-	gtk_widget_set_size_request(sw, -1, 200);
-
 	return sw;
 }
 
--- a/pidgin/gtkrequest.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkrequest.c	Mon Mar 14 04:34:34 2011 +0000
@@ -419,16 +419,6 @@
 	}
 	else {
 		if (multiline) {
-			GtkWidget *sw;
-
-			sw = gtk_scrolled_window_new(NULL, NULL);
-			gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-										   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-			gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-												GTK_SHADOW_IN);
-
-			gtk_widget_set_size_request(sw, 320, 130);
-
 			/* GtkTextView */
 			entry = gtk_text_view_new();
 			gtk_text_view_set_editable(GTK_TEXT_VIEW(entry), TRUE);
@@ -442,12 +432,12 @@
 
 			gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(entry), GTK_WRAP_WORD_CHAR);
 
-			gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-
 			if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
 				pidgin_setup_gtkspell(GTK_TEXT_VIEW(entry));
 
-			gtk_container_add(GTK_CONTAINER(sw), entry);
+			gtk_box_pack_start(GTK_BOX(vbox), 
+				pidgin_make_scrollable(entry, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 320, 130),
+				TRUE, TRUE, 0);
 		}
 		else {
 			entry = gtk_entry_new();
@@ -852,12 +842,6 @@
 	{
 		GtkWidget *textview;
 
-		widget = gtk_scrolled_window_new(NULL, NULL);
-		gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget),
-											GTK_SHADOW_IN);
-		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
-									   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-
 		textview = gtk_text_view_new();
 		gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
 								   TRUE);
@@ -867,11 +851,8 @@
 		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
 			pidgin_setup_gtkspell(GTK_TEXT_VIEW(textview));
 
-		gtk_container_add(GTK_CONTAINER(widget), textview);
 		gtk_widget_show(textview);
 
-		gtk_widget_set_size_request(widget, -1, 75);
-
 		if (value != NULL)
 		{
 			GtkTextBuffer *buffer;
@@ -893,6 +874,8 @@
 			g_signal_connect(G_OBJECT(buffer), "changed",
 							 G_CALLBACK(req_entry_field_changed_cb), field);
 	    }
+
+		widget = pidgin_make_scrollable(textview, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, 75);
 	}
 	else
 	{
@@ -1093,7 +1076,6 @@
 static GtkWidget *
 create_list_field(PurpleRequestField *field)
 {
-	GtkWidget *sw;
 	GtkWidget *treeview;
 	GtkListStore *store;
 	GtkCellRenderer *renderer;
@@ -1105,14 +1087,6 @@
 
 	icons = purple_request_field_list_get_icons(field);
 
-	/* Create the scrolled window */
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-				       GTK_POLICY_AUTOMATIC,
-				       GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_widget_show(sw);
 
 	/* Create the list store */
 	if (icons)
@@ -1188,10 +1162,9 @@
 	g_signal_connect(G_OBJECT(sel), "changed",
 					 G_CALLBACK(list_field_select_changed_cb), field);
 
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
 	gtk_widget_show(treeview);
 
-	return sw;
+	return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
 }
 
 static void *
@@ -1212,7 +1185,6 @@
 	GtkWidget *table;
 	GtkWidget *button;
 	GtkWidget *img;
-	GtkWidget *sw;
 	GtkSizeGroup *sg;
 	GList *gl, *fl;
 	PurpleRequestFieldGroup *group;
@@ -1297,18 +1269,10 @@
 	if(total_fields > 9) {
 		GtkWidget *hbox_for_spacing, *vbox_for_spacing;
 
-		sw = gtk_scrolled_window_new(NULL, NULL);
-		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-				GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-		gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-				GTK_SHADOW_NONE);
-		gtk_widget_set_size_request(sw, -1, 200);
-		gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-		gtk_widget_show(sw);
-
 		hbox_for_spacing = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-		gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw),
-				hbox_for_spacing);
+		gtk_box_pack_start(GTK_BOX(vbox), 
+			pidgin_make_scrollable(hbox_for_spacing, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, 200), 
+			TRUE, TRUE, 0);
 		gtk_widget_show(hbox_for_spacing);
 
 		vbox_for_spacing = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
--- a/pidgin/gtkroomlist.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkroomlist.c	Mon Mar 14 04:34:34 2011 +0000
@@ -548,15 +548,8 @@
 	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
 
 	/* scrolled window */
-	dialog->sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw),
-	                                    GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw),
-	                               GTK_POLICY_AUTOMATIC,
-	                               GTK_POLICY_AUTOMATIC);
+	dialog->sw = pidgin_make_scrollable(NULL, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 250);
 	gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0);
-	gtk_widget_set_size_request(dialog->sw, -1, 250);
-	gtk_widget_show(dialog->sw);
 
 	/* progress bar */
 	dialog->progress = gtk_progress_bar_new();
--- a/pidgin/gtksavedstatuses.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtksavedstatuses.c	Mon Mar 14 04:34:34 2011 +0000
@@ -438,20 +438,11 @@
 static GtkWidget *
 create_saved_status_list(StatusWindow *dialog)
 {
-	GtkWidget *sw;
 	GtkWidget *treeview;
 	GtkTreeSelection *sel;
 	GtkTreeViewColumn *column;
 	GtkCellRenderer *renderer;
 
-	/* Create the scrolled window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC,
-								   GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-
 	/* Create the list model */
 	dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS,
 									   G_TYPE_STRING,
@@ -472,8 +463,6 @@
 	g_signal_connect(G_OBJECT(sel), "changed",
 					 G_CALLBACK(status_selected_cb), dialog);
 
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
-
 	/* Add columns */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_column_set_title(column, _("Title"));
@@ -527,9 +516,9 @@
 	/* Populate list */
 	populate_saved_status_list(dialog);
 
-	gtk_widget_show_all(sw);
+	gtk_widget_show_all(treeview);
 
-	return sw;
+	return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1);
 }
 
 static gboolean
@@ -1092,7 +1081,6 @@
 	GtkWidget *entry;
 	GtkWidget *frame;
 	GtkWidget *hbox;
-	GtkWidget *sw;
 	GtkWidget *text;
 	GtkWidget *toolbar;
 	GtkWidget *vbox;
@@ -1181,14 +1169,6 @@
 	dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
 	gtk_container_add(GTK_CONTAINER(expander), dbox);
 
-	/* Different status message treeview */
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(dbox), sw, TRUE, TRUE, 0);
-
 	/* Create the list model */
 	dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS,
 									   G_TYPE_POINTER,
@@ -1205,7 +1185,9 @@
 	dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE);
 	gtk_widget_set_size_request(dialog->treeview, -1, 150);
-	gtk_container_add(GTK_CONTAINER(sw), dialog->treeview);
+	gtk_box_pack_start(GTK_BOX(dbox), 
+		pidgin_make_scrollable(dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
+		TRUE, TRUE, 0);
 
 	/* Add columns */
 	status_editor_add_columns(dialog);
--- a/pidgin/gtksmiley.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtksmiley.c	Mon Mar 14 04:34:34 2011 +0000
@@ -774,7 +774,6 @@
 
 static GtkWidget *smiley_list_create(SmileyManager *dialog)
 {
-	GtkWidget *sw;
 	GtkWidget *treeview;
 	GtkTreeSelection *sel;
 	GtkTargetEntry te[3] = {
@@ -783,14 +782,6 @@
 		{"STRING", 0, 2}
 	};
 
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-			GTK_POLICY_AUTOMATIC,
-			GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-			GTK_SHADOW_IN);
-	gtk_widget_show(sw);
-
 	/* Create the list model */
 	dialog->model = gtk_list_store_new(N_COL,
 			GDK_TYPE_PIXBUF,	/* ICON */
@@ -807,7 +798,6 @@
 
 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
 
 	g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smile_selected_cb), dialog);
 	g_signal_connect(G_OBJECT(treeview), "row_activated", G_CALLBACK(smiley_edit_cb), dialog);
@@ -822,7 +812,7 @@
 	add_columns(treeview, dialog);
 	populate_smiley_list(dialog);
 
-	return sw;
+	return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
 }
 
 static void refresh_list()
--- a/pidgin/gtkstatusbox.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkstatusbox.c	Mon Mar 14 04:34:34 2011 +0000
@@ -1308,15 +1308,19 @@
   *width = GTK_WIDGET(status_box)->allocation.width;
 
   hpolicy = vpolicy = GTK_POLICY_NEVER;
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
-				  hpolicy, vpolicy);
+	g_object_set(G_OBJECT(status_box->scrolled_window),
+		"hscrollbar-policy", hpolicy,
+		"vscrollbar-policy", vpolicy,
+		NULL);
   gtk_widget_size_request (status_box->popup_frame, &popup_req);
 
   if (popup_req.width > *width)
     {
       hpolicy = GTK_POLICY_ALWAYS;
-      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
-				      hpolicy, vpolicy);
+			g_object_set(G_OBJECT(status_box->scrolled_window),
+				"hscrollbar-policy", hpolicy,
+				"vscrollbar-policy", vpolicy,
+				NULL);
       gtk_widget_size_request (status_box->popup_frame, &popup_req);
     }
 
@@ -1351,8 +1355,10 @@
     {
       vpolicy = GTK_POLICY_ALWAYS;
 
-      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
-				      hpolicy, vpolicy);
+			g_object_set(G_OBJECT(status_box->scrolled_window),
+				"hscrollbar-policy", hpolicy,
+				"vscrollbar-policy", vpolicy,
+				NULL);
     }
 }
 
@@ -1795,19 +1801,6 @@
 
 	gtk_widget_show (status_box->popup_frame);
 
-	status_box->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
-			GTK_POLICY_NEVER,
-			GTK_POLICY_NEVER);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
-			GTK_SHADOW_NONE);
-
-	gtk_widget_show (status_box->scrolled_window);
-
-	gtk_container_add (GTK_CONTAINER (status_box->popup_frame),
-			status_box->scrolled_window);
-
 	status_box->tree_view = gtk_tree_view_new ();
 	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (status_box->tree_view));
 	gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
@@ -1826,7 +1819,11 @@
 	gtk_tree_view_column_set_attributes(status_box->column, icon_rend, "stock-id", ICON_STOCK_COLUMN, NULL);
 	gtk_tree_view_column_set_attributes(status_box->column, text_rend, "markup", TEXT_COLUMN, NULL);
 	gtk_tree_view_column_set_attributes(status_box->column, emblem_rend, "stock-id", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL);
-	gtk_container_add(GTK_CONTAINER(status_box->scrolled_window), status_box->tree_view);
+
+	status_box->scrolled_window = pidgin_make_scrollable(status_box->tree_view, GTK_POLICY_NEVER, GTK_POLICY_NEVER, GTK_SHADOW_NONE, -1, -1);
+	gtk_container_add (GTK_CONTAINER (status_box->popup_frame),
+			status_box->scrolled_window);
+
 	gtk_widget_show(status_box->tree_view);
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN);
 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(status_box->tree_view),
--- a/pidgin/gtkutils.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkutils.c	Mon Mar 14 04:34:34 2011 +0000
@@ -245,12 +245,6 @@
 		gtk_widget_show(sep);
 	}
 
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	gtk_widget_show(sw);
-
 	imhtml = gtk_imhtml_new(NULL, NULL);
 	gtk_imhtml_set_editable(GTK_IMHTML(imhtml), editable);
 	gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_IMAGE);
@@ -267,7 +261,8 @@
 	}
 	pidgin_setup_imhtml(imhtml);
 
-	gtk_container_add(GTK_CONTAINER(sw), imhtml);
+	sw = pidgin_make_scrollable(imhtml, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 
 	if (imhtml_ret != NULL)
 		*imhtml_ret = imhtml;
@@ -3495,6 +3490,29 @@
 }
 #endif
 
+GtkWidget *
+pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height)
+{
+	GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
+
+	if (G_LIKELY(sw)) {
+		gtk_widget_show(sw);
+		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), hscrollbar_policy, vscrollbar_policy);
+		gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), shadow_type);
+		if (width != -1 || height != -1)
+			gtk_widget_set_size_request(sw, width, height);
+		if (child) {
+			if (GTK_WIDGET_GET_CLASS(child)->set_scroll_adjustments_signal)
+				gtk_container_add(GTK_CONTAINER(sw), child);
+			else
+				gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), child);
+		}
+		return sw;
+	}
+
+	return child;
+}
+
 void pidgin_utils_init(void)
 {
 	gtk_imhtml_class_register_protocol("http://", url_clicked_cb, link_context_menu);
--- a/pidgin/gtkutils.h	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/gtkutils.h	Mon Mar 14 04:34:34 2011 +0000
@@ -845,6 +845,19 @@
 GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
 
 /**
+ * Add scrollbars to a widget
+ * @param widget      The child widget
+ * @hscrollbar_policy Horizontal scrolling policy
+ * @vscrollbar_policy Vertical scrolling policy
+ * @shadow            Shadow type
+ * @width             Desired widget width, or -1 for default
+ * @height            Desired widget height, or -1 for default
+ *
+ * @since 2.8.0
+ */
+GtkWidget *pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height);
+
+/**
  * Initialize some utility functions.
  *
  * @since 2.6.0
--- a/pidgin/plugins/disco/gtkdisco.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/disco/gtkdisco.c	Mon Mar 14 04:34:34 2011 +0000
@@ -651,15 +651,8 @@
 	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
 
 	/* scrolled window */
-	dialog->sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw),
-	                                    GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw),
-	                               GTK_POLICY_AUTOMATIC,
-	                               GTK_POLICY_AUTOMATIC);
+	dialog->sw = pidgin_make_scrollable(NULL, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, 250);
 	gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0);
-	gtk_widget_set_size_request(dialog->sw, -1, 250);
-	gtk_widget_show(dialog->sw);
 
 	/* progress bar */
 	dialog->progress = gtk_progress_bar_new();
--- a/pidgin/plugins/gevolution/add_buddy_dialog.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/gevolution/add_buddy_dialog.c	Mon Mar 14 04:34:34 2011 +0000
@@ -430,7 +430,6 @@
 {
 	GevoAddBuddyDialog *dialog;
 	GtkWidget *button;
-	GtkWidget *sw;
 	GtkWidget *label;
 	GtkWidget *vbox;
 	GtkWidget *hbox;
@@ -510,16 +509,6 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(clear_cb), dialog);
 
-	/* Scrolled Window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC,
-								   GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	gtk_widget_show(sw);
-
 	/* Create the list model for the treeview. */
 	dialog->model = gtk_list_store_new(NUM_COLUMNS,
 									   G_TYPE_STRING, GDK_TYPE_PIXBUF,
@@ -529,7 +518,9 @@
 	dialog->treeview =
 		gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE);
-	gtk_container_add(GTK_CONTAINER(sw), dialog->treeview);
+	gtk_box_pack_start(GTK_BOX(vbox), 
+		pidgin_make_scrollable(dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1), 
+		TRUE, TRUE, 0);
 	gtk_widget_show(dialog->treeview);
 
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
--- a/pidgin/plugins/gevolution/assoc-buddy.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/gevolution/assoc-buddy.c	Mon Mar 14 04:34:34 2011 +0000
@@ -314,7 +314,6 @@
 {
 	GevoAssociateBuddyDialog *dialog;
 	GtkWidget *button;
-	GtkWidget *sw;
 	GtkWidget *label;
 	GtkWidget *vbox;
 	GtkWidget *hbox;
@@ -389,16 +388,6 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(clear_cb), dialog);
 
-	/* Scrolled Window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC,
-								   GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	gtk_widget_show(sw);
-
 	/* Create the list model for the treeview. */
 	dialog->model = gtk_list_store_new(NUM_COLUMNS,
 									   G_TYPE_STRING, G_TYPE_POINTER);
@@ -407,7 +396,9 @@
 	dialog->treeview = gtk_tree_view_new_with_model(
 			GTK_TREE_MODEL(dialog->model));
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE);
-	gtk_container_add(GTK_CONTAINER(sw), dialog->treeview);
+	gtk_box_pack_start(GTK_BOX(vbox),
+		pidgin_make_scrollable(dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
+		TRUE, TRUE, 0);
 	gtk_widget_show(dialog->treeview);
 
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
@@ -437,19 +428,10 @@
 	 * User details
 	 */
 
-	/* Scrolled Window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_NEVER,
-								   GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_container_add(GTK_CONTAINER(expander), sw);
-	gtk_widget_show(sw);
-
 	/* Textview */
 	dialog->imhtml = gtk_imhtml_new(NULL, NULL);
-	gtk_container_add(GTK_CONTAINER(sw), dialog->imhtml);
+	gtk_container_add(GTK_CONTAINER(expander), 
+		pidgin_make_scrollable(dialog->imhtml, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1));
 	gtk_widget_show(dialog->imhtml);
 
 	/* Separator. */
--- a/pidgin/plugins/gevolution/gevolution.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/gevolution/gevolution.c	Mon Mar 14 04:34:34 2011 +0000
@@ -417,7 +417,6 @@
 	GtkWidget *ret;
 	GtkWidget *vbox;
 	GtkWidget *label;
-	GtkWidget *sw;
 	GtkWidget *treeview;
 	GtkTreeViewColumn *column;
 	GtkCellRenderer *renderer;
@@ -439,17 +438,6 @@
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
-	/* Scrolled window */
-	sw = gtk_scrolled_window_new(0, 0);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_AUTOMATIC,
-								   GTK_POLICY_ALWAYS);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
-										GTK_SHADOW_IN);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	gtk_widget_set_size_request(sw, 300, 300);
-	gtk_widget_show(sw);
-
 	/* Create the list model for the treeview. */
 	model = gtk_list_store_new(NUM_COLUMNS,
 							   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
@@ -458,7 +446,9 @@
 	/* Setup the treeview */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
-	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	gtk_box_pack_start(GTK_BOX(vbox),
+		pidgin_make_scrollable(treeview, GTK_POLICY_AUTO, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 300, 300),
+		TRUE, TRUE, 0);
 	gtk_widget_show(treeview);
 
 	/* Setup the column */
--- a/pidgin/plugins/spellchk.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/spellchk.c	Mon Mar 14 04:34:34 2011 +0000
@@ -2156,7 +2156,7 @@
 static GtkWidget *
 get_config_frame(PurplePlugin *plugin)
 {
-	GtkWidget *ret, *vbox, *win;
+	GtkWidget *ret, *vbox;
 	GtkWidget *hbox;
 	GtkWidget *button;
 	GtkSizeGroup *sg;
@@ -2173,15 +2173,6 @@
 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
 	gtk_widget_show(vbox);
 
-	win = gtk_scrolled_window_new(0, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), win, TRUE, TRUE, 0);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(win),
-										GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win),
-			GTK_POLICY_NEVER,
-			GTK_POLICY_ALWAYS);
-	gtk_widget_show(win);
-
 	tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
 	gtk_widget_set_size_request(tree, -1, 200);
@@ -2240,7 +2231,9 @@
 
 	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)),
 		 GTK_SELECTION_MULTIPLE);
-	gtk_container_add(GTK_CONTAINER(win), tree);
+	gtk_box_pack_start(GTK_BOX(vbox), 
+		pidgin_make_scrollable(tree, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1), 
+		TRUE, TRUE, 0);
 	gtk_widget_show(tree);
 
 	hbox = gtk_hbutton_box_new();
--- a/pidgin/plugins/xmppconsole.c	Mon Mar 14 04:15:33 2011 +0000
+++ b/pidgin/plugins/xmppconsole.c	Mon Mar 14 04:34:34 2011 +0000
@@ -744,7 +744,6 @@
 create_console(PurplePluginAction *action)
 {
 	GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
-	GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
 	GtkWidget *label;
 	GtkTextBuffer *buffer;
 	GtkWidget *toolbar;
@@ -784,16 +783,13 @@
 	gtk_box_pack_start(GTK_BOX(console->hbox), console->dropdown, TRUE, TRUE, 0);
 	g_signal_connect(G_OBJECT(console->dropdown), "changed", G_CALLBACK(dropdown_changed_cb), NULL);
 
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-				       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
 	console->imhtml = gtk_imhtml_new(NULL, NULL);
-	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	if (console->count == 0)
 		gtk_imhtml_append_text(GTK_IMHTML(console->imhtml),
 				       _("<font color='#777777'>Not connected to XMPP</font>"), 0);
-	gtk_container_add(GTK_CONTAINER(sw), console->imhtml);
+	gtk_box_pack_start(GTK_BOX(vbox), 
+		pidgin_make_scrollable(console->imhtml, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_ETCHED_IN, -1, -1),
+		TRUE, TRUE, 0);
 
 	toolbar = gtk_toolbar_new();
 	button = gtk_tool_button_new(NULL, "<iq/>");
@@ -810,21 +806,16 @@
 
 	gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
 
-	sw = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-				       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
 	console->entry = gtk_imhtml_new(NULL, NULL);
 	gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(console->entry), TRUE);
 	g_signal_connect(G_OBJECT(console->entry),"message_send", G_CALLBACK(message_send_cb), console);
 
-	gtk_box_pack_start(GTK_BOX(vbox), sw, FALSE, FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(sw), console->entry);
+	console->sw = pidgin_make_scrollable(console->entry, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_ETCHED_IN, -1, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), console->sw, FALSE, FALSE, 0);
 	gtk_imhtml_set_editable(GTK_IMHTML(console->entry), TRUE);
 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
 	g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(entry_changed_cb), NULL);
-	console->sw = sw;
+
 	entry_changed_cb(buffer, NULL);
 
 	gtk_widget_show_all(console->window);