changeset 10526:55e7d72fc09a

[gaim-migrate @ 11843] maquina writes: This patch implements a custom smiley API, and it also implements custom smileys for the msn protocol. As it stands, it is not able to cache custom smileys, and is not able to redefine a custom smiley without opening a new conversation. I modified it quite a bit, and didn't test it at all, so it probably doesn't work anymore. I'm not quite done with it yet either. Also, this is just receiving custom smileys. committer: Tailor Script <tailor@pidgin.im>
author Tim Ringenbach <marv@pidgin.im>
date Tue, 18 Jan 2005 18:31:32 +0000
parents ddea15f4cbc2
children 507c680b573e
files ChangeLog src/conversation.c src/conversation.h src/gtkconv.c src/gtkimhtml.c src/gtkimhtml.h src/protocols/msn/slp.c
diffstat 7 files changed, 280 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jan 18 15:36:39 2005 +0000
+++ b/ChangeLog	Tue Jan 18 18:31:32 2005 +0000
@@ -20,6 +20,7 @@
 	* Add a "Last Seen" field to tooltips for buddies in your buddy
 	  list (Richard Laager)
 	* Buddy icons in buddy list tooltips (Felipe Contreras)
+	* Custom smileys for MSN (maquina)
 
 	Bug fixes:
 	* Some memory leaks plugged (Miah Gregory, Felipe Contreras)
--- a/src/conversation.c	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/conversation.c	Tue Jan 18 18:31:32 2005 +0000
@@ -1755,6 +1755,51 @@
 	common_send(gaim_conv_im_get_conversation(im), message);
 }
 
+gboolean
+gaim_conv_custom_smiley_add(GaimConversation *conv, const char *smile,
+                            const char *cksum_type, const char *chksum)
+{
+	if (conv == NULL || smile == NULL || !*smile) {
+		return FALSE;
+	}
+
+	/* TODO: check if the icon is in the cache and return false if so */
+	/* TODO: add an icon cache (that doesn't suck) */
+	if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_add !=NULL) {
+		return conv->ui_ops->custom_smiley_add(conv, smile);
+	} else {
+		gaim_debug_info("conversation", "Could not find add custom smiley function");
+		return FALSE;
+	}
+
+}
+
+void
+gaim_conv_custom_smiley_write(GaimConversation *conv, const char *smile,
+                                   const char * data, gint64 size)
+{
+	g_return_if_fail(conv != NULL);
+	g_return_if_fail(smile != NULL && *smile);
+
+	if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_write != NULL)
+		conv->ui_ops->custom_smiley_write(conv, smile, data, size);
+	else
+		gaim_debug_info("conversation", "Could not find the smiley write function");
+}
+
+void
+gaim_conv_custom_smiley_close(GaimConversation *conv, const char *smile)
+{
+	g_return_if_fail(conv != NULL);
+	g_return_if_fail(smile != NULL && *smile);
+
+	if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_close != NULL)
+		conv->ui_ops->custom_smiley_close(conv, smile);
+	else
+		gaim_debug_info("conversation", "Could not find custom smiley close function");
+}
+
+
 /**************************************************************************
  * Chat Conversation API
  **************************************************************************/
--- a/src/conversation.h	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/conversation.h	Tue Jan 18 18:31:32 2005 +0000
@@ -199,6 +199,14 @@
 
 	gboolean (*has_focus)(GaimConversation *conv);
 
+	/* Custom Smileys */
+	gboolean (*custom_smiley_add)(GaimConversation *conv, const char *smile);
+	void (*custom_smiley_write)(GaimConversation *conv, const char *smile,
+	                            const char * data, gint64 size);
+	void (*custom_smiley_close)(GaimConversation *conv, const char *smile);
+
+
+
 	/* Events */
 	void (*updated)(GaimConversation *conv, GaimConvUpdateType type);
 
@@ -1033,6 +1041,47 @@
  */
 void gaim_conv_im_send(GaimConvIm *im, const char *message);
 
