changeset 8962:4ff4c34b7500

[gaim-migrate @ 9736] IM Image, WYSIWYG. It's still somewhat buggy, although the worse problems are with oscar's direct connect. We could always yank oscar's im image flag if we think it will cause too many bug reports. I made the GaimImgstore struct opque. I modified oscar's sending function to parse im images better, and everything seems to work. I made it write some errors to the conversation if you try to send an image and you aren't direct connected. That's just a hack until you can set formatting flags on a per conversation bases. There's a scrolling bug I haven't tracked down. I think it may exist normally and this just causes it better. It's worth noting jabber also uses this for pics in profiles, although I never did find a test case. Hopefully some other stuff can use this soon too, maybe Yahoo! pics in profiles or something. committer: Tailor Script <tailor@pidgin.im>
author Tim Ringenbach <marv@pidgin.im>
date Mon, 17 May 2004 06:47:20 +0000
parents 92e061a1db10
children 59f1eb8c76d2
files ChangeLog src/gtkconv.c src/gtkimhtml.c src/gtkimhtml.h src/gtkimhtmltoolbar.c src/gtknotify.c src/gtkutils.c src/gtkutils.h src/imgstore.c src/imgstore.h src/protocols/oscar/oscar.c
diffstat 11 files changed, 282 insertions(+), 236 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon May 17 04:39:14 2004 +0000
+++ b/ChangeLog	Mon May 17 06:47:20 2004 +0000
@@ -37,6 +37,8 @@
 	* New tabs should scroll correctly again (Tim Ringenbach)
 	* Ampersands in links should work right (Tim Ringenbach)
 	* Win32 transparency plugin fixes (Kevin Stange)
+	* IM Image support for AIM is back. It still depends on Direct
+	  Connect, and both are still as bugger as ever, if not more so.
 
 	Preference Changes:
 	* Added a "none" smiley theme to replace the "Show graphical
--- a/src/gtkconv.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkconv.c	Mon May 17 06:47:20 2004 +0000
@@ -334,7 +334,7 @@
 		return;
 
 	buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
-	clean = gaim_markup_strip_html(buf);
+	clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
 
 	gtk_widget_grab_focus(gtkconv->entry);
 
@@ -4532,7 +4532,6 @@
 	GaimConvWindow *win;
 	GaimConnection *gc;
 	int gtk_font_options = 0;
-	GSList *images = NULL;
 	char buf[BUF_LONG];
 	char buf2[BUF_LONG];
 	char mdate[64];
@@ -4556,9 +4555,6 @@
 		gaim_conv_window_show(win);
 	}
 
-	if (flags & GAIM_MESSAGE_IMAGES)
-		gaim_gtk_find_images(message, &images);
-
 	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", 0);
 
@@ -4595,8 +4591,7 @@
 			   "<FONT %s><FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B></FONT>",
 			   sml_attrib, mdate, message);
 
-		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
-										   buf2, 0, images);
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, 0);
 
 		/* Add the message to a conversations scrollback buffer */
 		conv->history = g_string_append(conv->history, buf);
@@ -4613,9 +4608,8 @@
 			   "<FONT %s><FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B></FONT>",
 			   sml_attrib, mdate, message);
 		
-		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
-						   buf2, 0, images);
-		
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, 0);
+
 		/* Add the message to a conversations scrollback buffer */
 		conv->history = g_string_append(conv->history, buf);
 		conv->history = g_string_append(conv->history, "<BR>\n");
@@ -4624,8 +4618,7 @@
 			   "<B><FONT %s COLOR=\"#777777\">%s</FONT></B>",
 			   sml_attrib, message);
 
-		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
-										   buf, 0, images);
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf, 0);
 	}
 	else {
 		char *new_message = g_memdup(message, length);
@@ -4706,8 +4699,8 @@
 
 		g_free(str);
 
-		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
-										   buf2, 0, images);
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
+										   buf2, 0);
 
 		if(gc){
 			char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
@@ -4727,8 +4720,8 @@
 		else
 			with_font_tag = g_memdup(new_message, length);
 
-		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
-							 with_font_tag, gtk_font_options, images);
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
+							 with_font_tag, gtk_font_options);
 
 		conv->history = g_string_append(conv->history, buf);
 		conv->history = g_string_append(conv->history, new_message);
