Switch core from strerror to g_strerror
[claws.git] / src / compose.c
index 41f0554bd956c370d9c2506b5d94355f4191cf1b..926a033a4c818cb02cb0d90e7e1abd4ae0438746 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2015 Hiroyuki Yamamoto and the Claws Mail team
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
- * 
+ *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -84,7 +84,6 @@
 #include "procmime.h"
 #include "statusbar.h"
 #include "about.h"
-#include "base64.h"
 #include "quoted-printable.h"
 #include "codeconv.h"
 #include "utils.h"
@@ -182,6 +181,9 @@ typedef enum {
 
 #define MAX_REFERENCES_LEN     999
 
+#define COMPOSE_DRAFT_TIMEOUT_UNSET -1
+#define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
+
 static GList *compose_list = NULL;
 static GSList *extra_headers = NULL;
 
@@ -365,7 +367,7 @@ static void compose_add_field_list  ( Compose *compose,
 
 static void compose_notebook_size_alloc (GtkNotebook *notebook,
                                         GtkAllocation *allocation,
-                                        Compose *compose);
+                                        GtkPaned *paned);
 static gboolean compose_edit_size_alloc (GtkEditable   *widget,
                                         GtkAllocation  *allocation,
                                         GtkSHRuler     *shruler);
@@ -560,6 +562,7 @@ static void compose_attach_update_label(Compose *compose);
 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
                                     gboolean respect_default_to);
 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
+static void from_name_activate_cb(GtkWidget *widget, gpointer data);
 
 static GtkActionEntry compose_popup_entries[] =
 {
@@ -734,6 +737,7 @@ static GtkRadioActionEntry compose_radio_enc_entries[] =
        ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
        ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
        ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
+       ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
        ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
        ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
        ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
@@ -760,8 +764,8 @@ static GtkTargetEntry compose_mime_types[] =
 
 static gboolean compose_put_existing_to_front(MsgInfo *info)
 {
-       GList *compose_list = compose_get_compose_list();
-       GList *elem = NULL;
+       const GList *compose_list = compose_get_compose_list();
+       const GList *elem = NULL;
        
        if (compose_list) {
                for (elem = compose_list; elem != NULL && elem->data != NULL; 
@@ -1203,7 +1207,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI
 
                for (curr = attach_files ; curr != NULL ; curr = curr->next) {
                        ainfo = (AttachInfo *) curr->data;
-                       compose_attach_append(compose, ainfo->file, ainfo->name,
+                       compose_attach_append(compose, ainfo->file, ainfo->file,
                                        ainfo->content_type, ainfo->charset);
                }
        }
@@ -1266,7 +1270,8 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI
        if (prefs_common.auto_exteditor)
                compose_exec_ext_editor(compose);
 
-       compose->draft_timeout_tag = -1;
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
+
        SCROLL_TO_CURSOR(compose);
 
        compose->modified = FALSE;
@@ -1707,7 +1712,7 @@ static Compose *compose_generic_reply(MsgInfo *msginfo,
        compose_set_title(compose);
 
        compose->updating = FALSE;
-       compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
        SCROLL_TO_CURSOR(compose);
        
        if (compose->deferred_destroy) {
@@ -1910,7 +1915,7 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
        compose_set_title(compose);
 
        compose->updating = FALSE;
-       compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
        SCROLL_TO_CURSOR(compose);
 
        if (compose->deferred_destroy) {
@@ -2058,7 +2063,7 @@ static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_
        compose_set_title(compose);
 
        compose->updating = FALSE;
-       compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
        SCROLL_TO_CURSOR(compose);
 
        if (compose->deferred_destroy) {
@@ -2105,6 +2110,44 @@ static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbu
        return FALSE;
 }
 
+static gboolean compose_update_folder_hook(gpointer source, gpointer data)
+{
+       FolderUpdateData *hookdata = (FolderUpdateData *)source;
+       Compose *compose = (Compose *)data;
+       FolderItem *old_item = NULL;
+       FolderItem *new_item = NULL;
+       gchar *old_id, *new_id;
+
+       if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
+        && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
+               return FALSE;
+
+       old_item = hookdata->item;
+       new_item = hookdata->item2;
+
+       old_id = folder_item_get_identifier(old_item);
+       new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
+
+       if (compose->targetinfo && compose->targetinfo->folder == old_item) {
+               debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
+               compose->targetinfo->folder = new_item;
+       }
+
+       if (compose->replyinfo && compose->replyinfo->folder == old_item) {
+               debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
+               compose->replyinfo->folder = new_item;
+       }
+
+       if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
+               debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
+               compose->fwdinfo->folder = new_item;
+       }
+
+       g_free(old_id);
+       g_free(new_id);
+       return FALSE;
+}
+
 static void compose_colorize_signature(Compose *compose)
 {
        GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
@@ -2131,21 +2174,21 @@ static void compose_colorize_signature(Compose *compose)
                                G_CALLBACK(text_inserted),              \
                                compose);                               \
 }
-#define UNBLOCK_WRAP() {                                               \
-       compose->autowrap = prev_autowrap;                              \
-       if (compose->autowrap) {                                        \
-               gint old = compose->draft_timeout_tag;                  \
-               compose->draft_timeout_tag = -2;                        \
-               compose_wrap_all(compose);                              \
-               compose->draft_timeout_tag = old;                       \
-       }                                                               \
-                                                                       \
-       g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
-                               G_CALLBACK(compose_changed_cb),         \
-                               compose);                               \
-       g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
-                               G_CALLBACK(text_inserted),              \
-                               compose);                               \
+#define UNBLOCK_WRAP() {                                                       \
+       compose->autowrap = prev_autowrap;                                      \
+       if (compose->autowrap) {                                                \
+               gint old = compose->draft_timeout_tag;                          \
+               compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
+               compose_wrap_all(compose);                                      \
+               compose->draft_timeout_tag = old;                               \
+       }                                                                       \
+                                                                               \
+       g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
+                               G_CALLBACK(compose_changed_cb),                 \
+                               compose);                                       \
+       g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
+                               G_CALLBACK(text_inserted),                      \
+                               compose);                                       \
 }
 
 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
