Merge hinted string account options from default soc.2012.gg

Tue, 14 Aug 2012 22:26:33 +0200

author
Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>
date
Tue, 14 Aug 2012 22:26:33 +0200
branch
soc.2012.gg
changeset 33338
8966c9ad5be9
parent 33337
458d578d553d (current diff)
parent 33267
c3ee96290bfd (diff)
child 33339
02ee2834030c

Merge hinted string account options from default

--- a/ChangeLog	Wed Aug 08 11:22:52 2012 +0200
+++ b/ChangeLog	Tue Aug 14 22:26:33 2012 +0200
@@ -1,6 +1,13 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 3.0.0 (??/??/????):
+	Pidgin:
+	* Support building with the GTK+ 3.x toolkit.  When configuring the
+	  build, use --with-gtk=<2|3> to determine which toolkit to use.  Using
+	  either 2 or 3 will attempt to build with specifically 2.x or 3.x
+	  support.  The default is 'auto', which will first look for 3.x
+	  development headers and then 2.x development headers.
+
 	Finch:
 	* Support the conversation-extended signal for extending the
 	  conversation menu. (Howard Chu) (#14818)
@@ -47,6 +54,11 @@
 	* The Offline Message Emulation plugin now adds a note that the message
 	  was an offline message. (Flavius Anton) (#2497)
 
+version 2.10.7:
+	Gadu-Gadu:
+	* Fix a crash at startup with large contact list. Avatar support for
+	  buddies will be disabled till 3.0.0. (#15226, #14305)
+
 	MSN:
 	* Fix a crash when removing a user before its icon is loaded. (Mark
 	  Barfield) (#15217)
--- a/finch/libgnt/Makefile.am	Wed Aug 08 11:22:52 2012 +0200
+++ b/finch/libgnt/Makefile.am	Tue Aug 14 22:26:33 2012 +0200
@@ -84,7 +84,7 @@
 	$(libgnt_la_headers)
 
 libgnt_la_DEPENDENCIES = 
-libgnt_la_LDFLAGS = -export-dynamic -version_info $(GNT_LT_VERSION_INFO) -no-undefined
+libgnt_la_LDFLAGS = -export-dynamic -version-info $(GNT_LT_VERSION_INFO) -no-undefined
 libgnt_la_LIBADD = \
 	$(GLIB_LIBS) \
 	$(GNT_LIBS) \
--- a/libpurple/accountopt.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/libpurple/accountopt.c	Tue Aug 14 22:26:33 2012 +0200
@@ -50,10 +50,17 @@
 
 	} default_value;
 
-	gboolean masked;        /**< Whether the value entered should be
-	                         *   obscured from view (for passwords and
-	                         *   similar options)
-	                         */
+	union
+	{
+		struct
+		{
+			gboolean masked; /**< Whether the value entered should
+			                  *   be obscured from view (for
+			                  *   passwords and similar options)
+			                  */
+			GSList *hints;    /**< List of hinted values */
+		} string;
+	} params;
 };
 
 /**
@@ -177,6 +184,7 @@
 	if (option->type == PURPLE_PREF_STRING)
 	{
 		g_free(option->default_value.string);
+		g_slist_free_full(option->params.string.hints, &g_free);
 	}
 	else if (option->type == PURPLE_PREF_STRING_LIST)
 	{
@@ -221,14 +229,23 @@
 }
 
 void
-purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked)
+purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked)
 {
 	g_return_if_fail(option != NULL);
 	g_return_if_fail(option->type == PURPLE_PREF_STRING);
 
-	option->masked = masked;
+	option->params.string.masked = masked;
 }
 
+void
+purple_account_option_string_set_hints(PurpleAccountOption *option, GSList *hints)
+{
+	g_return_if_fail(option != NULL);
+	g_return_if_fail(option->type == PURPLE_PREF_STRING);
+
+	g_slist_free_full(option->params.string.hints, &g_free);
+	option->params.string.hints = hints;
+}
 
 void
 purple_account_option_set_list(PurpleAccountOption *option, GList *values)
@@ -332,12 +349,21 @@
 }
 
 gboolean
-purple_account_option_get_masked(const PurpleAccountOption *option)
+purple_account_option_string_get_masked(const PurpleAccountOption *option)
 {
 	g_return_val_if_fail(option != NULL, FALSE);
 	g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE);
 
-	return option->masked;
+	return option->params.string.masked;
+}
+
+const GSList *
+purple_account_option_string_get_hints(const PurpleAccountOption *option)
+{
+	g_return_val_if_fail(option != NULL, FALSE);
+	g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE);
+
+	return option->params.string.hints;
 }
 
 GList *
--- a/libpurple/accountopt.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/libpurple/accountopt.h	Tue Aug 14 22:26:33 2012 +0200
@@ -158,7 +158,19 @@
  * @param masked The masking.
  */
 void
-purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked);
+purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked);
+
+/**
+ * Sets the hint list for an account option.
+ *
+ * The list passed will be owned by the account option, and the
+ * strings inside will be freed automatically.
+ *
+ * @param option The account option.
+ * @param hints The list of hints, stored as strings.
+ */
+void purple_account_option_string_set_hints(PurpleAccountOption *option,
+	GSList *hints);
 
 /**
  * Sets the list values for an account option.
@@ -261,7 +273,16 @@
  * @return %TRUE if the option's value should be obscured.
  */
 gboolean
-purple_account_option_get_masked(const PurpleAccountOption *option);
+purple_account_option_string_get_masked(const PurpleAccountOption *option);
+
+/**
+ * Returns the list of hints for an account option.
+ *
+ * @param option The account option.
+ *
+ * @constreturn A list of hints, stored as strings.
+ */
+const GSList * purple_account_option_string_get_hints(const PurpleAccountOption *option);
 
 /**
  * Returns the list values for an account option.
--- a/libpurple/plugins/perl/common/AccountOpts.xs	Wed Aug 08 11:22:52 2012 +0200
+++ b/libpurple/plugins/perl/common/AccountOpts.xs	Tue Aug 14 22:26:33 2012 +0200
@@ -102,7 +102,7 @@
 	Purple::Account::Option option
 
 gboolean
-purple_account_option_get_masked(option)
+purple_account_option_string_get_masked(option)
 	Purple::Account::Option option
 
 int
@@ -138,7 +138,7 @@
 	purple_account_option_set_list(option, t_GL);
 
 void
-purple_account_option_set_masked(option, masked)
+purple_account_option_string_set_masked(option, masked)
 	Purple::Account::Option option
 	gboolean masked
 
--- a/pidgin/gtkaccount.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkaccount.c	Tue Aug 14 22:26:33 2012 +0200
@@ -812,6 +812,7 @@
 	const char *str_value;
 	gboolean bool_value;
 	ProtocolOptEntry *opt_entry;
+	const GSList *str_hints;
 
 	if (dialog->protocol_frame != NULL) {
 		gtk_notebook_remove_page (GTK_NOTEBOOK(dialog->notebook), 1);
@@ -912,8 +913,25 @@
 						purple_account_option_get_default_string(option));
 				}
 
-				opt_entry->widget = entry = gtk_entry_new();
-				if (purple_account_option_get_masked(option))
+				str_hints = purple_account_option_string_get_hints(option);
+				if (str_hints)
+				{
+					const GSList *hint_it = str_hints;
+					entry = gtk_combo_box_entry_new_text();
+					while (hint_it)
+					{
+						const gchar *hint = hint_it->data;
+						hint_it = g_list_next(hint_it);
+						gtk_combo_box_append_text(GTK_COMBO_BOX(entry), hint);
+					}
+				}
+				else
+					entry = gtk_entry_new();
+				
+				opt_entry->widget = entry;
+				if (purple_account_option_string_get_masked(option) && str_hints)
+					g_warn_if_reached();
+				else if (purple_account_option_string_get_masked(option))
 				{
 					gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
 #if !GTK_CHECK_VERSION(2,16,0)
@@ -922,7 +940,9 @@
 #endif /* Less than GTK+ 2.16 */
 				}
 
-				if (str_value != NULL)
+				if (str_value != NULL && str_hints)
+					gtk_entry_set_text(GTK_ENTRY(GTK_BIN(entry)->child), str_value);
+				else
 					gtk_entry_set_text(GTK_ENTRY(entry), str_value);
 
 				title = g_strdup_printf("_%s:",
@@ -1453,7 +1473,10 @@
 
 			switch (opt_entry->type) {
 				case PURPLE_PREF_STRING:
-					value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
+					if (GTK_IS_COMBO_BOX(opt_entry->widget))
+						value = gtk_combo_box_get_active_text(GTK_COMBO_BOX(opt_entry->widget));
+					else
+						value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
 					purple_account_set_string(account, opt_entry->setting, value);
 					break;
 
--- a/pidgin/gtkconv.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkconv.c	Tue Aug 14 22:26:33 2012 +0200
@@ -64,8 +64,6 @@
 #include "gtkconv-theme.h"
 #include "gtkconv-theme-loader.h"
 #include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
 #include "gtklog.h"
 #include "gtkmenutray.h"
 #include "gtkpounce.h"
@@ -74,6 +72,7 @@
 #include "gtkthemes.h"
 #include "gtkutils.h"
 #include "gtkwebview.h"
+#include "gtkwebviewtoolbar.h"
 #include "pidginstock.h"
 #include "pidgintooltip.h"
 #include "smileyparser.h"
@@ -331,20 +330,24 @@
 default_formatize(PidginConversation *c)
 {
 	PurpleConversation *conv = c->active_conv;
-	gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), purple_conversation_get_features(conv));
+	gtk_webview_setup_entry(GTK_WEBVIEW(c->entry), purple_conversation_get_features(conv));
 }
 
 static void
 conversation_entry_clear(PidginConversation *gtkconv)
 {
-	GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->entry);
-	gtk_source_undo_manager_begin_not_undoable_action(imhtml->undo_manager);
-	gtk_imhtml_clear(imhtml);
-	gtk_source_undo_manager_end_not_undoable_action(imhtml->undo_manager);
-}
-
-static void
-clear_formatting_cb(GtkIMHtml *imhtml, PidginConversation *gtkconv)
+	GtkWebView *webview = GTK_WEBVIEW(gtkconv->entry);
+	gtk_webview_load_html_string(webview, "");
+#if 0
+	/* TODO WebKit */
+	gtk_source_undo_manager_begin_not_undoable_action(webview->undo_manager);
+	gtk_webview_clear(webview);
+	gtk_source_undo_manager_end_not_undoable_action(webview->undo_manager);
+#endif
+}
+
+static void
+clear_formatting_cb(GtkWebView *webview, PidginConversation *gtkconv)
 {
 	default_formatize(gtkconv);
 }
@@ -535,22 +538,18 @@
 	PidginConversation *gtkconv;
 	char *cmd;
 	const char *prefix;
-	GtkTextIter start;
 	gboolean retval = FALSE;
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
 	prefix = pidgin_get_cmd_prefix();
 
-	cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
-	gtk_text_buffer_get_start_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &start);
-
-	if (cmd && (strncmp(cmd, prefix, strlen(prefix)) == 0)
-	   && !gtk_text_iter_get_child_anchor(&start)) {
+	cmd = gtk_webview_get_body_text(GTK_WEBVIEW(gtkconv->entry));
+
+	if (cmd && purple_str_has_prefix(cmd, prefix)) {
 		PurpleCmdStatus status;
 		char *error, *cmdline, *markup, *send_history;
-		GtkTextIter end;
-
-		send_history = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
+
+		send_history = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry));
 		send_history_add(gtkconv, send_history);
 		g_free(send_history);
 
@@ -563,9 +562,8 @@
 			return TRUE;
 		}
 
-		gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1));
-		gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end);
-		markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end);
+		/* TODO WebKit: Cut out prefix for markup... */
+		markup = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry));
 		status = purple_cmd_do_command(conv, cmdline, markup, &error);
 		g_free(markup);
 
@@ -627,6 +625,7 @@
 	}
 
 	g_free(cmd);
+
 	return retval;
 }
 
@@ -653,8 +652,8 @@
 	if (!purple_account_is_connected(account))
 		return;
 
-	buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
-	clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
+	buf = gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry));
+	clean = gtk_webview_get_body_text(GTK_WEBVIEW(gtkconv->entry));
 
 	gtk_widget_grab_focus(gtkconv->entry);
 
@@ -666,16 +665,21 @@
 
 	purple_idle_touch();
 
+#if 0
+	/* TODO WebKit: Image stuff... */
 	/* XXX: is there a better way to tell if the message has images? */
-	if (GTK_IMHTML(gtkconv->entry)->im_images != NULL)
+	if (GTK_WEBVIEW(gtkconv->entry)->im_images != NULL)
 		flags |= PURPLE_MESSAGE_IMAGES;
-
-	gc = purple_account_get_connection(account);
+#endif
+
+	gc = NULL/*purple_account_get_connection(account)*/;
 	if (gc && (purple_conversation_get_features(conv) & PURPLE_CONNECTION_NO_NEWLINES)) {
+#if 0
+		/* TODO WebKit */
 		char **bufs;
 		int i;
 
-		bufs = gtk_imhtml_get_markup_lines(GTK_IMHTML(gtkconv->entry));
+		bufs = gtk_webview_get_markup_lines(GTK_WEBVIEW(gtkconv->entry));
 		for (i = 0; bufs[i]; i++) {
 			send_history_add(gtkconv, bufs[i]);
 			if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
@@ -685,6 +689,7 @@
 		}
 
 		g_strfreev(bufs);
+#endif
 
 	} else {
 		send_history_add(gtkconv, buf);
@@ -1265,13 +1270,12 @@
 {
 	PidginWindow *win = data;
 	PidginConversation *gtkconv;
-	GtkIMHtmlToolbar *toolbar;
+	GtkWebViewToolbar *toolbar;
 
 	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
-	toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
-
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link),
-			!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link)));
+	toolbar = GTK_WEBVIEWTOOLBAR(gtkconv->toolbar);
+
+	gtk_webviewtoolbar_activate(toolbar, GTK_WEBVIEWTOOLBAR_ACTION_LINK);
 }
 
 static void
