2007-05-30 [colin] 2.9.2cvs18
[claws.git] / src / compose.c
index 06269cfe7648d60e82cc15a9b35cfb7b6404ed9d..9b01223ddb353e5d73dcff7a391ac34c88295909 100644 (file)
 #include "message_search.h"
 #include "combobox.h"
 #include "hooks.h"
+#include "privacy.h"
 
 enum
 {
@@ -171,13 +172,6 @@ typedef enum
        COMPOSE_INSERT_NO_FILE
 } ComposeInsertResult;
 
-typedef enum
-{
-       COMPOSE_QUIT_EDITING,
-       COMPOSE_KEEP_EDITING,
-       COMPOSE_AUTO_SAVE
-} ComposeDraftAction;
-
 typedef enum
 {
        COMPOSE_WRITE_FOR_SEND,
@@ -347,7 +341,7 @@ static void compose_undo_state_changed              (UndoMain       *undostruct,
                                                 gpointer        data);
 
 static void compose_create_header_entry        (Compose *compose);
-static void compose_add_header_entry   (Compose *compose, gchar *header, gchar *text);
+static void compose_add_header_entry   (Compose *compose, const gchar *header, gchar *text);
 static void compose_remove_header_entries(Compose *compose);
 
 static void compose_update_priority_menu_item(Compose * compose);
@@ -374,7 +368,6 @@ static gboolean attach_button_pressed       (GtkWidget      *widget,
 static gboolean attach_key_pressed     (GtkWidget      *widget,
                                         GdkEventKey    *event,
                                         gpointer        data);
-
 static void compose_send_cb            (gpointer        data,
                                         guint           action,
                                         GtkWidget      *widget);
@@ -554,7 +547,7 @@ static GtkItemFactoryEntry compose_popup_entries[] =
 static GtkItemFactoryEntry compose_entries[] =
 {
        {N_("/_Message"),                               NULL, NULL, 0, "<Branch>"},
-       {N_("/_Message/_Send"),         "<control>Return",
+       {N_("/_Message/S_end"),         "<control>Return",
                                        compose_send_cb, 0, NULL},
        {N_("/_Message/Send _later"),   "<shift><control>S",
                                        compose_send_later_cb,  0, NULL},
@@ -1048,7 +1041,12 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI
                        pref_get_unescaped_pref(tmp, prefs_common.compose_subject_format);
 
                        subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
+#ifdef USE_ASPELL
+                       quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
+                                       compose->gtkaspell);
+#else
                        quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
+#endif
                        quote_fmt_scan_string(tmp);
                        quote_fmt_parse();
 
@@ -1083,7 +1081,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI
                        compose_quote_fmt(compose, dummyinfo,
                                          prefs_common.compose_body_format,
                                          NULL, tmp, FALSE, TRUE,
-                                                 _("New message body format error."));
+                                                 _("New message body format error at line %d."));
                        quote_fmt_reset_vartable();
 
                        g_free(tmp);
@@ -1445,7 +1443,7 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, gboolean quote,
                compose_quote_fmt(compose, compose->replyinfo,
                                  prefs_common.quotefmt,
                                  qmark, body, FALSE, TRUE,
-                                         _("Message reply format error."));
+                                         _("Message reply format error at line %d."));
                quote_fmt_reset_vartable();
        }
        if (procmime_msginfo_is_encrypted(compose->replyinfo)) {
@@ -1562,7 +1560,7 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
                compose_quote_fmt(compose, full_msginfo,
                                  prefs_common.fw_quotefmt,
                                  qmark, body, FALSE, TRUE,
-                                         _("Message forward format error."));
+                                         _("Message forward format error at line %d."));
                quote_fmt_reset_vartable();
                compose_attach_parts(compose, msginfo);
 
@@ -2057,7 +2055,7 @@ Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
        gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
 
        compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
-                                         _("Message redirect format error."));
+                                         _("Message redirect format error at line %d."));
        quote_fmt_reset_vartable();
        gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
 
