Index: libbalsa/Makefile.am
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/Makefile.am,v
retrieving revision 1.48
diff -u -b -r1.48 Makefile.am
--- libbalsa/Makefile.am	26 Apr 2002 09:21:34 -0000	1.48
+++ libbalsa/Makefile.am	21 May 2002 07:38:05 -0000
@@ -31,6 +31,8 @@
 	filter.h		\
 	folder-scanners.c	\
 	folder-scanners.h	\
+	html.h			\
+	html.c			\
 	identity.c		\
 	identity.h		\
 	information.c		\
Index: libbalsa/libbalsa.h
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/libbalsa.h,v
retrieving revision 1.31
diff -u -b -r1.31 libbalsa.h
--- libbalsa/libbalsa.h	24 Apr 2002 10:41:43 -0000	1.31
+++ libbalsa/libbalsa.h	21 May 2002 07:38:06 -0000
@@ -38,6 +38,7 @@
 #include "body.h"
 #include "files.h"
 #include "mime.h"
+#include "html.h"
 #include "notify.h"
 
 #include "information.h"
Index: libbalsa/mime.c
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/mime.c,v
retrieving revision 1.42
diff -u -b -r1.42 mime.c
--- libbalsa/mime.c	23 Feb 2002 22:30:15 -0000	1.42
+++ libbalsa/mime.c	21 May 2002 07:38:06 -0000
@@ -27,8 +27,12 @@
 #include "libbalsa.h"
 #include "mailbackend.h"
 #include "misc.h"
+#include "html.h"
 
-/* FIXME: The content of this file could go to message.c */
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libgnome/libgnome.h>
 
 static GString *process_mime_multipart(LibBalsaMessage * message,
                                        LibBalsaMessageBody * body,
@@ -36,6 +40,8 @@
 				       gint llen, gboolean ignore_html,
                                        gboolean flow);
 
+
+
 /* process_mime_part:
    returns string representation of given message part.
    NOTE: may return NULL(!).
@@ -49,6 +55,9 @@
     size_t alloced;
     gchar *res = NULL;
     GString *reply = NULL;
+    gchar *content_type = libbalsa_message_body_get_content_type(body);
+    gboolean ishtml=(g_strcasecmp(content_type, "text/html") == 0);
+
 
     switch (libbalsa_message_body_type(body)) {
     case LIBBALSA_MESSAGE_BODY_TYPE_OTHER:
@@ -56,19 +65,14 @@
     case LIBBALSA_MESSAGE_BODY_TYPE_APPLICATION:
     case LIBBALSA_MESSAGE_BODY_TYPE_IMAGE:
     case LIBBALSA_MESSAGE_BODY_TYPE_MODEL:
-    case LIBBALSA_MESSAGE_BODY_TYPE_MESSAGE:
     case LIBBALSA_MESSAGE_BODY_TYPE_VIDEO:
 	break;
     case LIBBALSA_MESSAGE_BODY_TYPE_MULTIPART:
+    case LIBBALSA_MESSAGE_BODY_TYPE_MESSAGE:
         reply = process_mime_multipart(message, body, reply_prefix_str,
                                        llen, ignore_html, flow);
 	break;
     case LIBBALSA_MESSAGE_BODY_TYPE_TEXT:
-	/* don't return text/html stuff... */
-	if (ignore_html && body->mutt_body->subtype &&
-	    !strcmp("html", body->mutt_body->subtype))
-	    break;
-
 	libbalsa_message_body_save_temporary(body, NULL);
 
 	part = fopen(body->temp_filename, "r");
@@ -79,6 +83,27 @@
 	if (!res)
 	    break;
 