@@ -1279,15 +1283,13 @@
 {
 	PidginWindow *win = data;
 	PidginConversation *gtkconv;
-	GtkIMHtmlToolbar *toolbar;
+	GtkWebViewToolbar *toolbar;
 
 	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
-	toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
-
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image),
-			!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image)));
-}
-
+	toolbar = GTK_WEBVIEWTOOLBAR(gtkconv->toolbar);
+
+	gtk_webviewtoolbar_activate(toolbar, GTK_WEBVIEWTOOLBAR_ACTION_IMAGE);
+}
 
 static void
 menu_alias_cb(GtkAction *action, gpointer data)
@@ -1619,12 +1621,17 @@
 static GtkTextMark *
 get_mark_for_user(PidginConversation *gtkconv, const char *who)
 {
+#if 0
+	/* TODO WebKit */
 	GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->webview));
 	char *tmp = g_strconcat("user:", who, NULL);
 	GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp);
 
 	g_free(tmp);
 	return mark;
+#else
+	return NULL;
+#endif
 }
 
 static void
@@ -1961,7 +1968,7 @@
 	PidginWindow *win;
 	int curconv;
 
-	win      = gtkconv->win;
+	win     = gtkconv->win;
 	curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
 
 	/* clear any tooltips */
@@ -2077,44 +2084,42 @@
 					break;
 
 				if (!gtkconv->send_history->prev) {
-					GtkTextIter start, end;
-
 					g_free(gtkconv->send_history->data);
 
-					gtk_text_buffer_get_start_iter(gtkconv->entry_buffer,
-												   &start);
-					gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end);
-
 					gtkconv->send_history->data =
-						gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
+						gtk_webview_get_body_html(GTK_WEBVIEW(gtkconv->entry));
 				}
 
 				if (gtkconv->send_history->next && gtkconv->send_history->next->data) {
 					GObject *object;
+#if 0
+					/* TODO WebKit: maybe not necessary? */
 					GtkTextIter iter;
 					GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
+#endif
 
 					gtkconv->send_history = gtkconv->send_history->next;
 
 					/* Block the signal to prevent application of default formatting. */
 					object = g_object_ref(G_OBJECT(gtkconv->entry));
-					g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
-													NULL, gtkconv);
+					g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
+					                                0, 0, NULL, NULL, gtkconv);
 					/* Clear the formatting. */
-					gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
+					gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry));
 					/* Unblock the signal. */
-					g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
-													  NULL, gtkconv);
+					g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
+					                                  0, 0, NULL, NULL, gtkconv);
 					g_object_unref(object);
 
-					gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
-					gtk_imhtml_append_text_with_images(
-						GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data,
-						0, NULL);
+					gtk_webview_load_html_string(GTK_WEBVIEW(gtkconv->entry),
+					                             gtkconv->send_history->data);
 					/* this is mainly just a hack so the formatting at the
 					 * cursor gets picked up. */
+#if 0
+					/* TODO WebKit: maybe not necessary? */
 					gtk_text_buffer_get_end_iter(buffer, &iter);
 					gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter);
+#endif
 				}
 
 				return TRUE;
@@ -2129,31 +2134,35 @@
 
 				if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) {
 					GObject *object;
+#if 0
+					/* TODO WebKit: maybe not necessary? */
 					GtkTextIter iter;
 					GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
+#endif
 
 					gtkconv->send_history = gtkconv->send_history->prev;
 
 					/* Block the signal to prevent application of default formatting. */
 					object = g_object_ref(G_OBJECT(gtkconv->entry));
-					g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
-													NULL, gtkconv);
+					g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
+					                                0, 0, NULL, NULL, gtkconv);
 					/* Clear the formatting. */
-					gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
+					gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry));
 					/* Unblock the signal. */
-					g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
-													  NULL, gtkconv);
+					g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
+					                                  0, 0, NULL, NULL, gtkconv);
 					g_object_unref(object);
 
-					gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
-					gtk_imhtml_append_text_with_images(
-						GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data,
-						0, NULL);
+					gtk_webview_load_html_string(GTK_WEBVIEW(gtkconv->entry),
+					                             gtkconv->send_history->data);
 					/* this is mainly just a hack so the formatting at the
 					 * cursor gets picked up. */
 					if (*(char *)gtkconv->send_history->data) {
+#if 0
+					/* TODO WebKit: maybe not necessary? */
 						gtk_text_buffer_get_end_iter(buffer, &iter);
 						gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter);
+#endif
 					} else {
 						/* Restore the default formatting */
 						default_formatize(gtkconv);
@@ -2199,6 +2208,12 @@
 			return TRUE;
 			break;
 
+		case GDK_KEY_KP_Enter:
+		case GDK_KEY_Return:
+			send_cb(entry, gtkconv);
+			return TRUE;
+			break;
+
 		}
 	}
 	return FALSE;
@@ -2240,6 +2255,7 @@
 	if ((event->state & GDK_CONTROL_MASK) ||
 		(event->keyval == GDK_KEY_F6) ||
 		(event->keyval == GDK_KEY_F10) ||
+		(event->keyval == GDK_KEY_Menu) ||
 		(event->keyval == GDK_KEY_Shift_L) ||
 		(event->keyval == GDK_KEY_Shift_R) ||
 		(event->keyval == GDK_KEY_Control_L) ||
@@ -2264,9 +2280,7 @@
 		return FALSE;
 	}
 
-	if (event->type == GDK_KEY_RELEASE)
-		gtk_widget_grab_focus(gtkconv->entry);
-
+	gtk_widget_grab_focus(gtkconv->entry);
 	gtk_widget_event(gtkconv->entry, (GdkEvent *)event);
 
 	return TRUE;
@@ -2280,7 +2294,7 @@
 {
 	PidginConversation *gtkconv;
 	PurpleConversation *old_conv;
-	GtkIMHtml *entry;
+	GtkWebView *entry;
 	const char *protocol_name;
 	PurpleConnectionFlags features;
 
@@ -2291,7 +2305,7 @@
 
 	purple_debug_info("gtkconv", "setting active conversation on toolbar %p\n",
 		conv);
-	gtk_imhtmltoolbar_switch_active_conversation(GTK_IMHTMLTOOLBAR(gtkconv->toolbar),
+	gtk_webviewtoolbar_switch_active_conversation(GTK_WEBVIEWTOOLBAR(gtkconv->toolbar),
 		conv);
 
 	if (old_conv == conv)
@@ -2303,14 +2317,14 @@
 	purple_conversation_set_logging(conv,
 		gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(gtkconv->win->menu.logging)));
 
-	entry = GTK_IMHTML(gtkconv->entry);
+	entry = GTK_WEBVIEW(gtkconv->entry);
 	protocol_name = purple_account_get_protocol_name(purple_conversation_get_account(conv));
-	gtk_imhtml_set_protocol_name(entry, protocol_name);
-	/* TODO WEBKIT: gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name); */
+	gtk_webview_set_protocol_name(entry, protocol_name);
+	gtk_webview_set_protocol_name(GTK_WEBVIEW(gtkconv->webview), protocol_name);
 
 	features = purple_conversation_get_features(conv);
 	if (!(features & PURPLE_CONNECTION_HTML))
-		gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
+		gtk_webview_clear_formatting(GTK_WEBVIEW(gtkconv->entry));
 	else if (features & PURPLE_CONNECTION_FORMATTING_WBFO &&
 	         !(purple_conversation_get_features(old_conv) & PURPLE_CONNECTION_FORMATTING_WBFO))
 	{
@@ -2323,50 +2337,62 @@
 		gboolean bold;
 		gboolean italic;
 		gboolean underline;
-		char *fontface   = gtk_imhtml_get_current_fontface(entry);
-		char *forecolor  = gtk_imhtml_get_current_forecolor(entry);
-		char *backcolor  = gtk_imhtml_get_current_backcolor(entry);
-		char *background = gtk_imhtml_get_current_background(entry);
-		gint fontsize    = gtk_imhtml_get_current_fontsize(entry);
+		gboolean strike;
+		char *fontface   = gtk_webview_get_current_fontface(entry);
+		char *forecolor  = gtk_webview_get_current_forecolor(entry);
+		char *backcolor  = gtk_webview_get_current_backcolor(entry);
+#if 0
+		/* TODO WebKit: Do we need this again? */
+		char *background = gtk_webview_get_current_background(entry);
+#endif
+		gint fontsize    = gtk_webview_get_current_fontsize(entry);
 		gboolean bold2;
 		gboolean italic2;
 		gboolean underline2;
-
-		gtk_imhtml_get_current_format(entry, &bold, &italic, &underline);
+		gboolean strike2;
+
+		gtk_webview_get_current_format(entry, &bold, &italic, &underline, &strike);
 
 		/* Clear existing formatting */
-		gtk_imhtml_clear_formatting(entry);
+		gtk_webview_clear_formatting(entry);
 
 		/* Apply saved formatting to the whole buffer. */
 
-		gtk_imhtml_get_current_format(entry, &bold2, &italic2, &underline2);
+		gtk_webview_get_current_format(entry, &bold2, &italic2, &underline2, &strike2);
 
 		if (bold != bold2)
-			gtk_imhtml_toggle_bold(entry);
+			gtk_webview_toggle_bold(entry);
 
 		if (italic != italic2)
-			gtk_imhtml_toggle_italic(entry);
+			gtk_webview_toggle_italic(entry);
 
 		if (underline != underline2)
-			gtk_imhtml_toggle_underline(entry);
-
-		gtk_imhtml_toggle_fontface(entry, fontface);
+			gtk_webview_toggle_underline(entry);
+
+		if (strike != strike2)
+			gtk_webview_toggle_strike(entry);
+
+		gtk_webview_toggle_fontface(entry, fontface);
 
 		if (!(features & PURPLE_CONNECTION_NO_FONTSIZE))
-			gtk_imhtml_font_set_size(entry, fontsize);
-
-		gtk_imhtml_toggle_forecolor(entry, forecolor);
+			gtk_webview_font_set_size(entry, fontsize);
+
+		gtk_webview_toggle_forecolor(entry, forecolor);
 
 		if (!(features & PURPLE_CONNECTION_NO_BGCOLOR))
 		{
-			gtk_imhtml_toggle_backcolor(entry, backcolor);
-			gtk_imhtml_toggle_background(entry, background);
+			gtk_webview_toggle_backcolor(entry, backcolor);
+#if 0
+			gtk_webview_toggle_background(entry, background);
+#endif
 		}
 
 		g_free(fontface);
 		g_free(forecolor);
 		g_free(backcolor);
+#if 0
 		g_free(background);
+#endif
 	}
 	else
 	{
@@ -2374,7 +2400,7 @@
 		 * which is (obviously) a clear_formatting signal handler.  However, if we're
 		 * here, we didn't call gtk_imhtml_clear_formatting() (because we want to
 		 * preserve the formatting exactly as it is), so we have to do this now. */
-		gtk_imhtml_set_whole_buffer_formatting_only(entry,
+		gtk_webview_set_whole_buffer_formatting_only(entry,
 			(features & PURPLE_CONNECTION_FORMATTING_WBFO));
 	}
 
@@ -4276,6 +4302,8 @@
 static gboolean
 tab_complete(PurpleConversation *conv)
 {
+#if 0
+	/* TODO WebKit */
 	PidginConversation *gtkconv;
 	GtkTextIter cursor, word_start, start_buffer;
 	int start;
@@ -4446,7 +4474,7 @@
 
 	g_free(entered);
 	g_free(partial);
-
+#endif
 	return TRUE;
 }
 
@@ -4706,25 +4734,23 @@
 	buddy_cb_common(PURPLE_BUDDY(node), conv, FALSE);
 }
 
-static void send_menu_cb(GtkWidget *widget, PidginConversation *gtkconv)
-{
-	g_signal_emit_by_name(gtkconv->entry, "message_send");
-}
-
-static void
-entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
+static void
+entry_popup_menu_cb(GtkWebView *webview, GtkMenu *menu, gpointer data)
 {
 	GtkWidget *menuitem;
 	PidginConversation *gtkconv = data;
+	char *tmp;
 
 	g_return_if_fail(menu != NULL);
 	g_return_if_fail(gtkconv != NULL);
 
 	menuitem = pidgin_new_item_from_stock(NULL, _("_Send"), NULL,
-										G_CALLBACK(send_menu_cb), gtkconv,
-										0, 0, NULL);
-	if (gtk_text_buffer_get_char_count(imhtml->text_buffer) == 0)
+	                                      G_CALLBACK(send_cb), gtkconv,
+	                                      0, 0, NULL);
+	tmp = gtk_webview_get_body_text(webview);
+	if (!tmp || !*tmp)
 		gtk_widget_set_sensitive(menuitem, FALSE);
+	g_free(tmp);
 	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 0);
 
 	menuitem = gtk_separator_menu_item_new();
@@ -4732,8 +4758,11 @@
 	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1);
 }
 
-static gboolean resize_imhtml_cb(PidginConversation *gtkconv)
-{
+static gboolean
+resize_webview_cb(PidginConversation *gtkconv)
+{
+#if 0
+	/* TODO WebKit: entry sizing */
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
 	int lines;
@@ -4797,6 +4826,8 @@
 
 	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
 		diff + lower_hbox_allocation.height);
+#endif
+	gtk_widget_set_size_request(gtkconv->lower_hbox, -1, -1);
 
 	return FALSE;
 }
@@ -4814,7 +4845,7 @@
 		conv = (PurpleConversation *)l->data;
 
 		if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
-			resize_imhtml_cb(PIDGIN_CONVERSATION(conv));
+			resize_webview_cb(PIDGIN_CONVERSATION(conv));
 
 		l = l->next;
 	}
@@ -5409,7 +5440,7 @@
 		/* Add the topic */
 		setup_chat_topic(gtkconv, vbox);
 
-		/* Add the gtkimhtml frame */
+		/* Add the gtkwebview frame */
 		hpaned = gtk_hpaned_new();
 		gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
 		gtk_widget_show(hpaned);
