/pidgin/main: 2bf06f291ff7: Add some smiley stuff to a GtkWebView.

Elliott Sales de Andrade qulogic at pidgin.im
Tue Aug 14 04:03:26 EDT 2012


Changeset: 2bf06f291ff7feb6a383656b53bc9cba248ab694
Author:	 Elliott Sales de Andrade <qulogic at pidgin.im>
Date:	 2012-08-13 18:25 -0400
Branch:	 default
URL: http://hg.pidgin.im/pidgin/main/rev/2bf06f291ff7

Description:

Add some smiley stuff to a GtkWebView.

This isn't used to parse anything yet, but just copies the API and
internal structures from the GtkIMHtml. As such, some stuff may or
may not be unnecessary.

diffstat:

 pidgin/gtkwebview.c |  510 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 pidgin/gtkwebview.h |   94 +++++++++
 2 files changed, 604 insertions(+), 0 deletions(-)

diffs (truncated from 661 to 300 lines):

diff --git a/pidgin/gtkwebview.c b/pidgin/gtkwebview.c
--- a/pidgin/gtkwebview.c
+++ b/pidgin/gtkwebview.c
@@ -75,6 +75,26 @@ typedef struct {
 	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;
@@ -94,6 +114,10 @@ typedef struct _GtkWebViewPriv {
 		gboolean block_changed:1;
 	} edit;
 
+	/* Smileys */
+	char *protocol_name;
+	GHashTable *smiley_data;
+	GtkSmileyTree *default_smilies;
 } GtkWebViewPriv;
 
 /******************************************************************************
@@ -103,6 +127,484 @@ typedef struct _GtkWebViewPriv {
 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)



More information about the Commits mailing list