+	if(ishtml) {
+	    if(ignore_html) {
+		gchar *res_ascii=convert_html(res);
+	    
+		if(res_ascii) {
+		    g_free(res);
+		    res=res_ascii;
+		}
+	    } else {
+		gchar *quoted=quote_html(res);
+
+		if(quoted) {
+		    g_free(res);
+		    res=quoted;
+		    reply_prefix_str=NULL;
+		    flow=FALSE;
+		}
+	    }
+	}
+	    
+
 	if (llen > 0) {
             if (flow && libbalsa_flowed_rfc2646(body)) {
                 /* we're making a `format=flowed' message, and the
@@ -122,11 +147,14 @@
 		reply = g_string_append_c(reply, '\n');
 		str = ptr;
 	    } while (str++);
-	} else
+	} else {
 	    reply = g_string_new(res);
+	}
 	g_free(res);
 	break;
     }
+    g_free(content_type);	
+    
     return reply;
 }
 
@@ -138,18 +166,37 @@
 {
     LibBalsaMessageBody *part;
     GString *res = NULL, *s;
+    gchar *content_type = libbalsa_message_body_get_content_type(body);
+
+    fprintf(stderr, "process_mime_multipart\n");
 
+    if(g_strcasecmp(content_type, "multipart/alternative")==0) {
+	part = preferred_part(body->parts, ignore_html);
+	if(part)
+	    res = process_mime_part(message, part, reply_prefix_str, llen,
+				    ignore_html, flow);
+    } else {
     for (part = body->parts; part; part = part->next) {
+	    if(part->mutt_body->disposition==DISPINLINE)
 	s = process_mime_part(message, part, reply_prefix_str, llen,
                           ignore_html, flow);
+	    else
+		s = NULL;
+	    fprintf(stderr, "%d %x %x\n", part->mutt_body->disposition, res, s);
+
 	if (!s)
 	    continue;
+	    
+
 	if (res) {
 	    res = g_string_append(res, s->str);
 	    g_string_free(s, TRUE);
 	} else
 	    res = s;
     }
+    }
+
+    g_free(content_type);
     return res;
 }
 
@@ -174,4 +221,38 @@
     }
 
     return reply;
+}
+
+LibBalsaMessageBody*
+preferred_part(LibBalsaMessageBody *parts, gboolean ignore_html)
+/* Remarks: Try to find LAST suitable part, which should be considered the
+            best according to RFC 1341 */
+{
+    /* TODO: - Look for additional specific types, and have more flags 
+               to control their selection?
+             - Let user preferences or previous selections override order 
+	       (e.g. choose text/html part even when text/plain is included
+	       after it)? */
+    LibBalsaMessageBody *body;
+    gchar *content_type;
+    LibBalsaMessageBody *best_part=NULL, *last_part=NULL;
+
+    for(body=parts; body; body=body->next) {
+	content_type = libbalsa_message_body_get_content_type(body);
+	
+	if(g_strcasecmp(content_type, "text/plain")==0 ||
+	   !ignore_html && (g_strcasecmp(content_type, "text/html")==0 ||
+			    g_strcasecmp(content_type, "multipart/related")==0) ||
+	   !best_part && g_strncasecmp(content_type, "text/", 5)==0)
+	    best_part=body;
+	   
+	last_part=body;
+
+	g_free(content_type);
+    }
+    
+    if(!best_part)
+	best_part=last_part;
+    
+    return best_part;
 }
Index: libbalsa/mime.h
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/mime.h,v
retrieving revision 1.16
diff -u -b -r1.16 mime.h
--- libbalsa/mime.h	23 Sep 2001 18:04:48 -0000	1.16
+++ libbalsa/mime.h	21 May 2002 07:38:06 -0000
@@ -31,8 +31,11 @@
 			   LibBalsaMessageBody * body,
 			   gchar * reply_prefix_str, gint llen,
 			   gboolean ignore_html, gboolean flow);
+
 GString *content2reply(LibBalsaMessage * message,
 		       gchar * reply_prefix_str, gint llen,
 		       gboolean ignore_html, gboolean flow);	/* arp */
+LibBalsaMessageBody*
+preferred_part(LibBalsaMessageBody *parts, gboolean ignore_html);
 
 #endif				/* __LIBBALSA_MIME_H__ */
Index: libbalsa/send.c
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/send.c,v
retrieving revision 1.155
diff -u -b -r1.155 send.c
--- libbalsa/send.c	10 May 2002 15:17:38 -0000	1.155
+++ libbalsa/send.c	21 May 2002 07:38:08 -0000
@@ -39,6 +39,7 @@
 #include "mailbox_imap.h"
 #include "misc.h"
 #include "information.h"
+#include "html.h"
 
 #ifdef BALSA_USE_THREADS
 #include "threads.h"
@@ -51,6 +52,9 @@
 #include <sys/stat.h>
 #endif
 
+
+
+
 typedef struct _MessageQueueItem MessageQueueItem;
 
 struct _MessageQueueItem {
@@ -303,6 +307,7 @@
     return body;
 }
 
+
 static BODY *
 add_mutt_body_as_extbody(const gchar *filename, const gchar *mime_type)
 {
@@ -337,6 +342,41 @@
     return body;
 }
 
+static BODY *real_body(BODY *body)
+{
+    if(body->disposition==DISPINLINE && body->type==TYPETEXT && 
+       strcmp(body->subtype, "html")==0) {
+	gchar txt_file[PATH_MAX + 1];
+
+	libbalsa_lock_mutt();
+	mutt_mktemp(txt_file);
+	
+	mutt_delete_parameter("format", &body->parameter);
+
+	if(convert_html_file(txt_file, body->filename)) {
+	    BODY *plain_body, *multipart;
+	    
+	    plain_body=libmutt_copy_body(body, body->hdr);
+	    plain_body->subtype=strdup("plain");
+	    plain_body->filename=g_strdup(txt_file);
+
+	    plain_body->next=body;
+
+	    multipart=mutt_make_multipart(plain_body);
+	    g_free(multipart->subtype);
+	    multipart->subtype=g_strdup("alternative");
+
+	    libbalsa_unlock_mutt();
+
+	    return multipart;
+	} 
+	libbalsa_unlock_mutt();
+    }
+    
+    return body;
+}
+
+
 #if 0
 /* you never know when you will need this one... */
 static void dump_queue(const char*msg)
@@ -1484,18 +1524,25 @@
 		}
 	    }
 	} else if (body->buffer) {
+	    gchar *type = NULL;
 	    newbdy = add_mutt_body_plain(body->charset, encoding, flow);
-	    if (body->mime_type) {
+	    
+	    if(has_html_code(body->buffer)) {
+		type=g_strdup ("text/html");
+	    } else if(body->mime_type)
+		type=g_strdup (body->mime_type);
+
+	    if (type) {
 		/* change the type and subtype within the mutt body */
-		gchar *type, *subtype;
+		gchar *subtype;
 		
-		type = g_strdup (body->mime_type);
 		if ((subtype = strchr (type, '/'))) {
 		    *subtype++ = 0;
 		    libbalsa_lock_mutt();
 		    newbdy->type = mutt_check_mime_type (type);
 		    g_free(newbdy->subtype);
 		    newbdy->subtype = g_strdup(subtype);
+		    
 		    libbalsa_unlock_mutt();
 		}
 		g_free (type);
@@ -1513,9 +1560,9 @@
 
 	if (newbdy) {
 	    if (last)
-		last->next = newbdy;
+		last->next = real_body(newbdy);
 	    else
-		msg->content = newbdy;
+		msg->content = real_body(newbdy);
 
 	    last = newbdy;
 	}
Index: src/balsa-app.h
===================================================================
RCS file: /cvs/gnome/balsa/src/balsa-app.h,v
retrieving revision 1.170
diff -u -b -r1.170 balsa-app.h
--- src/balsa-app.h	26 Apr 2002 09:21:37 -0000	1.170
+++ src/balsa-app.h	21 May 2002 07:38:09 -0000
@@ -279,6 +279,8 @@
 
     /* external editor */
     gchar *extern_editor_command;
+    gchar *html_editor_command;
+    gchar *html_convert_command;
     gboolean edit_headers;
 
     /* arp --- string to prefix "replied to" messages. */
Index: src/balsa-icons.c
===================================================================
RCS file: /cvs/gnome/balsa/src/balsa-icons.c,v
retrieving revision 1.25
diff -u -b -r1.25 balsa-icons.c
--- src/balsa-icons.c	4 Nov 2001 21:50:32 -0000	1.25
+++ src/balsa-icons.c	21 May 2002 07:38:10 -0000
@@ -49,7 +49,8 @@
 #include "pixmaps/balsa_marked_all.xpm"
 #include "pixmaps/balsa_identity.xpm"
 #include "pixmaps/balsa_close_mbox.xpm"
-
+#include "pixmaps/balsa_convert_html.xpm"
+#include "pixmaps/balsa_edit_html.xpm"
 #include "pixmaps/mbox_draft.xpm"
 #include "pixmaps/mbox_in.xpm"
 #include "pixmaps/mbox_out.xpm"
@@ -251,6 +252,8 @@
 	{ BALSA_PIXMAP_MARKED_ALL,	    balsa_marked_all_xpm,  24, 24 },
 	{ BALSA_PIXMAP_IDENTITY,	    balsa_identity_xpm,	   24, 24 },
 	{ BALSA_PIXMAP_CLOSE_MBOX,	    balsa_close_mbox_xpm,  24, 24 },
+	{ BALSA_PIXMAP_CONVERT_HTML,	    balsa_convert_html_xpm,24, 24 },
+	{ BALSA_PIXMAP_EDIT_HTML,	    balsa_edit_html_xpm,   24, 24 },
 
 	/* Menu icons */
 	{ BALSA_PIXMAP_MENU_NEW,	    menu_new_xpm,	   16, 16 },
Index: src/balsa-icons.h
===================================================================
RCS file: /cvs/gnome/balsa/src/balsa-icons.h,v
retrieving revision 1.19
diff -u -b -r1.19 balsa-icons.h
--- src/balsa-icons.h	4 Nov 2001 21:50:32 -0000	1.19
+++ src/balsa-icons.h	21 May 2002 07:38:10 -0000
@@ -47,6 +47,8 @@
 #define BALSA_PIXMAP_MARKED_ALL			"balsa_marked_all"
 #define BALSA_PIXMAP_IDENTITY			"balsa_identity"
 #define BALSA_PIXMAP_CLOSE_MBOX			"balsa_close_mbox"
+#define BALSA_PIXMAP_CONVERT_HTML               "balsa_convert_html"
+#define BALSA_PIXMAP_EDIT_HTML                  "balsa_edit_html"
 
 #define BALSA_PIXMAP_MENU_NEW			"menu_new"
 #define BALSA_PIXMAP_MENU_FLAGGED		"menu_flagged"
Index: src/balsa-message.c
===================================================================
RCS file: /cvs/gnome/balsa/src/balsa-message.c,v
retrieving revision 1.205
diff -u -b -r1.205 balsa-message.c
--- src/balsa-message.c	10 May 2002 15:17:50 -0000	1.205
+++ src/balsa-message.c	21 May 2002 07:38:13 -0000
@@ -57,6 +57,7 @@
 
 #ifdef HAVE_GNOME_VFS
 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
+# include <libgnomevfs/gnome-vfs-mime.h>
 #endif
 
 #define ELEMENTS(x) (sizeof (x) / sizeof (x[0]))
@@ -1984,11 +1985,22 @@
 }
 
 #ifdef HAVE_GTKHTML
+void balsa_message_display_html(GtkWidget *html, gchar * ptr, size_t len)
+{
+    GtkHTMLStream *stream;
+    
+    stream = gtk_html_begin(GTK_HTML(html));
+    
+    gtk_html_write(GTK_HTML(html), stream, ptr, len);
+    gtk_html_end(GTK_HTML(html), stream, GTK_HTML_STREAM_OK);
+    gtk_html_set_editable(GTK_HTML(html), FALSE);
+}
+
+
 static void
 part_info_init_html(BalsaMessage * bm, BalsaPartInfo * info, gchar * ptr,
 		    size_t len)
 {
-    GtkHTMLStream *stream;
     GtkWidget *html, *scroll;
 
     scroll = gtk_scrolled_window_new(NULL, NULL);
@@ -1997,10 +2009,9 @@
 
     html = gtk_html_new();
 
-    stream = gtk_html_begin(GTK_HTML(html));
-    gtk_html_write(GTK_HTML(html), stream, ptr, len);
-    gtk_html_end(GTK_HTML(html), stream, GTK_HTML_STREAM_OK);
-    gtk_html_set_editable(GTK_HTML(html), FALSE);
+    gtk_container_add(GTK_CONTAINER(scroll), html);
+
+    balsa_message_display_html(html, ptr, len);
 
     gtk_signal_connect(GTK_OBJECT(html), "size_request",
 		       (GtkSignalFunc) balsa_gtk_html_size_request,
@@ -2012,8 +2023,6 @@
 		       GTK_SIGNAL_FUNC(balsa_gtk_html_on_url),
 		       bm);
 
-    gtk_container_add(GTK_CONTAINER(scroll), html);
-
     gtk_widget_show(html);
     gtk_widget_show(scroll);
 
@@ -2104,12 +2113,22 @@
     info = part_info_new(body, bm->message);
 
 	if (is_multipart) {
-	    icon_title = g_strdup_printf("%s parts", strchr(content_type, '/')+1);
+	icon_title = g_strdup_printf(_("%s parts"), 
+				     strchr(content_type, '/')+1);
 	    *icon_title = toupper (*icon_title);
-	} else if (body->filename)
+    } else if (body->filename) {
+#ifdef HAVE_GNOME_VFS
+	const gchar *guess_type=gnome_vfs_mime_type_from_name(body->filename);
+
+	if(g_strcasecmp(guess_type, content_type)!=0 &&
+	   g_strcasecmp(guess_type, "application/octet-stream")!=0) {
+	    icon_title = g_strdup_printf("%s (\"%s\"/\"%s\")",
+					 body->filename, content_type, guess_type);
+	} else 
+#endif /* HAVE_GNOME_VFS */
 	icon_title =
 	    g_strdup_printf("%s (%s)", body->filename, content_type);
-    else
+    } else
 	icon_title = g_strdup_printf("(%s)", content_type);
 
     pix = libbalsa_icon_finder(content_type, body->filename, NULL);
@@ -2221,10 +2240,14 @@
     info->popup_menu = gtk_menu_new ();
     
     content_type = libbalsa_message_body_get_content_type (info->body);
+
     key_list = list = gnome_mime_get_keys (content_type);
 #ifdef HAVE_GNOME_VFS
     gdk_threads_leave(); /*FIXME: this hangs balsa */
     app_list = gnome_vfs_mime_get_short_list_applications(content_type);
+    if(!app_list && info->body->filename) {
+	app_list = gnome_vfs_mime_get_short_list_applications(gnome_vfs_mime_type_from_name(info->body->filename));
+    }
     gdk_threads_enter(); /* FIXME: this hangs balsa */
 
     if((def_app=gnome_vfs_mime_get_default_application(content_type))) {
@@ -2536,41 +2559,6 @@
     select_part(bmessage, index);
 }
 
-static LibBalsaMessageBody*
-preferred_part(LibBalsaMessageBody *parts)
-{
-    /* TODO: Consult preferences and/or previous selections */
-
-    LibBalsaMessageBody *body;
-    gchar *content_type;
-
-#ifdef HAVE_GTKHTML
-    for(body=parts; body; body=body->next) {
-	content_type = libbalsa_message_body_get_content_type(body);
-
-	if(g_strcasecmp(content_type, "text/html")==0) {
-	    g_free(content_type);
-	    return body;
-	}
-	g_free(content_type);
-    }
-#endif /* HAVE_GTKHTML */
-
-    for(body=parts; body; body=body->next) {
-	content_type = libbalsa_message_body_get_content_type(body);
-
-	if(g_strcasecmp(content_type, "text/plain")==0) {
-	    g_free(content_type);
-	    return body;
-	}
-	g_free(content_type);
-    }
-
-
-    return parts;
-}
-
-
 
 static gint part_icon_no(BalsaMessage *bm, const LibBalsaMessageBody *body)
 {
@@ -2612,7 +2600,13 @@
 	    add_body(bm, parent->parts);
 	} else if(g_strcasecmp(content_type, "multipart/alternative")==0) {
 	    /* Add the most suitable part. */
-	    add_body(bm, preferred_part(parent->parts));
+	    add_body(bm, preferred_part(parent->parts,
+#ifdef HAVE_GTKHTML
+					FALSE
+#else
+					TRUE
+#endif
+					));
 	} else {
 	    /* Add first (main) part + anything else with 
 	       Content-Disposition: inline */
Index: src/balsa-message.h
===================================================================
RCS file: /cvs/gnome/balsa/src/balsa-message.h,v
retrieving revision 1.29
diff -u -b -r1.29 balsa-message.h
--- src/balsa-message.h	4 Feb 2001 11:48:34 -0000	1.29
+++ src/balsa-message.h	21 May 2002 07:38:13 -0000
@@ -89,6 +89,10 @@
 gboolean balsa_message_can_select(BalsaMessage * bmessage);
 void balsa_message_copy_clipboard(BalsaMessage * bmessage);
 void balsa_message_select_all(BalsaMessage * bmessage);
+#ifdef HAVE_GTKHTML
+void balsa_message_display_html(GtkWidget *html, gchar * ptr, size_t len);
+#endif
+
 
 void reflow_string(gchar * str, gint mode, gint * cur_pos, int width);
 
Index: src/pref-manager.c
===================================================================
RCS file: /cvs/gnome/balsa/src/pref-manager.c,v
retrieving revision 1.191
diff -u -b -r1.191 pref-manager.c
--- src/pref-manager.c	23 Feb 2002 22:30:18 -0000	1.191
+++ src/pref-manager.c	21 May 2002 07:38:16 -0000
@@ -106,8 +106,10 @@
     GtkWidget *debug_message_menu;
     GtkWidget *fatal_message_menu;
 
-    /* External editor preferences */
+    /* External editor/converter preferences */
     GtkWidget *extern_editor_command;
+    GtkWidget *html_editor_command;
+    GtkWidget *html_convert_command;
     GtkWidget *edit_headers;
 
     /* arp */
@@ -438,10 +440,16 @@
     gtk_signal_connect(GTK_OBJECT(pui->forward_attached), "toggled",
 		       GTK_SIGNAL_FUNC(properties_modified_cb), property_box);
 
-    /* external editor */
+    /* external editor/converter */
     gtk_signal_connect(GTK_OBJECT(pui->extern_editor_command), "changed",
                        GTK_SIGNAL_FUNC(properties_modified_cb),
     		       property_box);
+    gtk_signal_connect(GTK_OBJECT(pui->html_editor_command), "changed",
+                       GTK_SIGNAL_FUNC(properties_modified_cb),
+    		       property_box);
+    gtk_signal_connect(GTK_OBJECT(pui->html_convert_command), "changed",
+                       GTK_SIGNAL_FUNC(properties_modified_cb),
+    		       property_box);
     gtk_signal_connect(GTK_OBJECT(pui->edit_headers), "toggled",
     		       GTK_SIGNAL_FUNC(properties_modified_cb),
     		       property_box);
@@ -718,11 +726,19 @@
         }
     }
 
-    /* external editor */
+    /* external editor/converter */
     g_free(balsa_app.extern_editor_command);
     balsa_app.extern_editor_command = 
     	g_strdup(gtk_entry_get_text(GTK_ENTRY(pui->extern_editor_command)));
     	
+    g_free(balsa_app.html_editor_command);
+    balsa_app.html_editor_command = 
+    	g_strdup(gtk_entry_get_text(GTK_ENTRY(pui->html_editor_command)));
+
+    g_free(balsa_app.html_convert_command);
+    balsa_app.html_convert_command = 
+    	g_strdup(gtk_entry_get_text(GTK_ENTRY(pui->html_convert_command)));
+
     balsa_app.edit_headers = GTK_TOGGLE_BUTTON(pui->edit_headers)->active;
 
     /* arp */
@@ -1003,9 +1019,13 @@
     gtk_widget_set_sensitive(pui->send_rfc2646_format_flowed,
 			     GTK_TOGGLE_BUTTON(pui->wordwrap)->active);
 
-    /* external editor */
+    /* external editor/converter */
     gtk_entry_set_text(GTK_ENTRY(pui->extern_editor_command), 
                        balsa_app.extern_editor_command);
+    gtk_entry_set_text(GTK_ENTRY(pui->html_editor_command), 
+                       balsa_app.html_editor_command);
+    gtk_entry_set_text(GTK_ENTRY(pui->html_convert_command), 
+                       balsa_app.html_convert_command);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pui->edit_headers),
                                  balsa_app.edit_headers);
 
