diff src/gtkimhtml.c @ 4032:2b3a9d8f168e

[gaim-migrate @ 4238] Smileys. These are just the default smileys--I'm adding the prpl-specific smileys right now. If you want to help by creating some png's, IM me. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Tue, 03 Dec 2002 02:02:16 +0000
parents 23c430e780eb
children 3a36ec242415
line wrap: on
line diff
--- a/src/gtkimhtml.c	Tue Dec 03 01:08:08 2002 +0000
+++ b/src/gtkimhtml.c	Tue Dec 03 02:02:16 2002 +0000
@@ -47,8 +47,76 @@
 	gchar *face;
 	gchar *fore;
 	gchar *back;
+	gchar *sml;
 };
 
+struct _GtkSmileyTree {
+	GString *values;
+	GtkSmileyTree **children;
+	gchar *image;
+};
+
+static GtkSmileyTree*
+gtk_smiley_tree_new ()
+{
+	return g_new0 (GtkSmileyTree, 1);
+}
+
+static void
+gtk_smiley_tree_insert (GtkSmileyTree *tree,
+			const gchar   *text,
+			const gchar   *path)
+{
+	GtkSmileyTree *t = tree;
+	const gchar *x = text;
+
+	if (!strlen (x))
+		return;
+
+	while (*x) {
+		gchar *pos;
+		gint 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 = (int) pos - (int) t->values->str;
+		
+		t = t->children [index];
+		
+		x++;
+	}
+	
+	t->image = path;
+}
+gtk_smiley_tree_destroy (GtkSmileyTree *tree)
+{
+	GSList *list = g_slist_append (NULL, tree);
+
+	while (list) {
+		GtkSmileyTree *t = list->data;
+		gint i;
+		list = g_slist_remove(list, t);
+		if (t->values) {
+			for (i = 0; i < t->values->len; i++)
+				list = g_slist_append (list, t->children [i]);
+			g_string_free (t->values, TRUE);
+			g_free (t->children);
+		}
+		g_free (t);
+	}
+}
+
+static GtkTextViewClass *parent_class = NULL;
+
+
 /* GtkIMHtml has one signal--URL_CLICKED */
 enum {
 	URL_CLICKED,
@@ -56,12 +124,24 @@
 };
 static guint signals [LAST_SIGNAL] = { 0 };
 
+static void
+gtk_imhtml_finalize (GObject *object)
+{
+	GtkIMHtml *imhtml = GTK_IMHTML(object);
+
+	g_hash_table_foreach_remove(imhtml->smiley_data, gtk_smiley_tree_destroy, NULL);
+	gtk_smiley_tree_destroy(imhtml->default_smilies);
+	G_OBJECT_CLASS(parent_class)->finalize (object);
+}
 
 /* Boring GTK stuff */
 static void gtk_imhtml_class_init (GtkIMHtmlClass *class)
 {
 	GtkObjectClass *object_class;
+	GObjectClass   *gobject_class;
 	object_class = (GtkObjectClass*) class;
+	gobject_class = (GObjectClass*) class;
+	parent_class = gtk_type_class(GTK_TYPE_TEXT_VIEW);
 	signals[URL_CLICKED] = gtk_signal_new("url_clicked",
 					      GTK_RUN_FIRST,
 					      GTK_CLASS_TYPE(object_class),
@@ -69,6 +149,7 @@
 					      gtk_marshal_NONE__POINTER,
 					      GTK_TYPE_NONE, 1,
 					      GTK_TYPE_POINTER);
+	gobject_class->finalize = gtk_imhtml_finalize;
 }
 
 static void gtk_imhtml_init (GtkIMHtml *imhtml)
@@ -98,6 +179,8 @@
 	imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
 	imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
 
+	imhtml->smiley_data = g_hash_table_new (g_str_hash, g_str_equal);
+	imhtml->default_smilies = gtk_smiley_tree_new();
 }
 
 GtkWidget *gtk_imhtml_new(void *a, void *b)
@@ -139,6 +222,149 @@
 	}
 }
 