+/**
+ * Adds a smiley to the conversation's smiley tree.
+ *
+ * @param conv The conversation to associate the smiley with.
+ * @param smile The text associated with the smiley
+ * @param cksum_type The type of checksum.
+ * @param chksum The checksum, as a NUL terminated base64 string.
+ * @return      @c TRUE if an icon is excepted, else FALSE. Note that
+ *              it is an error to never call gaim_conv_custom_smiley_close if
+ *              this function returns @c TRUE, but an error to call it if
+ *              @c FALSE is returned.
+ */
+
+gboolean gaim_conv_custom_smiley_add(GaimConversation *conv, const char *smile,
+                                      const char *cksum_type, const char *chksum);
+
+
+/**
+ * Updates the image associated with the current smiley.
+ *
+ * @param conv The conversation associated with the smiley.
+ * @param smile The text associated with the smiley.
+ * @param data The actual image data.
+ * @param size The length of the data.
+ */
+
+void gaim_conv_custom_smiley_write(GaimConversation *conv,
+                                   const char *smile, const char * data,
+                                   gint64 size);
+
+/**
+ * Close the custom smiley, all data has been written with
+ * gaim_conv_custom_smiley_write, and it is no longer valid
+ * to call that function on that smiley.
+ *
+ * @param conv The gaim conversation associated with the smiley.
+ * @param smile The text associated with the smiley
+ */
+
+void gaim_conv_custom_smiley_close(GaimConversation *conv, const char *smile);
+
 /*@}*/
 
 
--- a/src/gtkconv.c	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/gtkconv.c	Tue Jan 18 18:31:32 2005 +0000
@@ -5169,6 +5169,102 @@
 	return has_focus;
 }
 
+static gboolean
+gaim_gtkconv_custom_smiley_add(GaimConversation *conv, const char *smile)
+{
+	GaimGtkConversation *gtkconv;
+	GtkIMHtmlSmiley *smiley;
+	GdkPixbufLoader *loader;
+	const char *sml;
+
+	if (conv == NULL || smile == NULL) {
+		return FALSE;
+	}
+
+	sml = gaim_account_get_protocol_name(conv->account); /* XXX this sucks */
+	gtkconv = GAIM_GTK_CONVERSATION(conv);
+	smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
+
+	/* TODO: implement changing a custom smiley in the middle of a conversation */
+
+	if (smiley) {
+		return FALSE;
+	}
+
+
+	loader = gdk_pixbuf_loader_new();
+
+	/* this is wrong, this file ought not call g_new on GtkIMHtmlSmiley */
+	/* Let gtk_imhtml have a gtk_imhtml_smiley_new function, and let
+	   GtkIMHtmlSmiley by opque */
+	smiley = g_new0(GtkIMHtmlSmiley, 1);
+	smiley->file   = NULL;
+	smiley->smile  = g_strdup(smile);
+	smiley->loader = loader;
+
+	smiley->icon = gdk_pixbuf_loader_get_animation(loader);
+	if (smiley->icon)
+		g_object_ref(G_OBJECT(smiley->icon));
+
+	gtk_imhtml_associate_smiley(GTK_IMHTML(gtkconv->imhtml), sml, smiley);
+	
+	return TRUE;
+}
+
+static void 
+gaim_gtkconv_custom_smiley_write(GaimConversation *conv, const char *smile,
+                                      const char * data, gint64 size)
+{
+	GaimGtkConversation *gtkconv;
+	GtkIMHtmlSmiley *smiley;
+	GdkPixbufLoader *loader;	
+	const char *sml;
+
+	sml = gaim_account_get_protocol_name(conv->account);
+	gtkconv = GAIM_GTK_CONVERSATION(conv);
+	smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
+
+	if (!smiley) 
+		return;
+	
+	loader = smiley->loader;
+	if (!loader) 
+		return;
+
+	gdk_pixbuf_loader_write(loader, data, size, NULL);
+}
+
+static void
+gaim_gtkconv_custom_smiley_close(GaimConversation *conv, const char *smile)
+{
+	GaimGtkConversation *gtkconv;
+	GtkIMHtmlSmiley *smiley;
+	GdkPixbufLoader *loader;
+	const char *sml;
+
+	g_return_if_fail(conv  != NULL);
+	g_return_if_fail(smile != NULL);
+
+	sml = gaim_account_get_protocol_name(conv->account);
+	gtkconv = GAIM_GTK_CONVERSATION(conv);
+	smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
+
+	if (!smiley) 
+		return;
+
+	loader = smiley->loader;
+
+	if (!loader) 
+		return;
+	
+	gaim_debug_info("gtkconv", "About to close the smiley pixbuf\n");
+
+	gdk_pixbuf_loader_close(loader, NULL);
+	g_object_unref(G_OBJECT(loader));
+	smiley->loader = NULL;
+}
+
+
 static void
 gaim_gtkconv_updated(GaimConversation *conv, GaimConvUpdateType type)
 {
@@ -5320,6 +5416,9 @@
 	gaim_gtkconv_chat_update_user,   /* chat_update_user     */
 	NULL,                            /* update_progress      */
 	gaim_gtkconv_has_focus,          /* has_focus            */
+	gaim_gtkconv_custom_smiley_add,  /* custom_smiley_add */
+	gaim_gtkconv_custom_smiley_write, /* custom_smiley_write */
+	gaim_gtkconv_custom_smiley_close, /* custom_smiley_close */
 	gaim_gtkconv_updated             /* updated              */
 };
 