@@ -1754,15 +1774,20 @@
 
 	vbox2 = vbox_in_container(frame2);
 
-    table2 = GTK_TABLE(gtk_table_new(5, 2, FALSE));
+    table2 = GTK_TABLE(gtk_table_new(7, 2, FALSE));
     gtk_container_add(GTK_CONTAINER(vbox2), GTK_WIDGET(table2));
     gtk_container_set_border_width(GTK_CONTAINER(table2), 2);
     pui->extern_editor_command = 
         attach_entry(_("External editor command:"), 4, table2);
+    pui->html_editor_command = 
+        attach_entry(_("HTML editor command:"), 5, table2);
+    pui->html_convert_command = 
+        attach_entry(_("HTML to plain-text converter:"), 6, table2);
+
     pui->edit_headers = 
     	gtk_check_button_new_with_label(_("Edit headers in external editor"));
     gtk_box_pack_start(GTK_BOX(vbox2), pui->edit_headers, FALSE, TRUE, 0);
-    pui->quote_str = attach_entry(_("Reply Prefix:"), 5, table2);
+    pui->quote_str = attach_entry(_("Reply Prefix:"), 7, table2);
 
     pui->autoquote =
         gtk_check_button_new_with_label(_("Automatically quote original "
Index: src/save-restore.c
===================================================================
RCS file: /cvs/gnome/balsa/src/save-restore.c,v
retrieving revision 1.234
diff -u -b -r1.234 save-restore.c
--- src/save-restore.c	8 May 2002 18:27:14 -0000	1.234
+++ src/save-restore.c	21 May 2002 07:38:18 -0000
@@ -768,6 +768,12 @@
         gnome_config_get_string("ExternEditorCommand=gnome-edit %s");
     balsa_app.edit_headers = 
         gnome_config_get_bool("ExternEditorEditHeaders=false");
+    g_free(balsa_app.html_editor_command);
+    balsa_app.html_editor_command = 
+        gnome_config_get_string("HtmlEditorCommand=amaya %s");
+    g_free(balsa_app.html_convert_command);
+    balsa_app.html_convert_command = 
+        gnome_config_get_string("HtmlConvertCommand=lynx -dump -force_html %s");
 
     g_free(balsa_app.quote_str);
     balsa_app.quote_str = gnome_config_get_string("QuoteString=> ");
@@ -801,6 +807,7 @@
     balsa_app.close_mailbox_auto = gnome_config_get_bool("AutoCloseMailbox=true");
     balsa_app.close_mailbox_timeout = gnome_config_get_int("AutoCloseMailboxTimeout=10");
 
+
     balsa_app.remember_open_mboxes =
 	gnome_config_get_bool("RememberOpenMailboxes=false");
     gnome_config_get_vector("OpenMailboxes", &open_mailbox_count,
@@ -1034,6 +1041,10 @@
     gnome_config_set_string("ExternEditorCommand", 
                             balsa_app.extern_editor_command);
     gnome_config_set_bool("ExternEditorEditHeaders", balsa_app.edit_headers);
+    gnome_config_set_string("HtmlEditorCommand", 
+                            balsa_app.html_editor_command);
+    gnome_config_set_string("HtmlConvertCommand", 
+                            balsa_app.html_convert_command);
     gnome_config_set_string("QuoteString", balsa_app.quote_str);
 
     gnome_config_pop_prefix();
Index: src/sendmsg-window.c
===================================================================
RCS file: /cvs/gnome/balsa/src/sendmsg-window.c,v
retrieving revision 1.361
diff -u -b -r1.361 sendmsg-window.c
--- src/sendmsg-window.c	10 May 2002 15:17:51 -0000	1.361
+++ src/sendmsg-window.c	21 May 2002 07:38:26 -0000
@@ -68,6 +68,10 @@
 #include "balsa-index.h"
 #include "balsa-icons.h"
 
+#ifdef HAVE_GTKHTML
+#include <gtkhtml/gtkhtml.h>
+#endif
+
 #ifdef BALSA_USE_THREADS
 #include <pthread.h>
 #include "threads.h"
@@ -136,6 +140,7 @@
 static gint set_locale(GtkWidget *, BalsaSendmsg *, gint);
 
 static void edit_with_gnome(GtkWidget* widget, BalsaSendmsg* msg);
+static void edit_with_gnome_html(GtkWidget* widget, BalsaSendmsg* msg);
 static void change_identity_dialog_cb(GtkWidget*, BalsaSendmsg*);
 static void repl_identity_signature(BalsaSendmsg* msg, 
                                     LibBalsaIdentity* new_ident,
@@ -186,6 +191,8 @@
 static void wrap_body_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static void reflow_par_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static void reflow_body_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
+static void html_to_text_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
+static void edit_html_source_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static gint insert_signature_cb(GtkWidget *, BalsaSendmsg *);
 static gint quote_messages_cb(GtkWidget *, BalsaSendmsg *);
 
@@ -267,25 +274,39 @@
      (gpointer) reflow_body_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
      GDK_r, GDK_CONTROL_MASK | GDK_SHIFT_MASK, NULL},
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_SPELL_CHECK 13
+#define EDIT_MENU_CONVERT_HTML 13
+    {GNOME_APP_UI_ITEM, N_("HTML to Text"), NULL,
+     (gpointer) html_to_text_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
+     0, 0, NULL},
+    GNOMEUIINFO_SEPARATOR,
+
+#define EDIT_MENU_SPELL_CHECK 15
     GNOMEUIINFO_ITEM_STOCK(N_("_Check Spelling"), 
                            N_("Check the spelling of the message"),
                            spell_check_cb,
                            GNOME_STOCK_MENU_SPELLCHECK),
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_SELECT_IDENT 15
+#define EDIT_MENU_SELECT_IDENT 17
     GNOMEUIINFO_ITEM_STOCK(N_("Select _Identity..."), 
                            N_("Select the Identity to use for the message"),
                            change_identity_dialog_cb,
                            BALSA_PIXMAP_MENU_IDENTITY),
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_EDIT_GNOME 17
+#define EDIT_MENU_EDIT_GNOME 19
     GNOMEUIINFO_ITEM_STOCK(N_("_Edit with Gnome-Editor"),
                            N_("Edit the current message with "
                               "the default Gnome editor"),
                            edit_with_gnome,
                            BALSA_PIXMAP_MENU_IDENTITY), /*FIXME: Other icon */
+#define EDIT_MENU_EDIT_HTML 20
+    GNOMEUIINFO_ITEM_STOCK(N_("_Edit as HTML"),
+                           N_("Edit the current message with "
+                              "an HTML editor, possibly after some conversion"),
+                           edit_with_gnome_html,
+                           BALSA_PIXMAP_MENU_IDENTITY), /*FIXME: Other icon */   
+
     GNOMEUIINFO_END
+
 };
 
 static GnomeUIInfo view_menu[] = {
@@ -321,7 +342,9 @@
     BALSA_PIXMAP_SAVE,
     BALSA_PIXMAP_IDENTITY,
     GNOME_STOCK_PIXMAP_SPELLCHECK,
-    GNOME_STOCK_PIXMAP_CLOSE
+    GNOME_STOCK_PIXMAP_CLOSE,
+    BALSA_PIXMAP_CONVERT_HTML,
+    BALSA_PIXMAP_EDIT_HTML 
 };
 
 #if MENU_TOGGLE_KEYWORDS_POS+1 != VIEW_MENU_LENGTH
@@ -335,7 +358,8 @@
 static void lang_danish_cb(GtkWidget *, BalsaSendmsg *);
 static void lang_german_cb(GtkWidget *, BalsaSendmsg *);
 static void lang_dutch_cb(GtkWidget *, BalsaSendmsg *);
-static void lang_english_cb(GtkWidget *, BalsaSendmsg *);
+static void lang_english_uk_cb(GtkWidget *, BalsaSendmsg *);
+static void lang_english_us_cb(GtkWidget *, BalsaSendmsg *);
 static void lang_estonian_cb(GtkWidget *, BalsaSendmsg *);
 static void lang_finnish_cb(GtkWidget *, BalsaSendmsg *);
 static void lang_french_cb(GtkWidget *, BalsaSendmsg *);
@@ -366,7 +390,8 @@
     GNOMEUIINFO_ITEM_NONE(N_("Catalan"), NULL, lang_catalan_cb),
     GNOMEUIINFO_ITEM_NONE(N_("Danish"), NULL, lang_danish_cb),
     GNOMEUIINFO_ITEM_NONE(N_("Dutch"), NULL, lang_dutch_cb),
-    GNOMEUIINFO_ITEM_NONE(N_("English"), NULL, lang_english_cb),
+    GNOMEUIINFO_ITEM_NONE(N_("English (UK)"), NULL, lang_english_uk_cb),
+    GNOMEUIINFO_ITEM_NONE(N_("English (US)"), NULL, lang_english_us_cb),
     GNOMEUIINFO_ITEM_NONE(N_("Estonian"), NULL, lang_estonian_cb),
     GNOMEUIINFO_ITEM_NONE(N_("Finnish"), NULL, lang_finnish_cb),
     GNOMEUIINFO_ITEM_NONE(N_("French"), NULL, lang_french_cb),
@@ -485,53 +510,58 @@
     {"de_DE", "ISO-8859-15", N_("German")},
 #define LOC_DUTCH_POS     6
     {"nl_NL", "ISO-8859-15", N_("Dutch")},
-#define LOC_ENGLISH_POS   7
-    /* English -> American English, argh... */
-    {"en_US", "ISO-8859-1", N_("English")}, 
-#define LOC_ESTONIAN_POS  8
+#define LOC_ENGLISH_UK_POS   7
+    {"en_GB", "ISO-8859-1", N_("English (UK)")}, 
+#define LOC_ENGLISH_US_POS   8
+    {"en_US", "ISO-8859-1", N_("English (US)")}, 
+#define LOC_ESTONIAN_POS  9
     {"et_EE", "ISO-8859-15", N_("Estonian")},
-#define LOC_FINNISH_POS   9
+#define LOC_FINNISH_POS   10
     {"fi_FI", "ISO-8859-15", N_("Finnish")},
-#define LOC_FRENCH_POS    10
+#define LOC_FRENCH_POS 11
     {"fr_FR", "ISO-8859-15", N_("French")},
-#define LOC_GREEK_POS     11 
+#define LOC_GREEK_POS     12 
     {"el_GR", "ISO-8859-7", N_("Greek")},
-#define LOC_HUNGARIAN_POS 12
+#define LOC_HUNGARIAN_POS 13
     {"hu_HU", "ISO-8859-2", N_("Hungarian")},
-#define LOC_ITALIAN_POS   13
+#define LOC_ITALIAN_POS   14
     {"it_IT", "ISO-8859-15", N_("Italian")},
-#define LOC_JAPANESE_POS  14
+#define LOC_JAPANESE_POS  15
     {"ja_JP", "euc-jp", N_("Japanese")},
-#define LOC_KOREAN_POS    15
+#define LOC_KOREAN_POS    16
     {"ko_KR", "euc-kr", N_("Korean")},
-#define LOC_LATVIAN_POS    16
+#define LOC_LATVIAN_POS    17
     {"lv_LV", "ISO-8859-13", N_("Latvian")},
-#define LOC_LITHUANIAN_POS    17
+#define LOC_LITHUANIAN_POS    18
     {"lt_LT", "ISO-8859-13", N_("Lithuanian")},
-#define LOC_NORWEGIAN_POS 18
+#define LOC_NORWEGIAN_POS 19
     {"no_NO", "ISO-8859-1", N_("Norwegian")},
-#define LOC_POLISH_POS    19
+#define LOC_POLISH_POS    20
     {"pl_PL", "ISO-8859-2", N_("Polish")},
-#define LOC_PORTUGESE_POS 20
+#define LOC_PORTUGESE_POS 21
     {"pt_PT", "ISO-8859-15", N_("Portugese")},
-#define LOC_ROMANIAN_POS 21
+#define LOC_ROMANIAN_POS 22
     {"ro_RO", "ISO-8859-2", N_("Romanian")},
-#define LOC_RUSSIAN_ISO_POS   22
+#define LOC_RUSSIAN_ISO_POS   23
     {"ru_SU", "ISO-8859-5", N_("Russian (ISO)")},
-#define LOC_RUSSIAN_KOI_POS   23
+#define LOC_RUSSIAN_KOI_POS   24
     {"ru_RU", "KOI8-R", N_("Russian (KOI)")},
-#define LOC_SLOVAK_POS    24
+#define LOC_SLOVAK_POS    25
     {"sk_SK", "ISO-8859-2", N_("Slovak")},
-#define LOC_SPANISH_POS   25
+#define LOC_SPANISH_POS   26
     {"es_ES", "ISO-8859-15", N_("Spanish")},
-#define LOC_SWEDISH_POS   26
+#define LOC_SWEDISH_POS   27
     {"sv_SE", "ISO-8859-1", N_("Swedish")},
-#define LOC_TURKISH_POS   27
+#define LOC_TURKISH_POS   28
     {"tr_TR", "ISO-8859-9", N_("Turkish")},
-#define LOC_UKRAINIAN_POS 28
+#define LOC_UKRAINIAN_POS 29
     {"uk_UK", "KOI8-U", N_("Ukrainian")}
 };
 
+/* English = US English. I *really* don't like this, but I'll leave it like
+   that in order not to change the default behaviour - toralf@kscanners.com */
+#define LOC_ENGLISH_POS LOC_ENGLISH_US_POS
+
 static gint mail_headers_page;
 static gint spell_check_page;
 
@@ -708,6 +738,33 @@
     lang_menu[LANG_CURRENT_POS].label = (char *) locales[idxsys].lang_name;
 }
 