+static void
+gtk_smiley_tree_remove (GtkSmileyTree *tree,
+			const gchar   *text)
+{
+	GtkSmileyTree *t = tree;
+	const gchar *x = text;
+	gint len = 0;
+
+	while (*x) {
+		gchar *pos;
+
+		if (!t->values)
+			return;
+
+		pos = strchr (t->values->str, *x);
+		if (pos)
+			t = t->children [(int) pos - (int) t->values->str];
+		else
+			return;
+
+		x++; len++;
+	}
+
+	if (t->image)
+		t->image = NULL;
+}
+
+static gint
+gtk_smiley_tree_lookup (GtkSmileyTree *tree,
+			const gchar   *text)
+{
+	GtkSmileyTree *t = tree;
+	const gchar *x = text;
+	gint len = 0;
+
+	while (*x) {
+		gchar *pos;
+
+		if (!t->values)
+			break;
+
+		pos = strchr (t->values->str, *x);
+		if (pos)
+			t = t->children [(int) pos - (int) t->values->str];
+		else
+			break;
+
+		x++; len++;
+	}
+
+	if (t->image)
+		return len;
+
+	return 0;
+}
+
+void
+gtk_imhtml_associate_smiley (GtkIMHtml  *imhtml,
+			     gchar      *text,
+			     gchar      *sml,
+			     gchar      *path)
+{
+	GtkSmileyTree *tree;
+	g_return_if_fail (imhtml != NULL);
+	g_return_if_fail (GTK_IS_IMHTML (imhtml));
+	g_return_if_fail (text != NULL);
+
+	if (sml == NULL)
+		tree = imhtml->default_smilies;
+	else if ((tree = g_hash_table_lookup(imhtml->smiley_data, sml))) {
+	} else {
+		tree = gtk_smiley_tree_new();
+		g_hash_table_insert(imhtml->smiley_data, sml, tree);
+	}
+
+	if (path == NULL)
+		gtk_smiley_tree_remove (tree, text);
+	else
+		gtk_smiley_tree_insert (tree, text, path);
+}
+
+static gboolean
+gtk_imhtml_is_smiley (GtkIMHtml   *imhtml,
+		      GSList      *fonts,
+		      const gchar *text,
+		      gint        *len)
+{
+	GtkSmileyTree *tree;
+	FontDetail *font;
+	char *sml = NULL;
+
+	if (fonts) {
+		font = fonts->data;
+		sml = font->sml;
+	}
+
+	if (sml == NULL)
+		tree = imhtml->default_smilies;
+	else {
+		tree = g_hash_table_lookup(imhtml->smiley_data, sml);
+	}
+	if (tree == NULL)
+		return FALSE;
+	
+	*len = gtk_smiley_tree_lookup (tree, text);
+	return (*len > 0);
+}
+
+static gchar*
+gtk_smiley_tree_image (GtkIMHtml     *imhtml,
+		       const gchar   *sml,
+		       const gchar   *text)
+{
+	GtkSmileyTree *t;
+	const gchar *x = text;
+
+	if (sml == NULL)
+		t = imhtml->default_smilies;
+	else 
+		t = g_hash_table_lookup(imhtml->smiley_data, sml);
+	 
+
+	if (t == NULL)
+		return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+
+	while (*x) {
+		gchar *pos;
+
+		if (!t->values) {
+			return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+		}
+		
+		pos = strchr (t->values->str, *x);
+		if (pos) {
+			t = t->children [(int) pos - (int) t->values->str];
+		} else {
+			return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+		}
+		x++;
+	}
+
+	return t->image;
+}
 #define VALID_TAG(x)	if (!g_strncasecmp (string, x ">", strlen (x ">"))) {	\
 				*tag = g_strndup (string, strlen (x));		\
 				*len = strlen (x) + 1;				\
@@ -414,7 +640,7 @@
 	gchar *tag;
 	gchar *url = NULL;
 	gchar *bg = NULL;
-	gint tlen, wpos=0;
+	gint tlen, smilelen, wpos=0;
 	gint type;
 	const gchar *c;
 	gchar amp;
@@ -557,6 +783,8 @@
 							g_free (font->fore);
 						if (font->back)
 							g_free (font->back);