@@ -4741,18 +4734,6 @@
 
 	if(sml_attrib)
 		g_free(sml_attrib);
-
-	if (images) {
-		GSList *tmp;
-
-		for (tmp = images; tmp; tmp = tmp->next) {
-			GdkPixbuf *pixbuf = tmp->data;
-			if(pixbuf)
-				g_object_unref(pixbuf);
-		}
-
-		g_slist_free(images);
-	}
 }
 
 static void
--- a/src/gtkimhtml.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkimhtml.c	Mon May 17 06:47:20 2004 +0000
@@ -905,6 +905,7 @@
 {
 	GtkIMHtml *imhtml = GTK_IMHTML(object);
 	GList *scalables;
+	GSList *l;
 
 	g_hash_table_destroy(imhtml->smiley_data);
 	gtk_smiley_tree_destroy(imhtml->default_smilies);
@@ -923,12 +924,20 @@
 		scale->free(scale);
 	}
 
+	for (l = imhtml->im_images; l; l = l->next) {
+		int id;
+		id = GPOINTER_TO_INT(l->data);
+		if (imhtml->funcs->image_unref)
+			imhtml->funcs->image_unref(id);
+	}
+
 	if (imhtml->clipboard_text_string) {
 		g_free(imhtml->clipboard_text_string);
 		g_free(imhtml->clipboard_html_string);
 	}
 
 	g_list_free(imhtml->scalables);
+	g_slist_free(imhtml->im_images);
 	G_OBJECT_CLASS(parent_class)->finalize (object);
 }
 
@@ -2167,34 +2176,18 @@
 				case 46:	/* IMG (opt) */
 				case 59:	/* IMG */
 					{
-#if 0
-/* disabling this for now, it's easy to add it back... */
-						GdkPixbuf *img = NULL;
-						const gchar *filename = NULL;
+						const char *id;
+
+						gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
+						ws[0] = '\0'; wpos = 0;
 
 						if (!(imhtml->format_functions & GTK_IMHTML_IMAGE))
 							break;
 
-						if (images && images->data) {
-							img = images->data;
-							images = images->next;
-							filename = g_object_get_data(G_OBJECT(img), "filename");
-							g_object_ref(G_OBJECT(img));
-						} else {
-							img = gtk_widget_render_icon(GTK_WIDGET(imhtml),
-							    GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON,
-							    "gtkimhtml-missing-image");
-						}
-
-						scalable = gtk_imhtml_image_new(img, filename);
-						/* NEW_BIT(NEW_SCALABLE_BIT); */
-						gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
-						scalable->add_to(scalable, imhtml, iter);
-						scalable->scale(scalable, rect.width, rect.height);
-						imhtml->scalables = g_list_append(imhtml->scalables, scalable);
-
-						g_object_unref(G_OBJECT(img));
-#endif
+						id = gtk_imhtml_get_html_opt(tag, "ID=");
+
+						gtk_imhtml_insert_image_at_iter(imhtml, atoi(id), iter);
+						break;
 					}
 				case 47:	/* P (opt) */
 				case 48:	/* H3 (opt) */
@@ -2486,7 +2479,7 @@
 	imhtml->smiley_shortcuts = allow;
 }
 
-void 
+void
 gtk_imhtml_set_protocol_name(GtkIMHtml *imhtml, gchar *protocol_name) {
 	imhtml->protocol_name = protocol_name;
 }