+
+static void show_body(BalsaSendmsg *msg, gboolean force_text)
+{
+    gchar *text=gtk_editable_get_chars(GTK_EDITABLE(msg->text), 0, -1);
+    
+    if(!force_text && has_html_code(text)) {
+#ifdef HAVE_GTKHTML
+	balsa_message_display_html(msg->html, text, strlen(text));
+#endif /* HAVE_GTKHTML */
+	gtk_widget_hide(msg->text_box);
+	gtk_widget_show(msg->html_box);
+    } else {
+	gtk_widget_show(msg->text_box);
+	gtk_widget_hide(msg->html_box);
+    }
+
+    g_free(text);
+
+}
+
+static void hide_body(BalsaSendmsg *msg)
+{
+    gtk_widget_hide(msg->text_box);
+    gtk_widget_hide(msg->html_box);
+}
+
+
 static gboolean
 edit_with_gnome_check(gpointer data) {
     FILE *tmp;
@@ -763,11 +820,12 @@
     g_free(data_real->filename);
     fclose(tmp);
     unlink(data_real->filename);
-    gtk_widget_set_sensitive(data_real->msg->text, TRUE);
+    show_body(data_real->msg, FALSE);
     g_free(data);
     return FALSE;
 }
 
+
 /* Edit the current file with an external editor.
  *
  * We fork twice current process, so we get:
@@ -777,8 +835,8 @@
  * - New (child) process (forks and waits for child to finish)
  * - New (grandchild) process (executes editor)
  */