+						if (font->sml)
+							g_free (font->sml);
 						g_free (font);
 					}
 					break;
@@ -584,14 +812,14 @@
 					break;
 				case 43:	/* FONT (opt) */
 					{
-						gchar *color, *back, *face, *size;
+						gchar *color, *back, *face, *size, *sml;
 						FontDetail *font, *oldfont = NULL;
 						color = gtk_imhtml_get_html_opt (tag, "COLOR=");
 						back = gtk_imhtml_get_html_opt (tag, "BACK=");
 						face = gtk_imhtml_get_html_opt (tag, "FACE=");
 						size = gtk_imhtml_get_html_opt (tag, "SIZE=");
-						
-						if (!(color || back || face || size))
+						sml = gtk_imhtml_get_html_opt (tag, "SML=");
+						if (!(color || back || face || size || sml))
 							break;
 						
 						NEW_BIT (NEW_TEXT_BIT);
@@ -614,7 +842,12 @@
 							font->face = face;
 						else if (oldfont && oldfont->face)
 							font->face = g_strdup(oldfont->face);
-						
+
+						if (sml)
+							font->sml = sml;
+						else if (oldfont && oldfont->sml)
+							font->sml = g_strdup(oldfont->sml);
+
 						if (size && !(options & GTK_IMHTML_NO_SIZES)) {
 							if (*size == '+') {
 								sscanf (size + 1, "%hd", &font->size);
@@ -678,7 +911,21 @@
 			}
 			c++;
 			pos++;
-		} else if (*c) {
+		} else if (gtk_imhtml_is_smiley (imhtml, fonts, c, &smilelen) || gtk_imhtml_is_smiley(imhtml, NULL, c, &smilelen)) {
+			FontDetail *fd;
+			gchar *sml = NULL;
+			if (fonts) {
+				fd = fonts->data;
+				sml = fd->sml;
+			}
+			NEW_BIT (NEW_TEXT_BIT);
+			wpos = g_snprintf (ws, smilelen + 1, "%s", c);
+			gtk_text_buffer_insert_pixbuf(imhtml->text_buffer, &iter, gdk_pixbuf_new_from_file(gtk_smiley_tree_image (imhtml, sml, ws), NULL));
+			c += smilelen;
+			pos += smilelen;
+			wpos = 0;
+			ws[0] = 0;
+	} else if (*c) {
 			ws [wpos++] = *c++;
 			pos++;
 		} else {
@@ -693,6 +940,22 @@
 			str = g_string_append (str, "</A>");
 	}
 	
+	while (fonts) {
+		FontDetail *font = fonts->data;
+		fonts = g_slist_remove (fonts, font);
+		if (font->face)
+			g_free (font->face);
+		if (font->fore)
+			g_free (font->fore);
+		if (font->back)
+			g_free (font->back);
+		if (font->sml)
+			g_free (font->sml);
+		g_free (font);
+		if (str)
+			str = g_string_append (str, "</FONT>");
+	}
+
 	if (str) {
 		while (bold) {
 			str = g_string_append (str, "</B>");
@@ -727,9 +990,10 @@
 			pre--;
 		}
 	}
-	g_free(ws);
-	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (imhtml), mark,
-				      0, TRUE, 0.0, 1.0);
+	g_free (ws);
+	if (!(options & GTK_IMHTML_NO_SCROLL))
+		gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (imhtml), mark,
+					      0, TRUE, 0.0, 1.0);
 	gtk_text_buffer_delete_mark (imhtml->text_buffer, mark);
 	return str;
 }
@@ -746,9 +1010,6 @@
 void       gtk_imhtml_set_img_handler  (GtkIMHtml        *imhtml,
 	GtkIMHtmlImage    handler){}
 
-void       gtk_imhtml_associate_smiley (GtkIMHtml        *imhtml,
-					gchar            *text,
-	gchar           **xpm){}
 void 	   gtk_imhtml_init_smileys     (GtkIMHtml *imhtml){}
 void       gtk_imhtml_remove_smileys   (GtkIMHtml        *imhtml){}
 void       gtk_imhtml_reset_smileys   (GtkIMHtml        *imhtml){}