Added work offline availability.
[claws.git] / src / compose.c
index bdc2cea23534fb06f64ceaacf4816af8130b8916..b7fcada67dc467ca37be794b5453abb5715caa07 100644 (file)
@@ -159,7 +159,8 @@ 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);
+                                                Template       *tmpl,
+                                                gboolean        replace);
 static void compose_destroy                    (Compose        *compose);
 
 static void compose_entries_set                        (Compose        *compose,
@@ -198,13 +199,14 @@ static void compose_set_title                     (Compose        *compose);
 
 static PrefsAccount *compose_current_mail_account(void);
 /* static gint compose_send                    (Compose        *compose); */
+static gboolean compose_check_for_valid_recipient
+                                               (Compose        *compose);
+static gboolean compose_check_entries          (Compose        *compose);
 static gint compose_write_to_file              (Compose        *compose,
                                                 const gchar    *file,
                                                 gboolean        is_draft);
 static gint compose_write_body_to_file         (Compose        *compose,
                                                 const gchar    *file);
-static gint compose_save_to_outbox             (Compose        *compose,
-                                                const gchar    *file);
 static gint compose_remove_reedit_target       (Compose        *compose);
 static gint compose_queue                      (Compose        *compose,
                                                 gint           *msgnum,
@@ -568,8 +570,6 @@ static GtkItemFactoryEntry compose_entries[] =
                                        "<control><alt>L", compose_wrap_line_all, 0, NULL},
        {N_("/_Edit/Edit with e_xternal editor"),
                                        "<shift><control>X", compose_ext_editor_cb, 0, NULL},
-       {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
-       {N_("/_Edit/Actio_ns"),         NULL, NULL, 0, "<Branch>"},
 #if USE_PSPELL
        {N_("/_Spelling"),              NULL, NULL, 0, "<Branch>"},
        {N_("/_Spelling/_Check all or check selection"),
@@ -625,10 +625,11 @@ static GtkItemFactoryEntry compose_entries[] =
 #endif /* USE_GPGME */
        {N_("/_Message/---"),           NULL,           NULL,   0, "<Separator>"},
        {N_("/_Message/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
-       {N_("/_Tool"),                  NULL, NULL, 0, "<Branch>"},
-       {N_("/_Tool/Show _ruler"),      NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
-       {N_("/_Tool/_Address book"),    "<shift><control>A", compose_address_cb , 0, NULL},
-       {N_("/_Tool/_Template"),        NULL, NULL, 0, "<Branch>"},
+       {N_("/_Tools"),                 NULL, NULL, 0, "<Branch>"},
+       {N_("/_Tools/Show _ruler"),     NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
+       {N_("/_Tools/_Address book"),   "<shift><control>A", compose_address_cb , 0, NULL},
+       {N_("/_Tools/_Template"),       NULL, NULL, 0, "<Branch>"},
+       {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
        {N_("/_Help"),                  NULL, NULL, 0, "<Branch>"},
        {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
 };
@@ -684,7 +685,7 @@ Compose *compose_bounce(PrefsAccount *account, MsgInfo *msginfo)
        menu_set_sensitive(ifactory, "/Message/Encrypt", FALSE);
 #endif
        menu_set_sensitive(ifactory, "/Message/Request Return Receipt", FALSE);
-       menu_set_sensitive(ifactory, "/Tool/Template", FALSE);
+       menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
        
        gtk_widget_set_sensitive(c->insert_btn, FALSE);
        gtk_widget_set_sensitive(c->attach_btn, FALSE);
@@ -1765,7 +1766,7 @@ static void compose_exec_sig(Compose *compose, gchar *sigfile)
        if (strlen(sigfile) < 2)
          return;
  
-       sigprg = popen(sigfile+1, "rb");
+       sigprg = popen(sigfile+1, "r");
        if (sigprg) {
 
                buf = g_malloc(buf_len);
@@ -1997,6 +1998,9 @@ static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
        }                                                                    \
 }
 
+#define INDENT_CHARS   ">|#"
+#define SPACE_CHARS    " \t"
+
 static void compose_wrap_line(Compose *compose)
 {
        GtkSText *text = GTK_STEXT(compose->text);
@@ -2037,7 +2041,7 @@ static void compose_wrap_line(Compose *compose)
                        }
                        line_end = 1;
                } else {
-                       if (ch_len == 1 && strchr(">:#", *cbuf))
+                       if (ch_len == 1 && strchr(INDENT_CHARS, *cbuf))
                                quoted = 1;
                        else if (ch_len != 1 || !isspace(*cbuf))
                                quoted = 0;
@@ -2059,7 +2063,8 @@ static void compose_wrap_line(Compose *compose)
                        }
                        line_end = 1;
                } else {
-                       if (line_end && ch_len == 1 && strchr(">:#", *cbuf))
+                       if (line_end && ch_len == 1 &&
+                           strchr(INDENT_CHARS, *cbuf))
                                goto compose_end; /* quoted part */
 
                        line_end = 0;
@@ -2156,29 +2161,102 @@ compose_end:
        gtk_stext_thaw(text);
 }
 
+#undef WRAP_DEBUG
+#ifdef WRAP_DEBUG
+/* Darko: used when I debug wrapping */
+void dump_text(GtkSText *text, int pos, int tlen, int breakoncr)
+{
+       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");
+}
+#endif
+
+typedef enum {
+       WAIT_FOR_SPACE,
+       WAIT_FOR_INDENT_CHAR,
+       WAIT_FOR_INDENT_CHAR_OR_SPACE
+} IndentState;
+
+/* return indent length, we allow:
+   > followed by spaces/tabs
+   | followed by spaces/tabs
+   uppercase characters immediately followed by >,
+   and the repeating sequences of the above */
 /* return indent length */
 static guint get_indent_length(GtkSText *text, guint start_pos, guint text_len)
 {
-       gint indent_len = 0;
-       gint i, ch_len;
+       guint i_len = 0;
+       guint i, ch_len, alnum_cnt = 0;
+       IndentState state = WAIT_FOR_INDENT_CHAR;
        gchar cbuf[MB_LEN_MAX];
+       gboolean is_space;
+       gboolean is_indent;
 
        for (i = start_pos; i < text_len; i++) {
                GET_CHAR(i, cbuf, ch_len);
                if (ch_len > 1)
                        break;
-               /* allow space, tab, > or | */
-               if (cbuf[0] != ' ' && cbuf[0] != '\t' &&
-                   cbuf[0] != '>' && cbuf[0] != '|')
+
+               if (cbuf[0] == '\n')
+                       break;
+
+               is_indent = strchr(INDENT_CHARS, cbuf[0]) ? TRUE : FALSE;
+               is_space = strchr(SPACE_CHARS, cbuf[0]) ? TRUE : FALSE;
+
+               switch (state) {
+               case WAIT_FOR_SPACE:
+                       if (is_space == FALSE)
+                               goto out;
+                       state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
+                       break;
+               case WAIT_FOR_INDENT_CHAR_OR_SPACE:
+                       if (is_indent == FALSE && is_space == FALSE &&
+                           !isupper(cbuf[0]))
+                               goto out;
+                       if (is_space == TRUE) {
+                               alnum_cnt = 0;
+                               state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
+                       } else if (is_indent == TRUE) {
+                               alnum_cnt = 0;
+                               state = WAIT_FOR_SPACE;
+                       } else {
+                               alnum_cnt++;
+                               state = WAIT_FOR_INDENT_CHAR;
+                       }
+                       break;
+               case WAIT_FOR_INDENT_CHAR:
+                       if (is_indent == FALSE && !isupper(cbuf[0]))
+                               goto out;
+                       if (is_indent == TRUE) {
+                               alnum_cnt = 0;
+                               state = WAIT_FOR_SPACE;
+                       } else {
+                               alnum_cnt++;
+                       }
                        break;
-               indent_len++;
+               }
+
+               i_len++;
        }
 
-       return indent_len;
+out:
+       if ((i_len > 0) && (state == WAIT_FOR_INDENT_CHAR))
+               i_len -= alnum_cnt;
+
+       return i_len;
 }
 
 /* insert quotation string when line was wrapped */
-static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
+static guint ins_quote(GtkSText *text, guint indent_len,
                       guint prev_line_pos, guint text_len,
                       gchar *quote_fmt)
 {
@@ -2191,32 +2269,29 @@ 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 {
-               gtk_stext_insert(text, NULL, NULL, NULL, quote_fmt, quote_len);
-               ins_len = quote_len;
        }
 
        return ins_len;
 }
 
-#undef WRAP_DEBUG
-#ifdef WRAP_DEBUG
-/* Darko: used when I debug wrapping */
-void dump_text(GtkSText *text, int pos, int tlen, int breakoncr)
+/* check if we should join the next line */
+static gboolean join_next_line(GtkSText *text, guint start_pos, guint tlen,
+                              guint prev_ilen)
 {
-       gint i;
-       gchar ch;
+       guint indent_len, ch_len;
+       gboolean do_join = FALSE;
+       gchar cbuf[MB_LEN_MAX];
 
-       printf("%d [", pos);
-       for (i = pos; i < tlen; i++) {
-               ch = GTK_STEXT_INDEX(text, i);
-               if (breakoncr && ch == '\n')
-                       break;
-               printf("%c", ch);
+       indent_len = get_indent_length(text, start_pos, tlen);
+
+       if ((indent_len > 0) && (indent_len == prev_ilen)) {
+               GET_CHAR(start_pos + indent_len, cbuf, ch_len);
+               if (ch_len > 0 && (cbuf[0] != '\n'))
+                       do_join = TRUE;
        }
-       printf("]\n");
+
+       return do_join;
 }
-#endif
 
 static void compose_wrap_line_all(Compose *compose)
 {
@@ -2226,7 +2301,7 @@ static void compose_wrap_line_all(Compose *compose)
        gint line_len = 0, cur_len = 0;
        gint ch_len;
        gboolean is_new_line = TRUE, do_delete = FALSE;
-       guint qlen = 0, i_len = 0;
+       guint i_len = 0;
        gboolean linewrap_quote = TRUE;
        guint linewrap_len = prefs_common.linewrap_len;
        gchar *qfmt = prefs_common.quotemark;
@@ -2242,19 +2317,13 @@ static void compose_wrap_line_all(Compose *compose)
        for (; cur_pos < tlen; cur_pos++) {
                /* mark position of new line - needed for quotation wrap */
                if (is_new_line) {
-                       if (linewrap_quote) {
-                               qlen = gtkut_stext_str_compare
-                                       (text, cur_pos, tlen, qfmt);
-                               if (qlen)
-                                       i_len = get_indent_length
-                                               (text, cur_pos, tlen);
-                               else
-                                       i_len = 0;
-                       }
+                       if (linewrap_quote)
+                               i_len = get_indent_length(text, cur_pos, tlen);
+
                        is_new_line = FALSE;
                        p_pos = cur_pos;
 #ifdef WRAP_DEBUG
-                       printf("new line i_len=%d qlen=%d p_pos=", i_len, qlen);
+                       printf("new line i_len=%d p_pos=", i_len);
                        dump_text(text, p_pos, tlen, 1);
 #endif
                }
@@ -2282,31 +2351,20 @@ static void compose_wrap_line_all(Compose *compose)
                /* we have encountered line break */
                if (ch_len == 1 && *cbuf == '\n') {
                        gint clen;
-                       guint ilen;
                        gchar cb[MB_CUR_MAX];
 
+                       /* should we join the next line */
+                       if ((i_len != cur_len) && do_delete &&
+                           join_next_line(text, cur_pos + 1, tlen, i_len))
+                               do_delete = TRUE;
+                       else
+                               do_delete = FALSE;
+
 #ifdef WRAP_DEBUG
-                       printf("found CR at %d next line is ", cur_pos);
+                       printf("found CR at %d do_del is %d next line is ",
+                              cur_pos, do_delete);
                        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 =  gtkut_stext_str_compare_n
-                                       (text, cur_pos + 1, p_pos, i_len, tlen);
-                               if (cur_pos + ilen < tlen) {
-                                       GET_CHAR(cur_pos + ilen + 1, cb, clen);
-                                       /* no need to join the lines */
-                                       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;
-                       }
 
                        /* skip delete if it is continuous URL */
                        if (do_delete && (line_pos - p_pos <= i_len) &&
@@ -2314,8 +2372,8 @@ static void compose_wrap_line_all(Compose *compose)
                                do_delete = FALSE;
 
 #ifdef WRAP_DEBUG
-                       printf("qlen=%d l_len=%d wrap_len=%d do_del=%d\n",
-                               qlen, line_len, linewrap_len, do_delete);
+                       printf("l_len=%d wrap_len=%d do_del=%d\n",
+                               line_len, linewrap_len, do_delete);
 #endif
                        /* should we delete to perform smart wrapping */
                        if (line_len < linewrap_len && do_delete) {
@@ -2327,6 +2385,7 @@ static void compose_wrap_line_all(Compose *compose)
                                /* if text starts with quote fmt or with
                                   indent string, delete them */
                                if (i_len) {
+                                       guint ilen;
                                        ilen =  gtkut_stext_str_compare_n
                                                (text, cur_pos, p_pos, i_len,
                                                 tlen);
@@ -2335,13 +2394,6 @@ static void compose_wrap_line_all(Compose *compose)
                                                        (text, ilen);
                                                tlen -= ilen;
                                        }
-                               } else if (qlen) {
-                                       if (gtkut_stext_str_compare
-                                           (text, cur_pos, tlen, qfmt)) {
-                                               gtk_stext_forward_delete
-                                                       (text, qlen);
-                                               tlen -= qlen;
-                                       }
                                }
 
                                GET_CHAR(cur_pos, cb, clen);
@@ -2359,7 +2411,6 @@ static void compose_wrap_line_all(Compose *compose)
                                cur_pos = p_pos - 1;
                                line_pos = cur_pos;
                                line_len = cur_len = 0;
-                               qlen = 0;
                                do_delete = FALSE;
                                is_new_line = TRUE;
 #ifdef WRAP_DEBUG
@@ -2372,7 +2423,6 @@ static void compose_wrap_line_all(Compose *compose)
                        /* mark new line beginning */
                        line_pos = cur_pos + 1;
                        line_len = cur_len = 0;
-                       qlen = 0;
                        do_delete = FALSE;
                        is_new_line = TRUE;
                        continue;
@@ -2446,7 +2496,10 @@ static void compose_wrap_line_all(Compose *compose)
                        is_new_line = TRUE;
                        line_len = 0;
                        cur_len = 0;
-                       do_delete = TRUE;
+                       if (i_len)
+                               do_delete = TRUE;
+                       else
+                               do_delete = FALSE;
 #ifdef WRAP_DEBUG
                        printf("after CR insert ");
                        dump_text(text, line_pos, tlen, 1);
@@ -2454,7 +2507,7 @@ static void compose_wrap_line_all(Compose *compose)
 #endif
 
                        /* should we insert quotation ? */
-                       if (linewrap_quote && qlen) {
+                       if (linewrap_quote && i_len) {
                                /* only if line is not already quoted  */
                                if (!gtkut_stext_str_compare
                                        (text, line_pos, tlen, qfmt)) {
@@ -2462,8 +2515,8 @@ static void compose_wrap_line_all(Compose *compose)
 
                                        if (line_pos - p_pos > i_len) {
                                                ins_len = ins_quote
-                                                       (text, qlen, i_len,
-                                                        p_pos, tlen, qfmt);
+                                                       (text, i_len, p_pos,
+                                                        tlen, qfmt);
 
                                                /* gtk_stext_compact_buffer(text); */
                                                tlen += ins_len;
@@ -2574,12 +2627,38 @@ gboolean compose_check_for_valid_recipient(Compose *compose) {
        return recipient_found;
 }
 
+static gboolean compose_check_entries(Compose *compose)
+{
+       gchar *str;
+
+       if (compose_check_for_valid_recipient(compose) == FALSE) {
+               alertpanel_error(_("Recipient is not specified."));
+               return FALSE;
+       }
+
+       str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
+       if (*str == '\0') {
+               AlertValue aval;
+
+               aval = alertpanel(_("Send"),
+                                 _("Subject is empty. Send it anyway?"),
+                                 _("Yes"), _("No"), NULL);
+               if (aval != G_ALERTDEFAULT)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
 gint compose_send(Compose *compose)
 {
        gint msgnum;
        FolderItem *folder;
        gint val;
 
+       if (compose_check_entries(compose) == FALSE)
+               return -1;
+
        val = compose_queue(compose, &msgnum, &folder);
        if (val) {
                alertpanel_error(_("Could not queue message for sending"));
@@ -2608,8 +2687,7 @@ gint compose_send(Compose *compose)
 
        lock = TRUE;
 
-       if(!compose_check_for_valid_recipient(compose)) {
-               alertpanel_error(_("Recipient is not specified."));
+       if (compose_check_entries(compose) == FALSE) {
                lock = FALSE;
                return 1;
        }
@@ -2700,13 +2778,18 @@ gint compose_send(Compose *compose)
                                folderview_update_item
                                        (compose->targetinfo->folder, TRUE);
                }
-       }
+               /* save message to outbox */
+               if (prefs_common.savemsg) {
+                       FolderItem *outbox;
 
-       /* save message to outbox */
-       if (ok == 0 && prefs_common.savemsg) {
-               if (compose_save_to_outbox(compose, tmp) < 0)
-                       alertpanel_error
-                               (_("Can't save the message to outbox."));
+                       outbox = account_get_special_folder
+                               (compose->account, F_OUTBOX);
+                       if (procmsg_save_to_outbox(outbox, tmp, FALSE) < 0)
+                               alertpanel_error
+                                       (_("Can't save the message to outbox."));
+                       else
+                               folderview_update_item(outbox, TRUE);
+               }
        }
 
        unlink(tmp);
@@ -2838,20 +2921,24 @@ static gint compose_bounce_write_to_file(Compose *compose, const gchar *file)
                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 (!prefs_common.bounce_keep_from) {
+                       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)
@@ -3099,43 +3186,6 @@ static gint compose_write_body_to_file(Compose *compose, const gchar *file)
        return 0;
 }
 
-static gint compose_save_to_outbox(Compose *compose, const gchar *file)
-{
-       FolderItem *outbox;
-       gchar *path;
-       gint num;
-       FILE *fp;
-
-       debug_print(_("saving sent message...\n"));
-
-       outbox = folder_get_default_outbox();
-       path = folder_item_get_path(outbox);
-       if (!is_dir_exist(path))
-               make_dir_hier(path);
-
-       folder_item_scan(outbox);
-       if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
-               g_free(path);
-               g_warning(_("can't save message\n"));
-               return -1;
-       }
-
-       if ((fp = procmsg_open_mark_file(path, TRUE)) == NULL)
-               g_warning(_("can't open mark file\n"));
-       else {
-               MsgInfo newmsginfo;
-
-               newmsginfo.msgnum = num;
-               newmsginfo.flags.perm_flags = 0;
-               newmsginfo.flags.tmp_flags = 0;
-               procmsg_write_flags(&newmsginfo, fp);
-               fclose(fp);
-       }
-       g_free(path);
-
-       return 0;
-}
-
 static gint compose_remove_reedit_target(Compose *compose)
 {
        FolderItem *item;
@@ -3168,6 +3218,7 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
        GSList *cur;
        gchar buf[BUFFSIZE];
        gint num;
+       MsgFlags flag = {0, 0};
         static gboolean lock = FALSE;
        PrefsAccount *mailac = NULL, *newsac = NULL;
        
@@ -3177,12 +3228,11 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
 
         lock = TRUE;
        
-        if(!compose_check_for_valid_recipient(compose)) {
-                alertpanel_error(_("Recipient is not specified."));
+       if (compose_check_entries(compose) == FALSE) {
                 lock = FALSE;
                 return -1;
-        }
-                                                                       
+       }
+
        if (!compose->to_list && !compose->newsgroup_list) {
                g_warning(_("can't get recipient list."));
                lock = FALSE;
@@ -3335,10 +3385,8 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                g_free(tmp2);
                return -1;
        }
-                                               
-       /* queue message */
-       queue = folder_get_default_queue();
 
+       queue = account_get_special_folder(compose->account, F_QUEUE);
        folder_item_scan(queue);
        queue_path = folder_item_get_path(queue);
        if (!is_dir_exist(queue_path))
@@ -3363,19 +3411,7 @@ static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
                                (compose->targetinfo->folder, TRUE);
        }
 
-       if ((fp = procmsg_open_mark_file(queue_path, TRUE)) == NULL)
-               g_warning(_("can't open mark file\n"));
-       else {
-               MsgInfo newmsginfo;
-
-               newmsginfo.msgnum = num;
-               newmsginfo.flags.perm_flags = 0;
-               newmsginfo.flags.tmp_flags = 0;
-               procmsg_write_flags(&newmsginfo, fp);
-               fclose(fp);
-       }
-       g_free(queue_path);
-
+       procmsg_add_flags(queue, num, flag);
        folder_item_scan(queue);
        folderview_update_item(queue, TRUE);
 
@@ -4236,6 +4272,8 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
         GtkPspell * gtkpspell = NULL;
 #endif
 
+       static GdkGeometry geometry;
+
        g_return_val_if_fail(account != NULL, NULL);
 
        debug_print(_("Creating compose window...\n"));
@@ -4252,6 +4290,14 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
        gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
        gtk_widget_set_usize(window, -1, prefs_common.compose_height);
        gtk_window_set_wmclass(GTK_WINDOW(window), "compose window", "Sylpheed");
+
+       if (!geometry.max_width) {
+               geometry.max_width = gdk_screen_width();
+               geometry.max_height = gdk_screen_height();
+       }
+       gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
+                                     &geometry, GDK_HINT_MAX_SIZE);
+
        gtk_signal_connect(GTK_OBJECT(window), "delete_event",
                           GTK_SIGNAL_FUNC(compose_delete_cb), compose);
        gtk_signal_connect(GTK_OBJECT(window), "destroy",
@@ -4444,7 +4490,7 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
        menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
        menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
 
-       tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tool/Template");
+       tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
 #if 0 /* NEW COMPOSE GUI */
        gtk_widget_hide(bcc_hbox);
        gtk_widget_hide(bcc_entry);
@@ -4472,7 +4518,7 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
        }
 #endif
 
-       update_compose_actions_menu(ifactory, "/Edit/Actions", compose);
+       update_compose_actions_menu(ifactory, "/Tools/Actions", compose);
 
 
        undostruct = undo_init(text);
@@ -4890,8 +4936,8 @@ void compose_reflect_prefs_pixmap_theme(void)
        }
 }
 
-
-static void compose_template_apply(Compose *compose, Template *tmpl)
+static void compose_template_apply(Compose *compose, Template *tmpl,
+                                  gboolean replace)
 {
        gchar *qmark;
        gchar *parsed_str;
@@ -4906,7 +4952,8 @@ static void compose_template_apply(Compose *compose, Template *tmpl)
        if (tmpl->to && *tmpl->to != '\0')
                compose_entry_append(compose, tmpl->to, COMPOSE_TO);
 
-       gtk_stext_clear(GTK_STEXT(compose->text));
+       if (replace)
+               gtk_stext_clear(GTK_STEXT(compose->text));
 
        if (compose->replyinfo == NULL) {
                MsgInfo dummyinfo;
@@ -4924,9 +4971,17 @@ static void compose_template_apply(Compose *compose, Template *tmpl)
                                               tmpl->value, qmark, NULL);
        }
 
-       if (parsed_str && prefs_common.auto_sig)
+       if (replace && parsed_str && prefs_common.auto_sig)
                compose_insert_sig(compose);
 
+       if (replace && parsed_str) {
+               gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
+               gtk_stext_set_point(GTK_STEXT(compose->text), 0);
+       }
+
+       if (parsed_str)
+               compose_changed_cb(NULL, compose);
+
        gtk_stext_thaw(GTK_STEXT(compose->text));
 }
 
@@ -5222,7 +5277,7 @@ static void compose_attach_property_create(gboolean *cancelled)
                         GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
 
        optmenu = gtk_option_menu_new();
-       gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
 
        optmenu_menu = gtk_menu_new();
        MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT);
@@ -5774,36 +5829,27 @@ static void select_account(Compose * compose, PrefsAccount * ac)
                gtk_widget_queue_resize(compose->table_vbox);
 #endif
 #if USE_GPGME
-               if (ac->default_sign) {
-                       ifactory = gtk_item_factory_from_widget(compose->menubar);
+               ifactory = gtk_item_factory_from_widget(compose->menubar);
                        menu_set_sensitive(ifactory,
                                           "/Message/Sign", TRUE);
+                       menu_set_sensitive(ifactory,
+                                          "/Message/Encrypt", TRUE);
+
                        menuitem = gtk_item_factory_get_item(ifactory, "/Message/Sign");
+               if (ac->default_sign)
                        gtk_check_menu_item_set_active
                                (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
-               } else {
-                       ifactory = gtk_item_factory_from_widget(compose->menubar);
-                       menu_set_sensitive(ifactory,
-                                          "/Message/Sign", TRUE);
-                       menuitem = gtk_item_factory_get_item(ifactory, "/Message/Sign");
+               else
                        gtk_check_menu_item_set_active
                                (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
-               }
-               if (ac->default_encrypt) {
-                       ifactory = gtk_item_factory_from_widget(compose->menubar);
-                       menu_set_sensitive(ifactory,
-                                          "/Message/Encrypt", TRUE);
+
                        menuitem = gtk_item_factory_get_item(ifactory, "/Message/Encrypt");
+               if (ac->default_encrypt)
                        gtk_check_menu_item_set_active
                                (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
-               } else {
-                       ifactory = gtk_item_factory_from_widget(compose->menubar);
-                       menu_set_sensitive(ifactory,
-                                          "/Message/Encrypt", TRUE);
-                       menuitem = gtk_item_factory_get_item(ifactory, "/Message/Encrypt");
+               else
                        gtk_check_menu_item_set_active
                                (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
-               }
 #endif /* USE_GPGME */
 
 }
@@ -5873,6 +5919,12 @@ static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
 {
        Compose *compose = (Compose *)data;
        gint val;
+       
+       if (prefs_common.work_offline)
+               if (alertpanel(_("Offline warning"), 
+                              _("You're working offline. Override?"),
+                              _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
+               return;
 
        val = compose_send(compose);
 
@@ -5895,11 +5947,12 @@ static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
        FolderItem *draft;
        gchar *tmp;
        gint msgnum;
+       MsgFlags flag = {0, 0};
        static gboolean lock = FALSE;
 
        if (lock) return;
 
-       draft = folder_get_default_draft();
+       draft = account_get_special_folder(compose->account, F_DRAFT);
        g_return_if_fail(draft != NULL);
 
        lock = TRUE;
@@ -5914,7 +5967,7 @@ static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
        }
 
        folder_item_scan(draft);
-       if ((msgnum = folder_item_add_msg(draft, tmp, TRUE)) <= 0) {
+       if ((msgnum = folder_item_add_msg(draft, tmp, TRUE)) < 0) {
                unlink(tmp);
                g_free(tmp);
                lock = FALSE;
@@ -5931,6 +5984,7 @@ static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
                                               TRUE);
        }
 
+       procmsg_add_flags(draft, msgnum, flag);
        folder_item_scan(draft);
        folderview_update_item(draft, TRUE);
 
@@ -6052,11 +6106,22 @@ static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
 {
        Compose *compose = (Compose *)data;
        Template *tmpl;
+       gchar *msg;
+       AlertValue val;
 
        tmpl = gtk_object_get_data(GTK_OBJECT(widget), "template");
        g_return_if_fail(tmpl != NULL);
 
-       compose_template_apply(compose, tmpl);
+       msg = g_strdup_printf(_("Do you want to apply the template `%s' ?"),
+                             tmpl->name);
+       val = alertpanel(_("Apply template"), msg,
+                        _("Replace"), _("Insert"), _("Cancel"));
+       g_free(msg);
+
+       if (val == G_ALERTDEFAULT)
+               compose_template_apply(compose, tmpl, TRUE);
+       else if (val == G_ALERTALTERNATE)
+               compose_template_apply(compose, tmpl, FALSE);
 }
 
 static void compose_ext_editor_cb(gpointer data, guint action,
@@ -6578,6 +6643,9 @@ static gboolean compose_send_control_enter(Compose *compose)
        GtkAccelEntry *accel;
        GtkWidget *send_menu;
        GSList *list;
+       GdkModifierType ignored_mods =
+               (GDK_LOCK_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK |
+                GDK_MOD4_MASK | GDK_MOD5_MASK);
 
        ev = gtk_get_current_event();
        if (ev->type != GDK_KEY_PRESS) return FALSE;
@@ -6591,7 +6659,8 @@ static gboolean compose_send_control_enter(Compose *compose)
        list = gtk_accel_group_entries_from_object(GTK_OBJECT(send_menu));
        accel = (GtkAccelEntry *)list->data;
        if (accel->accelerator_key == kev->keyval &&
-           accel->accelerator_mods == kev->state) {
+           (accel->accelerator_mods & ~ignored_mods) ==
+           (kev->state & ~ignored_mods)) {
                compose_send_cb(compose, 0, NULL);
                return TRUE;
        }