@@ -2408,7 +2451,7 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
        compose_set_title(compose);
 
        compose->updating = FALSE;
-       compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
        SCROLL_TO_CURSOR(compose);
 
        if (compose->deferred_destroy) {
@@ -2518,7 +2561,7 @@ Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
        compose->modified = FALSE;
        compose_set_title(compose);
        compose->updating = FALSE;
-       compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
        SCROLL_TO_CURSOR(compose);
 
        if (compose->deferred_destroy) {
@@ -2531,7 +2574,7 @@ Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
        return compose;
 }
 
-GList *compose_get_compose_list(void)
+const GList *compose_get_compose_list(void)
 {
        return compose_list;
 }
@@ -3165,7 +3208,7 @@ static gboolean is_subscription(const gchar *ml_post, const gchar *from)
                *(strstr(left_from, "@")) = '\0';
        }
        
-       if (left_ml && left_from && right_ml && right_from
+       if (right_ml && right_from
        &&  !strncmp(left_from, left_ml, strlen(left_ml))
        &&  !strcmp(right_from, right_ml)) {
                result = TRUE;
@@ -3533,10 +3576,10 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
        gint len;
        FILE *fp;
        gboolean prev_autowrap;
-       gboolean badtxt = FALSE;
        struct stat file_stat;
        int ret;
        GString *file_contents = NULL;
+       ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
 
        cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
 
@@ -3602,9 +3645,17 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
 
                if (g_utf8_validate(buf, -1, NULL) == TRUE)
                        str = g_strdup(buf);
-               else
+               else {
+                       codeconv_set_strict(TRUE);
                        str = conv_codeset_strdup
                                (buf, cur_encoding, CS_INTERNAL);
+                       codeconv_set_strict(FALSE);
+
+                       if (!str) {
+                               result = COMPOSE_INSERT_INVALID_CHARACTER;
+                               break;
+                       }
+               }
                if (!str) continue;
 
                /* strip <CR> if DOS/Windows file,
@@ -3620,23 +3671,22 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
                g_free(str);
        }
 
-       gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
-       g_string_free(file_contents, TRUE);
+       if (result == COMPOSE_INSERT_SUCCESS) {
+               gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
 
-       compose_changed_cb(NULL, compose);
-       g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
-                                         G_CALLBACK(text_inserted),
-                                         compose);
-       compose->autowrap = prev_autowrap;
-       if (compose->autowrap)
-               compose_wrap_all(compose);
+               compose_changed_cb(NULL, compose);
+               g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
+                                                 G_CALLBACK(text_inserted),
+                                                 compose);
+               compose->autowrap = prev_autowrap;
+               if (compose->autowrap)
+                       compose_wrap_all(compose);
+       }
 
+       g_string_free(file_contents, TRUE);
        fclose(fp);
 
-       if (badtxt)
-               return COMPOSE_INSERT_INVALID_CHARACTER;
-       else 
-               return COMPOSE_INSERT_SUCCESS;
+       return result;
 }
 
 static gboolean compose_attach_append(Compose *compose, const gchar *file,
@@ -3854,7 +3904,7 @@ static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
 
                outfile = procmime_get_tmp_file_name(child);
                if ((err = procmime_get_part(outfile, child)) < 0)
-                       g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
+                       g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err));
                else {
                        gchar *content_type;
 
@@ -4293,7 +4343,7 @@ static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_it
        GtkTextIter iter, break_pos, end_of_line;
        gchar *quote_str = NULL;
        gint quote_len;
-       gboolean wrap_quote = prefs_common.linewrap_quote;
+       gboolean wrap_quote = force || prefs_common.linewrap_quote;
        gboolean prev_autowrap = compose->autowrap;
        gint startq_offset = -1, noq_offset = -1;
        gint uri_start = -1, uri_stop = -1;
@@ -4312,7 +4362,7 @@ static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_it
        if (force) {
                modified = TRUE;
        }
-       if (compose->draft_timeout_tag == -2) {
+       if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
                modified = TRUE;
        }
 
@@ -4329,7 +4379,7 @@ static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_it
        }
 
 
-       if (compose->draft_timeout_tag == -2) {
+       if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
                if (gtk_text_iter_ends_line(&iter)) {
                        while (gtk_text_iter_ends_line(&iter) &&
                               gtk_text_iter_forward_line(&iter))
@@ -4691,9 +4741,14 @@ static void compose_wrap_all_full(Compose *compose, gboolean force)
        buffer = gtk_text_view_get_buffer(text);
 
        gtk_text_buffer_get_start_iter(buffer, &iter);
+
+       undo_wrapping(compose->undostruct, TRUE);
+
        while (!gtk_text_iter_is_end(&iter) && modified)
                modified = compose_beautify_paragraph(compose, &iter, force);
 
+       undo_wrapping(compose->undostruct, FALSE);
+
 }
 
 static void compose_set_title(Compose *compose)
@@ -5100,7 +5155,7 @@ gint compose_send(Compose *compose)
                        alertpanel_error(_("Could not queue message for sending:\n\n"
                                           "Signature failed: %s"), privacy_get_error());
                } else if (val == -2 && errno != 0) {
-                       alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
+                       alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno));
                } else {
                        alertpanel_error(_("Could not queue message for sending."));
                }
@@ -5710,8 +5765,11 @@ static gint compose_write_body_to_file(Compose *compose, const gchar *file)
                (tmp, CS_INTERNAL, conv_get_locale_charset_str());
 
        g_free(tmp);
-       if (!chars) return -1;
-
+       if (!chars) {
+               fclose(fp);
+               claws_unlink(file);
+               return -1;
+       }
        /* write body */
        len = strlen(chars);
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
@@ -5967,17 +6025,25 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
        }
        /* Message-ID of message replying to */
        if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
-               gchar *folderid;
-               
-               folderid = folder_item_get_identifier(compose->replyinfo->folder);
+               gchar *folderid = NULL;
+
+               if (compose->replyinfo->folder)
+                       folderid = folder_item_get_identifier(compose->replyinfo->folder);
+               if (folderid == NULL)
+                       folderid = g_strdup("NULL");
+
                err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
                g_free(folderid);
        }
        /* Message-ID of message forwarding to */
        if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