@@ -2538,7 +2531,7 @@
 }
 
 /* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename)
+GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
 {
 	GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
 	GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(img));
@@ -2553,6 +2546,7 @@
 	im_image->height = gdk_pixbuf_get_height(img);
 	im_image->mark = NULL;
 	im_image->filename = filename ? g_strdup(filename) : NULL;
+	im_image->id = id;
 
 	g_object_ref(img);
 	return GTK_IMHTML_SCALABLE(im_image);
@@ -2733,6 +2727,7 @@
 {
 	GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
 	GtkWidget *box = gtk_event_box_new();
+	char *tag;
 	GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
 
 	gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
@@ -2740,6 +2735,10 @@
 	gtk_widget_show(GTK_WIDGET(image->image));
 	gtk_widget_show(box);
 
+	tag = g_strdup_printf("<IMG ID=\"%d\">", image->id);
+	g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", tag, g_free);
+	g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "[Image]");
+
 	gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
 	g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), image);
 }
@@ -3513,6 +3512,61 @@
 	g_free(unescaped);
 }
 
+void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter)
+{
+	GdkPixbuf *pixbuf = NULL;
+	const char *filename = NULL;
+	gpointer image;
+	GdkRectangle rect;
+	GtkIMHtmlScalable *scalable = NULL;
+	int minus;
+
+	if (!imhtml->funcs || !imhtml->funcs->image_get ||
+	    !imhtml->funcs->image_get_size || !imhtml->funcs->image_get_data ||
+	    !imhtml->funcs->image_get_filename || !imhtml->funcs->image_ref ||
+	    !imhtml->funcs->image_unref)
+		return;
+
+	image = imhtml->funcs->image_get(id);
+
+	if (image) {
+		gpointer data;
+		size_t len;
+
+		data = imhtml->funcs->image_get_data(image);
+		len = imhtml->funcs->image_get_size(image);
+
+		if (data && len) {
+			GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+			gdk_pixbuf_loader_write(loader, data, len, NULL);
+			pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+			if (pixbuf)
+				g_object_ref(G_OBJECT(pixbuf));
+			gdk_pixbuf_loader_close(loader, NULL);
+		}
+
+	}
+
+	if (pixbuf) {
+		filename = imhtml->funcs->image_get_filename(image);
+		imhtml->funcs->image_ref(id);
+		imhtml->im_images = g_slist_prepend(imhtml->im_images, GINT_TO_POINTER(id));
+	} else {
+		pixbuf = gtk_widget_render_icon(GTK_WIDGET(imhtml), GTK_STOCK_MISSING_IMAGE,
+						GTK_ICON_SIZE_BUTTON, "gtkimhtml-missing-image");
+	}
+
+	scalable = gtk_imhtml_image_new(pixbuf, filename, id);
+	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
+	scalable->add_to(scalable, imhtml, iter);
+	minus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(imhtml)) +
+		gtk_text_view_get_right_margin(GTK_TEXT_VIEW(imhtml));
+	scalable->scale(scalable, rect.width - minus, rect.height);
+	imhtml->scalables = g_list_append(imhtml->scalables, scalable);
+
+	g_object_unref(G_OBJECT(pixbuf));
+}
+
 static const gchar *tag_to_html_start(GtkTextTag *tag)
 {
 	const gchar *name;
@@ -3796,3 +3850,9 @@
 
 	return g_string_free(str, FALSE);
 }
+
+void gtk_imhtml_set_funcs(GtkIMHtml *imhtml, GtkIMHtmlFuncs *f)
+{
+	g_return_if_fail(imhtml != NULL);
+	imhtml->funcs = f;
+}
--- a/src/gtkimhtml.h	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkimhtml.h	Mon May 17 06:47:20 2004 +0000
@@ -48,6 +48,7 @@
 typedef struct _GtkIMHtmlScalable	GtkIMHtmlScalable;
 typedef struct _GtkIMHtmlImage		GtkIMHtmlImage;
 typedef struct _GtkIMHtmlHr			GtkIMHtmlHr;
+typedef struct _GtkIMHtmlFuncs		GtkIMHtmlFuncs;
 
 typedef enum {
 	GTK_IMHTML_BOLD =      1 << 0,
@@ -111,6 +112,9 @@
 
 	char *clipboard_text_string;
 	char *clipboard_html_string;
+
+	GSList *im_images;
+	GtkIMHtmlFuncs *funcs;
 };
 
 struct _GtkIMHtmlClass {
@@ -159,6 +163,7 @@
 	gchar *filename;
 	int width;
 	int height;
+	int id;
 };
 
 struct _GtkIMHtmlHr {
@@ -178,6 +183,23 @@
 	GTK_IMHTML_USE_POINTSIZE = 1 << 8
 } GtkIMHtmlOptions;
 
+typedef gpointer    (*GtkIMHtmlGetImageFunc)        (int id);
+typedef gpointer    (*GtkIMHtmlGetImageDataFunc)    (gpointer i);
+typedef size_t      (*GtkIMHtmlGetImageSizeFunc)    (gpointer i);
+typedef const char *(*GtkIMHtmlGetImageFilenameFunc)(gpointer i);
+typedef void        (*GtkIMHtmlImageRefFunc)        (int id);
+typedef void        (*GtkIMHtmlImageUnrefFunc)      (int id);
+
+struct _GtkIMHtmlFuncs {
+	GtkIMHtmlGetImageFunc image_get;
+	GtkIMHtmlGetImageDataFunc image_get_data;
+	GtkIMHtmlGetImageSizeFunc image_get_size;
+	GtkIMHtmlGetImageFilenameFunc image_get_filename;
+	GtkIMHtmlImageRefFunc image_ref;
+	GtkIMHtmlImageUnrefFunc image_unref;
+};
+
+
 GtkType    gtk_imhtml_get_type         (void);
 GtkWidget* gtk_imhtml_new              (void *, void *);
 
@@ -190,6 +212,8 @@
 
 void       gtk_imhtml_remove_smileys   (GtkIMHtml *imhtml);
 
+void       gtk_imhtml_set_funcs        (GtkIMHtml *imhtml, GtkIMHtmlFuncs *f);
+
 void       gtk_imhtml_show_comments    (GtkIMHtml *imhtml, gboolean show);
 
 void       gtk_imhtml_html_shortcuts(GtkIMHtml *imhtml, gboolean allow);
@@ -217,7 +241,7 @@
 void gtk_imhtml_font_zoom(GtkIMHtml *imhtml, double zoom);
 
 GtkIMHtmlScalable *gtk_imhtml_scalable_new();
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename);
+GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id);
 void gtk_imhtml_image_free(GtkIMHtmlScalable *);
 void gtk_imhtml_image_scale(GtkIMHtmlScalable *, int, int);
 void gtk_imhtml_image_add_to(GtkIMHtmlScalable *, GtkIMHtml *, GtkTextIter *);
@@ -248,6 +272,7 @@
 void gtk_imhtml_insert_link(GtkIMHtml *imhtml, GtkTextMark *mark, const char *url, const char *text);
 void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley);
 void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter);
+void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter);
 void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size);
 void gtk_imhtml_font_shrink(GtkIMHtml *imhtml);
 void gtk_imhtml_font_grow(GtkIMHtml *imhtml);
--- a/src/gtkimhtmltoolbar.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkimhtmltoolbar.c	Mon May 17 06:47:20 2004 +0000
@@ -413,6 +413,8 @@
 	size_t size;
 	GError *error = NULL;
 	int id;
+	GtkTextIter iter;
+	GtkTextMark *ins;
 
 	if (resp != GTK_RESPONSE_OK) {
 		//set_toggle(toolbar->image, FALSE);
@@ -461,12 +463,12 @@
 		return;
 	}
 
-	//im->images = g_slist_append(im->images, GINT_TO_POINTER(id));
-
-	/*buf = g_strdup_printf("<IMG ID=\"%d\" SRC=\"file://%s\">", id, filename);
-	gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer), buf, -1);
-	g_free(buf);
-	*/
+	ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
+	gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)),
+	                                 &iter, ins);
+	gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter);
+	gaim_imgstore_unref(id);
+	
 	g_free(name);
 }
 