@@ -2113,7 +2111,7 @@ GList *compose_get_compose_list(void)
 void compose_entry_append(Compose *compose, const gchar *address,
                          ComposeEntryType type)
 {
-       gchar *header;
+       const gchar *header;
        gchar *cur, *begin;
        gboolean in_quote = FALSE;
        if (!address || *address == '\0') return;
@@ -2139,7 +2137,7 @@ void compose_entry_append(Compose *compose, const gchar *address,
                header = N_("To:");
                break;
        }
-       header = prefs_common.trans_hdr ? gettext(header) : header;
+       header = prefs_common_translated_header_name(header);
        
        cur = begin = (gchar *)address;
        
@@ -2540,7 +2538,12 @@ static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
        }
 
        if (qmark != NULL) {
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
+#endif
                quote_fmt_scan_string(qmark);
                quote_fmt_parse();
 
@@ -2557,7 +2560,12 @@ static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
                        while (*trimmed_body == '\n')
                                trimmed_body++;
 
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
+#endif
                if (need_unescape) {
                        gchar *tmp = NULL;
 
@@ -2574,7 +2582,10 @@ static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
 
                buf = quote_fmt_get_buffer();
                if (buf == NULL) {
-                       alertpanel_error(err_msg);
+                       gint line = quote_fmt_get_line();
+                       gchar *msg = g_strdup_printf(err_msg, line);
+                       alertpanel_error(msg);
+                       g_free(msg);
                        goto error;
                }
        } else
@@ -3132,6 +3143,17 @@ static gboolean compose_attach_append(Compose *compose, const gchar *file,
        gboolean has_binary = FALSE;
 
        if (!is_file_exist(file)) {
+               gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
+               gboolean result = FALSE;
+               if (file_from_uri && is_file_exist(file_from_uri)) {
+                       result = compose_attach_append(
+                                               compose, file_from_uri,
+                                               filename,
+                                               content_type);
+               }
+               g_free(file_from_uri);
+               if (result)
+                       return TRUE;
                alertpanel_error("File %s doesn't exist\n", filename);
                return FALSE;
        }
@@ -3777,12 +3799,6 @@ static void compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter,
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
 
                if (quote_str) {
-                       if (!wrap_quote) {
-                               if (startq_offset == -1) {
-                                       startq_offset = gtk_text_iter_get_offset(&iter);
-                               }
-                               goto colorize;
-                       }
                        debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
                        if (startq_offset == -1) 
                                startq_offset = gtk_text_iter_get_offset(&iter);
@@ -3794,6 +3810,9 @@ static void compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter,
                                else
                                        quotelevel = 2;
                        }
+                       if (!wrap_quote) {
+                               goto colorize;
+                       }
                } else {
                        if (startq_offset == -1)
                                noq_offset = gtk_text_iter_get_offset(&iter);
@@ -3911,6 +3930,7 @@ colorize:
                        gtk_text_buffer_get_iter_at_offset(
                                buffer, &startquote, startq_offset);
                        endquote = iter;
+
                        switch (quotelevel) {
                        case 0: gtk_text_buffer_apply_tag_by_name(
                                        buffer, "quote0", &startquote, &endquote);
@@ -3999,11 +4019,16 @@ static void compose_set_title(Compose *compose)
        subject = gtk_editable_get_chars(
                        GTK_EDITABLE(compose->subject_entry), 0, -1);
 
+#ifndef MAEMO
        if (subject && strlen(subject))
                str = g_strdup_printf(_("%s - Compose message%s"),
                                      subject, edited); 
        else
                str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
+#else
+       str = g_strdup(_("Compose message"));
+#endif
+
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
        g_free(str);
        g_free(subject);
@@ -4177,13 +4202,13 @@ gboolean compose_check_for_valid_recipient(Compose *compose) {
                g_strstrip(entry);
                if (entry[0] != '\0') {
                        for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
-                               if (!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
+                               if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
                                        compose->to_list = address_list_append(compose->to_list, entry);
                                        recipient_found = TRUE;
                                }
                        }
                        for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
-                               if (!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
+                               if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
                                        compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
                                        recipient_found = TRUE;
                                }
@@ -4208,7 +4233,7 @@ static gboolean compose_check_for_set_recipients(Compose *compose)
                        header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
                        g_strstrip(entry);
                        if (strcmp(entry, compose->account->auto_cc)
-                       ||  strcmp(header, (prefs_common.trans_hdr ? gettext("Cc:") : "Cc:"))) {
+                       ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
                                found_other = TRUE;
                                g_free(entry);
                                break;
@@ -4239,7 +4264,7 @@ static gboolean compose_check_for_set_recipients(Compose *compose)
                        header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
                        g_strstrip(entry);
                        if (strcmp(entry, compose->account->auto_bcc)
-                       ||  strcmp(header, (prefs_common.trans_hdr ? gettext("Bcc:") : "Bcc:"))) {
+                       ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
                                found_other = TRUE;
                                g_free(entry);
                                break;
@@ -4337,6 +4362,8 @@ gint compose_send(Compose *compose)
                } else if (val == -5) {
                        alertpanel_error(_("Could not queue message for sending:\n\n"
                                           "Couldn't get recipient encryption key."));
+               } else if (val == -6) {
+                       /* silent error */
                } else if (val == -3) {
                        if (privacy_peek_error())
                        alertpanel_error(_("Could not queue message for sending:\n\n"
@@ -4379,8 +4406,6 @@ gint compose_send(Compose *compose)
                g_free(msgpath);
        }
        if (!discard_window) {
-               compose->sending = FALSE;
-               compose_allow_user_actions (compose, TRUE);
                if (val != 0) {
                        if (!queued_removed)
                                folder_item_remove_msg(folder, msgnum);
@@ -4410,8 +4435,11 @@ gint compose_send(Compose *compose)
                                procmsg_msginfo_free(tmp);
                        }
                }
-               if (!discard_window)
+               if (!discard_window) {
+                       compose->sending = FALSE;
+                       compose_allow_user_actions (compose, TRUE);
                        compose_close(compose);
+               }
        } else {
                if (errstr) {
                        gchar *tmp = g_strdup_printf(_("%s\nUse \"Send queued messages\" from "
@@ -4466,13 +4494,13 @@ static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
        GSList *list;
        ComposeHeaderEntry *headerentry;
        const gchar *headerentryname;
-       gchar *cc_hdr;
-       gchar *to_hdr;
+       const gchar *cc_hdr;
+       const gchar *to_hdr;
 
        debug_print("Writing redirect header\n");
 
-       cc_hdr = prefs_common.trans_hdr ? _("Cc:") : "Cc:";
-       to_hdr = prefs_common.trans_hdr ? _("To:") : "To:";
+       cc_hdr = prefs_common_translated_header_name("Cc:");
+       to_hdr = prefs_common_translated_header_name("To:");
 
        first_to_address = TRUE;
        for (list = compose->header_list; list; list = list->next) {
@@ -4969,6 +4997,32 @@ gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **ms
 {
        return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
 }
+
+static gboolean compose_warn_encryption(Compose *compose)
+{
+       const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
+       AlertValue val = G_ALERTALTERNATE;
+       
+       if (warning == NULL)
+               return TRUE;
+
+       val = alertpanel_full(_("Encryption warning"), warning,
+                 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
+                 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
+       if (val & G_ALERTDISABLE) {
+               val &= ~G_ALERTDISABLE;
+               if (val == G_ALERTALTERNATE)
+                       privacy_inhibit_encrypt_warning(compose->privacy_system,
+                               TRUE);
+       }
+
+       if (val == G_ALERTALTERNATE) {
+               return TRUE;
+       } else {
+               return FALSE;
+       } 
+}
+
 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
                              gchar **msgpath, gboolean check_subject,
                              gboolean remove_reedit_target)
@@ -5089,6 +5143,13 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing);
                if (compose->use_encryption) {
                        gchar *encdata;
+                       if (!compose_warn_encryption(compose)) {
+                               lock = FALSE;
+                               fclose(fp);
+                               g_unlink(tmp);
+                               g_free(tmp);
+                               return -6;
+                       }
                        if (mailac && mailac->encrypt_to_self) {
                                GSList *tmp_list = g_slist_copy(compose->to_list);
                                tmp_list = g_slist_append(tmp_list, compose->account->address);
@@ -5291,11 +5352,12 @@ static void compose_add_headerfield_from_headerlist(Compose *compose,
                                                    const gchar *fieldname,
                                                    const gchar *seperator)
 {
-       gchar *str, *fieldname_w_colon, *trans_fieldname;
+       gchar *str, *fieldname_w_colon;
        gboolean add_field = FALSE;
        GSList *list;
        ComposeHeaderEntry *headerentry;
-       const gchar * headerentryname;
+       const gchar *headerentryname;
+       const gchar *trans_fieldname;
        GString *fieldstr;
 
        if (IS_IN_CUSTOM_HEADER(fieldname))
@@ -5306,7 +5368,7 @@ static void compose_add_headerfield_from_headerlist(Compose *compose,
        fieldstr = g_string_sized_new(64);
 
        fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
-       trans_fieldname = (prefs_common.trans_hdr ? gettext(fieldname_w_colon) : fieldname_w_colon);
+       trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
 
        for (list = compose->header_list; list; list = list->next) {
                headerentry = ((ComposeHeaderEntry *)list->data);
@@ -5540,7 +5602,7 @@ static gchar *compose_get_header(Compose *compose)
                gchar *tmp;
                gchar *headername;
                gchar *headername_wcolon;
-               gchar *headername_trans;
+               const gchar *headername_trans;
                gchar *headervalue;
                gchar **string;
                gboolean standard_header = FALSE;
@@ -5568,8 +5630,8 @@ static gchar *compose_get_header(Compose *compose)
                subst_char(headervalue, '\n', ' ');
                string = std_headers;
                while (*string != NULL) {
-                       headername_trans = prefs_common.trans_hdr ? gettext(*string) : *string;
-                       if (!strcmp(headername_trans,headername_wcolon))
+                       headername_trans = prefs_common_translated_header_name(*string);
+                       if (!strcmp(headername_trans, headername_wcolon))
                                standard_header = TRUE;
                        string++;
                }
@@ -5651,6 +5713,48 @@ static void compose_convert_header(Compose *compose, gchar *dest, gint len, gcha
        g_free(tmpstr);
 }
 
+static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
+{
+       gchar *address;
+
+       g_return_if_fail(user_data != NULL);
+
+       address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
+       g_strstrip(address);
+       if (*address != '\0') {
+               gchar *name = procheader_get_fromname(address);
+               extract_address(address);
+               addressbook_add_contact(name, address, NULL);
+       }
+       g_free(address);
+}
+
+static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
+{
+       GtkWidget *menuitem;
+       gchar *address;
+
+       g_return_if_fail(menu != NULL);
+       g_return_if_fail(GTK_IS_MENU_SHELL(menu));
+
+       menuitem = gtk_separator_menu_item_new();
+       gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
+       gtk_widget_show(menuitem);
+
+       menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
+       gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
+
+       address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+       g_strstrip(address);
+       if (*address == '\0') {
+               gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
+       }
+
+       g_signal_connect(G_OBJECT(menuitem), "activate",
+                        G_CALLBACK(compose_add_to_addressbook_cb), entry);
+       gtk_widget_show(menuitem);
+}
+
 static void compose_create_header_entry(Compose *compose) 
 {
        gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
@@ -5669,7 +5773,7 @@ static void compose_create_header_entry(Compose *compose)
        combo = gtk_combo_new();
        string = headers; 
        while(*string != NULL) {
-               combo_list = g_list_append(combo_list, (prefs_common.trans_hdr ? gettext(*string) : *string));
+               combo_list = g_list_append(combo_list, (gchar*)prefs_common_translated_header_name(*string));
                string++;
        }
        gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_list);
@@ -5678,9 +5782,12 @@ static void compose_create_header_entry(Compose *compose)
        g_signal_connect(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
                         G_CALLBACK(compose_grab_focus_cb), compose);
        gtk_widget_show(combo);
-       gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1, compose->header_nextrow, compose->header_nextrow+1, GTK_SHRINK, GTK_FILL, 0, 0);
+       gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
+                       compose->header_nextrow, compose->header_nextrow+1,
+                       GTK_SHRINK, GTK_FILL, 0, 0);
        if (compose->header_last) {     
-               const gchar *last_header_entry = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
+               const gchar *last_header_entry = gtk_entry_get_text(
+                               GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
                string = headers;
                while (*string != NULL) {
                        if (!strcmp(*string, last_header_entry))
@@ -5693,10 +5800,10 @@ static void compose_create_header_entry(Compose *compose)
        if (!compose->header_last || !standard_header) {
                switch(compose->account->protocol) {
                        case A_NNTP:
-                               header = prefs_common.trans_hdr ? _("Newsgroups:") : "Newsgroups:";
+                               header = prefs_common_translated_header_name("Newsgroups:");
                                break;
                        default:
-                               header = prefs_common.trans_hdr ? _("To:") : "To:";
+                               header = prefs_common_translated_header_name("To:");
                                break;
                }                                                                   
        }
@@ -5711,7 +5818,9 @@ static void compose_create_header_entry(Compose *compose)
        gtk_widget_show(entry);
        gtk_tooltips_set_tip(compose->tooltips, entry,
                _("Use <tab> to autocomplete from addressbook"), NULL);
-       gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2, compose->header_nextrow, compose->header_nextrow+1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+       gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
+                       compose->header_nextrow, compose->header_nextrow+1,
+                       GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
         g_signal_connect(G_OBJECT(entry), "key-press-event", 
                         G_CALLBACK(compose_headerentry_key_press_event_cb), 
@@ -5732,6 +5841,9 @@ static void compose_create_header_entry(Compose *compose)
        g_signal_connect(G_OBJECT(entry), "drag-drop",
                         G_CALLBACK(compose_drag_drop),
                         compose);
+       g_signal_connect(G_OBJECT(entry), "populate-popup",
+                        G_CALLBACK(compose_entry_popup_extend),
+                        NULL);
        
        address_completion_register_entry(GTK_ENTRY(entry), TRUE);
 
@@ -5741,15 +5853,18 @@ static void compose_create_header_entry(Compose *compose)
         headerentry->headernum = compose->header_nextrow;
 
         compose->header_nextrow++;
-       compose->header_last = headerentry;
+       compose->header_last = headerentry;             
+       compose->header_list =
+               g_slist_append(compose->header_list,
+                              headerentry);
 }
 
-static void compose_add_header_entry(Compose *compose, gchar *header, gchar *text) 
+static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
 {
        ComposeHeaderEntry *last_header;
        
        last_header = compose->header_last;
-       
+
        gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header->combo)->entry), header);
        gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
 }
@@ -5764,6 +5879,7 @@ static void compose_remove_header_entries(Compose *compose)
                gtk_widget_destroy(headerentry->entry);
                g_free(headerentry);
        }
+       compose->header_last = NULL;
        g_slist_free(compose->header_list);
        compose->header_list = NULL;
        compose->header_nextrow = 1;
@@ -5807,6 +5923,17 @@ static GtkWidget *compose_create_header(Compose *compose)
        return header_scrolledwin ;
 }
 
+static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
+{
+       Compose *compose = (Compose *)data;
+       GdkEventButton event;
+       
+       event.button = 3;
+       event.time = gtk_get_current_event_time();
+
+       return attach_button_pressed(compose->attach_clist, &event, compose);
+}
+
 static GtkWidget *compose_create_attach(Compose *compose)
 {
        GtkWidget *attach_scrwin;
@@ -5863,6 +5990,15 @@ static GtkWidget *compose_create_attach(Compose *compose)
                         G_CALLBACK(attach_selected), compose);
        g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
                         G_CALLBACK(attach_button_pressed), compose);
+#ifndef MAEMO
+       g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
+                        G_CALLBACK(popup_attach_button_pressed), compose);
+#else
+       gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
+                       GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
+       g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
+                        G_CALLBACK(popup_attach_button_pressed), compose);
+#endif
        g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
                         G_CALLBACK(attach_key_pressed), compose);
 
@@ -6088,6 +6224,17 @@ static void compose_spell_menu_changed(void *data)
 }
 #endif
 
+static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
+{
+       Compose *compose = (Compose *)data;
+       GdkEventButton event;
+       
+       event.button = 3;
+       event.time = gtk_get_current_event_time();
+
+       return text_clicked(compose->text, &event, compose);
+}
+
 static gboolean compose_force_window_origin = TRUE;
 static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                                                 gboolean batch)
@@ -6151,7 +6298,8 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
 
        compose->tooltips = gtk_tooltips_new();
 
-       window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
+
        gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
        gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
 
@@ -6168,11 +6316,12 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
        }
        gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
                                      &geometry, GDK_HINT_MIN_SIZE);
-       
+
+#ifndef MAEMO  
        if (compose_force_window_origin)
                gtk_widget_set_uposition(window, prefs_common.compose_x, 
                                 prefs_common.compose_y);
-
+#endif
        g_signal_connect(G_OBJECT(window), "delete_event",
                         G_CALLBACK(compose_delete_cb), compose);
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
@@ -6289,6 +6438,15 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                         G_CALLBACK(text_inserted), compose);
        g_signal_connect(G_OBJECT(text), "button_press_event",
                         G_CALLBACK(text_clicked), compose);
+#ifndef MAEMO
+       g_signal_connect(G_OBJECT(text), "popup-menu",
+                        G_CALLBACK(compose_popup_menu), compose);
+#else
+       gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
+                       GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
+       g_signal_connect(G_OBJECT(text), "tap-and-hold",
+                        G_CALLBACK(compose_popup_menu), compose);
+#endif
        g_signal_connect(G_OBJECT(subject_entry), "changed",
                         G_CALLBACK(compose_changed_cb), compose);
 
@@ -6308,6 +6466,12 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
        paned = gtk_vpaned_new();
        gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
        gtk_container_add(GTK_CONTAINER(vbox2), paned);
+#ifdef MAEMO
+       if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
+               gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
+       else
+               gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
+#endif
        gtk_paned_add1(GTK_PANED(paned), notebook);
        gtk_paned_add2(GTK_PANED(paned), edit_vbox);
        gtk_widget_show_all(paned);
@@ -6352,6 +6516,7 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
 
        compose->paned = paned;
 
+       compose->notebook      = notebook;
        compose->edit_vbox     = edit_vbox;
        compose->ruler_hbox    = ruler_hbox;
        compose->ruler         = ruler;
@@ -6461,9 +6626,11 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
        menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
 
        if (account->protocol != A_NNTP)
-               gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), prefs_common.trans_hdr ? _("To:") : "To:");
+               gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
+                               prefs_common_translated_header_name("To:"));
        else