@@ -5439,45 +5470,48 @@
 	gtk_widget_show(gtkconv->lower_hbox);
 
 	/* Setup the toolbar, entry widget and all signals */
-	frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
+	frame = pidgin_create_webview(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
 	gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
 	gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
-	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
+	gtk_webview_set_protocol_name(GTK_WEBVIEW(gtkconv->entry),
 			purple_account_get_protocol_name(purple_conversation_get_account(conv)));
 
 	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
 	                 G_CALLBACK(entry_popup_menu_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
+	g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
 	                 G_CALLBACK(entry_key_press_cb), gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
-	                       G_CALLBACK(send_cb), gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
+#if 0
+	/* TODO WebKit: Why? */
+	g_signal_connect_after(G_OBJECT(gtkconv->entry), "button-press-event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-
-	gtkconv->entry_buffer =
-		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
-	g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
+#endif
 
 	if (!chat) {
 		/* For sending typing notifications for IMs */
+#if 0
+		/* TODO WebKit */
 		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
 						 G_CALLBACK(insert_text_cb), gtkconv);
 		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
 						 G_CALLBACK(delete_text_cb), gtkconv);
+#endif
 		gtkconv->u.im->typing_timer = 0;
 		gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
 		gtkconv->u.im->show_icon = TRUE;
 	}
 
+#if 0
+	/* TODO WebKit: sizing stuff? */
 	g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
-				 G_CALLBACK(resize_imhtml_cb), gtkconv);
+				 G_CALLBACK(resize_webview_cb), gtkconv);
 	g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "size-allocate",
-				 G_CALLBACK(resize_imhtml_cb), gtkconv);
+				 G_CALLBACK(resize_webview_cb), gtkconv);
+#endif
 
 	default_formatize(gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
+	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format-cleared",
 	                       G_CALLBACK(clear_formatting_cb), gtkconv);
 	return vbox;
 }
@@ -5609,7 +5643,6 @@
 
 static const GtkTargetEntry te[] =
 {
-	GTK_IMHTML_DND_TARGETS,
 	{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, PIDGIN_DRAG_BLIST_NODE},
 	{"application/x-im-contact", 0, PIDGIN_DRAG_IM_CONTACT}
 };
@@ -6884,31 +6917,26 @@
 }
 
 static gboolean
-add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile)
-{
-	GtkIMHtmlSmiley *smiley;
-
-	smiley = gtk_imhtml_smiley_get(imhtml, sml, smile);
+add_custom_smiley_for_webview(GtkWebView *webview, const char *sml, const char *smile)
+{
+	GtkWebViewSmiley *smiley;
+
+	smiley = gtk_webview_smiley_find(webview, sml, smile);
 
 	if (smiley) {
-		if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
+		if (!(gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM))
 			return FALSE;
-		}
-		gtk_imhtml_smiley_reload(smiley);
+
+		gtk_webview_smiley_reload(smiley);
 		return TRUE;
 	}
 
-	smiley = gtk_imhtml_smiley_create(NULL, smile, FALSE, GTK_IMHTML_SMILEY_CUSTOM);
-	gtk_imhtml_associate_smiley(imhtml, sml, smiley);
-	g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley);
-
-	return TRUE;
-}
-
-static gboolean
-add_custom_smiley_for_webview(GtkWebView *webview, const char *sml, const char *smile)
-{
-	/* TODO WEBKIT: Smileys need to be added to webkit stuff */
+	smiley = gtk_webview_smiley_create(NULL, smile, FALSE,
+	                                   GTK_WEBVIEW_SMILEY_CUSTOM);
+	gtk_webview_associate_smiley(webview, sml, smiley);
+	g_signal_connect_swapped(webview, "destroy",
+	                         G_CALLBACK(gtk_webview_smiley_destroy), smiley);
+
 	return TRUE;
 }
 
@@ -6943,7 +6971,7 @@
 		return FALSE;
 
 	if (!remote)	/* If it's a local custom smiley, then add it for the entry */
-		if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile))
+		if (!add_custom_smiley_for_webview(GTK_WEBVIEW(gtkconv->entry), sml, smile))
 			return FALSE;
 
 	return TRUE;
@@ -7041,7 +7069,7 @@
 {
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), message, 0);
+	gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), message);
 }
 
 /*
@@ -7057,7 +7085,7 @@
 	PurpleConnection *gc;
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	GdkPixbuf *window_icon = NULL;
-	GtkIMHtmlButtons buttons;
+	GtkWebViewButtons buttons;
 	PurpleAccount *account;
 
 	win     = pidgin_conv_get_window(gtkconv);
@@ -7148,18 +7176,18 @@
 		/* Deal with the toolbar */
 		if (features & PURPLE_CONNECTION_HTML)
 		{
-			buttons = GTK_IMHTML_ALL; /* Everything on */
+			buttons = GTK_WEBVIEW_ALL; /* Everything on */
 			if (features & PURPLE_CONNECTION_NO_BGCOLOR)
-				buttons &= ~GTK_IMHTML_BACKCOLOR;
+				buttons &= ~GTK_WEBVIEW_BACKCOLOR;
 			if (features & PURPLE_CONNECTION_NO_FONTSIZE)
 			{
-				buttons &= ~GTK_IMHTML_GROW;
-				buttons &= ~GTK_IMHTML_SHRINK;
+				buttons &= ~GTK_WEBVIEW_GROW;
+				buttons &= ~GTK_WEBVIEW_SHRINK;
 			}
 			if (features & PURPLE_CONNECTION_NO_URLDESC)
-				buttons &= ~GTK_IMHTML_LINKDESC;
+				buttons &= ~GTK_WEBVIEW_LINKDESC;
 		} else {
-			buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE;
+			buttons = GTK_WEBVIEW_SMILEY | GTK_WEBVIEW_IMAGE;
 		}
 
 		if (!(prpl_info->options & OPT_PROTO_IM_IMAGE)
@@ -7169,16 +7197,16 @@
 		}
 
 		if (features & PURPLE_CONNECTION_NO_IMAGES)
-			buttons &= ~GTK_IMHTML_IMAGE;
+			buttons &= ~GTK_WEBVIEW_IMAGE;
 
 		if (features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
-			buttons |= GTK_IMHTML_CUSTOM_SMILEY;
+			buttons |= GTK_WEBVIEW_CUSTOM_SMILEY;
 		else
-			buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
-
-		gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons);
+			buttons &= ~GTK_WEBVIEW_CUSTOM_SMILEY;
+
+		gtk_webview_set_format_functions(GTK_WEBVIEW(gtkconv->entry), buttons);
 		if (account != NULL)
-			gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
+			gtk_webviewtoolbar_associate_smileys(GTK_WEBVIEWTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
 
 		/* Deal with menu items */
 		gtk_action_set_sensitive(win->menu.view_log, TRUE);
@@ -7832,7 +7860,6 @@
 	GList *cl;
 	PurpleConversation *conv;
 	PidginConversation *gtkconv;
-	GtkSpell *spell;
 
 	for (cl = purple_get_conversations(); cl != NULL; cl = cl->next) {
 
@@ -7843,13 +7870,7 @@
 
 		gtkconv = PIDGIN_CONVERSATION(conv);
 
-		if (value)
-			pidgin_setup_gtkspell(GTK_TEXT_VIEW(gtkconv->entry));
-		else {
-			spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry));
-			if (spell)
-				gtkspell_detach(spell);
-		}
+		pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry), value);
 	}
 #endif
 }
@@ -7901,7 +7922,7 @@
 		else
 			gtk_widget_hide(gtkconv->toolbar);
 
-		g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv);
+		g_idle_add((GSourceFunc)resize_webview_cb, gtkconv);
 	}
 }
 
@@ -8837,8 +8858,6 @@
 #include "gtkprivacy.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
 
 static void
 do_close(GtkWidget *w, int resp, PidginWindow *win)
--- a/pidgin/gtkconv.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkconv.h	Tue Aug 14 22:26:33 2012 +0200
@@ -103,7 +103,6 @@
 	PidginConvTheme *theme;
 	PurpleMessageFlags last_flags;
 	GtkWidget *webview;
-	GtkTextBuffer *entry_buffer;
 	GtkWidget *entry;
 	gboolean auto_resize;   /* this is set to TRUE if the conversation
 				 * is being resized by a non-user-initiated
--- a/pidgin/gtkdebug.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkdebug.c	Tue Aug 14 22:26:33 2012 +0200
@@ -623,6 +623,28 @@
 	return FALSE;
 }
 
+static void
+regex_html_appended_cb(GtkWebView *webview, WebKitDOMRange *range, DebugWindow *win)
+{
+	if (!win || !win->window)
+		return;
+
+	if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
+		WebKitDOMDocument *dom;
+		WebKitDOMHTMLElement *body;
+		WebKitDOMNode *div;
+
+		dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
+		body = webkit_dom_document_get_body(dom);
+		div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
+
+		if (webkit_dom_element_webkit_matches_selector(WEBKIT_DOM_ELEMENT(div),
+		                                               "body>div:not(#pause)",
+		                                               NULL))
+			regex_match(win, dom, div);
+	}
+}
+
 static DebugWindow *
 debug_window_new(void)
 {
@@ -790,9 +812,13 @@
 	frame = pidgin_create_webview(FALSE, &win->text, NULL, NULL);
 	gtk_webview_set_format_functions(GTK_WEBVIEW(win->text),
 	                                 GTK_WEBVIEW_ALL ^ GTK_WEBVIEW_SMILEY ^ GTK_WEBVIEW_IMAGE);
+	gtk_webview_set_autoscroll(GTK_WEBVIEW(win->text), TRUE);
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
+	g_signal_connect(G_OBJECT(win->text), "html-appended",
+	                 G_CALLBACK(regex_html_appended_cb), win);
+
 	clear_cb(NULL, win);
 
 	gtk_widget_show_all(win->window);
@@ -990,31 +1016,7 @@
 	g_free(esc_s);
 	g_free(tmp);
 
-	//XXX: gtk_webview_append_html does delayed insert of new div, which is
-	//     needed by filtering below
-	//gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s);
-	{
-		WebKitDOMDocument *dom = NULL;
-		WebKitDOMHTMLElement *body = NULL;
-		dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(debug_win->text));
-		if (dom)
-			body = webkit_dom_document_get_body(dom);
-		if (body)
-			webkit_dom_html_element_insert_adjacent_html(body, "beforeend", s, NULL);
-	}
-
-	if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(debug_win->filter))) {
-		WebKitDOMDocument *dom = NULL;
-		WebKitDOMHTMLElement *body = NULL;
-		WebKitDOMNode *div = NULL;
-		dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(debug_win->text));
-		if (dom)
-			body = webkit_dom_document_get_body(dom);
-		if (body)
-			div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
-		if (div)
-			regex_match(debug_win, dom, div);
-	}
+	gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s);
 
 	g_free(s);
 }
--- a/pidgin/gtkdocklet.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkdocklet.c	Tue Aug 14 22:26:33 2012 +0200
@@ -323,7 +323,6 @@
 		}
 		docklet_update_status();
 	}
-
 }
 
 /**************************************************************************
@@ -1012,6 +1011,12 @@
 	return &i;
 }
 
+GtkStatusIcon *
+pidgin_docklet_get_status_icon(void)
+{
+	return docklet;
+}
+
 void
 pidgin_docklet_init()
 {
--- a/pidgin/gtkdocklet.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkdocklet.h	Tue Aug 14 22:26:33 2012 +0200
@@ -27,6 +27,11 @@
 
 G_BEGIN_DECLS
 
+/**
+ * Returns the GtkStatusIcon used for the docklet.
+ */
+GtkStatusIcon *pidgin_docklet_get_status_icon(void);
+
 void pidgin_docklet_init(void);
 void pidgin_docklet_uninit(void);
 void*pidgin_docklet_get_handle(void);
--- a/pidgin/gtkimhtml.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkimhtml.c	Tue Aug 14 22:26:33 2012 +0200
@@ -1713,6 +1713,7 @@
 	                                        _("Enable typing notification"),
 	                                        TRUE, G_PARAM_READABLE));
 
+#if 0
 	binding_set = gtk_binding_set_by_class (parent_class);
 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC);
@@ -1728,6 +1729,7 @@
 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_F14, 0, "undo", 0);
 	gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text");
+#endif
 }
 
 static void gtk_imhtml_init (GtkIMHtml *imhtml)
--- a/pidgin/gtkpluginpref.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkpluginpref.c	Tue Aug 14 22:26:33 2012 +0200
@@ -134,8 +134,10 @@
 				gtk_widget_show(spacer);
 
 				frame = pidgin_create_webview(TRUE, &webview, &toolbar, NULL);
-				if (!(format & PURPLE_STRING_FORMAT_TYPE_HTML))
+				if (!(format & PURPLE_STRING_FORMAT_TYPE_HTML)) {
 					gtk_widget_destroy(toolbar);
+					gtk_webview_set_format_functions(GTK_WEBVIEW(webview), 0);
+				}
 
 				if (format & PURPLE_STRING_FORMAT_TYPE_MULTILINE) {
 					gchar *tmp = purple_strreplace(purple_prefs_get_string(pref_name), "\n", "<br>");
--- a/pidgin/gtkstatusbox.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkstatusbox.c	Tue Aug 14 22:26:33 2012 +0200
@@ -72,8 +72,8 @@
 /* Timeout for typing notifications in seconds */
 #define TYPING_TIMEOUT 4
 
-static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data);
-static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data);
+static void webview_changed_cb(GtkWebView *webview, void *data);
+static void webview_format_changed_cb(GtkWebView *webview, GtkWebViewButtons buttons, void *data);
 static void remove_typing_cb(PidginStatusBox *box);
 static void update_size (PidginStatusBox *box);
 static gint get_statusbox_index(PidginStatusBox *box, PurpleSavedStatus *saved_status);
@@ -274,8 +274,11 @@
 			break;
 	}
 