-               gchar *folderid;
+               gchar *folderid = NULL;
                
-               folderid = folder_item_get_identifier(compose->fwdinfo->folder);
+               if (compose->fwdinfo->folder)
+                       folderid = folder_item_get_identifier(compose->fwdinfo->folder);
+               if (folderid == NULL)
+                       folderid = g_strdup("NULL");
+
                err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
                g_free(folderid);
        }
@@ -6084,13 +6150,14 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent)
                        }
                        continue;
                }
+               if (g_stat(ainfo->file, &statbuf) < 0)
+                       return -1;
+
                mimepart = procmime_mimeinfo_new();
                mimepart->content = MIMECONTENT_FILE;
                mimepart->data.filename = g_strdup(ainfo->file);
                mimepart->tmp = FALSE; /* or we destroy our attachment */
                mimepart->offset = 0;
-
-               g_stat(ainfo->file, &statbuf);
                mimepart->length = statbuf.st_size;
 
                type = g_strdup(ainfo->content_type);
@@ -6773,9 +6840,6 @@ static void compose_create_header_entry(Compose *compose)
        gboolean standard_header = FALSE;
        GtkListStore *model;
        GtkTreeIter iter;
-#if !(GTK_CHECK_VERSION(2,12,0))
-       GtkTooltips *tips = compose->tooltips;
-#endif
        
        headerentry = g_new0(ComposeHeaderEntry, 1);
 