-static void
-edit_with_gnome(GtkWidget* widget, BalsaSendmsg* msg)
+static void edit_externally(GtkWidget* widget, BalsaSendmsg* msg, 
+			    gchar *editor)
 {
     static const char TMP_PATTERN[] = "/tmp/balsa-edit-XXXXXX";
     gchar filename[sizeof(TMP_PATTERN)];
@@ -816,7 +874,7 @@
                 from, to, cc, bcc, subject, reply_to, comments);
     }
 
-    gtk_widget_set_sensitive(msg->text, FALSE);
+    hide_body(msg);
     fputs(gtk_editable_get_chars(GTK_EDITABLE(msg->text), 0,
                                  gtk_text_get_length(GTK_TEXT
                                                      (msg->text))), tmp);
@@ -828,7 +886,7 @@
     if (pid == 0) {
         setpgid(0, 0);
         
-        command = g_strdup_printf(balsa_app.extern_editor_command, filename); 
+        command = g_strdup_printf(editor, filename); 
         cmdline = g_strsplit(command, " ", 1024); 
         execvp (cmdline[0], cmdline); 
         perror ("execvp"); 
@@ -843,6 +901,32 @@
     gtk_idle_add((GtkFunction) edit_with_gnome_check, data);
 }
 
+static void edit_with_gnome(GtkWidget* widget, BalsaSendmsg* msg)
+{
+    edit_externally(widget, msg, balsa_app.extern_editor_command);
+}
+
+static void edit_with_gnome_html(GtkWidget* widget, BalsaSendmsg* msg)
+{
+    gchar *text=gtk_editable_get_chars(GTK_EDITABLE(msg->text), 0, -1);
+    GString *text_str=g_string_new(text);
+    gint pos=0;
+
+    g_free(text);
+    html_wrap(text_str, TRUE);
+
+    gtk_text_freeze(GTK_TEXT(msg->text));
+    gtk_editable_delete_text(GTK_EDITABLE(msg->text), 0, -1);
+    gtk_editable_insert_text(GTK_EDITABLE(msg->text), text_str->str, 
+			     text_str->len, &pos);
+
+    g_string_free(text_str, TRUE);
+
+    gtk_text_thaw(GTK_TEXT(msg->text));
+    
+    edit_externally(widget, msg, balsa_app.html_editor_command);
+}
+
 static void 
 change_identity_dialog_cb(GtkWidget* widget, BalsaSendmsg* msg)
 {
@@ -1630,6 +1714,7 @@
     gtk_editable_insert_text(GTK_EDITABLE(msg->text), text->str,
 			     text->len, &pos);
     g_string_free(text, TRUE);
+    show_body(msg, FALSE);
     
     return TRUE;
 }