-               gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), prefs_common.trans_hdr ? _("Newsgroups:") : "Newsgroups:");
+               gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
+                               prefs_common_translated_header_name("Newsgroups:"));
 
        addressbook_set_target_compose(compose);
        
@@ -6501,6 +6668,10 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                gtk_widget_realize(window);
        } else {
                gtk_widget_show(window);
+#ifdef MAEMO
+               maemo_window_full_screen_if_needed(GTK_WINDOW(window));
+               maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
+#endif
        }
        
        return compose;
@@ -6563,6 +6734,9 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
        g_signal_connect(G_OBJECT(optmenu), "changed",
                        G_CALLBACK(account_activated),
                        compose);
+       g_signal_connect(G_OBJECT(from_name), "populate-popup",
+                        G_CALLBACK(compose_entry_popup_extend),
+                        NULL);
 
        gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
        gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
@@ -6893,7 +7067,7 @@ static void compose_template_apply(Compose *compose, Template *tmpl,
        gchar *qmark;
        gchar *parsed_str = NULL;
        gint cursor_pos = 0;
-       const gchar *err_msg = _("Template body format error.");
+       const gchar *err_msg = _("Template body format error at line %d.");
        if (!tmpl) return;
 
        /* process the body */
@@ -6995,7 +7169,12 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl)
        }
 
        if (tmpl->to && *tmpl->to != '\0') {
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
+#endif
                quote_fmt_scan_string(tmpl->to);
                quote_fmt_parse();
 
@@ -7008,7 +7187,12 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl)
        }
 
        if (tmpl->cc && *tmpl->cc != '\0') {
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
+#endif
                quote_fmt_scan_string(tmpl->cc);
                quote_fmt_parse();
 
@@ -7021,7 +7205,12 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl)
        }
 
        if (tmpl->bcc && *tmpl->bcc != '\0') {
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
+#endif
                quote_fmt_scan_string(tmpl->bcc);
                quote_fmt_parse();
 
@@ -7035,7 +7224,12 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl)
 
        /* process the subject */
        if (tmpl->subject && *tmpl->subject != '\0') {
+#ifdef USE_ASPELL
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
+                               compose->gtkaspell);
+#else
                quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