-	gtk_imhtml_set_populate_primary_clipboard(
-		GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+	/* TODO WebKit: Doesn't do this? */
+	gtk_webview_set_populate_primary_clipboard(
+		GTK_WEBVIEW(status_box->webview), TRUE);
+#endif
 
 	if (status_no != -1) {
 		GtkTreePath *path;
@@ -291,15 +294,13 @@
 		if (!message || !*message)
 		{
 			gtk_widget_hide(status_box->vbox);
-			status_box->imhtml_visible = FALSE;
+			status_box->webview_visible = FALSE;
 		}
 		else
 		{
 			gtk_widget_show_all(status_box->vbox);
-			status_box->imhtml_visible = TRUE;
-			gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml));
-			gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml));
-			gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0);
+			status_box->webview_visible = TRUE;
+			gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), message);
 		}
 		gtk_widget_set_sensitive(GTK_WIDGET(status_box), TRUE);
 		pidgin_status_box_refresh(status_box);
@@ -929,7 +930,7 @@
 		message = purple_savedstatus_get_message(saved_status);
 
 		/*
-		 * If we are going to hide the imhtml, don't retain the
+		 * If we are going to hide the webview, don't retain the
 		 * message because showing the old message later is
 		 * confusing. If we are going to set the message to a pre-set,
 		 * then we need to do this anyway
@@ -937,25 +938,25 @@
 		 * Suppress the "changed" signal because the status
 		 * was changed programmatically.
 		 */
-		gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), FALSE);
-
-		gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml));
-		gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml));
+		gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), FALSE);
+
+		gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), "");
+		gtk_webview_clear_formatting(GTK_WEBVIEW(status_box->webview));
 
 		if (!purple_savedstatus_is_transient(saved_status) || !message || !*message)
 		{
-			status_box->imhtml_visible = FALSE;
+			status_box->webview_visible = FALSE;
 			gtk_widget_hide(status_box->vbox);
 		}
 		else
 		{
-			status_box->imhtml_visible = TRUE;
+			status_box->webview_visible = TRUE;
 			gtk_widget_show_all(status_box->vbox);
 
-			gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0);
+			gtk_webview_load_html_string(GTK_WEBVIEW(status_box->webview), message);
 		}
 
-		gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), TRUE);
+		gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), TRUE);
 		update_size(status_box);
 	}
 
@@ -1132,28 +1133,35 @@
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN);
 }
 
-static gboolean combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
+static gboolean
+combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkWebView *webview)
 {
 	pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
 	return TRUE;
 }
 
-static gboolean imhtml_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
+static gboolean
+webview_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkWebView *webview)
 {
 	if (event->direction == GDK_SCROLL_UP)
-		gtk_imhtml_page_up(imhtml);
+		gtk_webview_page_up(webview);
 	else if (event->direction == GDK_SCROLL_DOWN)
-		gtk_imhtml_page_down(imhtml);
+		gtk_webview_page_down(webview);
 	return TRUE;
 }
 
-static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
+static gboolean
+webview_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
 {
-	if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab)
+	if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
+		remove_typing_cb(status_box);
+		return TRUE;
+	}
+	else if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab)
 	{
 		/* If last inserted character is a tab, then remove the focus from here */
 		GtkWidget *top = gtk_widget_get_toplevel(w);
-		g_signal_emit_by_name(G_OBJECT(top), "move_focus",
+		g_signal_emit_by_name(G_OBJECT(top), "move-focus",
 				(event->state & GDK_SHIFT_MASK) ?
 				                  GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD);
 		return TRUE;
@@ -1166,8 +1174,11 @@
 	{
 		purple_timeout_remove(status_box->typing);
 		status_box->typing = 0;
-		gtk_imhtml_set_populate_primary_clipboard(
-			GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+	/* TODO WebKit: Doesn't do this? */
+		gtk_webview_set_populate_primary_clipboard(
+			GTK_WEBVIEW(status_box->webview), TRUE);
+#endif
 		if (status_box->account != NULL)
 			update_to_reflect_account_status(status_box, status_box->account,
 							purple_account_get_active_status(status_box->account));
@@ -1266,18 +1277,10 @@
 spellcheck_prefs_cb(const char *name, PurplePrefType type,
 					gconstpointer value, gpointer data)
 {
-#ifdef USE_GTKSPELL
 	PidginStatusBox *status_box = (PidginStatusBox *)data;
 
-	if (value)
-		pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml));
-	else
-	{
-		GtkSpell *spell;
-		spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(status_box->imhtml));
-		gtkspell_detach(spell);
-	}
-#endif
+	pidgin_webview_set_spellcheck(GTK_WEBVIEW(status_box->webview),
+	                              (gboolean)GPOINTER_TO_INT(value));
 }
 
 #if 0
@@ -1287,7 +1290,7 @@
 	if (event->button != 1)
 		return FALSE;
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box->toggle_button), FALSE);
-	if (!box->imhtml_visible)
+	if (!box->webview_visible)
 		g_signal_emit_by_name(G_OBJECT(box), "changed", NULL, NULL);
 	return TRUE;
 }
@@ -1703,14 +1706,13 @@
 }
 
 static void
-imhtml_cursor_moved_cb(gpointer data, GtkMovementStep step, gint count, gboolean extend,
-		GtkWidget *widget)
+webview_cursor_moved_cb(gpointer data, GtkWebView *webview)
 {
 	/* Restart the typing timeout if arrow keys are pressed while editing the message */
 	PidginStatusBox *status_box = data;
 	if (status_box->typing == 0)
 		return;
-	imhtml_changed_cb(NULL, status_box);
+	webview_changed_cb(NULL, status_box);
 }
 
 static void
@@ -1761,12 +1763,11 @@
 	GtkCellRenderer *text_rend;
 	GtkCellRenderer *icon_rend;
 	GtkCellRenderer *emblem_rend;
-	GtkTextBuffer *buffer;
 	GtkWidget *toplevel;
 	GtkTreeSelection *sel;
 
 	gtk_widget_set_has_window(GTK_WIDGET(status_box), FALSE);
-	status_box->imhtml_visible = FALSE;
+	status_box->webview_visible = FALSE;
 	status_box->network_available = purple_network_is_available();
 	status_box->connecting = FALSE;
 	status_box->typing = 0;
@@ -1859,10 +1860,9 @@
 	g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
 	status_box->vbox = gtk_vbox_new(0, FALSE);
-	status_box->sw = pidgin_create_imhtml(FALSE, &status_box->imhtml, NULL, NULL);
-	gtk_imhtml_set_editable(GTK_IMHTML(status_box->imhtml), TRUE);
-
-	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
+	status_box->sw = pidgin_create_webview(FALSE, &status_box->webview, NULL, NULL);
+	gtk_webview_set_editable(GTK_WEBVIEW(status_box->webview), TRUE);
+
 #if 0
 	g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
 			 G_CALLBACK(button_pressed_cb), status_box);
@@ -1873,19 +1873,17 @@
 	                 G_CALLBACK(toggle_key_press_cb), status_box);
 	g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
 	                 G_CALLBACK(toggled_cb), status_box);
-	g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(imhtml_changed_cb), status_box);
-	g_signal_connect(G_OBJECT(status_box->imhtml), "format_function_toggle",
-			 G_CALLBACK(imhtml_format_changed_cb), status_box);
-	g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "move_cursor",
-			 G_CALLBACK(imhtml_cursor_moved_cb), status_box);
-	g_signal_connect(G_OBJECT(status_box->imhtml), "key_press_event",
-			 G_CALLBACK(imhtml_remove_focus), status_box);
-	g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "message_send", G_CALLBACK(remove_typing_cb), status_box);
-
-#ifdef USE_GTKSPELL
+	g_signal_connect(G_OBJECT(status_box->webview), "changed",
+	                 G_CALLBACK(webview_changed_cb), status_box);
+	g_signal_connect(G_OBJECT(status_box->webview), "format-toggled",
+	                 G_CALLBACK(webview_format_changed_cb), status_box);
+	g_signal_connect_swapped(G_OBJECT(status_box->webview), "selection-changed",
+	                         G_CALLBACK(webview_cursor_moved_cb), status_box);
+	g_signal_connect(G_OBJECT(status_box->webview), "key-press-event",
+	                 G_CALLBACK(webview_remove_focus), status_box);
+
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
-		pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml));
-#endif
+		pidgin_webview_set_spellcheck(GTK_WEBVIEW(status_box->webview), TRUE);
 	gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box));
 	gtk_widget_show_all(status_box->vbox);
 
@@ -1893,9 +1891,9 @@
 
 	gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0);
 
-	g_signal_connect(G_OBJECT(status_box), "scroll_event", G_CALLBACK(combo_box_scroll_event_cb), NULL);
-	g_signal_connect(G_OBJECT(status_box->imhtml), "scroll_event",
-					G_CALLBACK(imhtml_scroll_event_cb), status_box->imhtml);
+	g_signal_connect(G_OBJECT(status_box), "scroll-event", G_CALLBACK(combo_box_scroll_event_cb), NULL);
+	g_signal_connect(G_OBJECT(status_box->webview), "scroll-event",
+	                 G_CALLBACK(webview_scroll_event_cb), status_box->webview);
 	g_signal_connect(G_OBJECT(status_box->popup_window), "button_release_event", G_CALLBACK(treeview_button_release_cb), status_box);
 	g_signal_connect(G_OBJECT(status_box->popup_window), "key_press_event", G_CALLBACK(treeview_key_press_event), status_box);
 	g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed",
@@ -1954,8 +1952,8 @@
 	*minimum_height = MAX(*minimum_height, 34) + border_width * 2;
 	*natural_height = MAX(*natural_height, 34) + border_width * 2;
 
-	/* If the gtkimhtml is visible, then add some additional padding */
-	if (PIDGIN_STATUS_BOX(widget)->imhtml_visible) {
+	/* If the gtkwebview is visible, then add some additional padding */
+	if (PIDGIN_STATUS_BOX(widget)->webview_visible) {
 		gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget)->vbox,
 			&box_min_height, &box_nat_height);
 
@@ -1980,8 +1978,8 @@
 	requisition->height = MAX(requisition->height, 34);
 	requisition->height += border_width * 2;
 
-	/* If the gtkimhtml is visible, then add some additional padding */
-	if (PIDGIN_STATUS_BOX(widget)->imhtml_visible) {
+	/* If the gtkwebview is visible, then add some additional padding */
+	if (PIDGIN_STATUS_BOX(widget)->webview_visible) {
 		gtk_widget_size_request(PIDGIN_STATUS_BOX(widget)->vbox, &box_req);
 		if (box_req.height > 1)
 			requisition->height += box_req.height + border_width * 2;
@@ -2419,7 +2417,7 @@
 	if (!message || !*message)
 	{
 		gtk_widget_hide(status_box->vbox);
-		status_box->imhtml_visible = FALSE;
+		status_box->webview_visible = FALSE;
 		if (message != NULL)
 		{
 			g_free(message);
@@ -2577,6 +2575,8 @@
 
 static void update_size(PidginStatusBox *status_box)
 {
+#if 0
+	/* TODO WebKit Sizing */
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
 	int display_lines;
@@ -2586,25 +2586,28 @@
 	int pad_top, pad_inside, pad_bottom;
 	gboolean interior_focus;
 	int focus_width;
-
-	if (!status_box->imhtml_visible)
+#endif
+
+	if (!status_box->webview_visible)
 	{
 		if (status_box->vbox != NULL)
 			gtk_widget_set_size_request(status_box->vbox, -1, -1);
 		return;
 	}
 
-	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
+#if 0
+	/* TODO WebKit: Entry sizing */
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->webview));
 
 	height = 0;
 	display_lines = 1;
 	gtk_text_buffer_get_start_iter(buffer, &iter);
 	do {
-		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline);
 		height += oneline.height;
 		display_lines++;
 	} while (display_lines <= 4 &&
-		gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter));
+		gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->webview), &iter));
 
 	/*
 	 * This check fixes the case where the last character entered is a
@@ -2618,7 +2621,7 @@
 		&& gtk_text_iter_backward_char(&iter)
 		&& gtk_text_iter_get_char(&iter) == '\n')
 	{
-		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline);
 		height += oneline.height;
 		display_lines++;
 	}
@@ -2629,14 +2632,14 @@
 	lines = MIN(lines, 4);
 	display_lines = MIN(display_lines, 4);
 
-	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->imhtml));
-	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->imhtml));
-	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->imhtml));
+	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->webview));
+	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->webview));
+	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->webview));
 
 	height += (pad_top + pad_bottom) * lines;
 	height += (pad_inside) * (display_lines - lines);
 
-	gtk_widget_style_get(status_box->imhtml,
+	gtk_widget_style_get(status_box->webview,
 	                     "interior-focus", &interior_focus,
 	                     "focus-line-width", &focus_width,
 	                     NULL);
@@ -2644,6 +2647,8 @@
 		height += 2 * focus_width;
 
 	gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE);
+#endif
+	gtk_widget_set_size_request(status_box->vbox, -1, -1);
 }
 
 static void remove_typing_cb(PidginStatusBox *status_box)
@@ -2655,8 +2660,11 @@
 		return;
 	}
 
-	gtk_imhtml_set_populate_primary_clipboard(
-		GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+	/* TODO WebKit: Doesn't do this? */
+	gtk_webview_set_populate_primary_clipboard(
+		GTK_WEBVIEW(status_box->webview), TRUE);
+#endif
 
 	purple_timeout_remove(status_box->typing);
 	status_box->typing = 0;
@@ -2735,7 +2743,7 @@
 		accounts = g_list_prepend(accounts, status_box->account);
 	else
 		accounts = purple_accounts_get_all_active();
-	status_box->imhtml_visible = FALSE;
+	status_box->webview_visible = FALSE;
 	for (node = accounts; node != NULL; node = node->next)
 	{
 		PurpleAccount *account;
@@ -2746,7 +2754,7 @@
 		if ((status_type != NULL) &&
 			(purple_status_type_get_attr(status_type, "message") != NULL))
 		{
-			status_box->imhtml_visible = TRUE;
+			status_box->webview_visible = TRUE;
 			break;
 		}
 	}
