sync with sylpheed 0.6.5cvs20
[claws.git] / src / compose.c
index 0b3ec7ad1645993a946a1ae7716f7924783dee7a..76cfb86e37ace162a2255e170ea785864a655597 100644 (file)
 #include "addr_compl.h"
 #include "quote_fmt.h"
 #include "template.h"
+#include "undo.h"
 
 #if USE_GPGME
 #  include "rfc2015.h"
@@ -123,6 +124,10 @@ static GdkColor quote_color = {0, 0, 0, 0xbfff};
 
 static GList *compose_list = NULL;
 
+static void compose_set_undo                   (UndoMain       *undostruct, 
+                                                 gint            undo_state, 
+                                                gint            redo_state, 
+                                                GtkWidget      *changewidget);
 Compose *compose_generic_new                   (PrefsAccount   *account,
                                                 const gchar    *to,
                                                 FolderItem     *item);
@@ -134,6 +139,8 @@ static void compose_toolbar_create          (Compose        *compose,
 static GtkWidget *compose_account_option_menu_create
                                                (Compose        *compose);
 static void compose_set_template_menu          (Compose        *compose);
+static void compose_template_apply             (Compose        *compose,
+                                                Template       *tmpl);
 static void compose_destroy                    (Compose        *compose);
 
 static void compose_entries_set                        (Compose        *compose,
@@ -312,6 +319,8 @@ static gint compose_delete_cb               (GtkWidget      *widget,
 static void compose_destroy_cb         (GtkWidget      *widget,
                                         Compose        *compose);
 
+static void compose_undo_cb            (Compose *compose);
+static void compose_redo_cb            (Compose *compose);
 static void compose_cut_cb             (Compose        *compose);
 static void compose_copy_cb            (Compose        *compose);
 static void compose_paste_cb           (Compose        *compose);
@@ -431,8 +440,8 @@ static GtkItemFactoryEntry compose_entries[] =
        {N_("/_File/_Close"),                   "<alt>W", compose_close_cb, 0, NULL},
 
        {N_("/_Edit"),             NULL, NULL, 0, "<Branch>"},
-       {N_("/_Edit/_Undo"),       "<control>Z", NULL, 0, NULL},
-       {N_("/_Edit/_Redo"),       "<control>Y", NULL, 0, NULL},
+       {N_("/_Edit/_Undo"),       "<control>Z", compose_undo_cb, 0, NULL},
+       {N_("/_Edit/_Redo"),       "<control>Y", compose_redo_cb, 0, NULL},
        {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
        {N_("/_Edit/Cu_t"),        "<control>X", compose_cut_cb,    0, NULL},
        {N_("/_Edit/_Copy"),       "<control>C", compose_copy_cb,   0, NULL},
@@ -740,12 +749,12 @@ static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
 
        if (prefs_common.auto_sig)
                compose_insert_sig(compose);
-       gtk_editable_set_position(GTK_EDITABLE(text), 0);
-       gtk_stext_set_point(text, 0);
 
-       if (quote && prefs_common.linewrap_quote) {
+       if (quote && prefs_common.linewrap_quote)
                compose_wrap_line_all(compose);
-       }
+
+       gtk_editable_set_position(GTK_EDITABLE(text), 0);
+       gtk_stext_set_point(text, 0);
 
        gtk_stext_thaw(text);
        gtk_widget_grab_focus(compose->text);
@@ -1052,6 +1061,10 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
 
        if (prefs_common.auto_sig)
                compose_insert_sig(compose);
+
+       if (prefs_common.linewrap_quote)
+               compose_wrap_line_all(compose);
+
        gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
        gtk_stext_set_point(GTK_STEXT(compose->text), 0);
 
@@ -1118,6 +1131,10 @@ Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
 
        if (prefs_common.auto_sig)
                compose_insert_sig(compose);
+
+       if (prefs_common.linewrap_quote)
+               compose_wrap_line_all(compose);
+
        gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
        gtk_stext_set_point(GTK_STEXT(compose->text), 0);
 
@@ -1305,9 +1322,9 @@ static void compose_entries_set(Compose *compose, const gchar *mailto)
        if (bcc)
                compose_entry_append(compose, bcc, COMPOSE_BCC);
        if (body) {
-               gtk_text_insert(GTK_TEXT(compose->text),
+               gtk_stext_insert(GTK_STEXT(compose->text),
                                NULL, NULL, NULL, body, -1);
-               gtk_text_insert(GTK_TEXT(compose->text),
+               gtk_stext_insert(GTK_STEXT(compose->text),
                                NULL, NULL, NULL, "\n", 1);
        }
 }
@@ -1495,15 +1512,18 @@ static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
                        Xstrdup_a(quote_str, buf, return NULL)
        }
 
-       quote_fmt_init(msginfo, quote_str);
-       quote_fmt_scan_string(fmt);
-       quote_fmt_parse();
+       if (fmt && *fmt != '\0') {
+               quote_fmt_init(msginfo, quote_str);
+               quote_fmt_scan_string(fmt);
+               quote_fmt_parse();
 
-       buf = quote_fmt_get_buffer();
-       if (buf == NULL) {
-               alertpanel_error(_("Message reply/forward format error."));
-               return NULL;
-       }
+               buf = quote_fmt_get_buffer();
+               if (buf == NULL) {
+                       alertpanel_error(_("Message reply/forward format error."));
+                       return NULL;
+               }
+       } else
+               buf = "";
 
        gtk_stext_freeze(text);
        gtk_stext_set_point(text, 0);
@@ -1895,6 +1915,16 @@ static void compose_attach_append(Compose *compose, const gchar *file,
        compose_attach_info(compose, ainfo, cnttype);
 }
 
+#define GET_CHAR(pos, buf, len)                                                     \
+{                                                                           \
+       if (text->use_wchar)                                                 \
+               len = wctomb(buf, (wchar_t)GTK_STEXT_INDEX(text, (pos)));     \
+       else {                                                               \
+               buf[0] = GTK_STEXT_INDEX(text, (pos));                       \
+               len = 1;                                                     \
+       }                                                                    \
+}
+
 static void compose_wrap_line(Compose *compose)
 {
        GtkSText *text = GTK_STEXT(compose->text);
@@ -1907,13 +1937,6 @@ static void compose_wrap_line(Compose *compose)
        gint line_pos, cur_pos;
        gint line_len, cur_len;
 
-#define GET_STEXT(pos)                                                        \
-       if (text->use_wchar)                                                 \
-               ch_len = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, (pos))); \
-       else {                                                               \
-               cbuf[0] = GTK_STEXT_INDEX(text, (pos));                       \
-               ch_len = 1;                                                  \
-       }
 
        gtk_stext_freeze(text);
 
@@ -1921,11 +1944,11 @@ static void compose_wrap_line(Compose *compose)
 
        /* check to see if the point is on the paragraph mark (empty line). */
        cur_pos = gtk_stext_get_point(text);
-       GET_STEXT(cur_pos);
+       GET_CHAR(cur_pos, cbuf, ch_len);
        if ((ch_len == 1 && *cbuf == '\n') || cur_pos == text_len) {
                if (cur_pos == 0)
                        goto compose_end; /* on the paragraph mark */
-               GET_STEXT(cur_pos - 1);
+               GET_CHAR(cur_pos - 1, cbuf, ch_len);
                if (ch_len == 1 && *cbuf == '\n')
                        goto compose_end; /* on the paragraph mark */
        }
@@ -1933,7 +1956,7 @@ static void compose_wrap_line(Compose *compose)
        /* find paragraph start. */
        line_end = quoted = 0;
        for (p_start = cur_pos; p_start >= 0; --p_start) {
-               GET_STEXT(p_start);
+               GET_CHAR(p_start, cbuf, ch_len);
                if (ch_len == 1 && *cbuf == '\n') {
                        if (quoted)
                                goto compose_end; /* quoted part */
@@ -1957,7 +1980,7 @@ static void compose_wrap_line(Compose *compose)
        /* find paragraph end. */
        line_end = 0;
        for (p_end = cur_pos; p_end < text_len; p_end++) {
-               GET_STEXT(p_end);
+               GET_CHAR(p_end, cbuf, ch_len);
                if (ch_len == 1 && *cbuf == '\n') {
                        if (line_end) {
                                p_end -= 1;
@@ -1984,7 +2007,7 @@ static void compose_wrap_line(Compose *compose)
        for (cur_pos = p_start; cur_pos < p_end; cur_pos++) {
                guint space = 0;
 
-               GET_STEXT(cur_pos);
+               GET_CHAR(cur_pos, cbuf, ch_len);
 
                if (ch_len < 0) {
                        cbuf[0] = '\0';
@@ -1998,7 +2021,7 @@ static void compose_wrap_line(Compose *compose)
                        guint replace = 0;
                        if (last_ch_len == 1 && !isspace(last_ch)) {
                                if (cur_pos + 1 < p_end) {
-                                       GET_STEXT(cur_pos + 1);
+                                       GET_CHAR(cur_pos + 1, cbuf, ch_len);
                                        if (ch_len == 1 && !isspace(*cbuf))
                                                replace = 1;
                                }
@@ -2029,7 +2052,7 @@ static void compose_wrap_line(Compose *compose)
                    line_len > 0) {
                        gint tlen = ch_len;
 
-                       GET_STEXT(line_pos - 1);
+                       GET_CHAR(line_pos - 1, cbuf, ch_len);
                        if (ch_len == 1 && isspace(*cbuf)) {
                                gtk_stext_set_point(text, line_pos);
                                gtk_stext_backward_delete(text, 1);
@@ -2060,25 +2083,17 @@ static void compose_wrap_line(Compose *compose)
 
 compose_end:
        gtk_stext_thaw(text);
-
-#undef GET_STEXT
 }
 
 /* return indent length */
-static guint get_indent_length(GtkSText *text, guint start_pos,
-                              guint text_len) {
+static guint get_indent_length(GtkSText *text, guint start_pos, guint text_len)
+{
        gint indent_len = 0;
        gint i, ch_len;
-       gchar cbuf[MB_CUR_MAX];
+       gchar cbuf[MB_LEN_MAX];
 
        for (i = start_pos; i < text_len; i++) {
-               if (text->use_wchar)
-                       ch_len = wctomb
-                               (cbuf, (wchar_t)GTK_STEXT_INDEX(text, i));
-               else {
-                       cbuf[0] = GTK_STEXT_INDEX(text, i);
-                       ch_len = 1;
-               }
+               GET_CHAR(i, cbuf, ch_len);
                if (ch_len > 1)
                        break;
                /* allow space, tab, > or | */
@@ -2091,11 +2106,11 @@ static guint get_indent_length(GtkSText *text, guint start_pos,
        return indent_len;
 }
 
-/* insert quotation string when line was wrapped, we know these
-   are single byte characters */
+/* insert quotation string when line was wrapped */
 static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
                       guint prev_line_pos, guint text_len,
-                      gchar *quote_fmt) {
+                      gchar *quote_fmt)
+{
        guint i, ins_len;
        gchar ch;
 
@@ -2105,8 +2120,7 @@ static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
                        gtk_stext_insert(text, NULL, NULL, NULL, &ch, 1);
                }
                ins_len = indent_len;
-       }
-       else {
+       } else {
                gtk_stext_insert(text, NULL, NULL, NULL, quote_fmt, quote_len);
                ins_len = quote_len;
        }
@@ -2119,18 +2133,17 @@ static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
 /* Darko: used when I debug wrapping */
 void dump_text(GtkSText *text, int pos, int tlen, int breakoncr)
 {
-    int i;
-    char ch;
+       gint i;
+       gchar ch;
 
-    printf("%d [", pos);
-    for (i = pos; i < tlen; i++)
-    {
-        ch = GTK_STEXT_INDEX(text, i);
-        if (breakoncr && ch == '\n')
-            break;
-        printf("%c", ch);
-    }
-    printf("]\n");
+       printf("%d [", pos);
+       for (i = pos; i < tlen; i++) {
+               ch = GTK_STEXT_INDEX(text, i);
+               if (breakoncr && ch == '\n')
+                       break;
+               printf("%c", ch);
+       }
+       printf("]\n");
 }
 #endif
 
@@ -2141,7 +2154,7 @@ static void compose_wrap_line_all(Compose *compose)
        guint line_pos = 0, cur_pos = 0, p_pos = 0;
        gint line_len = 0, cur_len = 0;
        gint ch_len;
-       gint is_new_line = 1, do_delete = 0;
+       gboolean is_new_line = TRUE, do_delete = FALSE;
        guint qlen = 0, i_len = 0;
        guint linewrap_quote = prefs_common.linewrap_quote;
        guint linewrap_len = prefs_common.linewrap_len;
@@ -2151,18 +2164,18 @@ static void compose_wrap_line_all(Compose *compose)
        gtk_stext_freeze(text);
 
        /* make text buffer contiguous */
-       gtk_stext_compact_buffer(text);
+       /* gtk_stext_compact_buffer(text); */
 
        tlen = gtk_stext_get_length(text);
 
        for (; cur_pos < tlen; cur_pos++) {
                /* mark position of new line - needed for quotation wrap */
                if (linewrap_quote && is_new_line) {
-                       qlen = gtkstext_str_strcmp(text, cur_pos, tlen, qfmt);
-                       is_new_line = 0;
-                       if (qlen) {
+                       qlen = gtkut_text_str_compare
+                               (text, cur_pos, tlen, qfmt);
+                       is_new_line = FALSE;
+                       if (qlen)
                                i_len = get_indent_length(text, cur_pos, tlen);
-                       }
                        else
                                i_len = 0;
                        p_pos = cur_pos;
@@ -2172,14 +2185,7 @@ static void compose_wrap_line_all(Compose *compose)
 #endif
                }
 
-               /* get character(s) at current position */
-               if (text->use_wchar)
-                       ch_len = wctomb
-                               (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
-               else {
-                       cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
-                       ch_len = 1;
-               }
+               GET_CHAR(cur_pos, cbuf, ch_len);
 
                /* fix line length for tabs */
                if (ch_len == 1 && *cbuf == '\t') {
@@ -2206,25 +2212,26 @@ static void compose_wrap_line_all(Compose *compose)
                        gchar cb[MB_CUR_MAX];
 
 #ifdef WRAP_DEBUG
-                       printf("found CR at %d\n", cur_pos);
+                       printf("found CR at %d next line is ", cur_pos);
+                       dump_text(text, cur_pos + 1, tlen, 1);
 #endif
                        /* if it's just quotation + newline skip it */
                        if (i_len && (cur_pos + 1 < tlen)) {
                                /* check if text at new line matches indent */
-                               ilen =  gtkstext_strncmp(text, cur_pos + 1,
-                                                        p_pos, i_len, tlen);
+                               ilen =  gtkut_text_str_compare_n
+                                       (text, cur_pos + 1, p_pos, i_len, tlen);
                                if (cur_pos + ilen < tlen) {
-                                       if (text->use_wchar)
-                                               clen = wctomb(cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos + ilen + 1));
-                                       else {
-                                               cb[0] = GTK_STEXT_INDEX(text,
-                                                           cur_pos + ilen + 1);
-                                               clen = 1;
-                                       }
+                                       GET_CHAR(cur_pos + ilen + 1, cb, clen);
                                        /* no need to join the lines */
-                                       if ((clen == 1) && (cb[0] == '\n'))
-                                               do_delete = 0;
+                                       if (clen == 1 && cb[0] == '\n')
+                                               do_delete = FALSE;
                                }
+                       /* if it's just newline skip it */
+                       } else if (do_delete && (cur_pos + 1 < tlen)) {
+                               GET_CHAR(cur_pos + 1, cb, clen);
+                               /* no need to join the lines */
+                               if (clen == 1 && cb[0] == '\n')
+                                       do_delete = FALSE;
                        }
 
 #ifdef WRAP_DEBUG
@@ -2232,7 +2239,7 @@ static void compose_wrap_line_all(Compose *compose)
                                qlen, line_len, linewrap_len, do_delete);
 #endif
                        /* should we delete to perform smart wrapping */
-                       if (qlen && line_len < linewrap_len && do_delete) {
+                       if (line_len < linewrap_len && do_delete) {
                                /* get rid of newline */
                                gtk_stext_set_point(text, cur_pos);
                                gtk_stext_forward_delete(text, 1);
@@ -2241,38 +2248,31 @@ static void compose_wrap_line_all(Compose *compose)
                                /* if text starts with quote fmt or with
                                   indent string, delete them */
                                if (i_len) {
-                                       ilen =  gtkstext_strncmp(text, cur_pos,
-                                                                p_pos, i_len,
-                                                                tlen);
+                                       ilen =  gtkut_text_str_compare_n
+                                               (text, cur_pos, p_pos, i_len,
+                                                tlen);
                                        if (ilen) {
                                                gtk_stext_forward_delete(text,
                                                                         ilen);
                                                tlen -= ilen;
                                        }
-                               }
-                               else if (qlen) {
-                                       if (gtkstext_str_strcmp(text, cur_pos,
-                                                               tlen, qfmt)) {
-                                               gtk_stext_forward_delete(text,
-                                                                qlen);
+                               } else if (qlen) {
+                                       if (gtkut_text_str_compare
+                                           (text, cur_pos, tlen, qfmt)) {
+                                               gtk_stext_forward_delete
+                                                       (text, qlen);
                                                tlen -= qlen;
                                        }
                                }
 
-                               if (text->use_wchar)
-                                       clen = wctomb
-                                               (cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
-                               else {
-                                       cb[0] = GTK_STEXT_INDEX(text, cur_pos);
-                                       clen = 1;
-                               }
+                               GET_CHAR(cur_pos, cb, clen);
 
                                /* insert space if it's alphanumeric */
                                if ((cur_pos != line_pos) &&
                                    ((clen > 1) || isalnum(cb[0]))) {
                                        gtk_stext_insert(text, NULL, NULL,
-                                                        NULL, " ", 1);
-                                       gtk_stext_compact_buffer(text);
+                                                       NULL, " ", 1);
+                                       /* gtk_text_compact_buffer(text); */
                                        tlen++;
                                }
 
@@ -2281,8 +2281,8 @@ static void compose_wrap_line_all(Compose *compose)
                                line_pos = cur_pos;
                                line_len = cur_len = 0;
                                qlen = 0;
-                               do_delete = 0;
-                               is_new_line = 1;
+                               do_delete = FALSE;
+                               is_new_line = TRUE;
 #ifdef WRAP_DEBUG
                                printf("after delete l_pos=");
                                dump_text(text, line_pos, tlen, 1);
@@ -2294,8 +2294,8 @@ static void compose_wrap_line_all(Compose *compose)
                        line_pos = cur_pos + 1;
                        line_len = cur_len = 0;
                        qlen = 0;
-                       do_delete = 0;
-                       is_new_line = 1;
+                       do_delete = FALSE;
+                       is_new_line = TRUE;
                        continue;
                }
 
@@ -2321,22 +2321,20 @@ static void compose_wrap_line_all(Compose *compose)
 #endif
                        /* force wrapping if it is one long word but not URL */
                        if (p_pos + i_len == line_pos)
-                               if (!is_url_string(text, line_pos, tlen))
+                               if (!gtkut_text_is_uri_string
+                                   (text, line_pos, tlen))
                                        line_pos = cur_pos - 1;
 #ifdef WRAP_DEBUG
                        printf("new line_pos=%d\n", line_pos);
 #endif
 
+                       GET_CHAR(line_pos - 1, cbuf, clen);
+
                        /* if next character is space delete it */
-                       if (text->use_wchar)
-                               clen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
-                       else {
-                               cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
-                               clen = 1;
-                       }
                         if (clen == 1 && isspace(*cbuf)) {
                                if (p_pos + i_len != line_pos ||
-                                   !is_url_string(text, line_pos, tlen)) {
+                                   !gtkut_text_is_uri_string
+                                       (text, line_pos, tlen)) {
                                        gtk_stext_set_point(text, line_pos);
                                        gtk_stext_backward_delete(text, 1);
                                        tlen--;
@@ -2349,7 +2347,7 @@ static void compose_wrap_line_all(Compose *compose)
 
                        /* if it is URL at beginning of line don't wrap */
                        if (p_pos + i_len == line_pos &&
-                            is_url_string(text, line_pos, tlen)) {
+                           gtkut_text_is_uri_string(text, line_pos, tlen)) {
 #ifdef WRAP_DEBUG
                                printf("found URL at ");
                                dump_text(text, line_pos, tlen, 1);
@@ -2360,43 +2358,37 @@ static void compose_wrap_line_all(Compose *compose)
                        /* insert CR */
                        gtk_stext_set_point(text, line_pos);
                        gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
-                       gtk_stext_compact_buffer(text);
+                       /* gtk_stext_compact_buffer(text); */
                        tlen++;
-                       cur_pos++;
                        line_pos++;
-                       cur_len = cur_len - line_len + ch_len;
+                       /* for loop will increase it */
+                       cur_pos = line_pos - 1;
+                       /* start over with current line */
+                       is_new_line = TRUE;
                        line_len = 0;
+                       cur_len = 0;
+                       do_delete = TRUE;
 #ifdef WRAP_DEBUG
                        printf("after CR insert ");
                        dump_text(text, line_pos, tlen, 1);
+                       dump_text(text, cur_pos, tlen, 1);
 #endif
 
                        /* should we insert quotation ? */
                        if (linewrap_quote && qlen) {
                                /* only if line is not already quoted  */
-                               if (!gtkstext_str_strcmp(text, line_pos,
-                                                        tlen, qfmt)) {
+                               if (!gtkut_text_str_compare
+                                       (text, line_pos, tlen, qfmt)) {
                                        guint ins_len;
 
                                        if (line_pos - p_pos > i_len) {
-                                               ins_len = ins_quote(text,
-                                                                   qlen,
-                                                                   i_len,
-                                                                   p_pos,
-                                                                   tlen,
-                                                                   qfmt);
-
-                                               gtk_stext_compact_buffer(text);
+                                               ins_len = ins_quote
+                                                       (text, qlen, i_len,
+                                                        p_pos, tlen, qfmt);
+
+                                               /* gtk_stext_compact_buffer(text); */
                                                tlen += ins_len;
                                        }
-
-                                       /* for loop will increase it */
-                                       cur_pos = line_pos - 1;
-                                       cur_len = 0;
-                                       line_len = 0;
-                                       /* start over with current line */
-                                       is_new_line = 1;
-                                       do_delete = 1;
 #ifdef WRAP_DEBUG
                                        printf("after quote insert ");
                                        dump_text(text, line_pos, tlen, 1);
@@ -2416,6 +2408,7 @@ static void compose_wrap_line_all(Compose *compose)
 
        gtk_stext_thaw(text);
 }
+#undef GET_CHAR
 
 static void compose_set_title(Compose *compose)
 {
@@ -2691,9 +2684,9 @@ static gint compose_bounce_write_headers_from_headerlist(Compose *compose,
                        }
                }
        }
-       /* if(!first_address) {
+       /* if(!first_address) { */
        fprintf(fp, "\n");
-       } */
+       /* } */
 
        return(0);
 }
@@ -2743,7 +2736,7 @@ static gint compose_bounce_write_to_file(Compose *compose, const gchar *file)
                return -1;
        }
 
-       if ((fdest = fopen(file, "a+")) == NULL) {
+       if ((fdest = fopen(file, "w")) == NULL) {
                FILE_OP_ERROR(file, "fopen");
                fclose(fp);
                return -1;
@@ -2762,8 +2755,26 @@ static gint compose_bounce_write_to_file(Compose *compose, const gchar *file)
                    || (g_strncasecmp(buf, "Delivered-To:",
                                      strlen("Delivered-To:")) == 0))
                        continue;
+
                if (fputs(buf, fdest) == -1)
                        goto error;
+
+               if (g_strncasecmp(buf, "From:", strlen("From:")) == 0) {
+                       fputs(" (by way of ", fdest);
+                       if (compose->account->name
+                           && *compose->account->name) {
+                               compose_convert_header
+                                       (buf, sizeof(buf),
+                                        compose->account->name,
+                                        strlen("From: "));
+                               fprintf(fdest, "%s <%s>",
+                                       buf, compose->account->address);
+                       } else
+                               fprintf(fdest, "%s",
+                                       compose->account->address);
+                       fputs(")", fdest);
+               }
+
                if (fputs("\n", fdest) == -1)
                        goto error;
        }
@@ -2796,7 +2807,7 @@ static gint compose_write_to_file(Compose *compose, const gchar *file,
        const gchar *out_codeset;
        EncodingType encoding;
 
-       if ((fp = fopen(file, "a+")) == NULL) {
+       if ((fp = fopen(file, "w")) == NULL) {
                FILE_OP_ERROR(file, "fopen");
                return -1;
        }
@@ -3020,7 +3031,7 @@ static gint compose_remove_reedit_target(Compose *compose)
 static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
 {
        FolderItem *queue;
-       gchar *tmpfilename, *queue_path;
+       gchar *tmp, *tmp2, *queue_path;
        FILE *fp, *src_fp;
        GSList *cur;
        gchar buf[BUFFSIZE];
@@ -3046,22 +3057,6 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                 return -1;
         }
 
-        if (prefs_common.linewrap_at_send)
-               compose_wrap_line_all(compose);
-                       
-       /* write to temporary file */
-       tmpfilename = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
-                                     G_DIR_SEPARATOR, (gint)compose);
-       if ((fp = fopen(tmpfilename, "w")) == NULL) {
-               FILE_OP_ERROR(tmpfilename, "fopen");
-               g_free(tmpfilename);
-               return -1;
-       }
-       if (change_file_mode_rw(fp, tmpfilename) < 0) {
-               FILE_OP_ERROR(tmpfilename, "chmod");
-               g_warning(_("can't change file mode\n"));
-       }
-
        if(compose->to_list) {
                if (compose->account->protocol != A_NNTP)
                        mailac = compose->account;
@@ -3070,7 +3065,6 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                else if (cur_account && cur_account->protocol != A_NNTP)
                        mailac = cur_account;
                else if (!(mailac = compose_current_mail_account())) {
-                       unlink(tmpfilename);
                        lock = FALSE;
                        alertpanel_error(_("No account for sending mails available!"));
                        return -1;
@@ -3081,13 +3075,56 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                 if (compose->account->protocol == A_NNTP)
                         newsac = compose->account;
                 else if(!(newsac = compose->orig_account) || (newsac->protocol != A_NNTP)) {
-                       unlink(tmpfilename);
                        lock = FALSE;
                        alertpanel_error(_("No account for posting news available!"));
                        return -1;
                }                       
        }
 
+        if (prefs_common.linewrap_at_send)
+               compose_wrap_line_all(compose);
+                       
+       /* write to temporary file */
+       tmp2 = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
+                                     G_DIR_SEPARATOR, (gint)compose);
+
+       if (compose->bounce_filename != NULL) {
+               if (compose_bounce_write_to_file(compose, tmp2) < 0) {
+                       unlink(tmp2);
+                       lock = FALSE;
+                       return -1;
+               }
+       }
+       else {
+               if (compose_write_to_file(compose, tmp2, FALSE) < 0) {
+                       unlink(tmp2);
+                       lock = FALSE;
+                       return -1;
+               }
+       }
+
+       /* add queue header */
+       tmp = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
+                                     G_DIR_SEPARATOR, (gint)compose);
+       if ((fp = fopen(tmp, "w")) == NULL) {
+               FILE_OP_ERROR(tmp, "fopen");
+               g_free(tmp);
+               return -1;
+       }
+       if ((src_fp = fopen(tmp2, "r")) == NULL) {
+               FILE_OP_ERROR(tmp2, "fopen");
+               fclose(fp);
+               unlink(tmp);
+               g_free(tmp);
+               unlink(tmp2);
+               g_free(tmp2);
+               return -1;
+       }
+       if (change_file_mode_rw(fp, tmp) < 0) {
+               FILE_OP_ERROR(tmp, "chmod");
+               g_warning(_("can't change file mode\n"));
+       }
+
        /* queueing variables */
        fprintf(fp, "AF:\n");
        fprintf(fp, "NF:0\n");
@@ -3136,21 +3173,27 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                fprintf(fp, "NAID:%d\n", newsac->account_id);
        }
        fprintf(fp, "\n");
-       fclose(fp);
 
-       if (compose->bounce_filename != NULL) {
-               if (compose_bounce_write_to_file(compose, tmpfilename) < 0) {
-                       unlink(tmpfilename);
-                       lock = FALSE;
+       while (fgets(buf, sizeof(buf), src_fp) != NULL) {
+               if (fputs(buf, fp) == EOF) {
+                       FILE_OP_ERROR(tmp, "fputs");
+                       fclose(fp);
+                       fclose(src_fp);
+                       unlink(tmp);
+                       g_free(tmp);
+                       unlink(tmp2);
+                       g_free(tmp2);
                        return -1;
                }
        }
-       else {
-               if (compose_write_to_file(compose, tmpfilename, FALSE) < 0) {
-                       unlink(tmpfilename);
-                       lock = FALSE;
-                       return -1;
-               }
+       fclose(src_fp);
+       if (fclose(fp) == EOF) {
+               FILE_OP_ERROR(tmp, "fclose");
+               unlink(tmp);
+               g_free(tmp);
+               unlink(tmp2);
+               g_free(tmp2);
+               return -1;
        }
                                                
        /* queue message */
@@ -3160,14 +3203,17 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
        queue_path = folder_item_get_path(queue);
        if (!is_dir_exist(queue_path))
                make_dir_hier(queue_path);
-       if ((num = folder_item_add_msg(queue, tmpfilename, TRUE)) < 0) {
+       if ((num = folder_item_add_msg(queue, tmp, TRUE)) < 0) {
                g_warning(_("can't queue the message\n"));
-               unlink(tmpfilename);
-               g_free(tmpfilename);
+               unlink(tmp);
+               g_free(tmp);
                g_free(queue_path);
                return -1;
        }
-       g_free(tmpfilename);
+       unlink(tmp);
+       g_free(tmp);
+       unlink(tmp2);
+       g_free(tmp2);
 
        if (compose->mode == COMPOSE_REEDIT) {
                compose_remove_reedit_target(compose);
@@ -3528,21 +3574,23 @@ static gint compose_write_headers(Compose *compose, FILE *fp,
                     cur = cur->next) {
                        CustomHeader *chdr = (CustomHeader *)cur->data;
 
-                       if (strcasecmp(chdr->name, "Date")                      != 0 &&
-                           strcasecmp(chdr->name, "From")                      != 0 &&
-                           strcasecmp(chdr->name, "To")                        != 0 &&
-                           strcasecmp(chdr->name, "Sender")                    != 0 &&
-                           strcasecmp(chdr->name, "Message-Id")                != 0 &&
-                           strcasecmp(chdr->name, "In-Reply-To")               != 0 &&
-                           strcasecmp(chdr->name, "References")                != 0 &&
-                           strcasecmp(chdr->name, "Mime-Version")              != 0 &&
-                           strcasecmp(chdr->name, "Content-Type")              != 0 &&
-                           strcasecmp(chdr->name, "Content-Transfer-Encoding") != 0)
+                       if (strcasecmp(chdr->name, "Date")         != 0 &&
+                           strcasecmp(chdr->name, "From")         != 0 &&
+                           strcasecmp(chdr->name, "To")           != 0 &&
+                        /* strcasecmp(chdr->name, "Sender")       != 0 && */
+                           strcasecmp(chdr->name, "Message-Id")   != 0 &&
+                           strcasecmp(chdr->name, "In-Reply-To")  != 0 &&
+                           strcasecmp(chdr->name, "References")   != 0 &&
+                           strcasecmp(chdr->name, "Mime-Version") != 0 &&
+                           strcasecmp(chdr->name, "Content-Type") != 0 &&
+                           strcasecmp(chdr->name, "Content-Transfer-Encoding")
+                           != 0) {
                                compose_convert_header
                                        (buf, sizeof(buf),
                                         chdr->value ? chdr->value : "",
                                         strlen(chdr->name) + 2);
-                       fprintf(fp, "%s: %s\n", chdr->name, buf);
+                               fprintf(fp, "%s: %s\n", chdr->name, buf);
+                       }
                }
        }
 
@@ -3691,10 +3739,10 @@ static void compose_create_header_entry(Compose *compose) {
        } else {
                switch(compose->account->protocol) {
                        case A_NNTP:
-                               header = _("Newsgroups:");
+                               header = prefs_common.trans_hdr ? _("Newsgroups:") : "Newsgroups:";
                                break;
                        default:
-                               header = _("To:");
+                               header = prefs_common.trans_hdr ? _("To:") : "To:";
                                break;
                }                                                                   
        }
@@ -3776,6 +3824,8 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
        GtkWidget *table;
        GtkWidget *hbox;
 
+       UndoMain *undostruct;
+
        gchar *titles[] = {_("MIME type"), _("Size"), _("Name")};
        guint n_menu_entries;
        GtkStyle  *style, *new_style;
@@ -4243,6 +4293,11 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
         compose->gtkpspell      = gtkpspell;
 #endif
 
+       undostruct = undo_init(text);
+
+       compose->undostruct = undostruct;
+       undo_set_undo_change_funct(undostruct, &compose_set_undo, GTK_WIDGET(compose->menubar));
+
 #if 0 /* NEW COMPOSE GUI */
        if (account->protocol != A_NNTP) {
                menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
@@ -4557,29 +4612,35 @@ void compose_reflect_prefs_all(void)
        }
 }
 
-static void compose_template_apply(Compose *compose, const gchar *tmpl_str)
+static void compose_template_apply(Compose *compose, Template *tmpl)
 {
        gchar *qmark;
        gchar *parsed_str;
 
-       if (!tmpl_str) return;
+       if (!tmpl || !tmpl->value) return;
 
        gtk_stext_freeze(GTK_STEXT(compose->text));
+        
+       if (tmpl->subject)
+               gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
+                                  tmpl->subject);
+       if (tmpl->to)
+               compose_entry_append(compose, tmpl->to, COMPOSE_TO);
 
        if (compose->replyinfo == NULL) {
                MsgInfo dummyinfo;
 
                memset(&dummyinfo, 0, sizeof(MsgInfo));
-               parsed_str = compose_quote_fmt(compose, &dummyinfo, tmpl_str,
-                                              NULL);
+               parsed_str = compose_quote_fmt(compose, &dummyinfo,
+                                              tmpl->value, NULL);
        } else {
                if (prefs_common.quotemark && *prefs_common.quotemark)
                        qmark = prefs_common.quotemark;
                else
                        qmark = "> ";
 
-               parsed_str = compose_quote_fmt(compose, compose->replyinfo, tmpl_str,
-                                              qmark);
+               parsed_str = compose_quote_fmt(compose, compose->replyinfo,
+                                              tmpl->value, qmark);
        }
 
        if (parsed_str && prefs_common.auto_sig)
@@ -5566,7 +5627,7 @@ static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
        tmpl = gtk_object_get_data(GTK_OBJECT(widget), "template");
        g_return_if_fail(tmpl != NULL);
 
-       compose_template_apply(compose, tmpl->value);
+       compose_template_apply(compose, tmpl);
 }
 
 static void compose_ext_editor_cb(gpointer data, guint action,
@@ -5582,6 +5643,16 @@ static void compose_destroy_cb(GtkWidget *widget, Compose *compose)
        compose_destroy(compose);
 }
 
+static void compose_undo_cb(Compose *compose) 
+{
+       undo_undo(compose->undostruct);
+}
+
+static void compose_redo_cb(Compose *compose) 
+{
+       undo_redo(compose->undostruct);
+}
+
 static void compose_cut_cb(Compose *compose)
 {
        if (compose->focused_editable &&
@@ -5975,3 +6046,70 @@ void compose_headerentry_changed_cb(GtkWidget *entry,
                         headerentry);
        }
 }
+
+/**
+ * undo_set_undo:
+ *
+ * Change the sensivity of the menuentries undo and redo
+ **/
+static void compose_set_undo(UndoMain *undostruct, gint undo_state, 
+                            gint redo_state, GtkWidget *changewidget) 
+{
+       GtkItemFactory *ifactory;
+        debug_print ("Set_undo.  UNDO:%i  REDO:%i\n",
+                 undo_state,
+                 redo_state);
+
+       g_return_if_fail (changewidget != NULL);
+
+       ifactory = gtk_item_factory_from_widget(changewidget);
+
+       /* Set undo */
+        switch (undo_state) {
+        case UNDO_STATE_TRUE:
+                if (!undostruct->undo_state) {
+                       debug_print ("Set_undo - Testpoint\n");
+                        undostruct->undo_state = TRUE;
+                       menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
+                }
+                break;
+        case UNDO_STATE_FALSE:
+                if (undostruct->undo_state) {
+                        undostruct->undo_state = FALSE;
+                       menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
+                }
+                break;
+        case UNDO_STATE_UNCHANGED:
+                break;
+        case UNDO_STATE_REFRESH:
+               menu_set_sensitive(ifactory, "/Edit/Undo", undostruct->undo_state);
+                break;
+        default:
+                g_warning ("Undo state not recognized");
+               break;
+        }
+
+        /* Set redo*/
+        switch (redo_state) {
+        case UNDO_STATE_TRUE:
+                if (!undostruct->redo_state) {
+                        undostruct->redo_state = TRUE;
+                       menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
+                }
+                break;
+        case UNDO_STATE_FALSE:
+                if (undostruct->redo_state) {
+                        undostruct->redo_state = FALSE;
+                       menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
+                }
+                break;
+        case UNDO_STATE_UNCHANGED:
+                break;
+        case UNDO_STATE_REFRESH:
+               menu_set_sensitive(ifactory, "/Edit/Redo", undostruct->redo_state);
+                break;
+        default:
+                g_warning ("Redo state not recognized");
+               break;
+        }
+}