@@ -2087,9 +2172,10 @@
 static GtkWidget *
 create_text_area(BalsaSendmsg * msg)
 {
-    GtkWidget *table;
+    GtkWidget *table, *scroll, *btn_box, *btn, *label;
 
     msg->text = gtk_text_new(NULL, NULL);
+
     if (msg->flow)
         gtk_signal_connect(GTK_OBJECT(msg->text), "insert-text",
                            insert_text_cb, NULL);
@@ -2098,17 +2184,62 @@
     balsa_spell_check_set_text(BALSA_SPELL_CHECK(msg->spell_checker),
 			       GTK_TEXT(msg->text));
 
-    table = gtk_scrolled_window_new(GTK_TEXT(msg->text)->hadj,
+    table = gtk_vbox_new(FALSE, 0);
+
+    msg->text_box = gtk_scrolled_window_new(GTK_TEXT(msg->text)->hadj,
 				    GTK_TEXT(msg->text)->vadj);
+    gtk_container_add(GTK_CONTAINER(msg->text_box), msg->text);
+    gtk_container_add(GTK_CONTAINER(table), msg->text_box);
+    
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(table),
     				   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-    gtk_container_add(GTK_CONTAINER(table), msg->text);
+    
     gtk_signal_connect(GTK_OBJECT(msg->text), "drag_data_received",
 		       GTK_SIGNAL_FUNC(drag_data_quote), msg);
     gtk_drag_dest_set(GTK_WIDGET(msg->text), GTK_DEST_DEFAULT_ALL,
 		      drop_types, ELEMENTS(drop_types),
 		      GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
 
+    /* *** HTML preview & action buttons */
+    msg->html_box=gtk_vbox_new(FALSE, 6);
+    gtk_container_add(GTK_CONTAINER(table), msg->html_box);
+
+    label=gtk_label_new(_("HTML message (no inline edit):"));
+    gtk_box_pack_start(GTK_BOX(msg->html_box), label, FALSE, FALSE, 0);
+    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+
+    btn_box=gtk_hbutton_box_new();
+    
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(btn_box), GTK_BUTTONBOX_END);
+    gtk_container_set_border_width(GTK_CONTAINER(btn_box), 6);
+
+    btn=gtk_button_new_with_label(_("Edit..."));
+    gtk_signal_connect(GTK_OBJECT(btn), "clicked", 
+		       GTK_SIGNAL_FUNC(edit_with_gnome_html), msg);
+    gtk_box_pack_start(GTK_BOX(btn_box), btn, FALSE, FALSE, 0);
+    
+    
+
+    btn=gtk_button_new_with_label(_("To Plain"));
+    gtk_signal_connect(GTK_OBJECT(btn), "clicked", 
+		       GTK_SIGNAL_FUNC(html_to_text_cb), msg);
+    gtk_box_pack_start(GTK_BOX(btn_box), btn, FALSE, FALSE, 0);
+
+    btn=gtk_button_new_with_label(_("Edit Source"));
+    gtk_signal_connect(GTK_OBJECT(btn), "clicked", 
+		       GTK_SIGNAL_FUNC(edit_html_source_cb), msg);
+    gtk_box_pack_start(GTK_BOX(btn_box), btn, FALSE, FALSE, 0);
+    
+
+    gtk_box_pack_end(GTK_BOX(msg->html_box), btn_box, FALSE, FALSE, 0);
+    
+#ifdef HAVE_GTKHTML
+    scroll = gtk_scrolled_window_new(NULL, NULL);
+    msg->html = gtk_html_new();
+    gtk_container_add(GTK_CONTAINER(scroll), msg->html);
+    gtk_box_pack_start(GTK_BOX(msg->html_box), scroll, TRUE, TRUE, 0);
+#endif /* HAVE_GTKHTML */
+    
     gtk_widget_show_all(GTK_WIDGET(table));
 
     return table;
@@ -2175,6 +2306,23 @@
     libbalsa_message_body_unref(message);
 }
 
+static gboolean in_plain(BalsaSendmsg *msg)
+{
+    guint cursor=gtk_editable_get_position(GTK_EDITABLE(msg->text));
+    
+    if(!cursor)
+	return FALSE;
+    else {
+	gchar *text=
+	    gtk_editable_get_chars(GTK_EDITABLE(msg->text), 0, cursor);
+	gboolean html=has_html_code(text);
+	
+	g_free(text);
+
+	return !html;
+    }
+}
+
 /* quoteBody ------------------------------------------------------------
    quotes properly the body of the message.
    Use GString to optimize memory usage.
@@ -2185,6 +2333,7 @@
     GString *body;
     gchar *str, *date = NULL;
     const gchar *personStr;
+    gboolean html_body=FALSE;
 
     libbalsa_message_body_ref(message);
 
@@ -2200,7 +2349,11 @@
 
 	body = content2reply(message, NULL,
 			     balsa_app.wordwrap ? balsa_app.wraplength : -1,
-			     balsa_app.reply_strip_html, msg->flow);
+			     (balsa_app.reply_strip_html || in_plain(msg)),
+			     msg->flow);
+	if(body)
+	    html_body=has_html_code(body->str);
+
         if(!body) /* empty message, only non-text content, etc */
             body = g_string_new(_("<No Text Body>\n"));
 
@@ -2268,7 +2421,6 @@
 	}
 	g_string_prepend(body, str);
 	g_free(str);
-
     } else {
 	if (date)
 	    str = g_strdup_printf(_("On %s %s wrote:\n"), date, personStr);
@@ -2280,7 +2432,7 @@
 			     balsa_app.wordwrap ? balsa_app.wraplength : -1,
 			     balsa_app.reply_strip_html, msg->flow);
 	if (body)
-	    g_string_prepend(body, str);
+	    html_body=has_html_code(body->str);
 	else
 	    body = g_string_new(str);
 	g_free(str);
@@ -2291,6 +2443,9 @@
     if (!msg->charset)
 	msg->charset = libbalsa_message_charset(message);
     libbalsa_message_body_unref(message);
+
+    html_wrap(body, html_body);
+    
     return body;
 }
 
@@ -2626,7 +2781,9 @@
         { BALSA_PIXMAP_SAVE,             save_message_cb },
         { BALSA_PIXMAP_SEND,             send_message_toolbar_cb },
         { GNOME_STOCK_PIXMAP_CLOSE,      close_window_cb },
-        { GNOME_STOCK_PIXMAP_SPELLCHECK, spell_check_cb } };
+        { GNOME_STOCK_PIXMAP_SPELLCHECK, spell_check_cb },
+	{ BALSA_PIXMAP_CONVERT_HTML,     html_to_text_cb },
+        { BALSA_PIXMAP_EDIT_HTML,        edit_with_gnome_html } };
     GtkWidget *window;
     GtkWidget *paned = gtk_vpaned_new();
     BalsaSendmsg *msg = NULL;
@@ -2813,6 +2970,8 @@
     else
 	fillBody(msg, message, type);
 
+    show_body(msg, FALSE);
+
     /* set the toolbar so we are consistant with the rest of balsa */
     {
 	GnomeDockItem *item;
@@ -3642,6 +3801,41 @@
     do_reflow(GTK_TEXT(bsmsg->text), -1);
 }
 
+static void 
+html_to_text_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
+{
+    gint dummy;
+    gchar *html, *ascii;
+    
+    hide_body(bsmsg);
+
+    html = gtk_editable_get_chars(GTK_EDITABLE(bsmsg->text), 0, -1);
+    
+    if(has_html_code(html)) {
+	ascii=convert_html(html);
+	g_free(html);
+	
+	gtk_text_freeze(GTK_TEXT(bsmsg->text));
+	gtk_editable_delete_text(GTK_EDITABLE(bsmsg->text), 0, -1);
+	dummy = 0;
+	gtk_editable_insert_text(GTK_EDITABLE(bsmsg->text), ascii,
+				 strlen(ascii), &dummy);
+	gtk_text_thaw(GTK_TEXT(bsmsg->text));
+	g_free(ascii);
+    } else
+	g_free(html);
+
+    show_body(bsmsg, FALSE);
+}
+
+
+static void 
+edit_html_source_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
+{
+    show_body(bsmsg, TRUE);
+}
+
+
 /* To field "changed" signal callback. */
 static void
 check_readiness(BalsaSendmsg * msg)
@@ -3965,10 +4159,16 @@
     set_locale(w, bsmsg, LOC_DUTCH_POS);
 }
 static void
-lang_english_cb(GtkWidget * w, BalsaSendmsg * bsmsg)
+lang_english_us_cb(GtkWidget * w, BalsaSendmsg * bsmsg)
+{
+    set_locale(w, bsmsg, LOC_ENGLISH_US_POS);
+}
+static void
+lang_english_uk_cb(GtkWidget * w, BalsaSendmsg * bsmsg)
 {
-    set_locale(w, bsmsg, LOC_ENGLISH_POS);
+    set_locale(w, bsmsg, LOC_ENGLISH_UK_POS);
 }
+
 static void
 lang_estonian_cb(GtkWidget * w, BalsaSendmsg * bsmsg)
 {
Index: src/sendmsg-window.h
===================================================================
RCS file: /cvs/gnome/balsa/src/sendmsg-window.h,v
retrieving revision 1.49
diff -u -b -r1.49 sendmsg-window.h
--- src/sendmsg-window.h	10 May 2002 15:17:52 -0000	1.49
+++ src/sendmsg-window.h	21 May 2002 07:38:26 -0000
@@ -57,9 +57,13 @@
             reply_to_info;
 	GtkWidget *comments[2], *keywords[2];
 	GtkWidget *attachments[4];
-	GtkWidget *text;
+	GtkWidget *text_box, *text;
 	GtkWidget *spell_checker;
 	GtkWidget *notebook;
+	GtkWidget *html_box;
+#ifdef HAVE_GTKHTML
+	GtkWidget *html;
+#endif
 	GdkFont *font;
 	LibBalsaMessage *orig_message;
 	SendType type;
Index: src/toolbar-factory.c
===================================================================
RCS file: /cvs/gnome/balsa/src/toolbar-factory.c,v
retrieving revision 1.19
diff -u -b -r1.19 toolbar-factory.c
--- src/toolbar-factory.c	7 May 2002 19:41:54 -0000	1.19
+++ src/toolbar-factory.c	21 May 2002 07:38:26 -0000
@@ -107,6 +107,8 @@
     BALSA_PIXMAP_SEND,
     GNOME_STOCK_PIXMAP_CLOSE,
     GNOME_STOCK_PIXMAP_SPELLCHECK,
+    BALSA_PIXMAP_CONVERT_HTML,
+    BALSA_PIXMAP_EDIT_HTML,
     NULL
 };
 