@@ -6810,7 +6874,11 @@ static void compose_create_header_entry(Compose *compose)
        g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
                         G_CALLBACK(compose_grab_focus_cb), compose);
        gtk_widget_show(combo);
-       
+
+       /* Putting only the combobox child into focus chain of its parent causes
+        * the parent to be skipped when changing focus via Tab or Shift+Tab.
+        * This eliminates need to pres Tab twice in order to really get from the
+        * combobox to next widget. */
        GList *l = NULL;
        l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
        gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
@@ -6819,7 +6887,7 @@ static void compose_create_header_entry(Compose *compose)
        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 && (compose->draft_timeout_tag != -2)) {
+       if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
                const gchar *last_header_entry = gtk_entry_get_text(
                                GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
                string = headers;
@@ -7016,9 +7084,11 @@ static GtkWidget *compose_create_header(Compose *compose)
 
        header_table = gtk_table_new(2, 2, FALSE);
        gtk_widget_show(header_table);
-       gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
+       gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
-       gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
+       gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
+                       gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
+       gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
 
        gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
                                  0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
@@ -7320,8 +7390,9 @@ static void compose_dict_changed(void *data)
 {
        Compose *compose = (Compose *) data;
 
-       if(compose->gtkaspell && 
-                  compose->gtkaspell->recheck_when_changing_dict == FALSE)
+       if(!compose->gtkaspell)
+               return; 
+       if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
                return;
 
        gtkaspell_highlight_all(compose->gtkaspell);
@@ -7401,10 +7472,6 @@ static Compose *compose_create(PrefsAccount *account,
        compose->mutex = cm_mutex_new();
        compose->set_cursor_pos = -1;
 
-#if !(GTK_CHECK_VERSION(2,12,0))
-       compose->tooltips = tips;
-#endif
-
        window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
 
        gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
@@ -7594,6 +7661,7 @@ static Compose *compose_create(PrefsAccount *account,
        MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
        MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_ISO_8859_5, "Options/Encoding/Cyrillic/"CS_ISO_8859_5, GTK_UI_MANAGER_MENUITEM)
        MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
        MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
        MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
 
@@ -7653,7 +7721,6 @@ static Compose *compose_create(PrefsAccount *account,
        
        /* Notebook */
        notebook = gtk_notebook_new();
-       gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
        gtk_widget_show(notebook);
 
        /* header labels and entries */
@@ -7693,7 +7760,7 @@ static Compose *compose_create(PrefsAccount *account,
        gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
        gtk_widget_show(subject);
 
-       label = gtk_label_new(_("Subject:"));
+       label = gtk_label_new_with_mnemonic(_("S_ubject:"));
        gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
        gtk_widget_show(label);
 
@@ -7705,6 +7772,7 @@ static Compose *compose_create(PrefsAccount *account,
        gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
        g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
                         G_CALLBACK(compose_grab_focus_cb), compose);
+       gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
        gtk_widget_show(subject_entry);
        compose->subject_entry = subject_entry;
        gtk_container_add(GTK_CONTAINER(subject_frame), subject);
@@ -7743,8 +7811,6 @@ static Compose *compose_create(PrefsAccount *account,
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
        
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
-       g_signal_connect(G_OBJECT(notebook), "size_allocate",
-                        G_CALLBACK(compose_notebook_size_alloc), compose);     
        g_signal_connect_after(G_OBJECT(text), "size_allocate",
                               G_CALLBACK(compose_edit_size_alloc),
                               ruler);
@@ -7781,8 +7847,12 @@ static Compose *compose_create(PrefsAccount *account,
        /* pane between attach clist and text */
        paned = gtk_vpaned_new();
        gtk_container_add(GTK_CONTAINER(vbox2), paned);
-       gtk_paned_add1(GTK_PANED(paned), notebook);
-       gtk_paned_add2(GTK_PANED(paned), edit_vbox);
+       gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
+       gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
+       gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
+       g_signal_connect(G_OBJECT(notebook), "size_allocate",
+                        G_CALLBACK(compose_notebook_size_alloc), paned);
+
        gtk_widget_show_all(paned);
 
 
@@ -7886,7 +7956,12 @@ static Compose *compose_create(PrefsAccount *account,
        compose->exteditor_file    = NULL;
        compose->exteditor_pid     = -1;
        compose->exteditor_tag     = -1;
-       compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
+
+       compose->folder_update_callback_id =
+               hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
+                               compose_update_folder_hook,
+                               (gpointer) compose);
 
 #if USE_ENCHANT
        cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
@@ -7988,12 +8063,10 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
        GtkWidget *hbox;
        GtkWidget *optmenu;
        GtkWidget *optmenubox;
+       GtkWidget *fromlabel;
        GtkListStore *menu;
        GtkTreeIter iter;
        GtkWidget *from_name = NULL;
-#if !(GTK_CHECK_VERSION(2,12,0))
-       GtkTooltips *tips = compose->tooltips;
-#endif
 
        gint num = 0, def_menu = 0;
        
@@ -8004,11 +8077,13 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
        optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
        menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
 
-       hbox = gtk_hbox_new(FALSE, 6);
+       hbox = gtk_hbox_new(FALSE, 4);
        from_name = gtk_entry_new();
        
        g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
                         G_CALLBACK(compose_grab_focus_cb), compose);
+       g_signal_connect_after(G_OBJECT(from_name), "activate",
+                        G_CALLBACK(from_name_activate_cb), optmenu);
 
        for (; accounts != NULL; accounts = accounts->next, num++) {
                PrefsAccount *ac = (PrefsAccount *)accounts->data;
@@ -8016,7 +8091,7 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
 
                if (ac == compose->account) def_menu = num;
 
-               name = g_markup_printf_escaped(_("From: <i>%s</i>"),
+               name = g_markup_printf_escaped(_("<i>%s</i>"),
                                       ac->account_name);
                
                if (ac == compose->account) {
@@ -8046,8 +8121,21 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
                         G_CALLBACK(compose_entry_popup_extend),
                         NULL);
 
+       fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
+       gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
+
+       gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
        gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
        gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
+
+       /* Putting only the GtkEntry into focus chain of parent hbox causes
+        * the account selector combobox next to it to be unreachable when
+        * navigating widgets in GtkTable with up/down arrow keys.
+        * Note: gtk_widget_set_can_focus() was not enough. */
+       GList *l = NULL;
+       l = g_list_prepend(l, from_name);
+       gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
+       g_list_free(l);
        
        CLAWS_SET_TIP(optmenubox,
                _("Account to use for this email"));
@@ -8250,6 +8338,7 @@ static void compose_set_out_encoding(Compose *compose)
                case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
                case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
                case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
+               case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
                case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
                case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
                case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
@@ -8593,6 +8682,24 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl)
                }
        }
 
+       if (tmpl->replyto && *tmpl->replyto != '\0') {
+#ifdef USE_ENCHANT
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
+                               compose->gtkaspell);
+#else
+               quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
+#endif
+               quote_fmt_scan_string(tmpl->replyto);
+               quote_fmt_parse();
+
+               buf = quote_fmt_get_buffer();
+               if (buf == NULL) {
+                       alertpanel_error(_("Template Reply-To format error."));
+               } else {
+                       compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
+               }
+       }
+
        /* process the subject */
        if (tmpl->subject && *tmpl->subject != '\0') {
 #ifdef USE_ENCHANT
@@ -8628,6 +8735,7 @@ static void compose_destroy(Compose *compose)
                compose->deferred_destroy = TRUE;
                return;
        }
+
        /* NOTE: address_completion_end() does nothing with the window
         * however this may change. */
        address_completion_end(compose->window);
@@ -8643,6 +8751,9 @@ static void compose_destroy(Compose *compose)
 
        g_hash_table_destroy(compose->email_hashtable);
 
+       hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
+                       compose->folder_update_callback_id);
+
        procmsg_msginfo_free(compose->targetinfo);
        procmsg_msginfo_free(compose->replyinfo);
        procmsg_msginfo_free(compose->fwdinfo);
@@ -8858,7 +8969,7 @@ static void compose_attach_property(GtkAction *action, gpointer data)
                gtk_widget_hide(attach_prop.window);
                gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
                
-               if (cancelled) 
+               if (cancelled)
                        break;
 
                entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
@@ -9284,6 +9395,8 @@ static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
        if (buf[0] == '0') {            /* success */
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
                GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
+               GtkTextIter start, end;
+               gchar *chars;
 
                gtk_text_buffer_set_text(buffer, "", -1);
                compose_insert_file(compose, compose->exteditor_file);
@@ -9292,6 +9405,14 @@ static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
 
                if (claws_unlink(compose->exteditor_file) < 0)
                        FILE_OP_ERROR(compose->exteditor_file, "unlink");
+
+               buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
+               gtk_text_buffer_get_start_iter(buffer, &start);
+               gtk_text_buffer_get_end_iter(buffer, &end);
+               chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
+               if (chars && strlen(chars) > 0)
+                       compose->modified = TRUE;
+               g_free(chars);
        } else if (buf[0] == '1') {     /* failed */
                g_warning("Couldn't exec external editor\n");
                if (claws_unlink(compose->exteditor_file) < 0)
@@ -9406,9 +9527,9 @@ static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
 
 static void compose_notebook_size_alloc(GtkNotebook *notebook,
                                        GtkAllocation *allocation,
-                                       Compose *compose)
+                                       GtkPaned *paned)
 {
-       prefs_common.compose_notebook_height = allocation->height;
+       prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
 }
 
 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
@@ -9464,7 +9585,7 @@ static void account_activated(GtkComboBox *optmenu, gpointer data)
 
        /* Get ID of active account in the combo box */
        menu = gtk_combo_box_get_model(optmenu);
-       gtk_combo_box_get_active_iter(optmenu, &iter);
+       cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
        gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
 
        ac = account_find_from_id(account_id);
@@ -9631,7 +9752,7 @@ static void compose_send_cb(GtkAction *action, gpointer data)
        
        if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
                g_source_remove(compose->draft_timeout_tag);
-               compose->draft_timeout_tag = -1;
+               compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
        }
 
        compose_send(compose);
@@ -9653,7 +9774,7 @@ static void compose_send_later_cb(GtkAction *action, gpointer data)
        } else if (val == -1) {
                alertpanel_error(_("Could not queue message."));
        } else if (val == -2) {
-               alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
+               alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
        } else if (val == -3) {
                if (privacy_peek_error())
                alertpanel_error(_("Could not queue message for sending:\n\n"
@@ -9716,7 +9837,7 @@ gboolean compose_draft (gpointer data, guint action)
                debug_print("couldn't lock mutex, probably sending\n");
                return FALSE;
        }
-       
+
        lock = TRUE;
 
        tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
@@ -9754,17 +9875,25 @@ gboolean compose_draft (gpointer data, guint action)
 
        /* Message-ID of message replying to */
        if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
-               gchar *folderid;
-               
-               folderid = folder_item_get_identifier(compose->replyinfo->folder);
+               gchar *folderid = NULL;
+
+               if (compose->replyinfo->folder)
+                       folderid = folder_item_get_identifier(compose->replyinfo->folder);
+               if (folderid == NULL)
+                       folderid = g_strdup("NULL");
+
                err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
                g_free(folderid);
        }
        /* Message-ID of message forwarding to */
        if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
-               gchar *folderid;
-               
-               folderid = folder_item_get_identifier(compose->fwdinfo->folder);
+               gchar *folderid = NULL;
+
+               if (compose->fwdinfo->folder)
+                       folderid = folder_item_get_identifier(compose->fwdinfo->folder);
+               if (folderid == NULL)
+                       folderid = g_strdup("NULL");
+
                err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
                g_free(folderid);
        }
@@ -11095,10 +11224,7 @@ static void compose_insert_drag_received_cb (GtkWidget         *widget,
                g_list_free(list);
                gtk_drag_finish(drag_context, TRUE, FALSE, time);
                return;
-       } else {
-               return;
        }
-       gtk_drag_finish(drag_context, TRUE, FALSE, time);
 }
 
 static void compose_header_drag_received_cb (GtkWidget         *widget,
@@ -11217,7 +11343,7 @@ static void compose_headerentry_changed_cb(GtkWidget *entry,
 
 static gboolean compose_defer_auto_save_draft(Compose *compose)
 {
-       compose->draft_timeout_tag = -1;
+       compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
        compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
        return FALSE;
 }
@@ -11341,7 +11467,7 @@ static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
 
        if (compose_can_autosave(compose) && 
            gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
-           compose->draft_timeout_tag != -2 /* disabled while loading */)
+           compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
                compose->draft_timeout_tag = g_timeout_add
                        (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
 }
@@ -11453,16 +11579,27 @@ gboolean compose_close(Compose *compose)
 {
        gint x, y;
 
+       cm_return_val_if_fail(compose, FALSE);
+
        if (!g_mutex_trylock(compose->mutex)) {
                /* we have to wait for the (possibly deferred by auto-save)
                 * drafting to be done, before destroying the compose under
                 * it. */
                debug_print("waiting for drafting to finish...\n");
                compose_allow_user_actions(compose, FALSE);
-               g_timeout_add (500, (GSourceFunc) compose_close, compose);
-               return FALSE;
+               if (compose->close_timeout_tag == 0) {
+                       compose->close_timeout_tag = 
+                               g_timeout_add (500, (GSourceFunc) compose_close,
+                               compose);
+               }
+               return TRUE;
        }
-       cm_return_val_if_fail(compose, FALSE);
+
+       if (compose->draft_timeout_tag >= 0) {
+               g_source_remove(compose->draft_timeout_tag);
+               compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
+       }
+
        gtkut_widget_get_uposition(compose->window, &x, &y);
        if (!compose->batch) {
                prefs_common.compose_x = x;
@@ -11757,6 +11894,12 @@ static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
        gtk_widget_grab_focus(compose->text);
 }
 
+static void from_name_activate_cb(GtkWidget *widget, gpointer data)
+{
+       gtk_combo_box_popup(GTK_COMBO_BOX(data));
+}
+
+
 /*
  * End of Source.
  */