@@ -2754,21 +2762,18 @@
 
 	if (gtk_widget_get_sensitive(GTK_WIDGET(status_box)))
 	{
-		if (status_box->imhtml_visible)
+		if (status_box->webview_visible)
 		{
-			GtkTextIter start, end;
-			GtkTextBuffer *buffer;
 			gtk_widget_show_all(status_box->vbox);
 			status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box);
-			gtk_widget_grab_focus(status_box->imhtml);
-			buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
-
-			gtk_imhtml_set_populate_primary_clipboard(
-				GTK_IMHTML(status_box->imhtml), FALSE);
-
-			gtk_text_buffer_get_bounds(buffer, &start, &end);
-			gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "insert"), &end);
-			gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "selection_bound"), &start);
+			gtk_widget_grab_focus(status_box->webview);
+#if 0
+			/* TODO WebKit: Doesn't do this? */
+			gtk_webview_set_populate_primary_clipboard(
+				GTK_WEBVIEW(status_box->webview), FALSE);
+#endif
+
+			webkit_web_view_select_all(WEBKIT_WEB_VIEW(status_box->webview));
 		}
 		else
 		{
@@ -2805,7 +2810,8 @@
 	return index;
 }
 
-static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data)
+static void
+webview_changed_cb(GtkWebView *webview, void *data)
 {
 	PidginStatusBox *status_box = (PidginStatusBox*)data;
 	if (gtk_widget_get_sensitive(GTK_WIDGET(status_box)))
@@ -2819,15 +2825,16 @@
 	pidgin_status_box_refresh(status_box);
 }
 
-static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data)
+static void
+webview_format_changed_cb(GtkWebView *webview, GtkWebViewButtons buttons, void *data)
 {
-	imhtml_changed_cb(NULL, data);
+	webview_changed_cb(NULL, data);
 }
 
 char *pidgin_status_box_get_message(PidginStatusBox *status_box)
 {
-	if (status_box->imhtml_visible)
-		return gtk_imhtml_get_markup(GTK_IMHTML(status_box->imhtml));
+	if (status_box->webview_visible)
+		return gtk_webview_get_body_html(GTK_WEBVIEW(status_box->webview));
 	else
 		return NULL;
 }
--- a/pidgin/gtkstatusbox.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkstatusbox.h	Tue Aug 14 22:26:33 2012 +0200
@@ -29,7 +29,7 @@
 #define __PIDGIN_STATUS_BOX_H__
 
 #include <gtk/gtk.h>
-#include "gtkimhtml.h"
+#include "gtkwebview.h"
 #include "account.h"
 #include "imgstore.h"
 #include "savedstatuses.h"
@@ -87,7 +87,7 @@
 	PurpleAccount *token_status_account;
 
 	GtkWidget *vbox, *sw;
-	GtkWidget *imhtml;
+	GtkWidget *webview;
 
 	PurpleStoredImage *buddy_icon_img;
 	GdkPixbuf *buddy_icon;
@@ -101,7 +101,7 @@
 	int icon_size;
 	gboolean icon_opaque;
 
-	gboolean imhtml_visible;
+	gboolean webview_visible;
 
 	GtkWidget *cell_view;
 	GtkCellRenderer *icon_rend;
--- a/pidgin/gtkthemes.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkthemes.c	Tue Aug 14 22:26:33 2012 +0200
@@ -30,9 +30,9 @@
 
 #include "gtkconv.h"
 #include "gtkdialogs.h"
-#include "gtkimhtml.h"
 #include "gtksmiley.h"
 #include "gtkthemes.h"
+#include "gtkwebview.h"
 
 GSList *smiley_themes = NULL;
 struct smiley_theme *current_smiley_theme;
@@ -120,21 +120,19 @@
 	g_free(theme_dir);
 }
 
-static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom)
+static void _pidgin_themes_smiley_themeize(GtkWidget *webview, gboolean custom)
 {
-	/* TODO WEBKIT: move imhtml dependency to use webview. */
-#if 0
 	struct smiley_list *list;
 	if (!current_smiley_theme)
 		return;
 
-	gtk_imhtml_remove_smileys(GTK_IMHTML(imhtml));
+	gtk_webview_remove_smileys(GTK_WEBVIEW(webview));
 	list = current_smiley_theme->list;
 	while (list) {
 		char *sml = !strcmp(list->sml, "default") ? NULL : list->sml;
 		GSList *icons = list->smileys;
 		while (icons) {
-			gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
+			gtk_webview_associate_smiley(GTK_WEBVIEW(webview), sml, icons->data);
 			icons = icons->next;
 		}
 
@@ -142,50 +140,36 @@
 			icons = pidgin_smileys_get_all();
 
 			while (icons) {
-				gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
+				gtk_webview_associate_smiley(GTK_WEBVIEW(webview), sml, icons->data);
 				icons = icons->next;
 			}
 		}
 
 		list = list->next;
 	}
-#endif /* if 0 */
 }
 
-void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
+void
+pidgin_themes_smiley_themeize(GtkWidget *webview)
 {
-	_pidgin_themes_smiley_themeize(imhtml, FALSE);
+	_pidgin_themes_smiley_themeize(webview, FALSE);
 }
 
-void pidgin_themes_smiley_themeize_custom(GtkWidget *imhtml)
+void
+pidgin_themes_smiley_themeize_custom(GtkWidget *webview)
 {
-	_pidgin_themes_smiley_themeize(imhtml, TRUE);
+	_pidgin_themes_smiley_themeize(webview, TRUE);
 }
 
 static void
 pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme)
 {
-	GHashTable *already_freed;
 	struct smiley_list *wer;
 
-	already_freed = g_hash_table_new(g_direct_hash, g_direct_equal);
 	for (wer = theme->list; wer != NULL; wer = theme->list) {
 		while (wer->smileys) {
-			GtkIMHtmlSmiley *uio = wer->smileys->data;
-
-			if (uio->imhtml) {
-				g_signal_handlers_disconnect_matched(uio->imhtml, G_SIGNAL_MATCH_DATA,
-					0, 0, NULL, NULL, uio);
-			}
-
-			if (uio->icon)
-				g_object_unref(uio->icon);
-			if (g_hash_table_lookup(already_freed, uio->file) == NULL) {
-				g_free(uio->file);
-				g_hash_table_insert(already_freed, uio->file, GINT_TO_POINTER(1));
-			}
-			g_free(uio->smile);
-			g_free(uio);
+			GtkWebViewSmiley *uio = wer->smileys->data;
+			gtk_webview_smiley_destroy(uio);
 			wer->smileys = g_slist_delete_link(wer->smileys, wer->smileys);
 		}
 		theme->list = wer->next;
@@ -193,8 +177,6 @@
 		g_free(wer);
 	}
 	theme->list = NULL;
-
-	g_hash_table_destroy(already_freed);
 }
 
 static void
@@ -323,7 +305,7 @@
 				if (!sfile) {
 					sfile = g_build_filename(dirname, l, NULL);
 				} else {
-					GtkIMHtmlSmiley *smiley = gtk_imhtml_smiley_create(sfile, l, hidden, 0);
+					GtkWebViewSmiley *smiley = gtk_webview_smiley_create(sfile, l, hidden, 0);
 					list->smileys = g_slist_prepend(list->smileys, smiley);
 					g_hash_table_insert (list->files, g_strdup(l), g_strdup(sfile));
 				}
--- a/pidgin/gtkwebview.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkwebview.c	Tue Aug 14 22:26:33 2012 +0200
@@ -53,6 +53,7 @@
 	CLEAR_FORMAT,
 	UPDATE_FORMAT,
 	CHANGED,
+	HTML_APPENDED,
 	LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -62,6 +63,21 @@
  *****************************************************************************/
 
 typedef struct {
+	WebKitWebInspector *inspector;
+	WebKitDOMNode *node;
+} GtkWebViewInspectData;
+
+typedef struct {
+	WebKitWebView *webview;
+	gunichar ch;
+} GtkWebViewInsertData;
+
+typedef struct {
+	const char *label;
+	gunichar ch;
+} GtkUnicodeMenuEntry;
+
+typedef struct {
 	char *name;
 	int length;
 
@@ -69,6 +85,26 @@
 	gboolean (*context_menu)(GtkWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu);
 } GtkWebViewProtocol;
 
+struct _GtkWebViewSmiley {
+	gchar *smile;
+	gchar *file;
+	GdkPixbufAnimation *icon;
+	gboolean hidden;
+	GdkPixbufLoader *loader;
+	GSList *anchors;
+	GtkWebViewSmileyFlags flags;
+	GtkWebView *webview;
+	gpointer data;
+	gsize datasize;
+};
+
+typedef struct _GtkSmileyTree GtkSmileyTree;
+struct _GtkSmileyTree {
+	GString *values;
+	GtkSmileyTree **children;
+	GtkWebViewSmiley *image;
+};
+
 typedef struct _GtkWebViewPriv {
 	/* Processing queues */
 	gboolean is_loading;
@@ -77,6 +113,7 @@
 
 	/* Scroll adjustments */
 	GtkAdjustment *vadj;
+	gboolean autoscroll;
 	guint scroll_src;
 	GTimer *scroll_time;
 
@@ -87,6 +124,10 @@
 		gboolean block_changed:1;
 	} edit;
 