--- a/src/gtkimhtml.c	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/gtkimhtml.c	Tue Jan 18 18:31:32 2005 +0000
@@ -1530,10 +1530,10 @@
 	return (*len > 0);
 }
 
-GdkPixbufAnimation *
-gtk_smiley_tree_image (GtkIMHtml     *imhtml,
-		       const gchar   *sml,
-		       const gchar   *text)
+GtkIMHtmlSmiley *
+gtk_imhtml_smiley_get(GtkIMHtml *imhtml,
+	const gchar *sml,
+	const gchar *text)
 {
 	GtkSmileyTree *t;
 	const gchar *x = text;
@@ -1544,31 +1544,49 @@
 
 
 	if (t == NULL)
-		return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+		return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
 
 	while (*x) {
 		gchar *pos;
 
 		if (!t->values) {
-			return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+			return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
 		}
 
 		pos = strchr (t->values->str, *x);
 		if (pos) {
 			t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
 		} else {
-			return sml ? gtk_smiley_tree_image(imhtml, NULL, text) : NULL;
+			return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
 		}
 		x++;
 	}
 
-	if (!t->image->file)
+	return t->image;
+}
+
+GdkPixbufAnimation *
+gtk_smiley_tree_image (GtkIMHtml     *imhtml,
+		       const gchar   *sml,
+		       const gchar   *text)
+{
+
+	GtkIMHtmlSmiley *smiley;
+
+	smiley = gtk_imhtml_smiley_get(imhtml,sml,text);
+
+	if (!smiley) 
 		return NULL;
 
-	if (!t->image->icon)
-		t->image->icon = gdk_pixbuf_animation_new_from_file(t->image->file, NULL);
-
-	return t->image->icon;
+	if (!smiley->icon && smiley->file) {
+		smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
+	} else if (!smiley->icon && smiley->loader) {
+		smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
+		if (smiley->icon)
+			g_object_ref(G_OBJECT(smiley->icon));
+	}
+	
+	return smiley->icon;
 }
 
 #define VALID_TAG(x)	if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) {	\
@@ -3839,19 +3857,31 @@
 	GtkTextChildAnchor *anchor;
 	char *unescaped = gaim_unescape_html(smiley);
 
