fix detection of account in --compose and --compose-from-file where the From value...
[claws.git] / src / compose.c
index 833d31708da083187b6f2000bac217c094057902..c88d9b2809bd8de4b8a550c1858ba7b284cee076 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2012 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
@@ -19,6 +19,7 @@
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
+#include "claws-features.h"
 #endif
 
 #include "defs.h"
@@ -93,6 +94,7 @@
 #include "alertpanel.h"
 #include "manage_window.h"
 #include "folder.h"
+#include "folder_item_prefs.h"
 #include "addr_compl.h"
 #include "quote_fmt.h"
 #include "undo.h"
@@ -181,6 +183,7 @@ typedef enum {
 #define MAX_REFERENCES_LEN     999
 
 static GList *compose_list = NULL;
+static GSList *extra_headers = NULL;
 
 static Compose *compose_generic_new                    (PrefsAccount   *account,
                                                 const gchar    *to,
@@ -501,6 +504,10 @@ static gboolean compose_drag_drop      (GtkWidget *widget,
                                             GdkDragContext *drag_context,
                                             gint x, gint y,
                                             guint time, gpointer user_data);
+static gboolean completion_set_focus_to_subject
+                                       (GtkWidget    *widget,
+                                        GdkEventKey  *event,
+                                        Compose      *user_data);
 
 static void text_inserted              (GtkTextBuffer  *buffer,
                                         GtkTextIter    *iter,
@@ -550,6 +557,7 @@ static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
 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 GtkActionEntry compose_popup_entries[] =
 {
@@ -597,9 +605,9 @@ static GtkActionEntry compose_entries[] =
        {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
 
        {"Edit/SpecialPaste",           NULL, N_("_Special paste") },
-       {"Edit/SpecialPaste/AsQuotation",       NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
-       {"Edit/SpecialPaste/Wrapped",   NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
-       {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
+       {"Edit/SpecialPaste/AsQuotation",       NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
+       {"Edit/SpecialPaste/Wrapped",   NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
+       {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
 
        {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
 
@@ -806,8 +814,8 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
        GdkColormap *cmap;
        gboolean success[8];
        int i;
-#endif
        GdkColor color[8];
+#endif
 
        buffer = gtk_text_view_get_buffer(text);
 
@@ -869,6 +877,7 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
        compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
        compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
 
+#if !GTK_CHECK_VERSION(2, 24, 0)
        color[0] = quote_color1;
        color[1] = quote_color2;
        color[2] = quote_color3;
@@ -877,16 +886,13 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
        color[5] = quote_bgcolor3;
        color[6] = signature_color;
        color[7] = uri_color;
-#if !GTK_CHECK_VERSION(2, 24, 0)
+
        cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
        gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
 
        for (i = 0; i < 8; i++) {
                if (success[i] == FALSE) {
-                       GtkStyle *style;
-
                        g_warning("Compose: color allocation failed.\n");
-                       style = gtk_widget_get_style(GTK_WIDGET(text));
                        quote_color1 = quote_color2 = quote_color3 = 
                                quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
                                signature_color = uri_color = black;
@@ -978,8 +984,15 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI
                /* mailto defines a from, check if we can get account prefs from it,
                   if not, the account prefs will be guessed using other ways, but we'll keep
                   the from anyway */
-               if (mailto_from)
+               if (mailto_from) {
                        mailto_account = account_find_from_address(mailto_from, TRUE);
+                       if (mailto_account == NULL) {
+                               gchar *tmp_from;
+                               Xstrdup_a(tmp_from, mailto_from, return NULL);
+                               extract_address(tmp_from);
+                               mailto_account = account_find_from_address(tmp_from, TRUE);
+                       }
+               }
                if (mailto_account)
                        account = mailto_account;
        }
@@ -1801,11 +1814,6 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
                const gchar *body_fmt = NULL;
                MsgInfo *full_msginfo;
 
-               if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
-                       body_fmt = gettext(prefs_common.fw_quotefmt);
-               else
-                       body_fmt = "";
-       
                full_msginfo = procmsg_msginfo_get_full_info(msginfo);
                if (!full_msginfo)
                        full_msginfo = procmsg_msginfo_copy(msginfo);
@@ -2343,8 +2351,8 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
        }
 
        if (fp != NULL) {
-               gboolean prev_autowrap = compose->autowrap;
-               GtkTextBuffer *buffer = textbuf;
+               gboolean prev_autowrap;
+               GtkTextBuffer *buffer;
                BLOCK_WRAP();
                while (fgets(buf, sizeof(buf), fp) != NULL) {
                        strcrchomp(buf);
@@ -2567,8 +2575,6 @@ void compose_entry_append(Compose *compose, const gchar *address,
                gchar *tmp = g_strdup(begin);
                gchar *o_tmp = tmp;
                tmp[cur-begin]='\0';
-               cur++;
-               begin = cur;
                while (*tmp == ' ' || *tmp == '\t')
                        tmp++;
                compose_add_header_entry(compose, header, tmp, pref_type);
@@ -2965,8 +2971,7 @@ static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
                                ref_id_list = g_slist_remove
                                        (ref_id_list, ref_id_list->next->data);
                        } else {
-                               slist_free_strings(ref_id_list);
-                               g_slist_free(ref_id_list);
+                               slist_free_strings_full(ref_id_list);
                                return NULL;
                        }
                } else
@@ -2980,8 +2985,7 @@ static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
                g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
        }
 
-       slist_free_strings(ref_id_list);
-       g_slist_free(ref_id_list);
+       slist_free_strings_full(ref_id_list);
 
        new_ref_str = new_ref->str;
        g_string_free(new_ref, FALSE);
@@ -3356,8 +3360,7 @@ static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
                        g_free(addr);
                }
                
-               slist_free_strings(cc_list);
-               g_slist_free(cc_list);
+               slist_free_strings_full(cc_list);
        }
        
        g_free(ac_email);
@@ -3508,6 +3511,7 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
        gboolean badtxt = FALSE;
        struct stat file_stat;
        int ret;
+       GString *file_contents = NULL;
 
        cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
 
@@ -3567,6 +3571,7 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
 
        cur_encoding = conv_get_locale_charset_str_no_utf8();
 
+       file_contents = g_string_new("");
        while (fgets(buf, sizeof(buf), fp) != NULL) {
                gchar *str;
 
@@ -3586,10 +3591,14 @@ static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *fi
                                if (str[len] == '\r') str[len] = '\n';
                }
 
-               gtk_text_buffer_insert(buffer, &iter, str, -1);
+               file_contents = g_string_append(file_contents, str);
                g_free(str);
        }
 
+       gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
+       g_string_free(file_contents, TRUE);
+
+       compose_changed_cb(NULL, compose);
        g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
                                          G_CALLBACK(text_inserted),
                                          compose);
@@ -4763,8 +4772,11 @@ compose_current_mail_account(void)
 static void compose_select_account(Compose *compose, PrefsAccount *account,
                                   gboolean init)
 {
-       gchar *from = NULL, *header;
+       gchar *from = NULL, *header = NULL;
        ComposeHeaderEntry *header_entry;
+#if GTK_CHECK_VERSION(2, 24, 0)
+       GtkTreeIter iter;
+#endif
 
        cm_return_if_fail(account != NULL);
 
@@ -4803,7 +4815,13 @@ static void compose_select_account(Compose *compose, PrefsAccount *account,
        }
        
        header_entry = (ComposeHeaderEntry *) compose->header_list->data;
+#if !GTK_CHECK_VERSION(2, 24, 0)
        header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
+#else
+       if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
+               gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
+                       header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
+#endif
        
        if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
                if (account->protocol == A_NNTP) {
@@ -4845,12 +4863,10 @@ gboolean compose_check_for_valid_recipient(Compose *compose) {
        gchar **strptr;
 
        /* free to and newsgroup list */
-        slist_free_strings(compose->to_list);
-       g_slist_free(compose->to_list);
+        slist_free_strings_full(compose->to_list);
        compose->to_list = NULL;
                        
-       slist_free_strings(compose->newsgroup_list);
-        g_slist_free(compose->newsgroup_list);
+       slist_free_strings_full(compose->newsgroup_list);
         compose->newsgroup_list = NULL;
 
        /* search header entries for to and newsgroup entries */
@@ -5753,17 +5769,13 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
        FILE *fp;
        GSList *cur;
        gint num;
-        static gboolean lock = FALSE;
        PrefsAccount *mailac = NULL, *newsac = NULL;
        gboolean err = FALSE;
 
        debug_print("queueing message...\n");
        cm_return_val_if_fail(compose->account != NULL, -1);
 
-        lock = TRUE;
-       
        if (compose_check_entries(compose, check_subject) == FALSE) {
-                lock = FALSE;
                if (compose->batch) {
                        gtk_widget_show_all(compose->window);
                }
@@ -5772,7 +5784,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 
        if (!compose->to_list && !compose->newsgroup_list) {
                g_warning("can't get recipient list.");
-               lock = FALSE;
                 return -1;
         }
 
@@ -5782,7 +5793,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                else if (cur_account && cur_account->protocol != A_NNTP)
                        mailac = cur_account;
                else if (!(mailac = compose_current_mail_account())) {
-                       lock = FALSE;
                        alertpanel_error(_("No account for sending mails available!"));
                        return -1;
                }
@@ -5792,7 +5802,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                 if (compose->account->protocol == A_NNTP)
                         newsac = compose->account;
                 else {
-                       lock = FALSE;
                        alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
                        return -1;
                }                       
@@ -5805,7 +5814,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
        if ((fp = g_fopen(tmp, "wb")) == NULL) {
                FILE_OP_ERROR(tmp, "fopen");
                g_free(tmp);
-               lock = FALSE;
                return -2;
        }
 
@@ -5867,7 +5875,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                if (compose->use_encryption) {
                        gchar *encdata;
                        if (!compose_warn_encryption(compose)) {
-                               lock = FALSE;
                                fclose(fp);
                                claws_unlink(tmp);
                                g_free(tmp);
@@ -5891,7 +5898,8 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                                err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
                                /* and if encdata was null, it means there's been a problem in 
                                 * key selection */
-                               lock = FALSE;
+                               if (err == TRUE)
+                                       g_warning("failed to write queue message");
                                fclose(fp);
                                claws_unlink(tmp);
                                g_free(tmp);
@@ -5938,7 +5946,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 
        if (compose->redirect_filename != NULL) {
                if (compose_redirect_write_to_file(compose, fp) < 0) {
-                       lock = FALSE;
                        fclose(fp);
                        claws_unlink(tmp);
                        g_free(tmp);
@@ -5947,7 +5954,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
        } else {
                gint result = 0;
                if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
-                       lock = FALSE;
                        fclose(fp);
                        claws_unlink(tmp);
                        g_free(tmp);
@@ -5959,14 +5965,12 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                fclose(fp);
                claws_unlink(tmp);
                g_free(tmp);
-               lock = FALSE;
                return -2;
        }
        if (fclose(fp) == EOF) {
                FILE_OP_ERROR(tmp, "fclose");
                claws_unlink(tmp);
                g_free(tmp);
-               lock = FALSE;
                return -2;
        }
 
@@ -5979,7 +5983,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                g_warning("can't find queue folder\n");
                claws_unlink(tmp);
                g_free(tmp);
-               lock = FALSE;
                return -1;
        }
        folder_item_scan(queue);
@@ -5987,7 +5990,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                g_warning("can't queue the message\n");
                claws_unlink(tmp);
                g_free(tmp);
-               lock = FALSE;
                return -1;
        }
        
@@ -6064,7 +6066,7 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent)
                    !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
                        mimepart->disposition = DISPOSITIONTYPE_INLINE;
                } else if (mimepart->type == MIMETYPE_TEXT) {
-                       if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
+                       if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
                                /* Text parts with no name come from multipart/alternative
                                * forwards. Make sure the recipient won't look at the 
                                * original HTML part by mistake. */
@@ -6614,6 +6616,54 @@ static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer
        gtk_widget_show(menuitem);
 }
 
+void compose_add_extra_header(gchar *header, GtkListStore *model)
+{
+       GtkTreeIter iter;
+       if (strcmp(header, "")) {
+               COMBOBOX_ADD(model, header, COMPOSE_TO);
+       }
+}
+
+void compose_add_extra_header_entries(GtkListStore *model)
+{
+       FILE *exh;
+       gchar *exhrc;
+       gchar buf[BUFFSIZE];
+       gint lastc;
+
+       if (extra_headers == NULL) {
+               exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
+               if ((exh = g_fopen(exhrc, "rb")) == NULL) {
+                       debug_print("extra headers file not found\n");
+                       goto extra_headers_done;
+               }
+               while (fgets(buf, BUFFSIZE, exh) != NULL) {
+                       lastc = strlen(buf) - 1;        /* remove trailing control chars */
+                       while (lastc >= 0 && buf[lastc] != ':')
+                               buf[lastc--] = '\0';
+                       if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
+                               buf[lastc] = '\0'; /* remove trailing : for comparison */
+                               if (custom_header_is_allowed(buf)) {
+                                       buf[lastc] = ':';
+                                       extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
+                               }
+                               else
+                                       g_message("disallowed extra header line: %s\n", buf);
+                       }
+                       else {
+                               if (buf[0] != '#')
+                                       g_message("invalid extra header line: %s\n", buf);
+                       }
+               }
+               fclose(exh);
+extra_headers_done:
+               g_free(exhrc);
+               extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
+               extra_headers = g_slist_reverse(extra_headers);
+       }
+       g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
+}
+
 static void compose_create_header_entry(Compose *compose) 
 {
        gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
@@ -6634,9 +6684,11 @@ static void compose_create_header_entry(Compose *compose)
        
        headerentry = g_new0(ComposeHeaderEntry, 1);
 
-       /* Combo box */
+       /* Combo box model */
        model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
+#if !GTK_CHECK_VERSION(2, 24, 0)
        combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
+#endif
        COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
                        COMPOSE_TO);
        COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
@@ -6649,11 +6701,26 @@ static void compose_create_header_entry(Compose *compose)
                        COMPOSE_REPLYTO);
        COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
                        COMPOSE_FOLLOWUPTO);
+       compose_add_extra_header_entries(model);
 
+       /* Combo box */
+#if GTK_CHECK_VERSION(2, 24, 0)
+       combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
+       GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+       gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
+       gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
+       gtk_combo_box_set_entry_text_column(combo, 0);
+#endif
        gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
-       g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
+       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);
+       
+       GList *l = NULL;
+       l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
+       gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
+       g_list_free(l);
+
        gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
                        compose->header_nextrow, compose->header_nextrow+1,
                        GTK_SHRINK, GTK_FILL, 0, 0);
@@ -7000,7 +7067,11 @@ static GtkWidget *compose_create_others(Compose *compose)
        g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
                         G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
 
+#if !GTK_CHECK_VERSION(2, 24, 0)
        savemsg_combo = gtk_combo_box_entry_new_text();
+#else
+       savemsg_combo = gtk_combo_box_text_new_with_entry();
+#endif
        compose->savemsg_checkbtn = savemsg_checkbtn;
        compose->savemsg_combo = savemsg_combo;
        gtk_widget_show(savemsg_combo);
@@ -7027,8 +7098,6 @@ static GtkWidget *compose_create_others(Compose *compose)
                         G_CALLBACK(compose_savemsg_select_cb),
                         compose);
 
-       rowcount++;
-
        return table;   
 }
 
@@ -7218,7 +7287,6 @@ static Compose *compose_create(PrefsAccount *account,
 
        UndoMain *undostruct;
 
-       gchar *titles[N_ATTACH_COLS];
        GtkWidget *popupmenu;
        GtkWidget *tmpl_menu;
        GtkActionGroup *action_group = NULL;
@@ -7234,16 +7302,11 @@ static Compose *compose_create(PrefsAccount *account,
        debug_print("Creating compose window...\n");
        compose = g_new0(Compose, 1);
 
-       titles[COL_MIMETYPE] = _("MIME type");
-       titles[COL_SIZE]     = _("Size");
-       titles[COL_NAME]     = _("Name");
-       titles[COL_CHARSET]  = _("Charset");
-
        compose->batch = batch;
        compose->account = account;
        compose->folder = folder;
        
-       compose->mutex = g_mutex_new();
+       compose->mutex = cm_mutex_new();
        compose->set_cursor_pos = -1;
 
 #if !(GTK_CHECK_VERSION(2,12,0))
@@ -7623,7 +7686,9 @@ static Compose *compose_create(PrefsAccount *account,
                         G_CALLBACK(compose_popup_menu), compose);
 #endif
        g_signal_connect(G_OBJECT(subject_entry), "changed",
-                        G_CALLBACK(compose_changed_cb), compose);
+                       G_CALLBACK(compose_changed_cb), compose);
+       g_signal_connect(G_OBJECT(subject_entry), "activate",
+                       G_CALLBACK(compose_subject_entry_activated), compose);
 
        /* drag and drop */
        gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
@@ -7635,6 +7700,9 @@ static Compose *compose_create(PrefsAccount *account,
        g_signal_connect(G_OBJECT(text), "drag-drop",
                         G_CALLBACK(compose_drag_drop),
                         compose);
+       g_signal_connect(G_OBJECT(text), "key-press-event",
+                        G_CALLBACK(completion_set_focus_to_subject),
+                        compose);
        gtk_widget_show_all(vbox);
 
        /* pane between attach clist and text */
@@ -8501,12 +8569,12 @@ static void compose_destroy(Compose *compose)
         * however this may change. */
        address_completion_end(compose->window);
 
-       slist_free_strings(compose->to_list);
-       g_slist_free(compose->to_list);
-       slist_free_strings(compose->newsgroup_list);
-       g_slist_free(compose->newsgroup_list);
-       slist_free_strings(compose->header_list);
-       g_slist_free(compose->header_list);
+       slist_free_strings_full(compose->to_list);
+       slist_free_strings_full(compose->newsgroup_list);
+       slist_free_strings_full(compose->header_list);
+
+       slist_free_strings_full(extra_headers);
+       extra_headers = NULL;
 
        compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
 
@@ -8569,7 +8637,7 @@ static void compose_destroy(Compose *compose)
        gtk_widget_destroy(compose->window);
        toolbar_destroy(compose->toolbar);
        g_free(compose->toolbar);
-       g_mutex_free(compose->mutex);
+       cm_mutex_free(compose->mutex);
        g_free(compose);
 }
 
@@ -8698,7 +8766,8 @@ static void compose_attach_property(GtkAction *action, gpointer data)
        gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
        gtk_widget_grab_focus(attach_prop.ok_btn);
        gtk_widget_show(attach_prop.window);
-       manage_window_set_transient(GTK_WINDOW(attach_prop.window));
+       gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
+                       GTK_WINDOW(compose->window));
 
        optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
        if (ainfo->encoding == ENC_UNKNOWN)
@@ -8848,7 +8917,11 @@ static void compose_attach_property_create(gboolean *cancelled)
        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
                         GTK_FILL, 0, 0, 0); 
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
+#if !GTK_CHECK_VERSION(2, 24, 0)
        mimetype_entry = gtk_combo_box_entry_new_text(); 
+#else
+       mimetype_entry = gtk_combo_box_text_new_with_entry();
+#endif
        gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
                         GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
                         
@@ -8870,7 +8943,11 @@ static void compose_attach_property_create(gboolean *cancelled)
 
        for (mime_type_list = strlist; mime_type_list != NULL; 
                mime_type_list = mime_type_list->next) {
+#if !GTK_CHECK_VERSION(2, 24, 0)
                gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
+#else
+               gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
+#endif
                g_free(mime_type_list->data);
        }
        g_list_free(strlist);
@@ -9314,8 +9391,12 @@ static void account_activated(GtkComboBox *optmenu, gpointer data)
        GSList *list, *saved_list = NULL;
        HeaderEntryState *state;
        GtkRcStyle *style = NULL;
+#if !GTK_CHECK_VERSION(3, 0, 0)
        static GdkColor yellow;
        static gboolean color_set = FALSE;
+#else
+       static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
+#endif
 
        /* Get ID of active account in the combo box */
        menu = gtk_combo_box_get_model(optmenu);
@@ -9343,12 +9424,14 @@ static void account_activated(GtkComboBox *optmenu, gpointer data)
                                        GTK_EDITABLE(hentry->entry), 0, -1);
                        state->type = hentry->type;
                                
+#if !GTK_CHECK_VERSION(3, 0, 0)
                        if (!color_set) {
                                gdk_color_parse("#f5f6be", &yellow);
                                color_set = gdk_colormap_alloc_color(
                                                        gdk_colormap_get_system(),
                                                        &yellow, FALSE, TRUE);
                        }
+#endif
                                
                        style = gtk_widget_get_modifier_style(hentry->entry);
                        state->entry_marked = gdk_color_equal(&yellow,
@@ -9430,14 +9513,8 @@ static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
                attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
                attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
                        
-               if (attach_nr_selected > 0)
-               {
-                       cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
-                       cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
-               } else {
-                       cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
-                       cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
-               }
+               cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
+               cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
                        
                gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
                               NULL, NULL, event->button, event->time);
@@ -9843,6 +9920,10 @@ static void compose_attach_cb(GtkAction *action, gpointer data)
        if (compose->redirect_filename != NULL)
                return;
 
+       /* Set focus_window properly, in case we were called via popup menu,
+        * which unsets it (via focus_out_event callback on compose window). */
+       manage_window_focus_in(compose->window, NULL, NULL);
+
        file_list = filesel_select_multiple_files_open(_("Select file"));
 
        if (file_list) {
@@ -10021,7 +10102,7 @@ static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
        tmpl = g_object_get_data(G_OBJECT(widget), "template");
        cm_return_if_fail(tmpl != NULL);
 
-       msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
+       msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
                              tmpl->name);
        val = alertpanel(_("Apply template"), msg,
                         _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
@@ -10362,14 +10443,12 @@ static void textview_move_backward_word (GtkTextView *text)
        GtkTextBuffer *buffer;
        GtkTextMark *mark;
        GtkTextIter ins;
-       gint count;
 
        cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
 
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
        mark = gtk_text_buffer_get_insert(buffer);
        gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
-       count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
        if (gtk_text_iter_backward_word_starts(&ins, 1))
                gtk_text_buffer_place_cursor(buffer, &ins);
 }
@@ -10835,6 +10914,21 @@ static gboolean compose_drag_drop(GtkWidget *widget,
        return TRUE;                                     
 }
 
+static gboolean completion_set_focus_to_subject
+                                       (GtkWidget    *widget,
+                                        GdkEventKey  *event,
+                                        Compose      *compose)
+{
+       cm_return_val_if_fail(compose != NULL, FALSE);
+
+       /* make backtab move to subject field */
+       if(event->keyval == GDK_KEY_ISO_Left_Tab) {
+               gtk_widget_grab_focus(compose->subject_entry);
+               return TRUE;
+       }
+       return FALSE;
+}
+
 static void compose_insert_drag_received_cb (GtkWidget         *widget,
                                             GdkDragContext     *drag_context,
                                             gint                x,
@@ -11569,6 +11663,15 @@ static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
 }
 #endif
 
+static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
+{
+       Compose *compose = (Compose *)data;
+
+       cm_return_if_fail(compose != NULL);
+
+       gtk_widget_grab_focus(compose->text);
+}
+
 /*
  * End of Source.
  */