+	/* Smileys */
+	char *protocol_name;
+	GHashTable *smiley_data;
+	GtkSmileyTree *default_smilies;
 } GtkWebViewPriv;
 
 /******************************************************************************
@@ -96,6 +137,531 @@
 static WebKitWebViewClass *parent_class = NULL;
 
 /******************************************************************************
+ * Smileys
+ *****************************************************************************/
+
+const char *
+gtk_webview_get_protocol_name(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv;
+
+	g_return_val_if_fail(webview != NULL, NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	return priv->protocol_name;
+}
+
+void
+gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name)
+{
+	GtkWebViewPriv *priv;
+
+	g_return_if_fail(webview != NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	priv->protocol_name = g_strdup(protocol_name);
+}
+
+static GtkSmileyTree *
+gtk_smiley_tree_new(void)
+{
+	return g_new0(GtkSmileyTree, 1);
+}
+
+static void
+gtk_smiley_tree_insert(GtkSmileyTree *tree, GtkWebViewSmiley *smiley)
+{
+	GtkSmileyTree *t = tree;
+	const char *x = smiley->smile;
+
+	if (!(*x))
+		return;
+
+	do {
+		char *pos;
+		gsize index;
+
+		if (!t->values)
+			t->values = g_string_new("");
+
+		pos = strchr(t->values->str, *x);
+		if (!pos) {
+			t->values = g_string_append_c(t->values, *x);
+			index = t->values->len - 1;
+			t->children = g_realloc(t->children, t->values->len * sizeof(GtkSmileyTree *));
+			t->children[index] = g_new0(GtkSmileyTree, 1);
+		} else
+			index = pos - t->values->str;
+
+		t = t->children[index];
+
+		x++;
+	} while (*x);
+
+	t->image = smiley;
+}
+
+static void
+gtk_smiley_tree_destroy(GtkSmileyTree *tree)
+{
+	GSList *list = g_slist_prepend(NULL, tree);
+
+	while (list) {
+		GtkSmileyTree *t = list->data;
+		gsize i;
+		list = g_slist_delete_link(list, list);
+		if (t && t->values) {
+			for (i = 0; i < t->values->len; i++)
+				list = g_slist_prepend(list, t->children[i]);
+			g_string_free(t->values, TRUE);
+			g_free(t->children);
+		}
+
+		g_free(t);
+	}
+}
+
+static void
+gtk_smiley_tree_remove(GtkSmileyTree *tree, GtkWebViewSmiley *smiley)
+{
+	GtkSmileyTree *t = tree;
+	const gchar *x = smiley->smile;
+	int len = 0;
+
+	while (*x) {
+		char *pos;
+
+		if (!t->values)
+			return;
+
+		pos = strchr(t->values->str, *x);
+		if (pos)
+			t = t->children[pos - t->values->str];
+		else
+			return;
+
+		x++; len++;
+	}
+
+	t->image = NULL;
+}
+
+static int
+gtk_smiley_tree_lookup(GtkSmileyTree *tree, const char *text)
+{
+	GtkSmileyTree *t = tree;
+	const char *x = text;
+	int len = 0;
+	const char *amp;
+	int alen;
+
+	while (*x) {
+		char *pos;
+
+		if (!t->values)
+			break;
+
+		if (*x == '&' && (amp = purple_markup_unescape_entity(x, &alen))) {
+			gboolean matched = TRUE;
+			/* Make sure all chars of the unescaped value match */
+			while (*(amp + 1)) {
+				pos = strchr(t->values->str, *amp);
+				if (pos)
+					t = t->children[pos - t->values->str];
+				else {
+					matched = FALSE;
+					break;
+				}
+				amp++;
+			}
+			if (!matched)
+				break;
+
+			pos = strchr(t->values->str, *amp);
+		}
+		else if (*x == '<') /* Because we're all WYSIWYG now, a '<' char should
+		                     * only appear as the start of a tag.  Perhaps a
+		                     * safer (but costlier) check would be to call
+		                     * gtk_imhtml_is_tag on it */
+			break;
+		else {
+			alen = 1;
+			pos = strchr(t->values->str, *x);
+		}
+
+		if (pos)
+			t = t->children[pos - t->values->str];
+		else
+			break;
+
+		x += alen;
+		len += alen;
+	}
+
+	if (t->image)
+		return len;
+
+	return 0;
+}
+
+static void
+gtk_webview_disassociate_smiley_foreach(gpointer key, gpointer value,
+                                        gpointer user_data)
+{
+	GtkSmileyTree *tree = (GtkSmileyTree *)value;
+	GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)user_data;
+	gtk_smiley_tree_remove(tree, smiley);
+}
+
+static void
+gtk_webview_disconnect_smiley(GtkWebView *webview, GtkWebViewSmiley *smiley)
+{
+	smiley->webview = NULL;
+	g_signal_handlers_disconnect_matched(webview, G_SIGNAL_MATCH_DATA, 0, 0,
+	                                     NULL, NULL, smiley);
+}
+
+static void
+gtk_webview_disassociate_smiley(GtkWebViewSmiley *smiley)
+{
+	if (smiley->webview) {
+		GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(smiley->webview);
+		gtk_smiley_tree_remove(priv->default_smilies, smiley);
+		g_hash_table_foreach(priv->smiley_data,
+			gtk_webview_disassociate_smiley_foreach, smiley);
+		g_signal_handlers_disconnect_matched(smiley->webview,
+		                                     G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+		                                     NULL, smiley);
+		smiley->webview = NULL;
+	}
+}
+
+void
+gtk_webview_associate_smiley(GtkWebView *webview, const char *sml,
+                             GtkWebViewSmiley *smiley)
+{
+	GtkSmileyTree *tree;
+	GtkWebViewPriv *priv;
+
+	g_return_if_fail(webview != NULL);
+	g_return_if_fail(GTK_IS_WEBVIEW(webview));
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	if (sml == NULL)
+		tree = priv->default_smilies;
+	else if (!(tree = g_hash_table_lookup(priv->smiley_data, sml))) {
+		tree = gtk_smiley_tree_new();
+		g_hash_table_insert(priv->smiley_data, g_strdup(sml), tree);
+	}
+
+	/* need to disconnect old webview, if there is one */
+	if (smiley->webview) {
+		g_signal_handlers_disconnect_matched(smiley->webview,
+		                                     G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+		                                     NULL, smiley);
+	}
+
+	smiley->webview = webview;
+
+	gtk_smiley_tree_insert(tree, smiley);
+
+	/* connect destroy signal for the webview */
+	g_signal_connect(webview, "destroy",
+	                 G_CALLBACK(gtk_webview_disconnect_smiley), smiley);
+}
+
+static gboolean
+gtk_webview_is_smiley(GtkWebViewPriv *priv, const char *sml, const char *text,
+                      int *len)
+{
+	GtkSmileyTree *tree;
+
+	if (!sml)
+		sml = priv->protocol_name;
+
+	if (!sml || !(tree = g_hash_table_lookup(priv->smiley_data, sml)))
+		tree = priv->default_smilies;
+
+	if (tree == NULL)
+		return FALSE;
+
+	*len = gtk_smiley_tree_lookup(tree, text);
+	return (*len > 0);
+}
+
+static GtkWebViewSmiley *
+gtk_webview_smiley_get_from_tree(GtkSmileyTree *t, const char *text)
+{
+	const char *x = text;
+	char *pos;
+
+	if (t == NULL)
+		return NULL;
+
+	while (*x) {
+		if (!t->values)
+			return NULL;
+
+		pos = strchr(t->values->str, *x);
+		if (!pos)
+			return NULL;
+
+		t = t->children[pos - t->values->str];
+		x++;
+	}
+
+	return t->image;
+}
+
+GtkWebViewSmiley *
+gtk_webview_smiley_find(GtkWebView *webview, const char *sml, const char *text)
+{
+	GtkWebViewPriv *priv;
+	GtkWebViewSmiley *ret;
+
+	g_return_val_if_fail(webview != NULL, NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	/* Look for custom smileys first */
+	if (sml != NULL) {
+		ret = gtk_webview_smiley_get_from_tree(g_hash_table_lookup(priv->smiley_data, sml), text);
+		if (ret != NULL)
+			return ret;
+	}
+
+	/* Fall back to check for default smileys */
+	return gtk_webview_smiley_get_from_tree(priv->default_smilies, text);
+}
+
+static GdkPixbufAnimation *
+gtk_smiley_get_image(GtkWebViewSmiley *smiley)
+{
+	if (!smiley->icon) {
+		if (smiley->file) {
+			smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
+		} else if (smiley->loader) {
+			smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
+			if (smiley->icon)
+				g_object_ref(G_OBJECT(smiley->icon));
+		}
+	}
+
+	return smiley->icon;
+}
+
+static void
+gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data)
+{
+	GtkWebViewSmiley *smiley;
+
+	smiley = (GtkWebViewSmiley *)user_data;
+	smiley->icon = gdk_pixbuf_loader_get_animation(loader);
+
+	if (smiley->icon)
+		g_object_ref(G_OBJECT(smiley->icon));
+}
+
+static void
+gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data)
+{
+	GtkWebViewSmiley *smiley;
+	GtkWidget *icon = NULL;
+	GtkTextChildAnchor *anchor = NULL;
+	GSList *current = NULL;
+
+	smiley = (GtkWebViewSmiley *)user_data;
+	if (!smiley->webview) {
+		g_object_unref(G_OBJECT(loader));
+		smiley->loader = NULL;
+		return;
+	}
+
+	for (current = smiley->anchors; current; current = g_slist_next(current)) {
+		anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
+		if (gtk_text_child_anchor_get_deleted(anchor))
+			icon = NULL;
+		else
+			icon = gtk_image_new_from_animation(smiley->icon);
+
+		if (icon) {
+			GList *wids;
+			gtk_widget_show(icon);
+
+			wids = gtk_text_child_anchor_get_widgets(anchor);
+
+			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext",
+			                       purple_unescape_html(smiley->smile), g_free);
+			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext",
+			                       g_strdup(smiley->smile), g_free);
+
+			if (smiley->webview) {
+				if (wids) {
+					GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data));
+					g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL);
+					g_list_free(children);
+					gtk_container_add(GTK_CONTAINER(wids->data), icon);
+				} else
+					gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->webview), icon, anchor);
+			}
+			g_list_free(wids);
+		}
+		g_object_unref(anchor);
+	}
+
+	g_slist_free(smiley->anchors);
+	smiley->anchors = NULL;
+
+	g_object_unref(G_OBJECT(loader));
+	smiley->loader = NULL;
+}
+
+static void
+gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data)
+{
+	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) {
+		int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size");
+		if (width <= custom_smileys_size && height <= custom_smileys_size)
+			return;
+
+		if (width >= height) {
+			height = height * custom_smileys_size / width;
+			width = custom_smileys_size;
+		} else {
+			width = width * custom_smileys_size / height;
+			height = custom_smileys_size;
+		}
+	}
+	gdk_pixbuf_loader_set_size(loader, width, height);
+}
+
+GtkWebViewSmiley *
+gtk_webview_smiley_create(const char *file, const char *shortcut, gboolean hide,
+                          GtkWebViewSmileyFlags flags)
+{
+	GtkWebViewSmiley *smiley = g_new0(GtkWebViewSmiley, 1);
+	smiley->file = g_strdup(file);
+	smiley->smile = g_strdup(shortcut);
+	smiley->hidden = hide;
+	smiley->flags = flags;
+	smiley->webview = NULL;
+	gtk_webview_smiley_reload(smiley);
+	return smiley;
+}
+
+void
+gtk_webview_smiley_reload(GtkWebViewSmiley *smiley)
+{
+	if (smiley->icon)
+		g_object_unref(smiley->icon);
+	if (smiley->loader)
+		g_object_unref(smiley->loader);
+
+	smiley->icon = NULL;
+	smiley->loader = NULL;
+
+	if (smiley->file) {
+		/* We do not use the pixbuf loader for a smiley that can be loaded
+		 * from a file. (e.g., local custom smileys)
+		 */
+		return;
+	}
+
+	smiley->loader = gdk_pixbuf_loader_new();
+
+	g_signal_connect(smiley->loader, "area_prepared",
+	                 G_CALLBACK(gtk_custom_smiley_allocated), smiley);
+	g_signal_connect(smiley->loader, "closed",
+	                 G_CALLBACK(gtk_custom_smiley_closed), smiley);
+	g_signal_connect(smiley->loader, "size_prepared",
+	                 G_CALLBACK(gtk_custom_smiley_size_prepared), smiley);
+}
+
+const char *
+gtk_webview_smiley_get_smile(const GtkWebViewSmiley *smiley)
+{
+	return smiley->smile;
+}
+
+const char *
+gtk_webview_smiley_get_file(const GtkWebViewSmiley *smiley)
+{
+	return smiley->file;
+}
+
+gboolean
+gtk_webview_smiley_get_hidden(const GtkWebViewSmiley *smiley)
+{
+	return smiley->hidden;
+}
+
+GtkWebViewSmileyFlags
+gtk_webview_smiley_get_flags(const GtkWebViewSmiley *smiley)
+{
+	return smiley->flags;
+}
+
+void
+gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley)
+{
+	gtk_webview_disassociate_smiley(smiley);
+	g_free(smiley->smile);
+	g_free(smiley->file);
+	if (smiley->icon)
+		g_object_unref(smiley->icon);
+	if (smiley->loader)
+		g_object_unref(smiley->loader);
+	g_free(smiley->data);
+	g_free(smiley);
+}
+
+void
+gtk_webview_remove_smileys(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv;
+
+	g_return_if_fail(webview != NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	g_hash_table_destroy(priv->smiley_data);
+	gtk_smiley_tree_destroy(priv->default_smilies);
+	priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+	                                          (GDestroyNotify)gtk_smiley_tree_destroy);
+	priv->default_smilies = gtk_smiley_tree_new();
+}
+
+void
+gtk_webview_insert_smiley(GtkWebView *webview, const char *sml,
+                          const char *smiley)
+{
+	GtkWebViewPriv *priv;
+	char *unescaped;
+	GtkWebViewSmiley *webview_smiley;
+
+	g_return_if_fail(webview != NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+
+	unescaped = purple_unescape_html(smiley);
+	webview_smiley = gtk_webview_smiley_find(webview, sml, unescaped);
+
+	if (priv->format_functions & GTK_WEBVIEW_SMILEY) {
+		char *tmp;
+		/* TODO Better smiley insertion... */
+		tmp = g_strdup_printf("<img isEmoticon src='purple-smiley:%p' alt='%s'>",
+		                      webview_smiley, smiley);
+		gtk_webview_append_html(webview, tmp);
+		g_free(tmp);
+	} else {
+		gtk_webview_append_html(webview, smiley);
+	}
+
+	g_free(unescaped);
+}
+
+/******************************************************************************
  * Helpers
  *****************************************************************************/
 
@@ -147,6 +713,9 @@
 	char *str;
 	WebKitDOMDocument *doc;
 	WebKitDOMHTMLElement *body;
+	WebKitDOMNode *start, *end;
+	WebKitDOMRange *range;
+	gboolean require_scroll = FALSE;
 
 	if (priv->is_loading) {
 		priv->loader = 0;
@@ -164,8 +733,43 @@
 		case LOAD_HTML:
 			doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
 			body = webkit_dom_document_get_body(doc);
+			start = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
+
+			if (priv->autoscroll) {
+				require_scroll = (gtk_adjustment_get_value(priv->vadj)
+				                 >= (gtk_adjustment_get_upper(priv->vadj) -
+				                 1.5*gtk_adjustment_get_page_size(priv->vadj)));
+			}
+
 			webkit_dom_html_element_insert_adjacent_html(body, "beforeend",
 			                                             str, NULL);
+
+			range = webkit_dom_document_create_range(doc);
+			if (start) {
+				end = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
+				webkit_dom_range_set_start_after(range,
+				                                 WEBKIT_DOM_NODE(start),
+				                                 NULL);
+				webkit_dom_range_set_end_after(range,
+				                               WEBKIT_DOM_NODE(end),
+				                               NULL);
+			} else {
+				webkit_dom_range_select_node_contents(range,
+				                                      WEBKIT_DOM_NODE(body),
+				                                      NULL);
+			}
+
+			if (require_scroll) {
+				if (start)
+					webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(start),
+					                                    TRUE);
+				else
+					webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(body),
+					                                    TRUE);
+			}
+
+			g_signal_emit(webview, signals[HTML_APPENDED], 0, range);
+
 			break;
 
 		case LOAD_JS:
@@ -204,6 +808,12 @@
 		priv->loader = g_idle_add((GSourceFunc)process_load_queue, webview);
 }
 
+static void
+webview_show_inspector_cb(GtkWidget *item, GtkWebViewInspectData *data)
+{
+	webkit_web_inspector_inspect_node(data->inspector, data->node);
+}
+
 static GtkWebViewProtocol *
 webview_find_protocol(const char *url, gboolean reverse)
 {
@@ -254,12 +864,113 @@
 	return TRUE;
 }
 