@@ -986,9 +988,6 @@
 	gtk_widget_show(sep);
 	toolbar->sml = NULL;
 	gtk_widget_show_all(hbox);
-
-	/* XXX - IMIMAGE - Fix IM images then remove the following line */
-	gtk_widget_hide(toolbar->image);
 }
 
 GtkWidget *gtk_imhtmltoolbar_new()
--- a/src/gtknotify.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtknotify.c	Mon May 17 06:47:20 2004 +0000
@@ -303,7 +303,6 @@
 	GtkWidget *button;
 	GtkWidget *imhtml;
 	GtkWidget *sw;
-	GSList *images = NULL;
 	int options = 0;
 	char label_text[2048];
 	char *linked_text;
@@ -376,29 +375,12 @@
 	options ^= GTK_IMHTML_NO_NEWLINE;
 	options ^= GTK_IMHTML_NO_SCROLL;
 
-	gaim_gtk_find_images(text, &images);
-
 	/* Make sure URLs are clickable */
 	linked_text = gaim_markup_linkify(text);
-	gtk_imhtml_append_text_with_images(GTK_IMHTML(imhtml), linked_text,
-									   options, images);
+	gtk_imhtml_append_text(GTK_IMHTML(imhtml), linked_text,
+									   options);
 	g_free(linked_text);
 