-	if (imhtml->format_functions & GTK_IMHTML_SMILEY)
-	{
+	if (imhtml->format_functions & GTK_IMHTML_SMILEY) {
 		annipixbuf = gtk_smiley_tree_image(imhtml, sml, unescaped);
-		if(annipixbuf) {
-			if(gdk_pixbuf_animation_is_static_image(annipixbuf)) {
+		if (annipixbuf) {
+			if (gdk_pixbuf_animation_is_static_image(annipixbuf)) {
 				pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf);
-				if(pixbuf)
+				if (pixbuf)
 					icon = gtk_image_new_from_pixbuf(pixbuf);
 			} else {
 				icon = gtk_image_new_from_animation(annipixbuf);
 			}
 		}
 	}
+#if 0
+	else {
+		GtkIMHtmlSmiley *imhtml_smiley;
+
+		if (imhtml_smiley->loader) { ; }
+		icon = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
+		imhtml_smiley = gtk_get_imhtml_smiley(imhtml, sml, unescaped);
+		if (!imhtml_smiley) {
+			gaim_debug_info("gtkimhtml", "geezz couldnt find smiley struct\n");
+		}
+		imhtml_smiley->orphan = g_slist_append(imhtml_smiley->orphan, icon);
+	}
+#endif
 
 	if (icon) {
 		anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
--- a/src/gtkimhtml.h	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/gtkimhtml.h	Tue Jan 18 18:31:32 2005 +0000
@@ -149,6 +149,7 @@
 	gchar *file;
 	GdkPixbufAnimation *icon;
 	gboolean hidden;
+	GdkPixbufLoader *loader;
 };
 
 struct _GtkIMHtmlScalable {
@@ -246,6 +247,18 @@
 GtkWidget *gtk_imhtml_new(void *, void *);
 
 /**
+ * Returns the smiley object associated with the text.
+ *
+ * @param imhtml The GTK IM/HTML.
+ * @param sml    The name of the smiley category.
+ * @param text   The text associated with the smiley.
+ */
+
+GtkIMHtmlSmiley *gtk_imhtml_smiley_get(GtkIMHtml * imhtml,
+                                        const gchar * sml, const gchar * text);
+
+
+/**
  * Associates a smiley with a GTK IM/HTML.
  *
  * @param imhtml The GTK IM/HTML.
--- a/src/protocols/msn/slp.c	Tue Jan 18 15:36:39 2005 +0000
+++ b/src/protocols/msn/slp.c	Tue Jan 18 18:31:32 2005 +0000
@@ -739,18 +739,24 @@
 got_emoticon(MsnSlpCall *slpcall,
 			 const char *data, long long size)
 {
-	gaim_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
+
+	GaimConversation *conv;
+	GaimConnection *gc;
+	const char *who;
 
-#if 0
-	serv_got_smiley(slpcall->slplink->session->account->gc, 
-		slpcall->slplink->remote_user, slpcall->data_info, data, size);
-#endif
+	gc = slpcall->slplink->session->account->gc;
+	who = slpcall->slplink->remote_user;
+	
+	conv = gaim_find_conversation_with_account(GAIM_CONV_ANY, who, gc->account);
 
-#if 0
-	GaimConversation *conv;
-	GaimConnection *gc = slpsession->swboard->servconn->session->account->gc;
-	serv_got_smiley(gc, info, data, size);
-#endif
+	/* FIXME: it would be better if we wrote the data as we received it
+	          instead of all at once, calling write multiple times and
+	          close once at the very end
+	*/
+	gaim_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
+	gaim_conv_custom_smiley_close(conv, slpcall->data_info );
+ 
+	gaim_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
 }
 
 void
@@ -763,6 +769,9 @@
 	char *smile;
 	const char *who;
 
+	GaimConversation *conversation;
+	GaimConnection *gc;
+
 	session = cmdproc->servconn->session;
 
 	tokens = g_strsplit(msg->body, "\t", 2);
@@ -774,7 +783,13 @@
 
 	slplink = msn_session_get_slplink(session, who);
 
-	msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
+	gc = slplink->session->account->gc;
+
+	conversation = gaim_find_conversation_with_account(GAIM_CONV_ANY, who, gc->account);
+
+	if (gaim_conv_custom_smiley_add(conversation, smile, "sha1" /* i think it's a sha1 checksum? */, "base64 encoded checksum here, shx should fix this!")) {
+		msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
+	}
 
 	g_strfreev(tokens);
 }