+static GtkWidget *
+get_input_methods_menu(WebKitWebView *webview)
+{
+	GtkSettings *settings;
+	gboolean show = TRUE;
+	GtkWidget *item;
+	GtkWidget *menu;
+	GtkIMContext *im;
+
+	settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default();
+
+	if (settings)
+		g_object_get(settings, "gtk-show-input-method-menu", &show, NULL);
+	if (!show)
+		return NULL;
+
+	item = gtk_image_menu_item_new_with_mnemonic(_("Input _Methods"));
+
+	g_object_get(webview, "im-context", &im, NULL);
+	menu = gtk_menu_new();
+	gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(im),
+	                                     GTK_MENU_SHELL(menu));
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
+
+	return item;
+}
+
+/* Values taken from gtktextutil.c */
+static const GtkUnicodeMenuEntry bidi_menu_entries[] = {
+	{ N_("LRM _Left-to-right mark"), 0x200E },
+	{ N_("RLM _Right-to-left mark"), 0x200F },
+	{ N_("LRE Left-to-right _embedding"), 0x202A },
+	{ N_("RLE Right-to-left e_mbedding"), 0x202B },
+	{ N_("LRO Left-to-right _override"), 0x202D },
+	{ N_("RLO Right-to-left o_verride"), 0x202E },
+	{ N_("PDF _Pop directional formatting"), 0x202C },
+	{ N_("ZWS _Zero width space"), 0x200B },
+	{ N_("ZWJ Zero width _joiner"), 0x200D },
+	{ N_("ZWNJ Zero width _non-joiner"), 0x200C }
+};
+
+static void
+insert_control_character_cb(GtkMenuItem *item, GtkWebViewInsertData *data)
+{
+	WebKitWebView *webview = data->webview;
+	gunichar ch = data->ch;
+	GtkWebViewPriv *priv;
+	WebKitDOMDocument *dom;
+	char buf[6];
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(GTK_WEBVIEW(webview));
+	dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
+
+	g_unichar_to_utf8(ch, buf);
+	priv->edit.block_changed = TRUE;
+	webkit_dom_document_exec_command(dom, "insertHTML", FALSE, buf);
+	priv->edit.block_changed = FALSE;
+}
+
+static GtkWidget *
+get_unicode_menu(WebKitWebView *webview)
+{
+	GtkSettings *settings;
+	gboolean show = TRUE;
+	GtkWidget *menuitem;
+	GtkWidget *menu;
+	int i;
+
+	settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default();
+
+	if (settings)
+		g_object_get(settings, "gtk-show-unicode-menu", &show, NULL);
+	if (!show)
+		return NULL;
+
+	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Insert Unicode Control Character"));
+
+	menu = gtk_menu_new();
+	for (i = 0; i < G_N_ELEMENTS(bidi_menu_entries); i++) {
+		GtkWebViewInsertData *data;
+		GtkWidget *item;
+
+		data = g_new0(GtkWebViewInsertData, 1);
+		data->webview = webview;
+		data->ch = bidi_menu_entries[i].ch;
+
+		item = gtk_menu_item_new_with_mnemonic(_(bidi_menu_entries[i].label));
+		g_signal_connect_data(item, "activate",
+		                      G_CALLBACK(insert_control_character_cb), data,
+		                      (GClosureNotify)g_free, 0);
+		gtk_widget_show(item);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+	}
+
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
+
+	return menuitem;
+}
+
 static void
 do_popup_menu(WebKitWebView *webview, int button, int time, int context,
-              WebKitDOMHTMLAnchorElement *link, const char *uri)
+              WebKitDOMNode *node, const char *uri)
 {
 	GtkWidget *menu;
 	GtkWidget *cut, *copy, *paste, *delete, *select;
+	WebKitWebSettings *settings;
+	gboolean inspector;
 
 	menu = gtk_menu_new();
 	g_signal_connect(menu, "selection-done",
@@ -270,11 +981,16 @@
 		GtkWebViewProtocol *proto = NULL;
 		GList *children;
 
-		if (uri && link)
+		while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) {
+			node = webkit_dom_node_get_parent_node(node);
+		}
+
+		if (uri && node)
 			proto = webview_find_protocol(uri, FALSE);
 
 		if (proto && proto->context_menu) {
-			proto->context_menu(GTK_WEBVIEW(webview), link, menu);
+			proto->context_menu(GTK_WEBVIEW(webview),
+			                    WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), menu);
 		}
 
 		children = gtk_container_get_children(GTK_CONTAINER(menu));
@@ -333,6 +1049,43 @@
 			webkit_web_view_can_cut_clipboard(webview));
 	}
 
+	settings = webkit_web_view_get_settings(webview);
+	g_object_get(G_OBJECT(settings), "enable-developer-extras", &inspector, NULL);
+	if (inspector) {
+		GtkWidget *inspect;
+		GtkWebViewInspectData *data;
+
+		data = g_new0(GtkWebViewInspectData, 1);
+		data->inspector = webkit_web_view_get_inspector(webview);
+		data->node = node;
+
+		pidgin_separator(menu);
+
+		inspect = pidgin_new_item_from_stock(menu, _("Inspect _Element"), NULL,
+		                                     NULL, NULL, 0, 0, NULL);
+		g_signal_connect_data(G_OBJECT(inspect), "activate",
+		                      G_CALLBACK(webview_show_inspector_cb),
+		                      data, (GClosureNotify)g_free, 0);
+	}
+
+	if (webkit_web_view_get_editable(webview)) {
+		GtkWidget *im = get_input_methods_menu(webview);
+		GtkWidget *unicode = get_unicode_menu(webview);
+
+		if (im || unicode)
+			pidgin_separator(menu);
+
+		if (im) {
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), im);
+			gtk_widget_show(im);
+		}
+
+		if (unicode) {
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), unicode);
+			gtk_widget_show(unicode);
+		}
+	}
+
 	g_signal_emit_by_name(G_OBJECT(webview), "populate-popup", menu);
 
 	gtk_menu_attach_to_widget(GTK_MENU(menu), GTK_WIDGET(webview), NULL);
@@ -342,9 +1095,31 @@
 static gboolean
 webview_popup_menu(WebKitWebView *webview)
 {
+	WebKitDOMDocument *doc;
+	WebKitDOMElement *active;
+	WebKitDOMElement *link;
+	int context;
+	char *uri;
+
+	context = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT;
+	uri = NULL;
+
+	doc = webkit_web_view_get_dom_document(webview);
+	active = webkit_dom_html_document_get_active_element(WEBKIT_DOM_HTML_DOCUMENT(doc));
+
+	link = active;
+	while (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link))
+		link = webkit_dom_node_get_parent_element(WEBKIT_DOM_NODE(link));
+	if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link)) {
+		context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
+		uri = webkit_dom_html_anchor_element_get_href(WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link));
+	}
+
 	do_popup_menu(webview, 0, gtk_get_current_event_time(),
-	              WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
-	              NULL, NULL);
+	              context, WEBKIT_DOM_NODE(active), uri);
+
+	g_free(uri);
+
 	return TRUE;
 }
 
@@ -364,14 +1139,8 @@
 		             "link-uri", &uri,
 		             NULL);
 
-		if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
-			while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) {
-				node = webkit_dom_node_get_parent_node(node);
-			}
-		}
-
 		do_popup_menu(webview, event->button, event->time, context,
-		              WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), uri);
+		              node, uri);
 
 		g_free(uri);
 
@@ -577,6 +1346,10 @@
 	}
 	g_queue_free(priv->load_queue);
 
+	g_hash_table_destroy(priv->smiley_data);
+	gtk_smiley_tree_destroy(priv->default_smilies);
+	g_free(priv->protocol_name);
+
 	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
 }
 
@@ -621,6 +1394,14 @@
 	                                G_STRUCT_OFFSET(GtkWebViewClass, changed),
 	                                NULL, NULL, g_cclosure_marshal_VOID__VOID,
 	                                G_TYPE_NONE, 0);
+	signals[HTML_APPENDED] = g_signal_new("html-appended",
+	                                      G_TYPE_FROM_CLASS(gobject_class),
+	                                      G_SIGNAL_RUN_FIRST,
+	                                      G_STRUCT_OFFSET(GtkWebViewClass, html_appended),
+	                                      NULL, NULL,
+	                                      g_cclosure_marshal_VOID__OBJECT,
+	                                      G_TYPE_NONE, 1, WEBKIT_TYPE_DOM_RANGE,
+	                                      NULL);
 
 	klass->toggle_format = webview_toggle_format;
 	klass->clear_format = webview_clear_formatting;
@@ -659,6 +1440,10 @@
 
 	priv->load_queue = g_queue_new();
 
+	priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+	                                          (GDestroyNotify)gtk_smiley_tree_destroy);
+	priv->default_smilies = gtk_smiley_tree_new();
+
 	g_signal_connect(G_OBJECT(webview), "button-press-event",
 	                 G_CALLBACK(webview_button_pressed), NULL);
 
@@ -824,6 +1609,28 @@
 }
 
 void
+gtk_webview_set_autoscroll(GtkWebView *webview, gboolean scroll)
+{
+	GtkWebViewPriv *priv;
+
+	g_return_if_fail(webview != NULL);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	priv->autoscroll = scroll;
+}
+
+gboolean
+gtk_webview_get_autoscroll(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv;
+
+	g_return_val_if_fail(webview != NULL, FALSE);
+
+	priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	return priv->autoscroll;
+}
+
+void
 gtk_webview_page_up(GtkWebView *webview)
 {
 	GtkWebViewPriv *priv;
@@ -1098,16 +1905,20 @@
 	WebKitDOMDocument *dom;
 	WebKitDOMDOMWindow *win;
 	WebKitDOMDOMSelection *sel;
-	WebKitDOMRange *range;
+	WebKitDOMRange *range = NULL;
 
 	g_return_val_if_fail(webview != NULL, NULL);
 
 	dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
 	win = webkit_dom_document_get_default_view(dom);
 	sel = webkit_dom_dom_window_get_selection(win);
-	range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
+	if (webkit_dom_dom_selection_get_range_count(sel))
+		range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
 
-	return webkit_dom_range_get_text(range);
+	if (range)
+		return webkit_dom_range_get_text(range);
+	else
+		return NULL;
 }
 
 GtkWebViewButtons
--- a/pidgin/gtkwebview.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkwebview.h	Tue Aug 14 22:26:33 2012 +0200
@@ -58,8 +58,13 @@
 	GTK_WEBVIEW_ALL           = -1
 } GtkWebViewButtons;
 
+typedef enum {
+	GTK_WEBVIEW_SMILEY_CUSTOM = 1 << 0
+} GtkWebViewSmileyFlags;
+
 typedef struct _GtkWebView GtkWebView;
 typedef struct _GtkWebViewClass GtkWebViewClass;
+typedef struct _GtkWebViewSmiley GtkWebViewSmiley;
 
 struct _GtkWebView
 {
@@ -77,6 +82,7 @@
 	void (*clear_format)(GtkWebView *);
 	void (*update_format)(GtkWebView *);
 	void (*changed)(GtkWebView *);
+	void (*html_appended)(GtkWebView *, WebKitDOMRange *);
 };
 
 G_BEGIN_DECLS
@@ -162,6 +168,25 @@
 void gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth);
 
 /**
+ * Set whether the GtkWebView stays at its end when HTML content is appended. If
+ * not already at the end before appending, then scrolling will not occur.
+ *
+ * @param webview The GtkWebView object
+ * @param scroll  Whether to automatically scroll
+ */
+void gtk_webview_set_autoscroll(GtkWebView *webview, gboolean scroll);
+
+/**
+ * Set whether the GtkWebView stays at its end when HTML content is appended. If
+ * not already at the end before appending, then scrolling will not occur.
+ *
+ * @param webview The GtkWebView object
+ *
+ * @return Whether to automatically scroll
+ */
+gboolean gtk_webview_get_autoscroll(GtkWebView *webview);
+
+/**
  * Scrolls a GtkWebView up by one page.
  *
  * @param webview The GtkWebView.
@@ -475,6 +500,124 @@
  */
 void gtk_webview_insert_image(GtkWebView *webview, int id);
 
+/**
+ * Gets the protocol name associated with this GtkWebView.
+ *
+ * @param webview The GtkWebView
+ */
+const char *gtk_webview_get_protocol_name(GtkWebView *webview);
+
+/**
+ * Associates a protocol name with a GtkWebView.
+ *
+ * @param webview       The GtkWebView
+ * @param protocol_name The protocol name to associate with the GtkWebView
+ */
+void gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name);
+
+/**
+ * Create a new GtkWebViewSmiley.
+ *
+ * @param file      The image file for the smiley
+ * @param shortcut  The key shortcut for the smiley
+ * @param hide      @c TRUE if the smiley should be hidden in the smiley dialog,
+ *                  @c FALSE otherwise
+ * @param flags     The smiley flags
+ *
+ * @return The newly created smiley
+ */
+GtkWebViewSmiley *gtk_webview_smiley_create(const char *file,
+                                            const char *shortcut,
+                                            gboolean hide,
+                                            GtkWebViewSmileyFlags flags);
+
+/**
+ * Reload the image data for the smiley.
+ *
+ * @param smiley    The smiley to reload
+ */
+void gtk_webview_smiley_reload(GtkWebViewSmiley *smiley);
+
+/**
+ * Destroy a GtkWebViewSmiley.
+ *
+ * @param smiley    The smiley to destroy
+ */
+void gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley);
+
+/**
+ * Returns the text associated with a smiley.
+ *
+ * @param smiley    The smiley
+ *
+ * @return The text
+ */
+const char *gtk_webview_smiley_get_smile(const GtkWebViewSmiley *smiley);
+
+/**
+ * Returns the file associated with a smiley.
+ *
+ * @param smiley    The smiley
+ *
+ * @return The file
+ */
+const char *gtk_webview_smiley_get_file(const GtkWebViewSmiley *smiley);
+
+/**
+ * Returns the invisibility of a smiley.
+ *
+ * @param smiley    The smiley
+ *
+ * @return The hidden status
+ */
+gboolean gtk_webview_smiley_get_hidden(const GtkWebViewSmiley *smiley);
+
+/**
+ * Returns the flags associated with a smiley.
+ *
+ * @param smiley    The smiley
+ *
+ * @return The flags
+ */
+GtkWebViewSmileyFlags gtk_webview_smiley_get_flags(const GtkWebViewSmiley *smiley);
+
+/**
+ * Returns the smiley object associated with the text.
+ *
+ * @param webview The GtkWebView
+ * @param sml     The name of the smiley category
+ * @param text    The text associated with the smiley
+ */
+GtkWebViewSmiley *gtk_webview_smiley_find(GtkWebView *webview, const char *sml,
+                                          const char *text);
+
+/**
+ * Associates a smiley with a GtkWebView.
+ *
+ * @param webview The GtkWebView
+ * @param sml     The name of the smiley category
+ * @param smiley  The GtkWebViewSmiley to associate
+ */
+void gtk_webview_associate_smiley(GtkWebView *webview, const char *sml,
+                                  GtkWebViewSmiley *smiley);
+
+/**
+ * Removes all smileys associated with a GtkWebView.
+ *
+ * @param webview The GtkWebView.
+ */
+void gtk_webview_remove_smileys(GtkWebView *webview);
+
+/**
+ * Inserts a smiley at the current location or selection in a GtkWebView.
+ *
+ * @param webview The GtkWebView
+ * @param sml     The category of the smiley
+ * @param smiley  The text of the smiley to insert
+ */
+void gtk_webview_insert_smiley(GtkWebView *webview, const char *sml,
+                               const char *smiley);
+
 G_END_DECLS
 
 #endif /* _PIDGIN_WEBVIEW_H_ */
--- a/pidgin/gtkwebviewtoolbar.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkwebviewtoolbar.c	Tue Aug 14 22:26:33 2012 +0200
@@ -632,7 +632,6 @@
 	gtk_widget_grab_focus(toolbar->webview);
 }
 