-	if (images)
-	{
-		GSList *tmp;
-
-		for (tmp = images; tmp; tmp = tmp->next)
-		{
-			GdkPixbuf *pixbuf = tmp->data;
-
-			if (pixbuf != NULL)
-				g_object_unref(pixbuf);
-		}
-
-		g_slist_free(images);
-	}
-
 	/* Show the window */
 	gtk_widget_show(window);
 
--- a/src/gtkutils.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkutils.c	Mon May 17 06:47:20 2004 +0000
@@ -70,6 +70,15 @@
 	g_idle_add(url_clicked_idle_cb, g_strdup(uri));
 }
 
+GtkIMHtmlFuncs gtkimhtml_cbs = {
+	(GtkIMHtmlGetImageFunc)gaim_imgstore_get,
+	(GtkIMHtmlGetImageDataFunc)gaim_imgstore_get_data,
+	(GtkIMHtmlGetImageSizeFunc)gaim_imgstore_get_size,
+	(GtkIMHtmlGetImageFilenameFunc)gaim_imgstore_get_filename,
+	gaim_imgstore_ref,
+	gaim_imgstore_unref,
+};
+
 void
 gaim_setup_imhtml(GtkWidget *imhtml)
 {
@@ -80,6 +89,8 @@
 					 G_CALLBACK(url_clicked_cb), NULL);
 
 	smiley_themeize(imhtml);
+
+	gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
 }
 
 void
@@ -924,62 +935,6 @@
 }
 
 void