@@ -172,6 +174,10 @@
      N_("Save the current item"), TOOLBAR_BUTTON_TYPE_BUTTON},
     {BALSA_PIXMAP_IDENTITY, N_("Identity"),
      N_("Set identity to use for this message"), TOOLBAR_BUTTON_TYPE_BUTTON},
+    {BALSA_PIXMAP_EDIT_HTML, N_("HTML"),
+     N_("Format as HTML/run HTML editor"), TOOLBAR_BUTTON_TYPE_BUTTON},
+    {BALSA_PIXMAP_CONVERT_HTML, N_("To Plain"),
+     N_("Convert HTML to plain text"), TOOLBAR_BUTTON_TYPE_BUTTON},
     {GNOME_STOCK_PIXMAP_SPELLCHECK, N_("Spelling"),
      N_("Run a spell check"), TOOLBAR_BUTTON_TYPE_BUTTON},
     {GNOME_STOCK_PIXMAP_CLOSE, N_("Cancel"), 
@@ -333,6 +339,8 @@
     BALSA_PIXMAP_SAVE,
     "",
     BALSA_PIXMAP_IDENTITY,
+    "",
+    BALSA_PIXMAP_EDIT_HTML,
     "",
     GNOME_STOCK_PIXMAP_SPELLCHECK,
     "",
--- libbalsa/html.c	Thu Jan  1 01:00:00 1970
+++ libbalsa/html.c	Sun May 19 13:14:52 2002
@@ -0,0 1,393 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* vim:set ts=4 sw=4 ai et: */
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2002 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+#include <stdlib.h>
+
+#include <string.h>
+#include <ctype.h>
+#include "config.h"
+
+/* *** Fixme: We perhaps don't want to use balsa_app directly here? */
+#include "src/balsa-app.h"
+#include "libbalsa.h"
+#include "mailbackend.h"
+#include "misc.h"
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libgnome/libgnome.h>
+
+#ifdef HAVE_GNOME_VFS
+#include <libgnomevfs/gnome-vfs-mime.h>
+#endif
+
+
+static gchar char_ref_char(const gchar *char_ref) 
+{
+    if(char_ref[1]=='#')
+	return (gchar )atoi(char_ref+1);
+    else {
+	int i;
+
+	const struct {
+	    const char *entity;
+	    int character;
+	} entity_table[] = {
+	    { "lt", '<' }, { "gt", '>' }, 
+	    { "amp", '&' }, { "nbsp", ' ' },
+	    { "aelig", 'æ' }, { "AElig", 'Æ' },
+	    { "oslash", 'ø' }, { "Oslash", 'Ø' },
+	    { "aring", 'å' }, { "Aring", 'Å' },
+	    { NULL, '?' }};
+	    
+	for(i=0; entity_table[i].entity; i++)
+	    if(strcmp(entity_table[i].entity, char_ref)==0)
+		return entity_table[i].character;
+    }
+   /* Return first character for the rest. Often makes sense because reference
+      is "<character base><info on accent etc.>" */
+   return char_ref[0];
+}
+
+static gchar *extract_tag(gchar **html, gchar end_sep)
+/* Pre : *html is at start of tag, i.e. points to '<' or similar
+   Post: *html points to position after end of tag. */
+{
+    gchar *end=strchr(*html, end_sep), *tag;
+
+    if(!end)			/* No '>', skip rest of string. */
+	end=*html+strlen(*html);
+    
+    tag=g_strndup(*html+1, end-*html-1);
+    *html=end+1;
+    
+    return tag;
+}
+
+static void insert_newline(gchar **txt, gint quote_level) 
+{
+    gint q;
+    gchar *ptr=*txt;
+
+    *ptr++='\n';
+
+    for(q=0; q<quote_level && balsa_app.quote_str; q++) {
+	strcpy(ptr, balsa_app.quote_str);
+	ptr+=strlen(balsa_app.quote_str);
+    }
+    *txt=ptr;
+}
+
+static gchar *convert_html_internal(const gchar *html)
+/* Remarks: Fall-back used when no real HTML converter was configured, or 
+            call failed. */
+{
+    gchar *txt=calloc(strlen(html)+1, sizeof(gchar));
+    gint quote_level=0;
+
+    if(txt) {
+	gchar *c_html=(gchar *)html, *c_txt=txt;
+	gboolean skip=FALSE;
+
+	while(*c_html) {
+	    if(*c_html=='<') {
+		gchar *tag=extract_tag(&c_html, '>');
+		
+		if(g_strncasecmp(tag, "head", 4)==0) {
+		    skip=TRUE;
+		} else if(g_strncasecmp(tag, "body", 4)==0) {
+		    skip=FALSE;
+		} 
+
+		if(!skip) {
+		    if(g_strcasecmp(tag, "br")==0 || 
+		       g_strcasecmp(tag, "tr")==0) {
+			insert_newline(&c_txt, quote_level);
+		    } else if(g_strcasecmp(tag, "p")==0 ||
+			      (tolower(tag[0])=='h' && isdigit(tag[1]))) {
+			insert_newline(&c_txt, quote_level);
+		    } else if(g_strcasecmp(tag, "li")==0) {
+			insert_newline(&c_txt, quote_level);
+			*c_txt++='\t';
+			*c_txt++='*';
+			*c_txt++=' ';
+		    } else if(g_strcasecmp(tag, "td")==0 || 
+			      g_strcasecmp(tag, "th")==0) {
+		     /* Note: Can't do a lot more than starting a new
+			      paragraph (more or less), since we are not
+			      able to interleave text from different cells. */
+			insert_newline(&c_txt, quote_level);
+			insert_newline(&c_txt, quote_level);
+			*c_txt++='\t';
+		    } else if (g_strncasecmp(tag, "blockquote", 10)==0) {
+			quote_level++;
+		    } else if (g_strncasecmp(tag, "/blockquote", 11)==0) {
+			quote_level--;
+		    } else {
+			gchar *ref=strstr(tag, "href=");
+		    
+			if(!ref) { /* *** Want 'strcasestr' */
+			    ref=strstr(tag, "HREF=");
+			}
+		    
+			if(ref) {
+			    *c_txt++='[';
+			    ref+=5;
+			    
+			    while(*ref && *ref!=' ')
+				*c_txt++=*ref++;
+			    *c_txt++=']';
+			}
+		    }
+		}
+		g_free(tag);
+	    } else if(skip) {
+		c_html++;
+	    } else if(*c_html=='&') {
+		gchar *char_ref=extract_tag(&c_html, ';');
+
+		*c_txt++=char_ref_char(char_ref);
+		g_free(char_ref);
+	    } else if(*c_html=='\n' || *c_html==' ' || *c_html=='\t') {
+		gchar prev=*(c_txt-1);
+
+		c_html++;
+		
+		if(c_txt!=txt && prev!=' ' && prev!='\n' && prev!='\t')
+		    *c_txt++=' ';
+	    } else {
+		*c_txt++=*c_html++;
+	    }
+	}
+    }
+    return txt;
+}
+
+int convert_html_file(const gchar *plain_file, const gchar *html_file)
+{
+    if(balsa_app.html_convert_command && *balsa_app.html_convert_command) {
+	gchar cmd[PATH_MAX + 1];
+	pid_t cmd_pid;
+	
+	snprintf(cmd, sizeof(cmd), balsa_app.html_convert_command, html_file);
+    
+	/* Note: Should probably find gnome_ alternative to fork() + exec(),
+	         but this is at least better than system() (gnome_execute_shell
+	         is not what we want as it will start process in background */
+    
+	if((cmd_pid=fork())==0) {
+	    gchar *shell=gnome_util_user_shell();
+	    
+	    /* Redirect stdout to plain_file */
+	    close(STDOUT_FILENO);
+	    creat(plain_file, S_IRUSR|S_IWUSR);
+
+	    execl(shell, shell, "-c", cmd, NULL);
+	    g_free(shell);	/* In case exec fails */
+	    _exit(1);
+	} else if(cmd_pid>0) {
+	    waitpid(cmd_pid, NULL, 0);
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+gchar *convert_html(const gchar *html)
+{
+    gchar html_file[PATH_MAX + 1], plain_file[PATH_MAX + 1];
+    gchar *txt=NULL;
+    FILE *fp;
+
+    libbalsa_lock_mutt();
+    mutt_mktemp(html_file);
+    mutt_mktemp(plain_file);
+    libbalsa_unlock_mutt();
+
+    fp=safe_fopen(html_file, "w");
+    
+    if(fp) {
+	fprintf(fp, "%s", html);
+	fclose(fp);
+
+	if(convert_html_file(plain_file, html_file)) {
+	    fp=fopen(plain_file, "r");
+	    if(fp) {
+		libbalsa_readfile(fp, &txt);
+		fclose(fp);
+	    }
+	}
+    }
+    unlink(plain_file);
+    unlink(html_file);
+
+    if(txt)
+	return txt;
+    
+    return convert_html_internal(html);
+}
+
+gboolean has_html_code(const gchar *txt)
+{
+#ifdef HAVE_GNOME_VFS
+    const char *mime_type;
+
+    mime_type=gnome_vfs_get_mime_type_for_data(txt, strlen(txt));
+    
+    if(mime_type && strcmp(mime_type, "text/html")==0)
+	return TRUE;
+#else
+    /* *** Look for tags "by hand"? */
+#endif
+    return FALSE;
+}
+
+static void html_close(GString *text, gboolean *pre, gint *quote_level, 
+		       gint pos)
+{
+    while(*quote_level>0) {
+	g_string_insert(text, pos, "</blockquote>\n");
+	(*quote_level)--;
+    }
+				    
+    if(*pre) {
+	g_string_insert(text, pos, "</pre>\n");
+	*pre=FALSE;
+    }
+
+}
+
+GString *html_wrap(GString *text, gboolean force_html)
+{
+    if(force_html || has_html_code(text->str)) {
+	gint i=0, quote_level=0;
+	gboolean pre=FALSE, html=FALSE;
+	
+	while(i<text->len) {
+	    gint old_len=text->len, end_pos;
+	    gchar *tag;
+
+	    if(text->str[i]=='<') {
+		gchar *tag_ptr=&text->str[i];
+		
+		tag=extract_tag(&tag_ptr, '>');
+		end_pos=tag_ptr-text->str;
+	    } else {
+		end_pos=i+1;
+		tag=NULL;
+	    }
+		
+	    if(i==0 && text->str[i]<=' ') {
+		/* Remove leading blanks, newlines etc., including the ones
+		   following removed doctype (see below.) Do we want this in
+		   all cases? */
+		g_string_erase(text, i, 1);
+	    } else if(tag && g_strncasecmp(tag, "!doctype", 8)==0) {
+		g_string_erase(text, i, end_pos-i);
+	    } else if(i==text->len-1 || 
+		      tag && g_strncasecmp(tag, "html", 4)==0) {
+		if(html)
+		    g_string_insert(text, i, "</html>\n"); 
+		
+		html_close(text, &pre, &quote_level, i);
+		
+		i=end_pos+(text->len-old_len);
+		pre=FALSE;
+		html=TRUE;
+	    } else if(i==0) {
+		g_string_prepend(text, "<html>\n<p>&nbsp;</p>\n<pre>\n");
+		i=end_pos+text->len-old_len;
+		html=pre=TRUE;
+	    } else if(tag && g_strncasecmp(tag, "/html", 5)==0) {
+		html_close(text, &pre, &quote_level, i);
+		
+		g_string_insert(text, end_pos+text->len-old_len, 
+				"<html>\n<pre>\n");
+		i=end_pos+text->len-old_len;
+		html=pre=TRUE;
+	    } else {
+		if(tag && g_strncasecmp(tag, "blockquote", 10)==0)
+		    quote_level++;
+		if (tag && g_strncasecmp(tag, "/blockquote", 11)==0)
+		    quote_level--;
+		i=end_pos;
+	    }
+	    if(tag)
+		g_free(tag);
+	}
+	
+	g_string_prepend(text, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//\">\n");
+    }
+    
+    return text;
+}
+
+
+
+gchar *quote_html(const gchar *html)
+{
+    gchar *html_tag=NULL, *body_tag=NULL, *quoted_html;
+    gchar *c_html=(gchar *)html, *body=NULL;
+    GString *s;
+    
+
+    while(*c_html && (!body_tag || !html_tag)) {
+	if(*c_html=='<') {
+	    gchar *tag=extract_tag(&c_html, '>');
+	    if(g_strncasecmp(tag, "body", 4)==0) {
+		body_tag=tag;
+		body=c_html;
+	    } else if(g_strncasecmp(tag, "html", 4)==0) {
+		html_tag=tag;
+		body=c_html;
+	    }
+	    else
+		g_free(tag);
+	} else
+	    c_html++;
+    }
+    s=g_string_new("");
+    if(html_tag) {
+	s=g_string_append(s, "<");
+	s=g_string_append(s, html_tag);
+	s=g_string_append(s, ">");
+	g_free(html_tag);
+    }
+    if(body_tag) {
+	s=g_string_append(s, "<");
+	s=g_string_append(s, body_tag);
+	s=g_string_append(s, ">");
+	g_free(body_tag);
+    }
+    s=g_string_append(s, "<blockquote>\n");
+    
+    
+    if(body)
+	s=g_string_append(s, body);
+    /* *** "</blockquote>" not included. Does it matter??? */
+
+    quoted_html=s->str;
+    g_string_free(s,FALSE);
+    return quoted_html;
+}
+
diff -N -ub balsa_tmp/libbalsa/html.h balsa/libbalsa/html.h
--- libbalsa/html.h	Thu Jan  1 01:00:00 1970
+++ libbalsa/html.h	Tue May 14 14:38:30 2002
@@ -0,0 +1,35 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2001 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+
+#ifndef __LIBBALSA_HTML_H__
+#define __LIBBALSA_HTML_H__
+
+#include "libbalsa.h"
+
+int convert_html_file(const gchar *plain_file, const gchar *html_file);
+gchar *convert_html(const gchar *html);
+gchar *quote_html(const gchar *html);
+gboolean has_html_code(const gchar *txt);
+GString *html_wrap(GString *text, gboolean force_html);
+
+
+#endif				/* __LIBBALSA_HTML_H__ */
Common subdirectories: balsa_tmp/src/pixmaps/CVS and balsa/src/pixmaps/CVS
diff -N -ub balsa_tmp/src/pixmaps/balsa_convert_html.xpm balsa/src/pixmaps/balsa_convert_html.xpm
--- src/pixmaps/balsa_convert_html.xpm	Thu Jan  1 01:00:00 1970
+++ src/pixmaps/balsa_convert_html.xpm	Tue May 14 14:07:23 2002
@@ -0,0 +1,44 @@
+/* XPM */
+static char *balsa_convert_html_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    24    24       11            1",
+/* colors */
+". c None",
+"# c #000000",
+"a c #000080",
+"b c #0000ff",
+"c c #008000",
+"d c #00ff00",
+"e c #808000",
+"f c #808080",
+"g c #c0c0c0",
+"h c #ffffff",
+"i c #ff0000",
+
+/* pixels */
+"...ffffffffffffff.......",
+"...fhhhhhhhhhhhhf#......",
+"...fhhhhhhhhhhhhfg#.....",
+"...fhhhhhhhhhhhhfhg#....",
+"...fhhffiffffffffhhg#...",
+"...fhhhhhihhhhhhfhhhg#..",
+"...fhhhhihihhhhhhfffff..",
+"...fhhfffififffffffhhg..",
+"...fhhhhihififghhhhhhg..",
+"...fhhhhhihihidfhhhhhg..",
+"...fhhffidicidid#ffhhg..",
+"...fhhhhfidididiehhhhg..",
+"...fhhhhibibicibihhhhg..",
+"...fhhffcibididieffhhg..",
+"...fhhhhihiaidiaihhhhg..",
+"...fhhhhhidiai#ihhhhhg..",
+"...fhhffffiai#ihiffhhg..",
+"...fhhhhhhhihihihhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhfffffffffffffhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fgggggggggggggggggg.."
+};
+
diff -N -ub balsa_tmp/src/pixmaps/balsa_edit_html.xpm balsa/src/pixmaps/balsa_edit_html.xpm
--- src/pixmaps/balsa_edit_html.xpm	Thu Jan  1 01:00:00 1970
+++ src/pixmaps/balsa_edit_html.xpm	Tue May 14 13:59:23 2002
@@ -0,0 +1,41 @@
+/* XPM */
+static char *balsa_edit_html_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    24    24       10            1",
+/* colors */
+". c None",
+"# c #000000",
+"a c #000080",
+"b c #0000ff",
+"c c #008000",
+"d c #00ff00",
+"e c #808000",
+"f c #808080",
+"g c #c0c0c0",
+"h c #ffffff",
+/* pixels */
+"...ffffffffffffff.......",
+"...fhhhhhhhhhhhhf#......",
+"...fhhhhhhhhhhhhfg#.....",
+"...fhhhhhhhhhhhhfhg#....",
+"...fhhfffffffffffhhg#...",
+"...fhhhhhhhhhhhhfhhhg#..",
+"...fhhhhhhhhhhhhhfffff..",
+"...fhhfffffffffffffhhg..",
+"...fhhhhhhhfffghhhhhhg..",
+"...fhhhhhhhhhddfhhhhhg..",
+"...fhhffhddccddd#ffhhg..",
+"...fhhhhfddbd#deehhhhg..",
+"...fhhhhfbbbccbbahhhhg..",
+"...fhhffccbbdddbeffhhg..",
+"...fhhhhghcaadea#hhhhg..",
+"...fhhhhhcd#ad#ahhhhhg..",
+"...fhhffffcaa##hfffhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhfffffffffffffhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fhhhhhhhhhhhhhhhhhg..",
+"...fgggggggggggggggggg.."
+};