+#endif
                quote_fmt_scan_string(tmpl->subject);
                quote_fmt_parse();
 
@@ -7350,7 +7544,7 @@ static void compose_attach_property_create(gboolean *cancelled)
 
        debug_print("Creating attach_property window...\n");
 
-       window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
        gtk_widget_set_size_request(window, 480, -1);
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
        gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
@@ -7930,7 +8124,7 @@ static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
        Compose *compose = (Compose *)data;
        
        if (prefs_common.work_offline && 
-           !inc_offline_should_override(
+           !inc_offline_should_override(TRUE,
                _("Claws Mail needs network access in order "
                  "to send this email.")))
                return;
@@ -7969,13 +8163,68 @@ static void compose_send_later_cb(gpointer data, guint action,
        } else if (val == -5) {
                alertpanel_error(_("Could not queue message for sending:\n\n"
                                   "Couldn't get recipient encryption key."));
+       } else if (val == -6) {
+               /* silent error */
        }
        toolbar_main_set_sensitive(mainwindow_get_mainwindow());
 }
 
-void compose_draft (gpointer data) 
+void compose_draft (gpointer data, guint action) 
+{
+       compose_draft_cb(data, action, NULL);   
+}
+
+#define DRAFTED_AT_EXIT "drafted_at_exit"
+void compose_clear_exit_drafts(void)
+{
+       gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                     DRAFTED_AT_EXIT, NULL);
+       if (is_file_exist(filepath))
+               g_unlink(filepath);
+       
+       g_free(filepath);
+}
+
+static void compose_register_draft(MsgInfo *info)
+{
+       gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                     DRAFTED_AT_EXIT, NULL);
+       FILE *fp = fopen(filepath, "ab");
+       
+       if (fp) {
+               fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
+                               info->msgnum);
+               fclose(fp);
+       }
+               
+       g_free(filepath);       
+}
+
+void compose_reopen_exit_drafts(void)
 {
-       compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);     
+       gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                     DRAFTED_AT_EXIT, NULL);
+       FILE *fp = fopen(filepath, "rb");
+       gchar buf[1024];
+       
+       if (fp) {
+               while (fgets(buf, sizeof(buf), fp)) {
+                       gchar **parts = g_strsplit(buf, "\t", 2);
+                       const gchar *folder = parts[0];
+                       int msgnum = parts[1] ? atoi(parts[1]):-1;
+                       
+                       if (folder && *folder && msgnum > -1) {
+                               FolderItem *item = folder_find_item_from_identifier(folder);
+                               MsgInfo *info = folder_item_get_msginfo(item, msgnum);
+                               if (info)
+                                       compose_reedit(info, FALSE);
+                       }
+                       g_strfreev(parts);
+               }       
+               fclose(fp);
+       }       
+       g_free(filepath);
+       compose_clear_exit_drafts();
 }
 
 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