-gaim_gtk_find_images(const char *message, GSList **list)
-{
-	GData *attribs;
-	const char *tmp, *start, *end;
-
-	g_return_if_fail(message != NULL);
-	g_return_if_fail(   list != NULL);
-
-	tmp = message;
-	while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
-		GaimStoredImage *image = NULL;
-		GdkPixbufLoader *loader = NULL;
-		GdkPixbuf *pixbuf = NULL;
-		GError *error = NULL;
-		char *id = g_datalist_get_data(&attribs, "id");
-
-		tmp = end + 1;
-
-		if (id)
-			image = gaim_imgstore_get(atoi(id));
-
-		g_datalist_clear(&attribs);
-
-		if (!image) {
-			*list = g_slist_append(*list, NULL);
-			continue;
-		}
-
-		loader = gdk_pixbuf_loader_new();
-
-		if (gdk_pixbuf_loader_write(loader, image->data, image->size, &error)
-			&& (pixbuf = gdk_pixbuf_loader_get_pixbuf(loader))) {
-
-			if (image->filename)
-				g_object_set_data_full(G_OBJECT(pixbuf), "filename",
-					g_strdup(image->filename), g_free);
-			g_object_ref(G_OBJECT(pixbuf));
-			*list = g_slist_append(*list, pixbuf);
-		} else {
-			if (error) {
-				gaim_debug(GAIM_DEBUG_ERROR, "gtkutils",
-						"Failed to make pixbuf from image store: %s\n",
-						error->message);
-				g_error_free(error);
-			} else {
-				gaim_debug(GAIM_DEBUG_ERROR, "gtkutils",
-						"Failed to make pixbuf from image store: unknown reason\n");
-			}
-			*list = g_slist_append(*list, NULL);
-		}
-
-		gdk_pixbuf_loader_close(loader, NULL);
-	}
-}
-
-void
 gaim_gtk_setup_gtkspell(GtkTextView *textview)
 {
 #ifdef USE_GTKSPELL
--- a/src/gtkutils.h	Mon May 17 04:39:14 2004 +0000
+++ b/src/gtkutils.h	Mon May 17 06:47:20 2004 +0000
@@ -269,18 +269,6 @@
 gboolean gaim_gtk_check_if_dir(const char *path, GtkFileSelection *filesel);
 
 /**
- * Parses a message to find \<IMG\> tags with valid ID attributes that
- * refer to images in Gaim's image store, and load them into a list
- * of GdkPixbufs. Image tags with missing ID paramaters, or those that
- * refer to images that are not in the store will have a corresponding
- * NULL entry on the list.
- *
- * @param message The message to parse for image tags.
- * @param list    A pointer to the GSList of GdkPixbufs that will be created.
- */
-void gaim_gtk_find_images(const char *message, GSList **list);
-
-/**
  * Sets up GtkSpell for the given GtkTextView, reporting errors
  * if encountered.
  *
--- a/src/imgstore.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/imgstore.c	Mon May 17 06:47:20 2004 +0000
@@ -31,6 +31,20 @@
 static GSList *imgstore = NULL;
 static int nextid = 0;
 
+/**
+ * Stored image
+ *
+ * Represents a single IM image awaiting display and/or transmission.
+ * Now that this type is basicly private too, these two structs could
+ * probably be combined.
+ */
+struct _GaimStoredImage
+{
+	char *data;		/**< The image data.		*/
+	size_t size;		/**< The image data's size.	*/
+	char *filename;		/**< The filename (for the UI)	*/
+};
+
 typedef struct
 {
 	int id;
@@ -117,6 +131,18 @@
 	return priv->img;
 }
 
+gpointer gaim_imgstore_get_data(GaimStoredImage *i) {
+	return i->data;
+}
+
+size_t gaim_imgstore_get_size(GaimStoredImage *i) {
+	return i->size;
+}
+
+const char *gaim_imgstore_get_filename(GaimStoredImage *i) {
+	return i->filename;
+}
+
 void gaim_imgstore_ref(int id) {
 	GaimStoredImagePriv *priv = gaim_imgstore_get_priv(id);
 
--- a/src/imgstore.h	Mon May 17 04:39:14 2004 +0000
+++ b/src/imgstore.h	Mon May 17 06:47:20 2004 +0000
@@ -26,17 +26,8 @@
 #ifndef _GAIM_CONV_IMGSTORE_H_
 #define _GAIM_CONV_IMGSTORE_H_
 
-/**
- * Stored image
- *
- * Represents a single IM image awaiting display and/or transmission.
- */
-typedef struct
-{
-	char *data;		/**< The image data.		*/
-	size_t size;		/**< The image data's size.	*/
-	char *filename;		/**< The filename (for the UI)	*/
-} GaimStoredImage;
+struct _GaimStoredImage;
+typedef struct _GaimStoredImage GaimStoredImage;
 
 #ifdef __cplusplus
 extern "C" {
@@ -66,6 +57,36 @@
 GaimStoredImage *gaim_imgstore_get(int id);
 
 /**
+ * Retrieves a pointer to the image's data.
+ *
+ * @param i	The Image
+ *
+ * @return A pointer to the data, which must not
+ *         be freed or modified.
+ */
+gpointer gaim_imgstore_get_data(GaimStoredImage *i);
+
+/**
+ * Retrieves the length of the image's data.
+ *
+ * @param i	The Image
+ *
+ * @return The size of the data that the pointer returned by
+ *         gaim_imgstore_get_data points to.
+ */
+size_t gaim_imgstore_get_size(GaimStoredImage *i);
+
+/**
+ * Retrieves a pointer to the image's filename.
+ *
+ * @param i	The Image
+ *
+ * @return A pointer to the filename, which must not
+ *         be freed or modified.
+ */
+const char *gaim_imgstore_get_filename(GaimStoredImage *i);
+
+/**
  * Increment the reference count for an image in the store. The
  * image will be removed from the store when the reference count
  * is zero.
--- a/src/protocols/oscar/oscar.c	Mon May 17 04:39:14 2004 +0000
+++ b/src/protocols/oscar/oscar.c	Mon May 17 06:47:20 2004 +0000
@@ -4524,15 +4524,18 @@
 	if (dim && dim->connected) {
 		/* If we're directly connected, send a direct IM */
 		ret = gaim_odc_send_im(od->sess, dim->conn, message, imflags);
-	} else if (imflags & GAIM_CONV_IM_IMAGES) {
-		/* Trying to send an IM image outside of a direct connection. */
-		oscar_ask_direct_im(gc, name);
-		ret = -ENOTCONN;
 	} else {
 		struct buddyinfo *bi;
 		struct aim_sendimext_args args;
 		struct stat st;
 		gsize len;
+		GaimConversation *conv = gaim_find_conversation_with_account(name, gaim_connection_get_account(gc));
+
+		if (strstr(message, "<IMG "))
+			gaim_conversation_write(conv, "",
+			                        _("Your IM Image was not sent. "
+			                        "You must be Direct Connected to send IM Images."),
+			                        GAIM_MESSAGE_ERROR, time(NULL));
 
 		bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, name));
 		if (!bi) {
@@ -5632,6 +5635,12 @@
 	buf = gaim_strdup_withhtml(message);
 	len = strlen(buf);
 
+	if (strstr(buf, "<IMG "))
+		gaim_conversation_write(conv, "",
+		                        _("Your IM Image was not sent. "
+		                        "You cannot send IM Images in AIM chats."),
+		                        GAIM_MESSAGE_ERROR, time(NULL));
+
 	encoding = oscar_encoding_check(buf);
 	if (encoding & AIM_IMFLAGS_UNICODE) {
 		gaim_debug_info("oscar", "Sending Unicode chat\n");
@@ -5929,8 +5938,8 @@
 }
 
 /*
- * This is called when each chunk of an image is received.  It can be used to 
- * update a progress bar, or to eat lots of dry cat food.  Wet cat food is 
+ * This is called when each chunk of an image is received.  It can be used to
+ * update a progress bar, or to eat lots of dry cat food.  Wet cat food is
  * nasty, you sicko.
  */
 static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
@@ -5947,7 +5956,7 @@
 	percent = va_arg(ap, double);
 	va_end(ap);
 
-	if (!(dim = find_direct_im(od, sn)))
+	if (!sn || !(dim = find_direct_im(od, sn)))
 		return 1;
 	if (dim->watcher) {
 		gaim_input_remove(dim->watcher);   /* Otherwise, the callback will callback */
@@ -6146,75 +6155,73 @@
 	char *buf;
 	size_t len;
 	int ret;
-
-	if (imflags & GAIM_CONV_IM_IMAGES) {
-		GString *msg = g_string_new("");
-		GString *data = g_string_new("<BINARY>");
-		GData *attribs;
-		const char *tmp, *start, *end, *last = NULL;
-		int oscar_id = 0;
-
-		tmp = message;
-
-		/* for each valid IMG tag... */
-		while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
-			GaimStoredImage *image = NULL;
-			const char *id;
-
-			last = end;
-			id = g_datalist_get_data(&attribs, "id");
-
-			/* ... if it refers to a valid gaim image ... */
-			if (id && (image = gaim_imgstore_get(atoi(id)))) {
-				/* ... append the message from start to the tag ... */
-				msg = g_string_append_len(msg, tmp, start - tmp);
-				oscar_id++;
-
-				/* ... insert a new img tag with the oscar id ... */
-				if (image->filename)
-					g_string_append_printf(msg,
-						"<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
-						image->filename, oscar_id, (int)image->size);
-				else
-					g_string_append_printf(msg,
-						"<IMG ID=\"%d\" DATASIZE=\"%d\">",
-						oscar_id, (int)image->size);
-
-				/* ... and append the data to the binary section ... */
-				g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%d\">",
-					oscar_id, (int)image->size);
-				data = g_string_append_len(data, image->data, image->size);
-				data = g_string_append(data, "</DATA>");
-			} else {
-				/* ... otherwise, allow the possibly invalid img tag through. */
-				/* should we do something else? */
-				msg = g_string_append_len(msg, tmp, (end + 1) - tmp);
-			}
-
-			g_datalist_clear(&attribs);
-
-			/* continue from the end of the tag */
-			tmp = end + 1;
+	GString *msg = g_string_new("");
+	GString *data = g_string_new("<BINARY>");
+	GData *attribs;
+	const char *start, *end, *last;
+	int oscar_id = 0;
+
+	last = message;
+
+	/* for each valid IMG tag... */
+	while (last && *last && gaim_markup_find_tag("img", last, &start, &end, &attribs)) {
+		GaimStoredImage *image = NULL;
+		const char *id;
+
+		if (start - last) {
+			g_string_append_len(msg, last, start - last);
 		}
 
-		/* append any remaining message data (without the > :-) */
-		if (last++ && *last)
-			msg = g_string_append(msg, last);
-
-		/* if we inserted any images in the binary section, append it */
-		if (oscar_id) {
-			msg = g_string_append_len(msg, data->str, data->len);
-			msg = g_string_append(msg, "</BINARY>");
+		id = g_datalist_get_data(&attribs, "id");
+
+		/* ... if it refers to a valid gaim image ... */
+		if (id && (image = gaim_imgstore_get(atoi(id)))) {
+			/* ... append the message from start to the tag ... */
+			size_t size = gaim_imgstore_get_size(image);
+			const char *filename = gaim_imgstore_get_filename(image);
+			gpointer imgdata = gaim_imgstore_get_data(image);
+
+			oscar_id++;
+
+			/* ... insert a new img tag with the oscar id ... */
+			if (filename)
+				g_string_append_printf(msg,
+					"<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%zu\">",
+					filename, oscar_id, size);
+			else
+				g_string_append_printf(msg,
+					"<IMG ID=\"%d\" DATASIZE=\"%zu\">",
+					oscar_id, size);
+
+			/* ... and append the data to the binary section ... */
+			g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%zu\">",
+				oscar_id, size);
+			data = g_string_append_len(data, imgdata, size);
+			data = g_string_append(data, "</DATA>");
 		}
-
-		len = msg->len;
-		buf = msg->str;
-		g_string_free(msg, FALSE);
-		g_string_free(data, TRUE);
-	} else {
-		len = strlen(message);
-		buf = g_memdup(message, len+1);
-	}
+			/* If the tag is invalid, skip it, thus no else here */
+
+		g_datalist_clear(&attribs);
+
+		/* continue from the end of the tag */
+		last = end + 1;
+	}
+
+	/* append any remaining message data (without the > :-) */
+	if (last && *last)
+		msg = g_string_append(msg, last);
+
+	/* if we inserted any images in the binary section, append it */
+	if (oscar_id) {
+		msg = g_string_append_len(msg, data->str, data->len);
+		msg = g_string_append(msg, "</BINARY>");
+	}
+
+	len = msg->len;
+	buf = msg->str;
+	g_string_free(msg, FALSE);
+	g_string_free(data, TRUE);
+
 
 	/* XXX - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
 	if (imflags & GAIM_CONV_IM_AUTO_RESP)