-#if 0
 static void
 destroy_smiley_dialog(GtkWebViewToolbar *toolbar)
 {
@@ -648,23 +647,21 @@
 close_smiley_dialog(GtkWebViewToolbar *toolbar)
 {
 	GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->smiley), FALSE);
+	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->smiley), FALSE);
 	return FALSE;
 }
 
-
 static void
 insert_smiley_text(GtkWidget *widget, GtkWebViewToolbar *toolbar)
 {
-	GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
 	char *smiley_text, *escaped_smiley;
 
 	smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text");
 	escaped_smiley = g_markup_escape_text(smiley_text, -1);
 
 	gtk_webview_insert_smiley(GTK_WEBVIEW(toolbar->webview),
-							 GTK_WEBVIEW(toolbar->webview)->protocol_name,
-							 escaped_smiley);
+	                          gtk_webview_get_protocol_name(GTK_WEBVIEW(toolbar->webview)),
+	                          escaped_smiley);
 
 	g_free(escaped_smiley);
 
@@ -675,34 +672,34 @@
 struct smiley_button_list {
 	int width, height;
 	GtkWidget *button;
-	const GtkIMHtmlSmiley *smiley;
+	const GtkWebViewSmiley *smiley;
 	struct smiley_button_list *next;
 };
 
 static struct smiley_button_list *
 sort_smileys(struct smiley_button_list *ls, GtkWebViewToolbar *toolbar,
-			 int *width, const GtkIMHtmlSmiley *smiley)
+             int *width, const GtkWebViewSmiley *smiley)
 {
-	GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
 	GtkWidget *image;
 	GtkWidget *button;
 	GtkRequisition size;
 	struct smiley_button_list *cur;
 	struct smiley_button_list *it, *it_last;
-	const gchar *filename = smiley->file;
-	gchar *face = smiley->smile;
+	const gchar *filename = gtk_webview_smiley_get_file(smiley);
+	const gchar *face = gtk_webview_smiley_get_smile(smiley);
 	PurpleSmiley *psmiley = NULL;
 	gboolean supports_custom = (gtk_webview_get_format_functions(GTK_WEBVIEW(toolbar->webview)) & GTK_WEBVIEW_CUSTOM_SMILEY);
 
 	cur = g_new0(struct smiley_button_list, 1);
 	it = ls;
-	it_last = ls; /* list iterators*/
+	it_last = ls; /* list iterators */
 	image = gtk_image_new_from_file(filename);
 
 	gtk_widget_size_request(image, &size);
 
-	if (size.width > 24 &&
-			smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM) { /* This is a custom smiley, let's scale it */
+	if ((size.width > 24)
+	 && (gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) {
+		/* This is a custom smiley, let's scale it */
 		GdkPixbuf *pixbuf = NULL;
 		GtkImageType type;
 
@@ -734,7 +731,7 @@
 	button = gtk_button_new();
 	gtk_container_add(GTK_CONTAINER(button), image);
 
-	g_object_set_data(G_OBJECT(button), "smiley_text", face);
+	g_object_set_data_full(G_OBJECT(button), "smiley_text", g_strdup(face), g_free);
 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_smiley_text), toolbar);
 
 	gtk_widget_set_tooltip_text(button, face);
@@ -742,11 +739,12 @@
 	/* these look really weird with borders */
 	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
 
-	psmiley = purple_smileys_find_by_shortcut(smiley->smile);
+	psmiley = purple_smileys_find_by_shortcut(face);
 	/* If this is a "non-custom" smiley, check to see if its shortcut is
 	  "shadowed" by any custom smiley. This can only happen if the connection
 	  is custom smiley-enabled */
-	if (supports_custom && psmiley && !(smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM)) {
+	if (supports_custom && psmiley
+	 && !(gtk_webview_smiley_get_flags(smiley) & GTK_WEBVIEW_SMILEY_CUSTOM)) {
 		gchar tip[128];
 		g_snprintf(tip, sizeof(tip),
 			_("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
@@ -779,12 +777,12 @@
 }
 
 static gboolean
-smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley)
+smiley_is_unique(GSList *list, GtkWebViewSmiley *smiley)
 {
-	GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+	const char *file = gtk_webview_smiley_get_file(smiley);
 	while (list) {
-		GtkIMHtmlSmiley *cur = (GtkIMHtmlSmiley *) list->data;
-		if (!strcmp(cur->file, smiley->file))
+		GtkWebViewSmiley *cur = (GtkWebViewSmiley *)list->data;
+		if (!strcmp(gtk_webview_smiley_get_file(cur), file))
 			return FALSE;
 		list = list->next;
 	}
@@ -818,7 +816,7 @@
 	line = gtk_hbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
 	for (; list; list = list->next) {
-		if (custom != !!(list->smiley->flags & GTK_WEBVIEW_SMILEY_CUSTOM))
+		if (custom != !!(gtk_webview_smiley_get_flags(list->smiley) & GTK_WEBVIEW_SMILEY_CUSTOM))
 			continue;
 		gtk_box_pack_start(GTK_BOX(line), list->button, FALSE, FALSE, 0);
 		gtk_widget_show(list->button);
@@ -832,12 +830,10 @@
 		}
 	}
 }
-#endif
 
 static void
 insert_smiley_cb(GtkAction *smiley, GtkWebViewToolbar *toolbar)
 {
-#if 0
 	GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
 	GtkWidget *dialog, *vbox;
 	GtkWidget *smiley_table = NULL;
@@ -858,13 +854,13 @@
 	else
 		smileys = pidgin_themes_get_proto_smileys(NULL);
 
-	/* Note: prepend smileys to list to avoid O(n^2) overhead when there is
-	  a large number of smileys... need to revers the list after for the dialog
-	  work... */
-	while(smileys) {
-		GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data;
-		if(!smiley->hidden) {
-			if(smiley_is_unique(unique_smileys, smiley)) {
+	/* Note: prepend smileys to list to avoid O(n^2) overhead when there is a
+	   large number of smileys... need to reverse the list after for the dialog
+	   to work... */
+	while (smileys) {
+		GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)smileys->data;
+		if (!gtk_webview_smiley_get_hidden(smiley)) {
+			if (smiley_is_unique(unique_smileys, smiley)) {
 				unique_smileys = g_slist_prepend(unique_smileys, smiley);
 			}
 		}
@@ -877,7 +873,7 @@
 
 		for (iterator = custom_smileys ; iterator ;
 			 iterator = g_slist_next(iterator)) {
-			GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data;
+			GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)iterator->data;
 			unique_smileys = g_slist_prepend(unique_smileys, smiley);
 		}
 	}
@@ -913,8 +909,8 @@
 
 		/* create list of smileys sorted by height */
 		while (unique_smileys) {
-			GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data;
-			if (!smiley->hidden) {
+			GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)unique_smileys->data;
+			if (!gtk_webview_smiley_get_hidden(smiley)) {
 				ls = sort_smileys(ls, toolbar, &max_line_width, smiley);
 			}
 			unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys);
@@ -942,7 +938,6 @@
 		g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
 	}
 
-
 	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(smiley_table);
@@ -981,7 +976,6 @@
 	priv->smiley_dialog = dialog;
 
 	gtk_widget_grab_focus(toolbar->webview);
-#endif
 }
 
 static void
@@ -1347,7 +1341,7 @@
 		{&priv->image, "InsertImage", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, N_("_Image"), N_("Insert IM Image"), insert_image_cb, FALSE},
 		{&priv->link, "InsertLink", PIDGIN_STOCK_TOOLBAR_INSERT_LINK, N_("_Link"), N_("Insert Link"), insert_link_cb, TRUE},
 		{&priv->hr, "InsertHR", NULL, N_("_Horizontal rule"), N_("Insert Horizontal rule"), insert_hr_cb, FALSE},
-		{&priv->smiley, "InsertSmiley", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("_Smile!"), N_("Insert Smiley"), insert_smiley_cb, FALSE},
+		{&priv->smiley, "InsertSmiley", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("_Smile!"), N_("Insert Smiley"), insert_smiley_cb, TRUE},
 		{&priv->attention, "SendAttention", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, N_("_Attention!"), N_("Send Attention"), send_attention_cb, FALSE},
 	};
 
@@ -1637,3 +1631,88 @@
 		PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL);
 }
 
+void
+gtk_webviewtoolbar_activate(GtkWebViewToolbar *toolbar,
+                            GtkWebViewToolbarAction action)
+{
+	GtkWebViewToolbarPriv *priv;
+	GtkAction *act;
+
+	g_return_if_fail(toolbar != NULL);
+
+	priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+	switch (action) {
+		case GTK_WEBVIEWTOOLBAR_ACTION_BOLD:
+			act = priv->bold;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_ITALIC:
+			act = priv->italic;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_UNDERLINE:
+			act = priv->underline;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_STRIKE:
+			act = priv->strike;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_LARGER:
+			act = priv->larger_size;
+			break;
+
+#if 0
+		case GTK_WEBVIEWTOOLBAR_ACTION_NORMAL:
+			act = priv->normal_size;
+			break;
+#endif
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_SMALLER:
+			act = priv->smaller_size;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_FONTFACE:
+			act = priv->font;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_FGCOLOR:
+			act = priv->fgcolor;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_BGCOLOR:
+			act = priv->bgcolor;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_CLEAR:
+			act = priv->clear;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_IMAGE:
+			act = priv->image;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_LINK:
+			act = priv->link;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_HR:
+			act = priv->hr;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_SMILEY:
+			act = priv->smiley;
+			break;
+
+		case GTK_WEBVIEWTOOLBAR_ACTION_ATTENTION:
+			act = priv->attention;
+			break;
+
+		default:
+			g_return_if_reached();
+			break;
+	}
+
+	gtk_action_activate(act);
+}
+
--- a/pidgin/gtkwebviewtoolbar.h	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/gtkwebviewtoolbar.h	Tue Aug 14 22:26:33 2012 +0200
@@ -35,6 +35,27 @@
 #define GTK_IS_WEBVIEWTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEWTOOLBAR))
 #define GTK_WEBVIEWTOOLBAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_WEBVIEWTOOLBAR, GtkWebViewToolbarClass))
 
+typedef enum {
+	GTK_WEBVIEWTOOLBAR_ACTION_BOLD,
+	GTK_WEBVIEWTOOLBAR_ACTION_ITALIC,
+	GTK_WEBVIEWTOOLBAR_ACTION_UNDERLINE,
+	GTK_WEBVIEWTOOLBAR_ACTION_STRIKE,
+	GTK_WEBVIEWTOOLBAR_ACTION_LARGER,
+#if 0
+	GTK_WEBVIEWTOOLBAR_ACTION_NORMAL,
+#endif
+	GTK_WEBVIEWTOOLBAR_ACTION_SMALLER,
+	GTK_WEBVIEWTOOLBAR_ACTION_FONTFACE,
+	GTK_WEBVIEWTOOLBAR_ACTION_FGCOLOR,
+	GTK_WEBVIEWTOOLBAR_ACTION_BGCOLOR,
+	GTK_WEBVIEWTOOLBAR_ACTION_CLEAR,
+	GTK_WEBVIEWTOOLBAR_ACTION_IMAGE,
+	GTK_WEBVIEWTOOLBAR_ACTION_LINK,
+	GTK_WEBVIEWTOOLBAR_ACTION_HR,
+	GTK_WEBVIEWTOOLBAR_ACTION_SMILEY,
+	GTK_WEBVIEWTOOLBAR_ACTION_ATTENTION
+} GtkWebViewToolbarAction;
+
 typedef struct _GtkWebViewToolbar GtkWebViewToolbar;
 typedef struct _GtkWebViewToolbarClass GtkWebViewToolbarClass;
 
@@ -90,6 +111,15 @@
 void gtk_webviewtoolbar_switch_active_conversation(GtkWebViewToolbar *toolbar,
                                                    PurpleConversation *conv);
 
+/**
+ * Activate a GtkWebViewToolbar action
+ *
+ * @param toolbar The GtkWebViewToolbar object
+ * @param action  The GtkWebViewToolbarAction
+ */
+void gtk_webviewtoolbar_activate(GtkWebViewToolbar *toolbar,
+                                 GtkWebViewToolbarAction action);
+
 G_END_DECLS
 
 #endif /* _PIDGINWEBVIEWTOOLBAR_H_ */
--- a/pidgin/plugins/perl/common/Makefile.PL.in	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/plugins/perl/common/Makefile.PL.in	Tue Aug 14 22:26:33 2012 +0200
@@ -10,7 +10,7 @@
        'AUTHOR'        => 'Pidgin <http://pidgin.im/>') :  ()),
     'DEFINE'        => '@DEBUG_CFLAGS@',
     'dynamic_lib'       => { 'OTHERLDFLAGS' => '@LDFLAGS@' },
-    'INC'           => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@',
+    'INC'           => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@ @WEBKIT_CFLAGS@',
     'OBJECT'        => '$(O_FILES)', # link all the C files too
     'TYPEMAPS'      => ["@top_srcdir@/libpurple/plugins/perl/common/typemap"],
 #    'OPTIMIZE'      => '-g', # For debugging.
--- a/pidgin/plugins/vvconfig.c	Wed Aug 08 11:22:52 2012 +0200
+++ b/pidgin/plugins/vvconfig.c	Tue Aug 14 22:26:33 2012 +0200
@@ -39,7 +39,7 @@
 	/* "esdmon",	"ESD", ? */
 	"osssrc",	"OSS",
 	"pulsesrc",	"PulseAudio",
-	"libsndiosrc",	"sndio",
+	"sndiosrc",	"sndio",
 	/* "audiotestsrc wave=silence", "Silence", */
 	"audiotestsrc",	"Test Sound",
 	NULL
@@ -51,7 +51,7 @@
 	"esdsink",	"ESD",
 	"osssink",	"OSS",
 	"pulsesink",	"PulseAudio",
-	"libsndiosink",	"sndio",
+	"sndiosink",	"sndio",
 	NULL
 };
 

mercurial