@@ -8098,12 +8347,15 @@ static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
                        procmsg_msginfo_set_flags(newmsginfo, 0,
                                                  MSG_HAS_ATTACHMENT);
 
+               if (action == COMPOSE_DRAFT_FOR_EXIT) {
+                       compose_register_draft(newmsginfo);
+               }
                procmsg_msginfo_free(newmsginfo);
        }
        
        folder_item_scan(draft);
        
-       if (action == COMPOSE_QUIT_EDITING) {
+       if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
                lock = FALSE;
                g_mutex_unlock(compose->mutex); /* must be done before closing */
                compose_close(compose);
@@ -8226,6 +8478,11 @@ static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
        return TRUE;
 }
 
+void compose_close_toolbar(Compose *compose)
+{
+       compose_close_cb(compose, 0, NULL);
+}
+
 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
 {
        Compose *compose = (Compose *)data;
@@ -8417,15 +8674,21 @@ static void entry_allsel(GtkWidget *entry)
 
 static void compose_cut_cb(Compose *compose)
 {
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           )
                entry_cut_clipboard(compose->focused_editable);
 }
 
 static void compose_copy_cb(Compose *compose)
 {
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           )
                entry_copy_clipboard(compose->focused_editable);
 }
 
@@ -8445,8 +8708,11 @@ static void compose_paste_cb(Compose *compose)
 static void compose_paste_as_quote_cb(Compose *compose)
 {
        gint wrap_quote = prefs_common.linewrap_quote;
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           ) {
                /* let text_insert() (called directly or at a later time
                 * after the gtk_editable_paste_clipboard) know that 
                 * text is to be inserted as a quotation. implemented
@@ -8470,8 +8736,11 @@ static void compose_paste_no_wrap_cb(Compose *compose)
        gint prev_autowrap;
        GtkTextBuffer *buffer;
        BLOCK_WRAP();
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           )
                entry_paste_clipboard(compose, compose->focused_editable, FALSE,
                        GDK_SELECTION_CLIPBOARD, NULL);
        UNBLOCK_WRAP();
@@ -8482,8 +8751,11 @@ static void compose_paste_wrap_cb(Compose *compose)
        gint prev_autowrap;
        GtkTextBuffer *buffer;
        BLOCK_WRAP();
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           )
                entry_paste_clipboard(compose, compose->focused_editable, TRUE,
                        GDK_SELECTION_CLIPBOARD, NULL);
        UNBLOCK_WRAP();
@@ -8491,8 +8763,11 @@ static void compose_paste_wrap_cb(Compose *compose)
 
 static void compose_allsel_cb(Compose *compose)
 {
-       if (compose->focused_editable &&
-           GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
+       if (compose->focused_editable 
+#ifndef MAEMO
+           && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
+#endif
+           )
                entry_allsel(compose->focused_editable);
 }
 
@@ -8815,12 +9090,52 @@ static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
 
        if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
                compose->focused_editable = widget;
+       
+#ifdef MAEMO
+       if (GTK_IS_TEXT_VIEW(widget) 
+           && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
+               gtk_widget_ref(compose->notebook);
+               gtk_widget_ref(compose->edit_vbox);
+               gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
+               gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
+               gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
+               gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
+               gtk_widget_unref(compose->notebook);
+               gtk_widget_unref(compose->edit_vbox);
+               g_signal_handlers_block_by_func(G_OBJECT(widget),
+                                       G_CALLBACK(compose_grab_focus_cb),
+                                       compose);
+               gtk_widget_grab_focus(widget);
+               g_signal_handlers_unblock_by_func(G_OBJECT(widget),
+                                       G_CALLBACK(compose_grab_focus_cb),
+                                       compose);
+       } else if (!GTK_IS_TEXT_VIEW(widget) 
+                  && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
+               gtk_widget_ref(compose->notebook);
+               gtk_widget_ref(compose->edit_vbox);
+               gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
+               gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
+               gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
+               gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
+               gtk_widget_unref(compose->notebook);
+               gtk_widget_unref(compose->edit_vbox);
+               g_signal_handlers_block_by_func(G_OBJECT(widget),
+                                       G_CALLBACK(compose_grab_focus_cb),
+                                       compose);
+               gtk_widget_grab_focus(widget);
+               g_signal_handlers_unblock_by_func(G_OBJECT(widget),
+                                       G_CALLBACK(compose_grab_focus_cb),
+                                       compose);
+       }
+#endif
 }
 
 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
 {
        compose->modified = TRUE;
+#ifndef MAEMO
        compose_set_title(compose);
+#endif
 }
 
 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
@@ -8912,10 +9227,13 @@ static void compose_attach_drag_received_cb (GtkWidget          *widget,
            && gtk_drag_get_source_widget(context) != 
                summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
                list = uri_list_extract_filenames((const gchar *)data->data);
-               for (tmp = list; tmp != NULL; tmp = tmp->next)
+               for (tmp = list; tmp != NULL; tmp = tmp->next) {
+                       gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
                        compose_attach_append
                                (compose, (const gchar *)tmp->data,
-                                (const gchar *)tmp->data, NULL);
+                                utf8_filename, NULL);
+                       g_free(utf8_filename);
+               }
                if (list) compose_changed_cb(NULL, compose);
                list_free_strings(list);
                g_list_free(list);
@@ -9121,10 +9439,6 @@ static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
                                    ComposeHeaderEntry *headerentry)
 {
        if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
-               headerentry->compose->header_list =
-                       g_slist_append(headerentry->compose->header_list,
-                                      headerentry);
-               
                compose_create_header_entry(headerentry->compose);
                g_signal_handlers_disconnect_matched
                        (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
@@ -9178,7 +9492,7 @@ static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
                gtk_text_buffer_place_cursor(buffer, iter);
 
                compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
-                                                 _("Quote format error."));
+                                                 _("Quote format error at line %d."));
                quote_fmt_reset_vartable();
                g_free(new_text);
                g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
@@ -9482,29 +9796,29 @@ static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
                gchar *entry = gtk_editable_get_chars(
                                                                GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
 
-               if ( strcasecmp(header, (prefs_common.trans_hdr ? gettext("To:") : "To:")) == 0 ) {
+               if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
                        if ( newmsginfo->to == NULL ) {
                                newmsginfo->to = g_strdup(entry);
-                       } else {
+                       } else if (entry && *entry) {
                                gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
                                g_free(newmsginfo->to);
                                newmsginfo->to = tmp;
                        }
                } else
-               if ( strcasecmp(header, (prefs_common.trans_hdr ? gettext("Cc:") : "Cc:")) == 0 ) {
+               if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
                        if ( newmsginfo->cc == NULL ) {
                                newmsginfo->cc = g_strdup(entry);
-                       } else {
+                       } else if (entry && *entry) {
                                gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
                                g_free(newmsginfo->cc);
                                newmsginfo->cc = tmp;
                        }
                } else
                if ( strcasecmp(header,
-                                               (prefs_common.trans_hdr ? gettext("Newsgroups:") : "Newsgroups:")) == 0 ) {
+                                               prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
                        if ( newmsginfo->newsgroups == NULL ) {
                                newmsginfo->newsgroups = g_strdup(entry);
-                       } else {
+                       } else if (entry && *entry) {
                                gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
                                g_free(newmsginfo->newsgroups);
                                newmsginfo->newsgroups = tmp;