[balsa/gtk4: 263/298] sendmsg-window: Build with gtk4




commit be0c676fe7316f0d94ceb022fbfd46c16b0d6929
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Wed Nov 11 11:50:58 2020 -0500

    sendmsg-window: Build with gtk4

 src/sendmsg-window.c | 2143 ++++++++++++++++++++++++++++++--------------------
 src/sendmsg-window.h |   32 +-
 2 files changed, 1304 insertions(+), 871 deletions(-)
---
diff --git a/src/sendmsg-window.c b/src/sendmsg-window.c
index f1c97439a..ce9cfae6d 100644
--- a/src/sendmsg-window.c
+++ b/src/sendmsg-window.c
@@ -90,8 +90,6 @@ typedef struct {
     BalsaSendmsg *bsmsg;
 } balsa_edit_with_gnome_data;
 
-typedef enum { QUOTE_HEADERS, QUOTE_ALL, QUOTE_NOPREFIX } QuoteType;
-
 static gint message_postpone(BalsaSendmsg * bsmsg);
 
 static void balsa_sendmsg_destroy_handler(BalsaSendmsg * bsmsg);
@@ -144,26 +142,6 @@ static void sw_buffer_set_undo(BalsaSendmsg * bsmsg, gboolean undo,
                               gboolean redo);
 #endif                          /* HAVE_GTKSOURCEVIEW */
 
-/* Standard DnD types */
-enum {
-    TARGET_MESSAGES,
-    TARGET_URI_LIST,
-    TARGET_EMAIL,
-    TARGET_STRING
-};
-
-static GtkTargetEntry drop_types[] = {
-    {"x-application/x-message-list", GTK_TARGET_SAME_APP, TARGET_MESSAGES},
-    {"text/uri-list", 0, TARGET_URI_LIST},
-    { "STRING",     0, TARGET_STRING },
-    { "text/plain", 0, TARGET_STRING },
-};
-
-static GtkTargetEntry email_field_drop_types[] = {
-    { "STRING",     0, TARGET_STRING },
-    { "text/plain", 0, TARGET_STRING }
-};
-
 static void bsmsg_set_subject_from_body(BalsaSendmsg * bsmsg,
                                         LibBalsaMessageBody * body,
                                         LibBalsaIdentity * ident);
@@ -272,16 +250,30 @@ G_DECLARE_FINAL_TYPE(BalsaAttachInfo,
 struct _BalsaAttachInfo {
     GObject parent_object;
 
-    BalsaSendmsg *bm;                 /* send message back reference */
+    BalsaSendmsg *bsmsg;              /* send message back reference */
 
     GtkWidget *popup_menu;            /* popup menu */
     LibbalsaVfs *file_uri;            /* file uri of the attachment */
-    gchar *uri_ref;                   /* external body URI reference */
-    gchar *force_mime_type;           /* force using this particular mime type */
-    gchar *charset;                   /* forced character set */
-    gboolean delete_on_destroy;       /* destroy the file when not used any more */
-    gint mode;                        /* LIBBALSA_ATTACH_AS_ATTACHMENT etc. */
+    char *uri_ref;                    /* external body URI reference */
+    char *force_mime_type;            /* force using this particular mime type */
+    char *charset;                    /* forced character set */
+    gboolean is_a_temp_file;          /* destroy the file when not used any more */
+    int mode;                         /* LIBBALSA_ATTACH_AS_ATTACHMENT etc. */
     LibBalsaMessageHeaders *headers;  /* information about a forwarded message */
+
+    /* passed to a response handler: */
+    GtkTreeIter iter;
+    GtkTreeModel *model;
+    GSimpleAction *action;
+    GVariant      *parameter;
+
+    /* passed to a thread: */
+    const char *filename;
+
+    /* passed to an idle handler */
+    gboolean is_fwd_message;
+    char *content_type;
+    char *icon_name;
 };
 
 static BalsaAttachInfo* balsa_attach_info_new();
@@ -307,17 +299,17 @@ balsa_attach_info_init(BalsaAttachInfo *info)
     info->file_uri = NULL;
     info->force_mime_type = NULL;
     info->charset = NULL;
-    info->delete_on_destroy = FALSE;
+    info->is_a_temp_file = FALSE;
     info->mode = LIBBALSA_ATTACH_AS_ATTACHMENT;
     info->headers = NULL;
 }
 
 static BalsaAttachInfo*
-balsa_attach_info_new(BalsaSendmsg *bm)
+balsa_attach_info_new(BalsaSendmsg *bsmsg)
 {
     BalsaAttachInfo * info = g_object_new(BALSA_TYPE_ATTACH_INFO, NULL);
 
-    info->bm = bm;
+    info->bsmsg = bsmsg;
     return info;
 }
 
@@ -329,8 +321,8 @@ balsa_attach_info_finalize(GObject * object)
     info = BALSA_ATTACH_INFO(object);
 
     /* unlink the file if necessary */
-    if (info->delete_on_destroy && info->file_uri) {
-        gchar * folder_name;
+    if (info->is_a_temp_file && info->file_uri != NULL) {
+        char *folder_name;
 
         /* unlink the file */
        g_debug("%s:%s: unlink `%s'", __FILE__, __func__,
@@ -340,16 +332,15 @@ balsa_attach_info_finalize(GObject * object)
         /* remove the folder if possible */
         folder_name = g_filename_from_uri(libbalsa_vfs_get_folder(info->file_uri),
                                           NULL, NULL);
-        if (folder_name) {
-            g_debug("%s:%s: rmdir `%s'", __FILE__, __func__,
-                         folder_name);
+        if (folder_name != NULL) {
+            g_debug("%s:%s: rmdir `%s'", __FILE__, __func__, folder_name);
             g_rmdir(folder_name);
             g_free(folder_name);
         }
     }
 
     /* clean up memory */
-    if (info->file_uri)
+    if (info->file_uri != NULL)
         g_object_unref(info->file_uri);
     g_free(info->force_mime_type);
     g_free(info->charset);
@@ -394,8 +385,7 @@ address_book_cb(LibBalsaAddressView * address_view,
     ab = g_object_get_data(G_OBJECT(bsmsg->window),
                            BALSA_SENDMSG_ADDRESS_BOOK_KEY);
     if (ab) {
-        gtk_window_present_with_time(GTK_WINDOW(ab),
-                                     gtk_get_current_event_time());
+        gtk_window_present(GTK_WINDOW(ab));
         return;
     }
 
@@ -411,7 +401,7 @@ address_book_cb(LibBalsaAddressView * address_view,
                            (GDestroyNotify) gtk_tree_row_reference_free);
     g_object_set_data(G_OBJECT(bsmsg->window),
                       BALSA_SENDMSG_ADDRESS_BOOK_KEY, ab);
-    gtk_widget_show_all(ab);
+    gtk_widget_show(ab);
 }
 
 /* Callback for the "response" signal for the address book dialog. */
@@ -429,7 +419,7 @@ address_book_response(GtkWidget * ab, gint response,
         g_free(t);
     }
 
-    gtk_widget_destroy(ab);
+    gtk_window_destroy(GTK_WINDOW(ab));
     g_object_set_data(G_OBJECT(parent), BALSA_SENDMSG_ADDRESS_BOOK_KEY,
                       NULL);
     gtk_widget_set_sensitive(GTK_WIDGET(address_view), TRUE);
@@ -449,20 +439,49 @@ sw_delete_draft(BalsaSendmsg * bsmsg)
                                       LIBBALSA_MESSAGE_FLAG_DELETED, 0);
 }
 
-static gint
+static void
+delete_handler_response(GtkDialog *dialog,
+                        int        reply,
+                        gpointer   user_data)
+{
+    BalsaSendmsg *bsmsg = user_data;
+    gboolean keep_open = FALSE;
+
+    gtk_window_destroy(GTK_WINDOW(dialog));
+
+    switch (reply) {
+    case GTK_RESPONSE_YES:
+        if (bsmsg->state == SENDMSG_STATE_MODIFIED)
+            if (!message_postpone(bsmsg))
+                keep_open = TRUE;
+        break;
+    case GTK_RESPONSE_NO:
+        if (!bsmsg->is_continue)
+            sw_delete_draft(bsmsg);
+        break;
+    default:
+        keep_open = TRUE;
+    }
+
+    if (!keep_open)
+        gtk_window_destroy(GTK_WINDOW(bsmsg->window));
+}
+
+static void
 delete_handler(BalsaSendmsg * bsmsg)
 {
     InternetAddressList *list;
     InternetAddress *ia;
     const gchar *tmp = NULL;
     gchar *free_me = NULL;
-    gint reply;
-    GtkWidget *d;
+    GtkWidget *dialog;
 
     g_debug("%s", __func__);
 
-    if (bsmsg->state == SENDMSG_STATE_CLEAN)
-        return FALSE;
+    if (bsmsg->state == SENDMSG_STATE_CLEAN) {
+        gtk_window_destroy(GTK_WINDOW(bsmsg->window));
+        return;
+    }
 
     list = libbalsa_address_view_get_list(bsmsg->recipient_view, "To:");
     ia = internet_address_list_get_address(list, 0);
@@ -474,46 +493,34 @@ delete_handler(BalsaSendmsg * bsmsg)
     if (!tmp || !*tmp)
         tmp = _("(No name)");
 
-    d = gtk_message_dialog_new(GTK_WINDOW(bsmsg->window),
-                               GTK_DIALOG_DESTROY_WITH_PARENT,
-                               GTK_MESSAGE_QUESTION,
-                               GTK_BUTTONS_YES_NO,
-                               _("The message to “%s” is modified.\n"
-                                 "Save message to Draftbox?"), tmp);
+    dialog = gtk_message_dialog_new(GTK_WINDOW(bsmsg->window),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_QUESTION,
+                                    GTK_BUTTONS_YES_NO,
+                                    _("The message to “%s” is modified.\n"
+                                      "Save message to Draftbox?"), tmp);
     g_free(free_me);
 #if HAVE_MACOSX_DESKTOP
-    libbalsa_macosx_menu_for_parent(d, GTK_WINDOW(bsmsg->window));
+    libbalsa_macosx_menu_for_parent(dialog, GTK_WINDOW(bsmsg->window));
 #endif
     g_object_unref(list);
-    gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_YES);
-    gtk_dialog_add_button(GTK_DIALOG(d),
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
+    gtk_dialog_add_button(GTK_DIALOG(dialog),
                           _("_Cancel"), GTK_RESPONSE_CANCEL);
-    reply = gtk_dialog_run(GTK_DIALOG(d));
-    gtk_widget_destroy(d);
 
-    switch (reply) {
-    case GTK_RESPONSE_YES:
-        if (bsmsg->state == SENDMSG_STATE_MODIFIED)
-            if (!message_postpone(bsmsg))
-                return TRUE;
-        break;
-    case GTK_RESPONSE_NO:
-        if (!bsmsg->is_continue)
-            sw_delete_draft(bsmsg);
-        break;
-    default:
-        return TRUE;
-    }
-
-    return FALSE;
+    g_signal_connect(dialog, "response", G_CALLBACK(delete_handler_response), bsmsg);
+    gtk_widget_show(dialog);
 }
 
-static gint
-delete_event_cb(GtkWidget * widget, GdkEvent * e, gpointer data)
+static gboolean
+sw_close_request_cb(GtkWidget * widget, GdkEvent * e, gpointer data)
 {
     BalsaSendmsg *bsmsg = data;
 
-    return delete_handler(bsmsg);
+    delete_handler(bsmsg);
+
+    /* Block closing; delete_handler will close if appropriate */
+    return TRUE;
 }
 
 static void
@@ -526,8 +533,7 @@ sw_close_activated(GSimpleAction * action,
     g_debug("close_window_cb: start");
     g_object_set_data(G_OBJECT(bsmsg->window), "destroying",
                       GINT_TO_POINTER(TRUE));
-    if(!delete_handler(bsmsg))
-       gtk_widget_destroy(bsmsg->window);
+    delete_handler(bsmsg);
     g_debug("close_window_cb: end");
 }
 
@@ -552,13 +558,13 @@ balsa_sendmsg_destroy_handler(BalsaSendmsg * bsmsg)
 {
     g_assert(bsmsg != NULL);
 
-    if (balsa_app.main_window) {
+    if (balsa_app.main_window != NULL) {
         g_signal_handler_disconnect(balsa_app.main_window,
                                     bsmsg->delete_sig_id);
         g_signal_handler_disconnect(balsa_app.main_window,
                                     bsmsg->identities_changed_id);
         g_object_weak_unref(G_OBJECT(balsa_app.main_window),
-                            (GWeakNotify) gtk_widget_destroy, bsmsg->window);
+                            (GWeakNotify) gtk_window_destroy, bsmsg->window);
     }
     g_debug("balsa_sendmsg_destroy()_handler: Start.");
 
@@ -587,14 +593,14 @@ balsa_sendmsg_destroy_handler(BalsaSendmsg * bsmsg)
     }
 
     g_debug("balsa_sendmsg_destroy_handler: Freeing bsmsg");
-    gtk_widget_destroy(bsmsg->window);
+    gtk_window_destroy(GTK_WINDOW(bsmsg->window));
     g_free(bsmsg->fcc_url);
     g_free(bsmsg->in_reply_to);
     g_list_free_full(bsmsg->references, g_free);
 
 #if !(HAVE_GSPELL || HAVE_GTKSPELL)
     if (bsmsg->spell_checker)
-        gtk_widget_destroy(bsmsg->spell_checker);
+        gtk_window_destroy(GTK_WINDOW(bsmsg->spell_checker));
 #endif                          /* HAVE_GTKSPELL */
     if (bsmsg->autosave_timeout_id != 0)
         g_source_remove(bsmsg->autosave_timeout_id);
@@ -698,7 +704,7 @@ edit_with_gnome_check(gpointer data) {
                 line[strlen(line) - 1] = '\0';
 
             if (libbalsa_str_has_prefix(line, _("Subject:")) == 0) {
-                gtk_entry_set_text(GTK_ENTRY(data_real->bsmsg->subject[1]),
+                gtk_editable_set_text(GTK_EDITABLE(data_real->bsmsg->subject[1]),
                                    line + strlen(_("Subject:")) + 1);
                 continue;
             }
@@ -791,7 +797,7 @@ sw_edit_activated(GSimpleAction * action,
         guint type;
 
         fprintf(tmp, "%s %s\n", _("Subject:"),
-                gtk_entry_get_text(GTK_ENTRY(bsmsg->subject[1])));
+                gtk_editable_get_text(GTK_EDITABLE(bsmsg->subject[1])));
         for (type = 0; type < G_N_ELEMENTS(address_types); type++) {
             InternetAddressList *list =
                 libbalsa_address_view_get_list(bsmsg->recipient_view,
@@ -1122,7 +1128,7 @@ update_bsmsg_identity(BalsaSendmsg* bsmsg, LibBalsaIdentity* ident)
     }
 
     /* change the subject to use the reply/forward strings */
-    subject = gtk_entry_get_text(GTK_ENTRY(bsmsg->subject[1]));
+    subject = gtk_editable_get_text(GTK_EDITABLE(bsmsg->subject[1]));
 
     /*
      * If the subject begins with the old reply string
@@ -1147,12 +1153,12 @@ update_bsmsg_identity(BalsaSendmsg* bsmsg, LibBalsaIdentity* ident)
     if (((replen = strlen(old_forward_string)) > 0) &&
        (strncmp(subject, old_reply_string, replen) == 0)) {
        tmpstr = g_strconcat(reply_string, &(subject[replen]), NULL);
-       gtk_entry_set_text(GTK_ENTRY(bsmsg->subject[1]), tmpstr);
+       gtk_editable_set_text(GTK_EDITABLE(bsmsg->subject[1]), tmpstr);
        g_free(tmpstr);
     } else if (((fwdlen = strlen(old_forward_string)) > 0) &&
               (strncmp(subject, old_forward_string, fwdlen) == 0)) {
        tmpstr = g_strconcat(forward_string, &(subject[fwdlen]), NULL);
-       gtk_entry_set_text(GTK_ENTRY(bsmsg->subject[1]), tmpstr);
+       gtk_editable_set_text(GTK_EDITABLE(bsmsg->subject[1]), tmpstr);
        g_free(tmpstr);
     } else {
         if ((replen == 0 && reply_type) ||
@@ -1284,7 +1290,7 @@ remove_attachment(GSimpleAction *action,
     BalsaAttachInfo *test_info;
 
     /* get the selected element */
-    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(info->bm->tree_view));
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(info->bsmsg->tree_view));
     if (!gtk_tree_selection_get_selected(selection, &model, &iter))
        return;
 
@@ -1299,30 +1305,49 @@ remove_attachment(GSimpleAction *action,
     g_object_unref(test_info);
 }
 
+static void
+change_attach_mode_response(GtkDialog *dialog,
+                            int        response_id,
+                            gpointer   user_data)
+{
+    BalsaAttachInfo *info = user_data;
+
+    if (dialog != NULL)
+        gtk_window_destroy(GTK_WINDOW(dialog));
+
+    if (response_id == GTK_RESPONSE_YES) {
+        /* change the attachment mode */
+        gtk_list_store_set(GTK_LIST_STORE(info->model), &info->iter,
+                           ATTACH_MODE_COLUMN, info->mode, -1);
+        g_simple_action_set_state(info->action, info->parameter);
+    }
+
+    g_variant_unref(info->parameter);
+    gtk_popover_popdown((GtkPopover *) info->popup_menu);
+}
+
 /* change attachment mode - right mouse button callback */
 static void
 change_attach_mode(GSimpleAction *action,
                    GVariant      *parameter,
                    gpointer       user_data)
 {
-    gint new_mode = g_variant_get_int32(parameter);
     BalsaAttachInfo *info = user_data;
-    GtkTreeIter iter;
-    GtkTreeModel *model;
+    int new_mode;
     GtkTreeSelection *selection;
     BalsaAttachInfo *test_info;
-    gint result = GTK_RESPONSE_YES;
 
+    new_mode = g_variant_get_int32(parameter);
     if (new_mode == info->mode)
         return;
 
     /* get the selected element */
-    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(info->bm->tree_view));
-    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(info->bsmsg->tree_view));
+    if (!gtk_tree_selection_get_selected(selection, &info->model, &info->iter))
        return;
 
     /* make sure we got the right element */
-    gtk_tree_model_get(model, &iter, ATTACH_INFO_COLUMN, &test_info, -1);
+    gtk_tree_model_get(info->model, &info->iter, ATTACH_INFO_COLUMN, &test_info, -1);
     if (test_info != info) {
         g_object_unref(test_info);
 
@@ -1330,11 +1355,15 @@ change_attach_mode(GSimpleAction *action,
     }
     g_object_unref(test_info);
 
-    /* verify that the user *really* wants to attach as reference */
+    info->mode = new_mode;
+    info->action = action;
+    info->parameter = g_variant_ref(parameter);
     if (new_mode == LIBBALSA_ATTACH_AS_EXTBODY) {
-       GtkWidget *extbody_dialog, *parent;
+        /* verify that the user *really* wants to attach as reference */
+       GtkWidget *extbody_dialog;
+       GtkRoot *parent;
 
-       parent = gtk_widget_get_toplevel(info->bm->window);
+       parent = gtk_widget_get_root(info->bsmsg->window);
        extbody_dialog =
            gtk_message_dialog_new(GTK_WINDOW(parent),
                                   GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -1354,19 +1383,12 @@ change_attach_mode(GSimpleAction *action,
 #endif
        gtk_window_set_title(GTK_WINDOW(extbody_dialog),
                             _("Attach as Reference?"));
-       result = gtk_dialog_run(GTK_DIALOG(extbody_dialog));
-       gtk_widget_destroy(extbody_dialog);
-    }
 
-    if (result == GTK_RESPONSE_YES) {
-        /* change the attachment mode */
-        info->mode = new_mode;
-        gtk_list_store_set(GTK_LIST_STORE(model), &iter, ATTACH_MODE_COLUMN, info->mode, -1);
-        g_simple_action_set_state(action, parameter);
+        g_signal_connect(extbody_dialog, "response", G_CALLBACK(change_attach_mode_response), info);
+        gtk_widget_show(extbody_dialog);
+    } else {
+        change_attach_mode_response(NULL, GTK_RESPONSE_YES, info);
     }
-
-    if (GTK_IS_POPOVER(info->popup_menu))
-        gtk_popover_popdown((GtkPopover *) info->popup_menu);
 }
 
 
@@ -1394,30 +1416,39 @@ launch_app_activated(GSimpleAction *action,
 
 
 /* URL external body - right mouse button callback */
+static void
+open_attachment_finish(GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+    GtkWindow *parent = GTK_WINDOW(source_object);
+    char *url = user_data;
+    GError *error = NULL;
+
+    if (!gtk_show_uri_full_finish(parent, res, &error)) {
+        balsa_information(LIBBALSA_INFORMATION_WARNING,
+                          _("Error showing %s: %s\n"), url, error->message);
+        g_error_free(error);
+    }
+
+    g_free(url);
+}
+
 static void
 open_attachment(GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
 {
     BalsaAttachInfo *info = user_data;
-    GtkWidget *toplevel;
-    GError *err = NULL;
-    const gchar * uri;
+    GtkRoot *root;
+    const char * uri;
 
     uri = libbalsa_vfs_get_uri(info->file_uri);
 
     g_debug("open URL %s", uri);
-    toplevel = gtk_widget_get_toplevel(info->bm->window);
-    if (gtk_widget_is_toplevel(toplevel)) {
-        gtk_show_uri_on_window(GTK_WINDOW(toplevel), uri,
-                               gtk_get_current_event_time(), &err);
-    }
-    if (err) {
-        balsa_information(LIBBALSA_INFORMATION_WARNING,
-                         _("Error showing %s: %s\n"),
-                         uri, err->message);
-        g_error_free(err);
-    }
+    root = gtk_widget_get_root(info->bsmsg->window);
+    gtk_show_uri_full(GTK_WINDOW(root), uri, GDK_CURRENT_TIME, NULL,
+                      open_attachment_finish, g_strdup(uri));
 }
 
 static GtkWidget * sw_attachment_list(BalsaSendmsg *bsmsg);
@@ -1428,10 +1459,10 @@ show_attachment_widget(BalsaSendmsg *bsmsg)
     GtkWidget *child;
 
     outer_paned = GTK_PANED(bsmsg->paned);
-    child = gtk_paned_get_child1(outer_paned);
+    child = gtk_paned_get_start_child(outer_paned);
 
     if (!GTK_IS_PANED(child)) {
-        gint position;
+        int position;
         GtkRequisition minimum_size;
         GtkWidget *paned;
         GtkPaned *inner_paned;
@@ -1441,23 +1472,20 @@ show_attachment_widget(BalsaSendmsg *bsmsg)
             gtk_widget_get_preferred_size(child, &minimum_size, NULL);
             position = minimum_size.height;
         }
-        gtk_container_remove(GTK_CONTAINER(bsmsg->paned),
-                             g_object_ref(child));
+        gtk_widget_unparent(g_object_ref(child));
 
         paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
-        gtk_widget_show(paned);
 
         inner_paned = GTK_PANED(paned);
-        gtk_paned_add1(inner_paned, child);
+        gtk_paned_set_start_child(inner_paned, child);
         g_object_unref(child);
 
         child = sw_attachment_list(bsmsg);
-        gtk_widget_show_all(child);
-        gtk_paned_add2(inner_paned, child);
+        gtk_paned_set_end_child(inner_paned, child);
         gtk_paned_set_position(inner_paned, position);
 
         gtk_widget_get_preferred_size(child, &minimum_size, NULL);
-        gtk_paned_add1(outer_paned, paned);
+        gtk_paned_set_start_child(outer_paned, paned);
         gtk_paned_set_position(outer_paned,
                                position + minimum_size.height);
     }
@@ -1472,12 +1500,57 @@ sw_charset_combo_box_changed(GtkComboBox * combo_box,
                              gtk_combo_box_get_active(combo_box) == 0);
 }
 
-static LibBalsaCodeset
-sw_get_user_codeset(BalsaSendmsg * bsmsg, gboolean * change_type,
-                    const gchar * mime_type, const char *fname)
+typedef struct {
+    BalsaSendmsg   *bsmsg;
+    GMutex          lock;
+    GCond           cond;
+
+    const char     *filename;
+    const char     *content_type;
+    gboolean       *change_type;
+    GtkWidget      *combo_box;
+    GtkWidget      *charset_button;
+
+    LibBalsaCodeset codeset;
+    gboolean        done;
+} user_codeset_data;
+
+static void
+sw_get_user_codeset_response(GtkDialog *dialog,
+                             int        response_id,
+                             gpointer   user_data)
+{
+    user_codeset_data *data = user_data;
+
+    g_mutex_lock(&data->lock);
+
+    if (response_id == GTK_RESPONSE_OK) {
+        if (data->change_type != NULL)
+            *data->change_type =
+                gtk_combo_box_get_active(GTK_COMBO_BOX(data->combo_box)) != 0;
+        if (data->change_type == NULL || !*data->change_type)
+           data->codeset = gtk_combo_box_get_active(GTK_COMBO_BOX(data->charset_button));
+    }
+
+    data->done = TRUE;
+
+    g_cond_signal(&data->cond);
+    g_mutex_unlock(&data->lock);
+
+    gtk_window_destroy(GTK_WINDOW(dialog));
+}
+
+static gboolean
+sw_get_user_codeset(gpointer user_data)
 {
+    user_codeset_data *data = user_data;
+
+    BalsaSendmsg *bsmsg   = data->bsmsg;
+    gboolean *change_type = data->change_type;
+    const char *mime_type = data->content_type;
+    const char *fname     = data->filename;
+
     GtkWidget *combo_box = NULL;
-    gint codeset = -1;
     GtkWidget *dialog =
         gtk_dialog_new_with_buttons(_("Choose character set"),
                                     GTK_WINDOW(bsmsg->window),
@@ -1486,10 +1559,9 @@ sw_get_user_codeset(BalsaSendmsg * bsmsg, gboolean * change_type,
                                     _("_OK"), GTK_RESPONSE_OK,
                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     NULL);
-    gchar *msg = g_strdup_printf
-        (_("File\n%s\nis not encoded in US-ASCII or UTF-8.\n"
-           "Please choose the character set used to encode the file."),
-         fname);
+    char *msg = g_strdup_printf(_("File\n%s\nis not encoded in US-ASCII or UTF-8.\n"
+                                  "Please choose the character set used to encode the file."),
+                                fname);
     GtkWidget *info = gtk_label_new(msg);
     GtkWidget *charset_button = libbalsa_charset_button_new();
     GtkWidget *content_box;
@@ -1503,19 +1575,17 @@ sw_get_user_codeset(BalsaSendmsg * bsmsg, gboolean * change_type,
 
     gtk_widget_set_margin_top(info, 5);
     gtk_widget_set_margin_bottom(info, 5);
-    gtk_container_add(GTK_CONTAINER(content_box), info);
+    gtk_box_append(GTK_BOX(content_box), info);
 
     gtk_widget_set_vexpand(charset_button, TRUE);
     gtk_widget_set_valign(charset_button, GTK_ALIGN_FILL);
     gtk_widget_set_margin_top(charset_button, 5);
     gtk_widget_set_margin_bottom(charset_button, 5);
-    gtk_container_add(GTK_CONTAINER(content_box), charset_button);
+    gtk_box_append(GTK_BOX(content_box), charset_button);
 
-    gtk_widget_show(info);
-    gtk_widget_show(charset_button);
     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
 
-    if (change_type) {
+    if (change_type != NULL) {
         GtkWidget *label = gtk_label_new(_("Attach as MIME type:"));
         GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
         combo_box = gtk_combo_box_text_new();
@@ -1524,11 +1594,11 @@ sw_get_user_codeset(BalsaSendmsg * bsmsg, gboolean * change_type,
         gtk_widget_set_valign(hbox, GTK_ALIGN_FILL);
         gtk_widget_set_margin_top(hbox, 5);
         gtk_widget_set_margin_bottom(hbox, 5);
-        gtk_container_add(GTK_CONTAINER(content_box), hbox);
+        gtk_box_append(GTK_BOX(content_box), hbox);
 
         gtk_widget_set_hexpand(label, TRUE);
         gtk_widget_set_halign(label, GTK_ALIGN_FILL);
-        gtk_container_add(GTK_CONTAINER(hbox), label);
+        gtk_box_append(GTK_BOX(hbox), label);
 
         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box),
                                        mime_type);
@@ -1541,51 +1611,70 @@ sw_get_user_codeset(BalsaSendmsg * bsmsg, gboolean * change_type,
 
         gtk_widget_set_hexpand(combo_box, TRUE);
         gtk_widget_set_halign(combo_box, GTK_ALIGN_FILL);
-        gtk_container_add(GTK_CONTAINER(hbox), combo_box);
-
-        gtk_widget_show_all(hbox);
+        gtk_box_append(GTK_BOX(hbox), combo_box);
     }
 
-    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
-        if (change_type)
-            *change_type =
-                gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box)) != 0;
-        if (!change_type || !*change_type)
-           codeset = gtk_combo_box_get_active(GTK_COMBO_BOX(charset_button));
-    }
+    data->change_type = change_type;
+    data->combo_box = combo_box;
+    data->charset_button = charset_button;
+
+    g_signal_connect(dialog, "response", G_CALLBACK(sw_get_user_codeset_response), data);
+    gtk_widget_show(dialog);
 
-    gtk_widget_destroy(dialog);
-    return (LibBalsaCodeset) codeset;
+    return G_SOURCE_REMOVE;
 }
 
 static gboolean
-sw_set_charset(BalsaSendmsg * bsmsg, const gchar * filename,
-               const gchar * content_type, gboolean * change_type,
-               gchar ** attach_charset)
+sw_set_charset(BalsaSendmsg *bsmsg,
+               const char   *filename,
+               const char   *content_type,
+               gboolean     *change_type,
+               char        **attach_charset)
 {
-    const gchar *charset;
+    const char *charset;
     LibBalsaTextAttribute attr;
 
+    g_assert(libbalsa_am_i_subthread());
+
     attr = libbalsa_text_attr_file(filename);
-    if ((gint) attr < 0)
+    if ((int) attr < 0)
         return FALSE;
 
-    if (attr == 0)
+    if ((int) attr == 0)
         charset = "us-ascii";
-    else if (attr & LIBBALSA_TEXT_HI_UTF8)
+    else if ((int) (attr & LIBBALSA_TEXT_HI_UTF8) != 0)
         charset = "UTF-8";
     else {
+        user_codeset_data data;
         LibBalsaCodesetInfo *info;
-        LibBalsaCodeset codeset =
-            sw_get_user_codeset(bsmsg, change_type, content_type, filename);
+
+        g_mutex_init(&data.lock);
+        g_cond_init(&data.cond);
+
+        data.bsmsg = bsmsg;
+        data.change_type = change_type;
+        data.content_type = content_type;
+        data.filename = filename;
+        data.codeset = (LibBalsaCodeset) (-1);
+
+        g_mutex_lock(&data.lock);
+        data.done = FALSE;
+        g_idle_add(sw_get_user_codeset, &data);
+        while (!data.done)
+            g_cond_wait(&data.cond, &data.lock);
+        g_mutex_unlock(&data.lock);
+
+        g_mutex_clear(&data.lock);
+        g_cond_clear(&data.cond);
+
         if (*change_type)
             return TRUE;
-        if (codeset == (LibBalsaCodeset) (-1))
+        if (data.codeset == (LibBalsaCodeset) (-1))
             return FALSE;
 
-        info = &libbalsa_codeset_info[codeset];
+        info = &libbalsa_codeset_info[data.codeset];
         charset = info->std;
-        if (info->win && (attr & LIBBALSA_TEXT_HI_CTRL)) {
+        if (info->win != NULL && (attr & LIBBALSA_TEXT_HI_CTRL) != 0) {
             charset = info->win;
             balsa_information_parented(GTK_WINDOW(bsmsg->window),
                                        LIBBALSA_INFORMATION_WARNING,
@@ -1649,24 +1738,19 @@ get_fwd_mail_headers(const gchar *mailfile_uri)
 }
 
 
-/* add_attachment:
-   adds given filename (uri format) to the list.
-*/
+/*
+ * add_attachment:
+ * adds given filename (uri format) to the list.
+ */
 
-gboolean
-add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
-               gboolean is_a_temp_file, const gchar *forced_mime_type)
+static gboolean
+add_attachment_idle(gpointer user_data)
 {
-    LibbalsaVfs * file_uri;
+    BalsaAttachInfo *attach_data = user_data;
+    char *utf8name;
     GtkTreeModel *model;
     GtkTreeIter iter;
-    BalsaAttachInfo *attach_data;
-    gboolean can_inline, is_fwd_message;
-    gchar *content_type = NULL;
-    gchar *utf8name;
-    GError *err = NULL;
-    GdkPixbuf *pixbuf;
-    gchar *content_desc;
+    gboolean can_inline;
     GSimpleActionGroup *simple;
     static GActionEntry attachment_entries[] = {
         {"new-mode", NULL, "i", "1", change_attach_mode},
@@ -1676,59 +1760,14 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
     GMenu *menu;
     GMenu *section;
     static int attachment_number = 0;
-    gchar *attachment_namespace;
-
-    g_debug("Trying to attach '%s'", filename);
-    if (!(file_uri = libbalsa_vfs_new_from_uri(filename))) {
-        balsa_information_parented(GTK_WINDOW(bsmsg->window),
-                                   LIBBALSA_INFORMATION_ERROR,
-                                   _("Cannot create file URI object for %s"),
-                                   filename);
-        return FALSE;
-    }
-    if (!libbalsa_vfs_is_regular_file(file_uri, &err)) {
-        balsa_information_parented(GTK_WINDOW(bsmsg->window),
-                                   LIBBALSA_INFORMATION_ERROR,
-                                   "%s: %s", filename,
-                                   err && err->message ? err->message : _("Unknown error"));
-       g_error_free(err);
-       g_object_unref(file_uri);
-       return FALSE;
-    }
+    char *attachment_namespace;
+    char *content_desc;
 
-    /* get the pixbuf for the attachment's content type */
-    is_fwd_message = forced_mime_type &&
-       !g_ascii_strncasecmp(forced_mime_type, "message/", 8) && is_a_temp_file;
-    if (is_fwd_message)
-       content_type = g_strdup(forced_mime_type);
-    pixbuf =
-        libbalsa_icon_finder(GTK_WIDGET(bsmsg->window), forced_mime_type,
-                             file_uri, &content_type,
-                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-    if (!content_type)
-       /* Last ditch. */
-       content_type = g_strdup("application/octet-stream");
+    if (--attach_data->bsmsg->n_attachments == 0)
+        gtk_window_destroy(GTK_WINDOW(attach_data->bsmsg->attach_dialog));
 
-    /* create a new attachment info block */
-    attach_data = balsa_attach_info_new(bsmsg);
-    attach_data->charset = NULL;
-    if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
-       gboolean change_type = FALSE;
-       if (!sw_set_charset(bsmsg, filename, content_type,
-                           &change_type, &attach_data->charset)) {
-           g_free(content_type);
-           g_object_unref(attach_data);
-           return FALSE;
-       }
-       if (change_type) {
-           forced_mime_type = "application/octet-stream";
-           g_free(content_type);
-           content_type = g_strdup(forced_mime_type);
-       }
-    }
-
-    if (is_fwd_message) {
-       attach_data->headers = get_fwd_mail_headers(filename);
+    if (attach_data->is_fwd_message) {
+       attach_data->headers = get_fwd_mail_headers(attach_data->filename);
        if (!attach_data->headers)
            utf8name = g_strdup(_("forwarded message"));
        else {
@@ -1741,8 +1780,8 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
            g_free(tmp);
        }
     } else {
-        const gchar *uri_utf8 = libbalsa_vfs_get_uri_utf8(file_uri);
-       const gchar *home = g_getenv("HOME");
+        const char *uri_utf8 = libbalsa_vfs_get_uri_utf8(attach_data->file_uri);
+       const char *home = g_getenv("HOME");
 
        if (home && !strncmp(uri_utf8, "file://", 7) &&
             !strncmp(uri_utf8 + 7, home, strlen(home))) {
@@ -1751,18 +1790,14 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
            utf8name = g_strdup(uri_utf8);
     }
 
-    show_attachment_widget(bsmsg);
+    show_attachment_widget(attach_data->bsmsg);
 
-    model = BALSA_MSG_ATTACH_MODEL(bsmsg);
+    model = BALSA_MSG_ATTACH_MODEL(attach_data->bsmsg);
     gtk_list_store_append(GTK_LIST_STORE(model), &iter);
 
-    attach_data->file_uri = file_uri;
-    attach_data->force_mime_type = g_strdup(forced_mime_type);
-
-    attach_data->delete_on_destroy = is_a_temp_file;
-    can_inline = !is_a_temp_file &&
-       (!g_ascii_strncasecmp(content_type, "text/", 5) ||
-        !g_ascii_strncasecmp(content_type, "image/", 6));
+    can_inline = !attach_data->is_a_temp_file &&
+       (!g_ascii_strncasecmp(attach_data->content_type, "text/", 5) ||
+        !g_ascii_strncasecmp(attach_data->content_type, "image/", 6));
     attach_data->mode = LIBBALSA_ATTACH_AS_ATTACHMENT;
 
     /* build the attachment's popup menu */
@@ -1773,7 +1808,7 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
                                     attach_data);
 
     attachment_namespace = g_strdup_printf("attachment-%d", ++attachment_number);
-    gtk_widget_insert_action_group(bsmsg->window,
+    gtk_widget_insert_action_group(attach_data->bsmsg->window,
                                    attachment_namespace,
                                    G_ACTION_GROUP(simple));
     g_object_unref(simple);
@@ -1791,7 +1826,7 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
     }
 
     /* all real files can be attachments */
-    if (can_inline || !is_a_temp_file) {
+    if (can_inline || !attach_data->is_a_temp_file) {
         GMenuItem *menu_item =
             g_menu_item_new(_(attach_modes[LIBBALSA_ATTACH_AS_ATTACHMENT]), NULL);
         g_menu_item_set_action_and_target(menu_item, "new-mode", "i",
@@ -1801,7 +1836,7 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
     }
 
     /* real files may be references (external body) */
-    if (!is_a_temp_file) {
+    if (!attach_data->is_a_temp_file) {
         GMenuItem *menu_item =
             g_menu_item_new(_(attach_modes[LIBBALSA_ATTACH_AS_EXTBODY]), NULL);
         g_menu_item_set_action_and_target(menu_item, "new-mode", "i",
@@ -1818,36 +1853,125 @@ add_attachment(BalsaSendmsg * bsmsg, const gchar *filename,
 
     /* add the usual vfs menu so the user can inspect what (s)he actually
        attached... (only for non-message attachments) */
-    if (!is_fwd_message) {
+    if (!attach_data->is_fwd_message) {
         section = g_menu_new();
-        libbalsa_vfs_fill_menu_by_content_type(section, content_type, "launch-app");
+        libbalsa_vfs_fill_menu_by_content_type(section, attach_data->content_type, "launch-app");
         g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
         g_object_unref(section);
     }
 
     attach_data->popup_menu =
-        libbalsa_popup_widget_new(bsmsg->tree_view, G_MENU_MODEL(menu), attachment_namespace);
+        libbalsa_popup_widget_new(attach_data->bsmsg->tree_view,
+                                  G_MENU_MODEL(menu),
+                                  attachment_namespace);
 
     g_object_unref(menu);
     g_free(attachment_namespace);
 
     /* append to the list store */
-    content_desc =libbalsa_vfs_content_description(content_type);
+    content_desc = libbalsa_vfs_content_description(attach_data->content_type);
     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
                       ATTACH_INFO_COLUMN, attach_data,
-                      ATTACH_ICON_COLUMN, pixbuf,
+                      ATTACH_ICON_COLUMN, attach_data->icon_name,
                       ATTACH_TYPE_COLUMN, content_desc,
                       ATTACH_MODE_COLUMN, attach_data->mode,
-                      ATTACH_SIZE_COLUMN, libbalsa_vfs_get_size(file_uri),
+                      ATTACH_SIZE_COLUMN, libbalsa_vfs_get_size(attach_data->file_uri),
                       ATTACH_DESC_COLUMN, utf8name,
                       -1);
     g_object_unref(attach_data);
-    g_object_unref(pixbuf);
+    g_free(attach_data->icon_name);
     g_free(utf8name);
-    g_free(content_type);
+    g_free(attach_data->content_type);
     g_free(content_desc);
 
-    return TRUE;
+    return G_SOURCE_REMOVE;
+}
+
+static gpointer
+add_attachment_thread(gpointer user_data)
+{
+    BalsaAttachInfo *attach_data = user_data;
+
+    /* get the icon for the attachment's content type */
+    attach_data->is_fwd_message = attach_data->force_mime_type != NULL &&
+       g_ascii_strncasecmp(attach_data->force_mime_type, "message/", 8) == 0 &&
+        attach_data->is_a_temp_file;
+
+    if (attach_data->is_fwd_message)
+       attach_data->content_type = g_strdup(attach_data->force_mime_type);
+    else
+        attach_data->content_type = NULL;
+
+    attach_data->icon_name = libbalsa_icon_name_finder(attach_data->force_mime_type,
+                                                       attach_data->file_uri,
+                                                       &attach_data->content_type);
+
+    if (attach_data->content_type == NULL) {
+       /* Last ditch. */
+       attach_data->content_type = g_strdup("application/octet-stream");
+    }
+
+    attach_data->charset = NULL;
+    if (g_ascii_strncasecmp(attach_data->content_type, "text/", 5) == 0) {
+       gboolean change_type = FALSE;
+
+       if (!sw_set_charset(attach_data->bsmsg, attach_data->filename, attach_data->content_type,
+                           &change_type, &attach_data->charset)) {
+           g_free(attach_data->content_type);
+           g_object_unref(attach_data);
+           return NULL;
+       }
+
+       if (change_type) {
+            g_free(attach_data->force_mime_type);
+           attach_data->force_mime_type = g_strdup("application/octet-stream");
+           g_free(attach_data->content_type);
+           attach_data->content_type = g_strdup(attach_data->force_mime_type);
+       }
+    }
+
+    g_idle_add(add_attachment_idle, attach_data);
+
+    return NULL;
+}
+
+void
+add_attachment(BalsaSendmsg *bsmsg,
+               const char   *filename,
+               gboolean      is_a_temp_file,
+               const char   *forced_mime_type)
+{
+    BalsaAttachInfo *attach_data;
+    LibbalsaVfs * file_uri;
+    GError *err = NULL;
+
+    g_debug("Trying to attach '%s'", filename);
+    if (!(file_uri = libbalsa_vfs_new_from_uri(filename))) {
+        balsa_information_parented(GTK_WINDOW(bsmsg->window),
+                                   LIBBALSA_INFORMATION_ERROR,
+                                   _("Cannot create file URI object for %s"),
+                                   filename);
+        return;
+    }
+    if (!libbalsa_vfs_is_regular_file(file_uri, &err)) {
+        balsa_information_parented(GTK_WINDOW(bsmsg->window),
+                                   LIBBALSA_INFORMATION_ERROR,
+                                   "%s: %s", filename,
+                                   err && err->message ? err->message : _("Unknown error"));
+       g_error_free(err);
+       g_object_unref(file_uri);
+       return;
+    }
+
+    /* create a new attachment info block */
+    attach_data = balsa_attach_info_new(bsmsg);
+
+    attach_data->is_a_temp_file  = is_a_temp_file;
+    attach_data->force_mime_type = g_strdup(forced_mime_type);
+    attach_data->filename        = filename;
+    attach_data->file_uri        = file_uri;
+
+    g_thread_unref(g_thread_new("add-attachment-thread", add_attachment_thread, attach_data));
 }
 
 /* add_urlref_attachment:
@@ -1859,7 +1983,6 @@ add_urlref_attachment(BalsaSendmsg * bsmsg, const gchar *url)
     GtkTreeModel *model;
     GtkTreeIter iter;
     BalsaAttachInfo *attach_data;
-    GdkPixbuf * pixbuf;
     GSimpleActionGroup *simple;
     static GActionEntry attachment_entries[] = {
         {"remove", remove_attachment},
@@ -1870,11 +1993,6 @@ add_urlref_attachment(BalsaSendmsg * bsmsg, const gchar *url)
 
     g_debug("Trying to attach '%s'", url);
 
-    /* get the pixbuf for the attachment's content type */
-    pixbuf =
-        gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
-                                 "go-jump", GTK_ICON_SIZE_MENU, 0, NULL);
-
     /* create a new attachment info block */
     attach_data = balsa_attach_info_new(bsmsg);
     attach_data->charset = NULL;
@@ -1886,7 +2004,7 @@ add_urlref_attachment(BalsaSendmsg * bsmsg, const gchar *url)
 
     attach_data->uri_ref = g_strconcat("URL:", url, NULL);
     attach_data->force_mime_type = g_strdup("message/external-body");
-    attach_data->delete_on_destroy = FALSE;
+    attach_data->is_a_temp_file = FALSE;
     attach_data->mode = LIBBALSA_ATTACH_AS_EXTBODY;
     attach_data->file_uri = libbalsa_vfs_new_from_uri(url);
 
@@ -1924,14 +2042,13 @@ add_urlref_attachment(BalsaSendmsg * bsmsg, const gchar *url)
     /* append to the list store */
     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
                       ATTACH_INFO_COLUMN, attach_data,
-                      ATTACH_ICON_COLUMN, pixbuf,
+                      ATTACH_ICON_COLUMN, "go-jump",
                       ATTACH_TYPE_COLUMN, _("(URL)"),
                       ATTACH_MODE_COLUMN, attach_data->mode,
                       ATTACH_SIZE_COLUMN, 0,
                       ATTACH_DESC_COLUMN, url,
                       -1);
     g_object_unref(attach_data);
-    g_object_unref(pixbuf);
 
     return TRUE;
 }
@@ -1944,42 +2061,51 @@ static void
 attach_dialog_response(GtkWidget * dialog, gint response,
                       BalsaSendmsg * bsmsg)
 {
-    GtkFileChooser * fc;
-    GSList *files, *list;
-    int res = 0;
+    GtkFileChooser * chooser;
+    GListModel *list;
+    unsigned n_items;
+    unsigned position;
+    GFile *file;
 
     g_object_set_data(G_OBJECT(bsmsg->window),
                       "balsa-sendmsg-window-attach-dialog", NULL);
 
     if (response != GTK_RESPONSE_OK) {
-       gtk_widget_destroy(dialog);
+       gtk_window_destroy(GTK_WINDOW(dialog));
        return;
     }
 
-    fc = GTK_FILE_CHOOSER(dialog);
-    files = gtk_file_chooser_get_uris(fc);
-    for (list = files; list; list = list->next) {
-        if(!add_attachment(bsmsg, list->data, FALSE, NULL))
-           res++;
-        g_free(list->data);
+    chooser = GTK_FILE_CHOOSER(dialog);
+    list = gtk_file_chooser_get_files(chooser);
+
+    bsmsg->attach_dialog = dialog;
+    bsmsg->n_attachments = n_items = g_list_model_get_n_items(list);
+    for (position = 0; position < n_items; position++) {
+        char *path;
+
+        file = g_list_model_get_item(list, position);
+        path = g_file_get_path(file);
+        g_object_unref(file);
+
+        add_attachment(bsmsg, path, FALSE, NULL);
+        g_free(path);
     }
 
-    g_slist_free(files);
+    g_object_unref(list);
 
     g_free(balsa_app.attach_dir);
-    balsa_app.attach_dir = gtk_file_chooser_get_current_folder_uri(fc);
-
-    if (res == 0)
-        gtk_widget_destroy(dialog);
+    file = gtk_file_chooser_get_current_folder(chooser);
+    balsa_app.attach_dir = g_file_get_path(file);
+    g_object_unref(file);
 }
 
 static GtkFileChooser *
 sw_attach_dialog(BalsaSendmsg * bsmsg)
 {
-    GtkWidget *fsw;
-    GtkFileChooser *fc;
+    GtkWidget *dialog;
+    GtkFileChooser *chooser;
 
-    fsw =
+    dialog =
         gtk_file_chooser_dialog_new(_("Attach file"),
                                     GTK_WINDOW(bsmsg->window),
                                     GTK_FILE_CHOOSER_ACTION_OPEN,
@@ -1987,23 +2113,25 @@ sw_attach_dialog(BalsaSendmsg * bsmsg)
                                     _("_OK"),     GTK_RESPONSE_OK,
                                     NULL);
 #if HAVE_MACOSX_DESKTOP
-    libbalsa_macosx_menu_for_parent(fsw, GTK_WINDOW(bsmsg->window));
+    libbalsa_macosx_menu_for_parent(dialog, GTK_WINDOW(bsmsg->window));
 #endif
-    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fsw),
-                                    libbalsa_vfs_local_only());
-    gtk_window_set_destroy_with_parent(GTK_WINDOW(fsw), TRUE);
+    gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
 
-    fc = GTK_FILE_CHOOSER(fsw);
-    gtk_file_chooser_set_select_multiple(fc, TRUE);
-    if (balsa_app.attach_dir)
-       gtk_file_chooser_set_current_folder_uri(fc, balsa_app.attach_dir);
+    chooser = GTK_FILE_CHOOSER(dialog);
+    gtk_file_chooser_set_select_multiple(chooser, TRUE);
+    if (balsa_app.attach_dir != NULL) {
+        GFile *file;
 
-    g_signal_connect(fc, "response",
-                    G_CALLBACK(attach_dialog_response), bsmsg);
+        file = g_file_new_for_path(balsa_app.attach_dir);
+       gtk_file_chooser_set_current_folder(chooser, file, NULL);
+        g_object_unref(file);
+    }
 
-    gtk_widget_show(fsw);
+    g_signal_connect(dialog, "response", G_CALLBACK(attach_dialog_response), bsmsg);
 
-    return fc;
+    gtk_widget_show(dialog);
+
+    return chooser;
 }
 
 /* attach_clicked - menu callback */
@@ -2041,25 +2169,63 @@ attach_message(BalsaSendmsg *bsmsg, LibBalsaMessage *message)
     return TRUE;
 }
 
+static gboolean
+sw_insert_message_idle(gpointer data)
+{
+    BalsaSendmsg *bsmsg = data;
+    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+    GString *body = bsmsg->body;
+
+    gtk_text_buffer_insert_at_cursor(buffer, body->str, body->len);
+
+    g_string_free(body, TRUE);
+
+    return G_SOURCE_REMOVE;
+}
+
+static gpointer
+sw_insert_message_thread(gpointer data)
+{
+    BalsaSendmsg *bsmsg = data;
+
+    if (bsmsg->message != NULL) {
+        bsmsg->body = quote_message_body(bsmsg, bsmsg->message, bsmsg->quote_type);
+        g_idle_add(sw_insert_message_idle, bsmsg);
+        g_object_remove_weak_pointer(G_OBJECT(bsmsg->message), (gpointer *) &bsmsg->message);
+    }
+
+    return NULL;
+}
+
+static void
+sw_insert_message(BalsaSendmsg    *bsmsg,
+                  LibBalsaMessage *message,
+                  QuoteType        qtype)
+{
+    bsmsg->message = message;
+    g_object_add_weak_pointer(G_OBJECT(message), (gpointer *) &bsmsg->message);
+    bsmsg->quote_type = qtype;
+
+    g_thread_unref(g_thread_new("insert-message", sw_insert_message_thread, bsmsg));
+}
+
 static void
 insert_selected_messages(BalsaSendmsg *bsmsg, QuoteType type)
 {
-    GtkTextBuffer *buffer =
-        gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
-    GtkWidget *index =
+    GtkWidget *bindex =
        balsa_window_find_current_index(balsa_app.main_window);
-    GList *l;
+    GList *selected_list;
 
-    if (index && (l = balsa_index_selected_list(BALSA_INDEX(index)))) {
+    if (bindex != NULL &&
+        (selected_list = balsa_index_selected_list(BALSA_INDEX(bindex))) != NULL) {
        GList *node;
 
-       for (node = l; node; node = g_list_next(node)) {
+       for (node = selected_list; node != NULL; node = node->next) {
            LibBalsaMessage *message = node->data;
-            GString *body = quote_message_body(bsmsg, message, type);
-            gtk_text_buffer_insert_at_cursor(buffer, body->str, body->len);
-            g_string_free(body, TRUE);
+
+            sw_insert_message(bsmsg, message, type);
        }
-       g_list_free_full(l, g_object_unref);
+       g_list_free_full(selected_list, g_object_unref);
     }
 }
 
@@ -2102,14 +2268,14 @@ sw_attach_messages_activated(GSimpleAction * action,
 }
 
 
-/* attachments_add - attachments field D&D callback */
+/* sw_attachment_drop - attachments field D&D callback */
 
 /* Helper: check if the passed parameter contains a valid RFC 2396 URI (leading
  * & trailing whitespaces allowed). Return a newly allocated string with the
  * spaces stripped on success or NULL on fail. Note that the URI may still be
  * malformed. */
 static gchar *
-rfc2396_uri(const gchar *instr)
+rfc2396_uri(const char *instr)
 {
     gchar *s1, *uri;
     static const gchar *uri_extra = ";/?:@&=+$,-_.!~*'()%";
@@ -2140,29 +2306,29 @@ rfc2396_uri(const gchar *instr)
     return uri;
 }
 
-static void
-attachments_add(GtkWidget * widget,
-               GdkDragContext * context,
-               gint x,
-               gint y,
-               GtkSelectionData * selection_data,
-               guint info, guint32 time, BalsaSendmsg * bsmsg)
+static gboolean
+sw_attachment_drop(GtkDropTarget *drop_target,
+                   GValue        *value,
+                   double         x,
+                   double         y,
+                   gpointer       user_data)
 {
-    gboolean drag_result = TRUE;
+    BalsaSendmsg *bsmsg = user_data;
+    gboolean drag_result = FALSE;
+
+    g_debug("sw_attachment_drop");
 
-    g_debug("attachments_add: info %d", info);
-    if (info == TARGET_MESSAGES) {
-       BalsaIndex *index =
-            *(BalsaIndex **) gtk_selection_data_get_data(selection_data);
-       LibBalsaMailbox *mailbox = balsa_index_get_mailbox(index);
-        GArray *selected = balsa_index_selected_msgnos_new(index);
-       guint i;
+    if (G_VALUE_HOLDS(value, BALSA_TYPE_INDEX)) {
+       BalsaIndex *bindex = g_value_get_object(value);
+       LibBalsaMailbox *mailbox = balsa_index_get_mailbox(bindex);
+        GArray *selected = balsa_index_selected_msgnos_new(bindex);
+       unsigned i;
 
         for (i = 0; i < selected->len; i++) {
-           guint msgno = g_array_index(selected, guint, i);
+           unsigned msgno = g_array_index(selected, guint, i);
            LibBalsaMessage *message =
                libbalsa_mailbox_get_message(mailbox, msgno);
-            if (!message)
+            if (message == NULL)
                 continue;
 
             if(!attach_message(bsmsg, message))
@@ -2172,51 +2338,54 @@ attachments_add(GtkWidget * widget,
                                              "Possible reason: not enough temporary space"));
            g_object_unref(message);
         }
-        balsa_index_selected_msgnos_free(index, selected);
-    } else if (info == TARGET_URI_LIST) {
-        gchar **uris, **uri;
+        balsa_index_selected_msgnos_free(bindex, selected);
+
+        drag_result = TRUE;
+    } else if (G_VALUE_HOLDS(value, G_TYPE_STRV)) {
+        const char **uri;
 
-        uris = gtk_selection_data_get_uris(selection_data);
-        for (uri = uris; uri != NULL; ++uri)
+        for (uri = g_value_get_pointer(value); uri != NULL; ++uri)
            add_attachment(bsmsg, *uri, FALSE, NULL);
-        g_strfreev(uris);
-    } else if( info == TARGET_STRING) {
-       gchar *url =
-            rfc2396_uri((gchar *)
-                        gtk_selection_data_get_data(selection_data));
+
+        drag_result = TRUE;
+    } else if (G_VALUE_HOLDS_STRING(value)) {
+        const char *url_string;
+       char *url;
+
+        url_string = g_value_get_string(value);
+        url = rfc2396_uri(url_string);
 
        if (url != NULL) {
            add_urlref_attachment(bsmsg, url);
             g_free(url);
-        } else {
-           drag_result = FALSE;
+            drag_result = TRUE;
         }
     }
 
-    gtk_drag_finish(context, drag_result, FALSE, time);
+    return drag_result;
 }
 
-/* to_add - address-view D&D callback; we assume it's a To: address */
-static void
-to_add(GtkWidget * widget,
-       GdkDragContext * context,
-       gint x,
-       gint y,
-       GtkSelectionData * selection_data,
-       guint info, guint32 time)
+/* sw_address_view_drop - address-view D&D callback; we assume it's a To: address */
+static gboolean
+sw_address_view_drop(GtkDropTarget *drop_target,
+                     GValue        *value,
+                     double         x,
+                     double         y,
+                     gpointer       user_data)
 {
-    gboolean drag_result = FALSE;
+    GtkWidget *address_view = user_data;
+    GdkDrop *drop;
+    const char *address;
 
-    if (info == TARGET_STRING) {
-        const gchar *address;
+    drop = gtk_drop_target_get_drop(drop_target);
 
-        address =
-            (const gchar *) gtk_selection_data_get_data(selection_data);
-        libbalsa_address_view_add_from_string(LIBBALSA_ADDRESS_VIEW
-                                              (widget), "To:", address);
-        drag_result = TRUE;
-    }
-    gtk_drag_finish(context, drag_result, FALSE, time);
+    if (drop == NULL || !G_VALUE_HOLDS_STRING(value))
+        return FALSE;
+
+    address = g_value_get_string(value);
+    libbalsa_address_view_add_from_string(LIBBALSA_ADDRESS_VIEW(address_view), "To:", address);
+
+    return TRUE;
 }
 
 /*
@@ -2245,7 +2414,7 @@ create_email_or_string_entry(BalsaSendmsg * bsmsg,
 
     mnemonic_widget = arr[1];
     if (GTK_IS_FRAME(mnemonic_widget))
-        mnemonic_widget = gtk_bin_get_child(GTK_BIN(mnemonic_widget));
+        mnemonic_widget = gtk_frame_get_child(GTK_FRAME(mnemonic_widget));
     arr[0] = gtk_label_new_with_mnemonic(label);
     gtk_label_set_mnemonic_widget(GTK_LABEL(arr[0]), mnemonic_widget);
     gtk_widget_set_halign(arr[0], GTK_ALIGN_START);
@@ -2266,7 +2435,7 @@ create_email_or_string_entry(BalsaSendmsg * bsmsg,
                                           BALSA_COMPOSE_ENTRY);
 
         css_provider = gtk_css_provider_new();
-        gtk_css_provider_load_from_data(css_provider, css, -1, NULL);
+        gtk_css_provider_load_from_data(css_provider, css, -1);
         g_free(css);
 
         gtk_style_context_add_provider(gtk_widget_get_style_context(arr[1]) ,
@@ -2328,12 +2497,13 @@ create_email_entry(BalsaSendmsg         * bsmsg,
                    guint                  n_types)
 {
     GtkWidget *scroll;
+    GtkDropTarget *drop_target;
 
     *view = libbalsa_address_view_new(types, n_types,
                                       balsa_app.address_book_list,
                                       balsa_app.convert_unknown_8bit);
 
-    scroll = gtk_scrolled_window_new(NULL, NULL);
+    scroll = gtk_scrolled_window_new();
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_AUTOMATIC);
@@ -2341,22 +2511,20 @@ create_email_entry(BalsaSendmsg         * bsmsg,
      * recipient list is more than one line high: */
     gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll),
                                                60);
-    gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(*view));
+    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), GTK_WIDGET(*view));
 
     widget[1] = gtk_frame_new(NULL);
-    gtk_frame_set_shadow_type(GTK_FRAME(widget[1]), GTK_SHADOW_IN);
-    gtk_container_add(GTK_CONTAINER(widget[1]), scroll);
+    gtk_frame_set_child(GTK_FRAME(widget[1]), scroll);
 
     create_email_or_string_entry(bsmsg, grid, _(label), y_pos, widget);
 
-    g_signal_connect(*view, "drag_data_received",
-                     G_CALLBACK(to_add), NULL);
     g_signal_connect(*view, "open-address-book",
                     G_CALLBACK(address_book_cb), bsmsg);
-    gtk_drag_dest_set(GTK_WIDGET(*view), GTK_DEST_DEFAULT_ALL,
-                     email_field_drop_types,
-                         G_N_ELEMENTS(email_field_drop_types),
-                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+    /* Drag and drop */
+    drop_target = gtk_drop_target_new(G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+    gtk_widget_add_controller(GTK_WIDGET(*view), GTK_EVENT_CONTROLLER(drop_target));
+    g_signal_connect(drop_target, "drop", G_CALLBACK(sw_address_view_drop), *view);
 
     libbalsa_address_view_set_domain(*view, libbalsa_identity_get_domain(bsmsg->ident));
     g_signal_connect_swapped(gtk_tree_view_get_model(GTK_TREE_VIEW(*view)),
@@ -2392,27 +2560,23 @@ create_from_entry(GtkWidget * grid, BalsaSendmsg * bsmsg)
 }
 
 static void
-attachment_button_press_cb(GtkGestureMultiPress *multi_press,
-                           gint                  n_press,
-                           gdouble               x,
-                           gdouble               y,
-                           gpointer              user_data)
+attachment_button_press_cb(GtkGestureClick *click,
+                           int              n_press,
+                           double           x,
+                           double           y,
+                           gpointer         user_data)
 {
     GtkTreeView *tree_view = user_data;
-    GtkGesture *gesture;
-    const GdkEvent *event;
+    GdkEvent *event;
     GtkTreePath *path;
-    gint bx, by;
+    int bx, by;
 
-    gesture = GTK_GESTURE(multi_press);
-    event = gtk_gesture_get_last_event(gesture, gtk_gesture_get_last_updated_sequence(gesture));
+    event = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(click));
 
-    if (!gdk_event_triggers_context_menu(event) ||
-        gdk_event_get_window(event) != gtk_tree_view_get_bin_window(tree_view)) {
+    if (!gdk_event_triggers_context_menu(event))
         return;
-    }
 
-    gtk_tree_view_convert_widget_to_bin_window_coords(tree_view, (gint) x, (gint) y,
+    gtk_tree_view_convert_widget_to_bin_window_coords(tree_view, (int) x, (int) y,
                                                       &bx, &by);
 
     if (gtk_tree_view_get_path_at_pos(tree_view, bx, by,
@@ -2506,7 +2670,11 @@ create_info_pane(BalsaSendmsg * bsmsg)
     grid = gtk_grid_new();
     gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
     gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
-    gtk_container_set_border_width(GTK_CONTAINER(grid), 6);
+
+    gtk_widget_set_margin_top(grid, 6);
+    gtk_widget_set_margin_bottom(grid, 6);
+    gtk_widget_set_margin_start(grid, 6);
+    gtk_widget_set_margin_end(grid, 6);
 
     /* From: */
     create_from_entry(grid, bsmsg);
@@ -2566,7 +2734,6 @@ create_info_pane(BalsaSendmsg * bsmsg)
     create_email_or_string_entry(bsmsg, grid, _("F_CC:"), ++row,
                                  bsmsg->fcc);
 
-    gtk_widget_show_all(grid);
     return grid;
 }
 
@@ -2583,11 +2750,21 @@ sw_attachment_list(BalsaSendmsg *bsmsg)
     GtkTreeViewColumn *column;
     GtkWidget *frame;
     GtkGesture *gesture;
+    GtkDropTarget *drop_target;
+    GType drop_types[] = {
+        BALSA_TYPE_INDEX,
+        G_TYPE_STRV,
+        G_TYPE_STRING
+    };
 
     grid = gtk_grid_new();
     gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
     gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
-    gtk_container_set_border_width(GTK_CONTAINER(grid), 6);
+
+    gtk_widget_set_margin_top(grid, 6);
+    gtk_widget_set_margin_bottom(grid, 6);
+    gtk_widget_set_margin_start(grid, 6);
+    gtk_widget_set_margin_end(grid, 6);
 
     /* Attachment list */
     label = gtk_label_new_with_mnemonic(_("_Attachments:"));
@@ -2600,7 +2777,7 @@ sw_attachment_list(BalsaSendmsg *bsmsg)
 
     gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
 
-    sw = gtk_scrolled_window_new(NULL, NULL);
+    sw = gtk_scrolled_window_new();
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_AUTOMATIC);
@@ -2625,7 +2802,7 @@ sw_attachment_list(BalsaSendmsg *bsmsg)
     g_object_set(renderer, "xalign", 0.0, NULL);
     gtk_tree_view_insert_column_with_attributes(view,
                                                -1, NULL, renderer,
-                                               "pixbuf", ATTACH_ICON_COLUMN,
+                                               "icon-name", ATTACH_ICON_COLUMN,
                                                NULL);
 
     /* column for the mime type */
@@ -2671,21 +2848,22 @@ sw_attachment_list(BalsaSendmsg *bsmsg)
     g_signal_connect(view, "popup-menu",
                      G_CALLBACK(attachment_popup_cb), NULL);
 
-    gesture = gtk_gesture_multi_press_new(tree_view);
+    gesture = gtk_gesture_click_new();
+    gtk_widget_add_controller(tree_view, GTK_EVENT_CONTROLLER(gesture));
     gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0);
     g_signal_connect(gesture, "pressed",
                      G_CALLBACK(attachment_button_press_cb), view);
 
-    g_signal_connect(bsmsg->window, "drag_data_received",
-                    G_CALLBACK(attachments_add), bsmsg);
-    gtk_drag_dest_set(GTK_WIDGET(bsmsg->window), GTK_DEST_DEFAULT_ALL,
-                     drop_types, G_N_ELEMENTS(drop_types),
-                     GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+    /* Drag and drop */
+    drop_target =
+        gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+    gtk_drop_target_set_gtypes(drop_target, drop_types, G_N_ELEMENTS(drop_types));
+    gtk_widget_add_controller(GTK_WIDGET(bsmsg->window), GTK_EVENT_CONTROLLER(drop_target));
+    g_signal_connect(drop_target, "drop", G_CALLBACK(sw_attachment_drop), bsmsg);
 
     frame = gtk_frame_new(NULL);
-    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
-    gtk_container_add(GTK_CONTAINER(sw), tree_view);
-    gtk_container_add(GTK_CONTAINER(frame), sw);
+    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), tree_view);
+    gtk_frame_set_child(GTK_FRAME(frame), sw);
 
     gtk_widget_set_hexpand(frame, TRUE);
     gtk_grid_attach(GTK_GRID(grid), frame, 1, 0, 1, 1);
@@ -2694,7 +2872,7 @@ sw_attachment_list(BalsaSendmsg *bsmsg)
 }
 
 typedef struct {
-    gchar * name;
+    const char * name;
     gboolean found;
 } has_file_attached_t;
 
@@ -2704,11 +2882,12 @@ has_file_attached(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
 {
     has_file_attached_t *find_file = (has_file_attached_t *)data;
     BalsaAttachInfo *info;
-    const gchar * uri;
+    const char * uri;
 
     gtk_tree_model_get(model, iter, ATTACH_INFO_COLUMN, &info, -1);
-    if (!info)
+    if (info == NULL)
        return FALSE;
+
     uri = libbalsa_vfs_get_uri(info->file_uri);
     if (g_strcmp0(find_file->name, uri) == 0)
        find_file->found = TRUE;
@@ -2717,51 +2896,44 @@ has_file_attached(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
     return find_file->found;
 }
 
-/* drag_data_quote - text area D&D callback */
-static void
-drag_data_quote(GtkWidget * widget,
-                GdkDragContext * context,
-                gint x,
-                gint y,
-                GtkSelectionData * selection_data,
-                guint info, guint32 time, BalsaSendmsg * bsmsg)
+/* sw_quote_drop - text area D&D callback */
+static gboolean
+sw_quote_drop(GtkDropTarget *drop_target,
+              GValue        *value,
+              double         x,
+              double         y,
+              gpointer       user_data)
 {
-    GtkTextBuffer *buffer;
-    BalsaIndex *index;
-    LibBalsaMailbox *mailbox;
-    GArray *selected;
-    guint i;
+    BalsaSendmsg *bsmsg = user_data;
+    gboolean drag_result = FALSE;
 
-    switch(info) {
-    case TARGET_MESSAGES:
-       index =
-            *(BalsaIndex **) gtk_selection_data_get_data(selection_data);
-       mailbox = balsa_index_get_mailbox(index);
-        selected = balsa_index_selected_msgnos_new(index);
-       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
+    if (G_VALUE_HOLDS(value, BALSA_TYPE_INDEX)) {
+        BalsaIndex *bindex;
+        LibBalsaMailbox *mailbox;
+        GArray *selected;
+        unsigned i;
+
+       bindex = g_value_get_object(value);
+       mailbox = balsa_index_get_mailbox(bindex);
+        selected = balsa_index_selected_msgnos_new(bindex);
 
         for (i = 0; i < selected->len; i++) {
-           guint msgno = g_array_index(selected, guint, i);
+           unsigned msgno = g_array_index(selected, unsigned, i);
            LibBalsaMessage *message;
-            GString *body;
 
            message = libbalsa_mailbox_get_message(mailbox, msgno);
-            if (!message)
+            if (message == NULL)
                 continue;
 
-            body = quote_message_body(bsmsg, message, QUOTE_ALL);
+            sw_insert_message(bsmsg, message, QUOTE_ALL);
            g_object_unref(message);
-            gtk_text_buffer_insert_at_cursor(buffer, body->str, body->len);
-            g_string_free(body, TRUE);
         }
-        balsa_index_selected_msgnos_free(index, selected);
-        break;
-    case TARGET_URI_LIST: {
-        gchar **uris, **uri;
-
-        uris = gtk_selection_data_get_uris(selection_data);
+        balsa_index_selected_msgnos_free(bindex, selected);
+        drag_result = TRUE;
+    } else if (G_VALUE_HOLDS(value, G_TYPE_STRV)) {
+        const char **uri;
 
-        for (uri = uris; uri != NULL; ++uri) {
+        for (uri = g_value_get_pointer(value); uri != NULL; ++uri) {
             /* Since current GtkTextView gets this signal twice for
              * every action (#150141) we need to check for duplicates,
              * which is a good idea anyway. */
@@ -2770,17 +2942,16 @@ drag_data_quote(GtkWidget * widget,
             if (bsmsg->tree_view)
                 gtk_tree_model_foreach(BALSA_MSG_ATTACH_MODEL(bsmsg),
                                        has_file_attached, &find_file);
-            if (!find_file.found)
+            if (!find_file.found) {
                 add_attachment(bsmsg, *uri, FALSE, NULL);
+                drag_result = TRUE;
+            }
         }
-        g_strfreev(uris);
-    }
-        break;
-    case TARGET_EMAIL:
-    case TARGET_STRING: /* perhaps we should allow dropping in these, too? */
-    default: return;
     }
-    gtk_drag_finish(context, TRUE, FALSE, time);
+    /* case TARGET_EMAIL:
+     * case TARGET_STRING: perhaps we should allow dropping in these, too? */
+
+    return drag_result;
 }
 
 /* create_text_area
@@ -2821,6 +2992,12 @@ create_text_area(BalsaSendmsg * bsmsg)
     GspellTextView *gspell_view;
 #endif                          /* HAVE_GSPELL */
     GtkWidget *scroll;
+    GtkDropTarget *drop_target;
+    GType drop_types[] = {
+        BALSA_TYPE_INDEX,
+        G_TYPE_STRV,
+        G_TYPE_STRING
+    };
 
 #if HAVE_GTKSOURCEVIEW
     bsmsg->text = libbalsa_source_view_new(TRUE);
@@ -2840,7 +3017,7 @@ create_text_area(BalsaSendmsg * bsmsg)
                                           BALSA_COMPOSE_ENTRY);
 
         css_provider = gtk_css_provider_new();
-        gtk_css_provider_load_from_data(css_provider, css, -1, NULL);
+        gtk_css_provider_load_from_data(css_provider, css, -1);
         g_free(css);
 
         gtk_widget_set_name(bsmsg->text, BALSA_COMPOSE_ENTRY);
@@ -2876,18 +3053,17 @@ create_text_area(BalsaSendmsg * bsmsg)
     }
 #endif                          /* HAVE_GSPELL */
 
-    scroll = gtk_scrolled_window_new(NULL, NULL);
+    scroll = gtk_scrolled_window_new();
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-    gtk_container_add(GTK_CONTAINER(scroll), bsmsg->text);
-    g_signal_connect(bsmsg->text, "drag_data_received",
-                    G_CALLBACK(drag_data_quote), bsmsg);
-    /* GTK_DEST_DEFAULT_ALL in drag_set would trigger bug 150141 */
-    gtk_drag_dest_set(GTK_WIDGET(bsmsg->text), 0,
-                     drop_types, G_N_ELEMENTS(drop_types),
-                     GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), bsmsg->text);
 
-    gtk_widget_show_all(scroll);
+    /* Drag and drop */
+    drop_target =
+        gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+    gtk_drop_target_set_gtypes(drop_target, drop_types, G_N_ELEMENTS(drop_types));
+    gtk_widget_add_controller(GTK_WIDGET(bsmsg->text), GTK_EVENT_CONTROLLER(drop_target));
+    g_signal_connect(drop_target, "drop", G_CALLBACK(sw_quote_drop), bsmsg);
 
     return scroll;
 }
@@ -3338,21 +3514,55 @@ unselect_decrypted(GtkTreeModel              *model,
                                   GtkTreeIter               *iter,
                                   gpointer G_GNUC_UNUSED     data)
 {
-    gboolean decrypted;
+    gboolean decrypted;
+
+    gtk_tree_model_get(model, iter, QUOTE_DECRYPTED, &decrypted, -1);
+    if (decrypted) {
+       gtk_tree_store_set(GTK_TREE_STORE(model), iter,
+                  QUOTE_INCLUDE, FALSE, -1);
+    }
+       return FALSE;
+}
+
+/*
+ * Quote message parts
+ */
+
+typedef struct {
+    GMutex lock;
+    GCond cond;
+
+    GtkTreeStore               *tree_store;
+    GtkWindow                  *parent;
+    const reply_collect_stat_t *stats;
+
+    gboolean ok;
+    gboolean done;
+} quote_parts_data;
+
+static void
+quote_parts_select_dlg_response(GtkDialog *dialog,
+                                int        response_id,
+                                gpointer   user_data)
+{
+    quote_parts_data *data = user_data;
 
-    gtk_tree_model_get(model, iter, QUOTE_DECRYPTED, &decrypted, -1);
-    if (decrypted) {
-       gtk_tree_store_set(GTK_TREE_STORE(model), iter,
-                  QUOTE_INCLUDE, FALSE, -1);
-    }
-       return FALSE;
+    gtk_window_destroy(GTK_WINDOW(dialog));
+
+    g_mutex_lock(&data->lock);
+    data->ok = response_id == GTK_RESPONSE_OK;
+    data->done = TRUE;
+    g_cond_signal(&data->cond);
+    g_mutex_unlock(&data->lock);
 }
 
 static gboolean
-quote_parts_select_dlg(GtkTreeStore               *tree_store,
-                                          GtkWindow                  *parent,
-                                          const reply_collect_stat_t *stats)
+quote_parts_select_dlg_idle(gpointer user_data)
 {
+    quote_parts_data *data = user_data;
+    GtkTreeStore *tree_store = data->tree_store;
+    GtkWindow *parent = data->parent;
+    const reply_collect_stat_t *stats = data->stats;
     GtkWidget *dialog;
     GtkWidget *label;
     GtkWidget *image;
@@ -3363,7 +3573,6 @@ quote_parts_select_dlg(GtkTreeStore               *tree_store,
     GtkTreeViewColumn *column;
     GtkCellRenderer *renderer;
     GtkTreeIter iter;
-    gboolean result;
     GtkWidget *content_box;
 
     dialog = gtk_dialog_new_with_buttons(_("Select parts for quotation"),
@@ -3382,25 +3591,24 @@ quote_parts_select_dlg(GtkTreeStore               *tree_store,
                             " which shall be quoted in the reply"), FALSE);
     gtk_widget_set_valign(label, GTK_ALIGN_START);
 
-    image = gtk_image_new_from_icon_name("dialog-question",
-                                         GTK_ICON_SIZE_DIALOG);
+    image = gtk_image_new_from_icon_name("dialog-question");
     gtk_widget_set_valign(image, GTK_ALIGN_START);
 
     /* stolen form gtk/gtkmessagedialog.c */
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
 
-    gtk_container_add(GTK_CONTAINER(vbox), label);
-    gtk_container_add(GTK_CONTAINER(hbox), image);
+    gtk_box_append(GTK_BOX(vbox), label);
+    gtk_box_append(GTK_BOX(hbox), image);
 
     gtk_widget_set_hexpand(vbox, TRUE);
     gtk_widget_set_halign(vbox, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(hbox), vbox);
+    gtk_box_append(GTK_BOX(hbox), vbox);
 
     content_box = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
     gtk_widget_set_vexpand(hbox, TRUE);
     gtk_widget_set_valign(hbox, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(content_box), hbox);
+    gtk_box_append(GTK_BOX(content_box), hbox);
 
     if (stats->decrypted > 0U) {
        GtkWidget *warning;
@@ -3420,21 +3628,29 @@ quote_parts_select_dlg(GtkTreeStore               *tree_store,
                          "unintentionally leak sensitive information."), FALSE);
        }
         gtk_widget_set_valign(warning, GTK_ALIGN_START);
-        gtk_container_add(GTK_CONTAINER(vbox), warning);
+        gtk_box_append(GTK_BOX(vbox), warning);
     }
 
-    gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
-    gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+    gtk_widget_set_margin_top(dialog, 6);
+    gtk_widget_set_margin_bottom(dialog, 6);
+    gtk_widget_set_margin_start(dialog, 6);
+    gtk_widget_set_margin_end(dialog, 6);
+
+    gtk_widget_set_margin_top(hbox, 6);
+    gtk_widget_set_margin_bottom(hbox, 6);
+    gtk_widget_set_margin_start(hbox, 6);
+    gtk_widget_set_margin_end(hbox, 6);
+
     gtk_box_set_spacing(GTK_BOX(content_box), 14);
 
     /* scrolled window for the tree view */
-    scroll = gtk_scrolled_window_new(NULL, NULL);
+    scroll = gtk_scrolled_window_new();
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                    GTK_POLICY_AUTOMATIC,
                                    GTK_POLICY_AUTOMATIC);
     gtk_widget_set_vexpand(scroll, TRUE);
     gtk_widget_set_valign(scroll, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(vbox), scroll);
+    gtk_box_append(GTK_BOX(vbox), scroll);
 
     /* add the tree view */
     tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store));
@@ -3457,11 +3673,12 @@ quote_parts_select_dlg(GtkTreeStore               *tree_store,
     calculate_expander_toggles(GTK_TREE_MODEL(tree_store), &iter);
 
     /* add, show & run */
-    gtk_container_add(GTK_CONTAINER(scroll), tree_view);
-    gtk_widget_show_all(hbox);
-    result = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK;
-    gtk_widget_destroy(dialog);
-    return result;
+    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), tree_view);
+
+    g_signal_connect(dialog, "response", G_CALLBACK(quote_parts_select_dlg_response), data);
+    gtk_widget_show(dialog);
+
+    return G_SOURCE_REMOVE;
 }
 
 
@@ -3478,30 +3695,42 @@ tree_find_single_part(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
        return FALSE;
 }
 
+static void
+show_decrypted_warning_response(GtkDialog *dialog,
+                                int        response_id,
+                                gpointer   user_data)
+{
+    GtkWidget *remind_btn = user_data;
+
+    balsa_app.warn_reply_decrypted = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remind_btn));
+
+    gtk_window_destroy(GTK_WINDOW(dialog));
+}
+
 static void
 show_decrypted_warning(GtkWindow *parent)
 {
-       GtkWidget *dialog;
-       GtkWidget *message_area;
-       GtkWidget *remind_btn;
-
-       dialog = gtk_message_dialog_new(parent,
-               GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(),
-               GTK_MESSAGE_WARNING,
-               GTK_BUTTONS_CLOSE,
-               _("You reply to an encrypted message. The reply will contain "
-                 "the decrypted contents of the original message.\n"
-                 "Consider to encrypt the reply, and verify that you do not "
-                 "unintentionally leak sensitive information."));
-        message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog));
-
-       remind_btn = gtk_check_button_new_with_label(_("Do not remind me again."));
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remind_btn), FALSE);
-       gtk_widget_show(remind_btn);
-       gtk_container_add(GTK_CONTAINER(message_area), remind_btn);
-       gtk_dialog_run(GTK_DIALOG(dialog));
-       balsa_app.warn_reply_decrypted = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remind_btn));
-       gtk_widget_destroy(dialog);
+    GtkWidget *dialog;
+    GtkWidget *message_area;
+    GtkWidget *remind_btn;
+
+    dialog =
+        gtk_message_dialog_new(parent,
+                               GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(),
+                               GTK_MESSAGE_WARNING,
+                               GTK_BUTTONS_CLOSE,
+                               _("You reply to an encrypted message. The reply will contain "
+                                 "the decrypted contents of the original message.\n"
+                                 "Consider to encrypt the reply, and verify that you do not "
+                                 "unintentionally leak sensitive information."));
+    message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog));
+
+    remind_btn = gtk_check_button_new_with_label(_("Do not remind me again."));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remind_btn), FALSE);
+    gtk_box_append(GTK_BOX(message_area), remind_btn);
+
+    g_signal_connect(dialog, "response", G_CALLBACK(show_decrypted_warning_response), remind_btn);
+    gtk_widget_show(dialog);
 }
 
 static GString *
@@ -3517,8 +3746,10 @@ collect_for_quote(BalsaSendmsg        * bsmsg,
     GString *q_body = NULL;
     reply_collect_stat_t stats;
 
-    if (!root)
-        return q_body;
+    g_assert(libbalsa_am_i_subthread());
+
+    if (root == NULL)
+        return NULL;
 
     message = root->message;
     libbalsa_message_body_ref(message, FALSE, FALSE);
@@ -3546,7 +3777,26 @@ collect_for_quote(BalsaSendmsg        * bsmsg,
            q_body = process_mime_part(message, this_body, reply_prefix_str,
                                       llen, FALSE, flow);
     } else if (stats.parts > 1U) {
-       if (quote_parts_select_dlg(tree_store, GTK_WINDOW(bsmsg->window), &stats)) {
+        quote_parts_data data;
+
+        g_mutex_init(&data.lock);
+        g_cond_init(&data.cond);
+
+        data.tree_store = tree_store;
+        data.parent = GTK_WINDOW(bsmsg->window);
+        data.stats = &stats;
+
+        g_mutex_lock(&data.lock);
+        g_idle_add(quote_parts_select_dlg_idle, &data);
+        data.done = FALSE;
+        while (!data.done)
+            g_cond_wait(&data.cond, &data.lock);
+        g_mutex_unlock(&data.lock);
+
+        g_mutex_clear(&data.lock);
+        g_cond_clear(&data.cond);
+
+       if (data.ok) {
            GtkTreeIter iter;
 
            q_body = g_string_new("");
@@ -3570,88 +3820,81 @@ collect_for_quote(BalsaSendmsg        * bsmsg,
    eg. a new message is composed.
 */
 static GString *
-quote_body(BalsaSendmsg * bsmsg, LibBalsaMessageHeaders *headers,
-           const gchar *message_id, GList *references,
-           LibBalsaMessageBody *root, QuoteType qtype)
+quote_body(BalsaSendmsg           *bsmsg,
+           LibBalsaMessageHeaders *headers,
+           const char             *message_id,
+           GList                  *references,
+           LibBalsaMessageBody    *root,
+           QuoteType               qtype)
 {
     GString *body;
-    gchar *str, *date = NULL;
-    gchar *personStr;
-    const gchar *orig_address;
+    char *date = NULL;
+    char *personStr;
+    const char *orig_address;
 
-    g_return_val_if_fail(headers, NULL);
+    g_assert(libbalsa_am_i_subthread());
 
-    if (headers->from &&
-       (orig_address =
-        libbalsa_address_get_name_from_list(headers->from))) {
+    if (headers->from != NULL &&
+       (orig_address = libbalsa_address_get_name_from_list(headers->from)) != NULL) {
         personStr = g_strdup(orig_address);
-        libbalsa_utf8_sanitize(&personStr,
-                               balsa_app.convert_unknown_8bit,
-                               NULL);
-    } else
+        libbalsa_utf8_sanitize(&personStr, balsa_app.convert_unknown_8bit, NULL);
+    } else {
         personStr = g_strdup(_("you"));
+    }
 
-    if (headers->date)
-        date = libbalsa_message_headers_date_to_utf8(headers,
-                                                     balsa_app.date_string);
+    if (headers->date != 0)
+        date = libbalsa_message_headers_date_to_utf8(headers, balsa_app.date_string);
 
     if (qtype == QUOTE_HEADERS) {
-       gchar *subject;
+        char *str;
+       char *subject;
 
-       str = g_strdup_printf(_("------forwarded message from %s------\n"),
-                             personStr);
+       str = g_strdup_printf(_("------forwarded message from %s------\n"), personStr);
        body = g_string_new(str);
        g_free(str);
 
-       if (date)
+       if (date != NULL)
            g_string_append_printf(body, "%s %s\n", _("Date:"), date);
 
        subject = message_part_get_subject(root);
-       if (subject)
+       if (subject != NULL)
            g_string_append_printf(body, "%s %s\n", _("Subject:"), subject);
        g_free(subject);
 
-       if (headers->from) {
-           gchar *from =
-               internet_address_list_to_string(headers->from, NULL,
-                                               FALSE);
+       if (headers->from != NULL) {
+           char *from = internet_address_list_to_string(headers->from, NULL, FALSE);
            g_string_append_printf(body, "%s %s\n", _("From:"), from);
            g_free(from);
        }
 
        if (internet_address_list_length(headers->to_list) > 0) {
-           gchar *to_list =
-               internet_address_list_to_string(headers->to_list, NULL,
-                                               FALSE);
+           char *to_list = internet_address_list_to_string(headers->to_list, NULL, FALSE);
            g_string_append_printf(body, "%s %s\n", _("To:"), to_list);
            g_free(to_list);
        }
 
        if (internet_address_list_length(headers->cc_list) > 0) {
-           gchar *cc_list =
-               internet_address_list_to_string(headers->cc_list, NULL,
-                                               FALSE);
+           char *cc_list = internet_address_list_to_string(headers->cc_list, NULL, FALSE);
            g_string_append_printf(body, "%s %s\n", _("CC:"), cc_list);
            g_free(cc_list);
        }
 
-       g_string_append_printf(body, _("Message-ID: %s\n"),
-                               message_id);
+       g_string_append_printf(body, _("Message-ID: %s\n"), message_id);
 
-       if (references) {
+       if (references != NULL) {
            GList *ref_list;
 
            g_string_append(body, _("References:"));
 
-           for (ref_list = references; ref_list;
-                 ref_list = g_list_next(ref_list))
-               g_string_append_printf(body, " <%s>",
-                                      (gchar *) ref_list->data);
+           for (ref_list = references; ref_list != NULL; ref_list = ref_list->next)
+               g_string_append_printf(body, " <%s>", (char *) ref_list->data);
 
            g_string_append_c(body, '\n');
        }
     } else {
-       if (date)
+        char *str;
+
+       if (date != NULL)
            str = g_strdup_printf(_("On %s, %s wrote:\n"), date, personStr);
        else
            str = g_strdup_printf(_("%s wrote:\n"), personStr);
@@ -3662,12 +3905,11 @@ quote_body(BalsaSendmsg * bsmsg, LibBalsaMessageHeaders *headers,
                                 qtype == QUOTE_ALL ? balsa_app.quote_str : NULL,
                                 bsmsg->flow ? -1 : balsa_app.wraplength,
                                 balsa_app.reply_strip_html, bsmsg->flow);
-       if (body) {
-           gchar *buf;
+       if (body != NULL) {
+           char *buf;
 
            buf = g_string_free(body, FALSE);
-           libbalsa_utf8_sanitize(&buf, balsa_app.convert_unknown_8bit,
-                                  NULL);
+           libbalsa_utf8_sanitize(&buf, balsa_app.convert_unknown_8bit, NULL);
            body = g_string_new(buf);
            g_free(buf);
            g_string_prepend(body, str);
@@ -3688,34 +3930,51 @@ quote_body(BalsaSendmsg * bsmsg, LibBalsaMessageHeaders *headers,
    and then adds the signature.
    Optionally prepends the signature to quoted text.
 */
-static void
-fill_body_from_part(BalsaSendmsg * bsmsg, LibBalsaMessageHeaders *headers,
-                    const gchar *message_id, GList *references,
-                    LibBalsaMessageBody *root, QuoteType qtype)
+
+static gpointer
+fill_body_from_part_thread(gpointer data)
 {
+    BalsaSendmsg *bsmsg = data;
     GString *body;
-    GtkTextBuffer *buffer =
-        gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
     GtkTextIter start;
 
-    g_assert(headers);
+    g_assert(bsmsg->headers != NULL);
 
-    body = quote_body(bsmsg, headers, message_id, references,
-                      root, qtype);
+    body = quote_body(bsmsg, bsmsg->headers, bsmsg->message_id, bsmsg->fill_references,
+                      bsmsg->root, bsmsg->quote_type);
+    g_free(bsmsg->message_id);
 
-    g_return_if_fail(body != NULL);
-
-    if(body->len && body->str[body->len] != '\n')
+    if (body->len > 0 && body->str[body->len - 1] != '\n')
         g_string_append_c(body, '\n');
     gtk_text_buffer_insert_at_cursor(buffer, body->str, body->len);
+    g_string_free(body, TRUE);
 
-    if(qtype == QUOTE_HEADERS)
+    if (bsmsg->quote_type == QUOTE_HEADERS)
         gtk_text_buffer_get_end_iter(buffer, &start);
     else
         gtk_text_buffer_get_start_iter(buffer, &start);
 
     gtk_text_buffer_place_cursor(buffer, &start);
-    g_string_free(body, TRUE);
+
+    return NULL;
+}
+
+static void
+fill_body_from_part(BalsaSendmsg           *bsmsg,
+                    LibBalsaMessageHeaders *headers,
+                    const char             *message_id,
+                    GList                  *references,
+                    LibBalsaMessageBody    *root,
+                    QuoteType               qtype)
+{
+    bsmsg->headers = headers;
+    bsmsg->message_id = g_strdup(message_id);
+    bsmsg->fill_references = references;
+    bsmsg->root = root;
+    bsmsg->quote_type = qtype;
+
+    g_thread_unref(g_thread_new("fill-body-from-part", fill_body_from_part_thread, bsmsg));
 }
 
 static GString*
@@ -3725,6 +3984,8 @@ quote_message_body(BalsaSendmsg * bsmsg,
 {
     GString *res;
 
+    g_assert(libbalsa_am_i_subthread());
+
     if (libbalsa_message_body_ref(message, FALSE, FALSE)) {
         res = quote_body(bsmsg,
                          libbalsa_message_get_headers(message),
@@ -3734,6 +3995,7 @@ quote_message_body(BalsaSendmsg * bsmsg,
                          qtype);
         libbalsa_message_body_unref(message);
     } else res = g_string_new("");
+
     return res;
 }
 
@@ -3908,7 +4170,7 @@ bsmsg_set_subject_from_body(BalsaSendmsg * bsmsg,
         }
     }
 
-    gtk_entry_set_text(GTK_ENTRY(bsmsg->subject[1]), subject);
+    gtk_editable_set_text(GTK_EDITABLE(bsmsg->subject[1]), subject);
     g_free(subject);
 }
 
@@ -4180,15 +4442,14 @@ sw_set_language_change_state(GSimpleAction  * action,
     g_simple_action_set_state(action, state);
 }
 
-static const gchar *
-create_lang_menu(GtkWidget * parent, BalsaSendmsg * bsmsg)
+static GMenu *
+create_lang_menu(BalsaSendmsg *bsmsg, const char **active_lang)
 {
     GSimpleActionGroup *simple;
     static const GActionEntry entries[] = {
         {"set", NULL, "s", "''", sw_set_language_change_state}
     };
-    guint i;
-    GtkWidget *langs;
+    unsigned i;
     static gboolean locales_sorted = FALSE;
 #if HAVE_GSPELL
     const GList *lang_list, *l;
@@ -4198,10 +4459,11 @@ create_lang_menu(GtkWidget * parent, BalsaSendmsg * bsmsg)
 #if !HAVE_GSPELL && !HAVE_GTKSPELL
     EnchantBroker *broker;
 #endif                          /* !HAVE_GSPELL && !HAVE_GTKSPELL */
-    const gchar *preferred_lang;
-    const gchar *active_lang = NULL;
+    const char *preferred_lang;
     GMenu *menu;
 
+    *active_lang = NULL;
+
 #if HAVE_GTKSPELL
     lang_list = gtk_spell_checker_get_language_list();
 #elif HAVE_GSPELL
@@ -4264,9 +4526,9 @@ create_lang_menu(GtkWidget * parent, BalsaSendmsg * bsmsg)
                                               "s", locales[i].locale);
             g_menu_append_item(menu, item);
 
-            if (active_lang == NULL ||
+            if (*active_lang == NULL ||
                 strcmp(preferred_lang, locales[i].locale) == 0)
-                active_lang = locales[i].locale;
+                *active_lang = locales[i].locale;
         }
     }
 
@@ -4290,23 +4552,17 @@ create_lang_menu(GtkWidget * parent, BalsaSendmsg * bsmsg)
             g_menu_append_item(menu, item);
             g_object_unref(item);
 
-            if (active_lang == NULL || strcmp(preferred_lang, lang) == 0)
-                active_lang = lang;
+            if (*active_lang == NULL || strcmp(preferred_lang, lang) == 0)
+                *active_lang = lang;
         }
     }
 #if !HAVE_GSPELL
     g_list_free_full(lang_list, (GDestroyNotify) g_free);
 #endif                          /* HAVE_GSPELL */
 
-    g_action_change_state(bsmsg->set_language_action, g_variant_new_string(active_lang));
-
-    langs = gtk_menu_new_from_model(G_MENU_MODEL(menu));
-    g_object_unref(menu);
-
-    gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent), langs);
-    gtk_widget_show(parent);
+    g_action_change_state(bsmsg->set_language_action, g_variant_new_string(*active_lang));
 
-    return active_lang;
+    return menu;
 }
 
 /* Standard buttons; "" means a separator. */
@@ -4614,9 +4870,10 @@ sendmsg_window_process_url(BalsaSendmsg *bsmsg,
 #define NO_SECURITY_ISSUES_WITH_ATTACHMENTS TRUE
 #if defined(NO_SECURITY_ISSUES_WITH_ATTACHMENTS)
 static void
-sw_attach_file(BalsaSendmsg * bsmsg, const gchar * val)
+sw_attach_file(BalsaSendmsg * bsmsg, const char * val)
 {
     GtkFileChooser *attach;
+    GFile *file;
 
     if (!g_path_is_absolute(val)) {
         balsa_information_parented(GTK_WINDOW(bsmsg->window),
@@ -4642,7 +4899,7 @@ sw_attach_file(BalsaSendmsg * bsmsg, const gchar * val)
     }
     attach = g_object_get_data(G_OBJECT(bsmsg->window),
                                "balsa-sendmsg-window-attach-dialog");
-    if (!attach) {
+    if (attach == NULL) {
         attach = sw_attach_dialog(bsmsg);
         g_object_set_data(G_OBJECT(bsmsg->window),
                           "balsa-sendmsg-window-attach-dialog", attach);
@@ -4665,7 +4922,10 @@ sw_attach_file(BalsaSendmsg * bsmsg, const gchar * val)
             return;
         }
     }
-    gtk_file_chooser_select_filename(attach, val);
+
+    file = g_file_new_for_path(val);
+    gtk_file_chooser_set_file(attach, file, NULL);
+    g_object_unref(file);
 }
 #endif
 
@@ -4694,8 +4954,6 @@ sendmsg_window_set_field(BalsaSendmsg * bsmsg, const gchar * key,
 #endif
     if(g_ascii_strcasecmp(key, "subject") == 0) {
         append_comma_separated(GTK_EDITABLE(bsmsg->subject[1]), val);
-        gtk_widget_show_all(bsmsg->subject[0]);
-        gtk_widget_show_all(bsmsg->subject[1]);
         return;
     }
 
@@ -4723,8 +4981,8 @@ sendmsg_window_set_field(BalsaSendmsg * bsmsg, const gchar * key,
             g_object_set_data(G_OBJECT(bsmsg->window),
                               "balsa-sendmsg-window-url-bcc", dialog);
             g_signal_connect(dialog, "response",
-                             G_CALLBACK(gtk_widget_destroy), NULL);
-            gtk_widget_show_all(dialog);
+                             G_CALLBACK(gtk_window_destroy), NULL);
+            gtk_widget_show(dialog);
         }
     }
     else if(g_ascii_strcasecmp(key, "replyto") == 0) {
@@ -4742,94 +5000,170 @@ sendmsg_window_set_field(BalsaSendmsg * bsmsg, const gchar * key,
 /* opens the load file dialog box, allows selection of the file and includes
    it at current point */
 
+typedef struct {
+    BalsaSendmsg *bsmsg;
+    char         *string;
+    size_t        len;
+    char         *charset;
+    char         *fname;
+    char         *converted_string;
+} insert_string_data;
+
 static void
-do_insert_string_select_ch(BalsaSendmsg* bsmsg, GtkTextBuffer *buffer,
-                           const gchar* string, size_t len,
-                           const gchar* fname)
+insert_string_data_free(insert_string_data *insert_data)
+{
+    g_object_unref(insert_data->bsmsg);
+    g_free(insert_data->string);
+    g_free(insert_data->charset);
+    g_free(insert_data->fname);
+    g_free(insert_data->converted_string);
+
+    g_free(insert_data);
+}
+
+static gboolean
+insert_string_idle(gpointer user_data)
+{
+    insert_string_data *insert_data = user_data;
+    BalsaSendmsg  *bsmsg = insert_data->bsmsg;
+    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+
+    gtk_text_buffer_insert_at_cursor(buffer, insert_data->converted_string, -1);
+
+    insert_string_data_free(insert_data);
+
+    return G_SOURCE_REMOVE;
+}
+
+static gpointer
+do_insert_string_thread(gpointer user_data)
 {
-    const gchar *charset = NULL;
-    LibBalsaTextAttribute attr = libbalsa_text_attr_string(string);
+    insert_string_data *insert_data = user_data;
+    LibBalsaTextAttribute attr = libbalsa_text_attr_string(insert_data->string);
+    user_codeset_data data;
+
+    /* Make sure that len can be passed as gssize: */
+    g_assert(insert_data->len <= G_MAXSIZE / 2);
+
+    data.bsmsg = insert_data->bsmsg;
+    data.change_type = NULL;
+    data.content_type = NULL;
+    data.filename = insert_data->fname;
+
+    g_mutex_init(&data.lock);
+    g_cond_init(&data.cond);
 
     do {
-       LibBalsaCodeset codeset;
        LibBalsaCodesetInfo *info;
-       gchar* s;
+        const char *new_charset;
 
-        if ((codeset = sw_get_user_codeset(bsmsg, NULL, NULL, fname))
-            == (LibBalsaCodeset) (-1))
+        g_debug("Trying charset: %s", insert_data->charset);
+
+        insert_data->converted_string = NULL;
+        if (sw_can_convert(insert_data->string, insert_data->len, "UTF-8", insert_data->charset,
+                           &insert_data->converted_string)) {
+            g_idle_add(insert_string_idle, insert_data);
+            insert_data = NULL;
             break;
-        info = &libbalsa_codeset_info[codeset];
+        }
 
-       charset = info->std;
-        if (info->win && (attr & LIBBALSA_TEXT_HI_CTRL))
-            charset = info->win;
+        g_mutex_lock(&data.lock);
+        data.codeset = (LibBalsaCodeset) (-1);
+        data.done = FALSE;
+        g_idle_add(sw_get_user_codeset, &data);
+        while (!data.done)
+            g_cond_wait(&data.cond, &data.lock);
+        g_mutex_unlock(&data.lock);
 
-        g_debug("Trying charset: %s", charset);
-        if (sw_can_convert(string, len, "UTF-8", charset, &s)) {
-            gtk_text_buffer_insert_at_cursor(buffer, s, -1);
-            g_free(s);
+        if (data.codeset == (LibBalsaCodeset) (-1))
             break;
-        }
-    } while(1);
+        info = &libbalsa_codeset_info[data.codeset];
+
+       new_charset = info->std;
+        if (info->win != NULL && (attr & LIBBALSA_TEXT_HI_CTRL) != 0)
+            new_charset = info->win;
+
+        g_free(insert_data->charset);
+        insert_data->charset = g_strdup(new_charset);
+    } while (1);
+
+    g_mutex_clear(&data.lock);
+    g_cond_clear(&data.cond);
+
+    if (insert_data != NULL)
+        insert_string_data_free(insert_data);
+
+    return NULL;
 }
 
 static void
-insert_file_response(GtkWidget    *selector,
-                                        gint          response,
-                                        BalsaSendmsg *bsmsg)
+insert_file_response(GtkDialog *selector,
+                     int        response,
+                     gpointer   user_data)
 {
-    GtkFileChooser *fc;
-    gchar *fname;
+    BalsaSendmsg *bsmsg = user_data;
+    GtkFileChooser *chooser;
+    GFile *file;
+    char *path;
+    char *string;
+    size_t len;
+    GError *error = NULL;
 
     if (response != GTK_RESPONSE_OK) {
-       gtk_widget_destroy(selector);
+       gtk_window_destroy(GTK_WINDOW(selector));
        return;
     }
 
-    fc = GTK_FILE_CHOOSER(selector);
-    fname = gtk_file_chooser_get_filename(fc);
-    if (fname != NULL) {
-        gchar *string;
-        gsize len;
-        GError *error = NULL;
+    chooser = GTK_FILE_CHOOSER(selector);
+    file = gtk_file_chooser_get_file(chooser);
+    path = g_file_get_path(file);
+    g_object_unref(file);
 
-       if (g_file_get_contents(fname, &string, &len, &error)) {
-            LibBalsaTextAttribute attr;
-            GtkTextBuffer *buffer;
+    if (path == NULL) {
+       gtk_window_destroy(GTK_WINDOW(selector));
+       return;
+    }
 
-            buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
-            attr = libbalsa_text_attr_string(string);
-            if (!attr || (attr & LIBBALSA_TEXT_HI_UTF8)) {
-                /* Ascii or utf-8 */
-                gtk_text_buffer_insert_at_cursor(buffer, string, -1);
-            } else {
-                /* Neither ascii nor utf-8... */
-                gchar *s = NULL;
-                const gchar *charset = sw_preferred_charset(bsmsg);
-
-                if (sw_can_convert(string, -1, "UTF-8", charset, &s)) {
-                    /* ...but seems to be in current charset. */
-                    gtk_text_buffer_insert_at_cursor(buffer, s, -1);
-                    g_free(s);
-                } else
-                    /* ...and can't be decoded from current charset. */
-                    do_insert_string_select_ch(bsmsg, buffer, string, len, fname);
-            }
-            g_free(string);
+    if (g_file_get_contents(path, &string, &len, &error)) {
+        LibBalsaTextAttribute attr;
+        GtkTextBuffer *buffer;
 
-            /* Use the same folder as for attachments. */
-            g_free(balsa_app.attach_dir);
-            balsa_app.attach_dir = gtk_file_chooser_get_current_folder(fc);
-       } else {
-               balsa_information_parented(GTK_WINDOW(bsmsg->window),
-                       LIBBALSA_INFORMATION_WARNING, _("Cannot read the file “%s”: %s"), fname, 
error->message);
-               g_error_free(error);
-       }
+        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+        attr = libbalsa_text_attr_string(string);
+        if (attr == 0 || (attr & LIBBALSA_TEXT_HI_UTF8) != 0) {
+            /* Ascii or utf-8 */
+            gtk_text_buffer_insert_at_cursor(buffer, string, -1);
+        } else {
+            /* Neither ascii nor utf-8... */
+            const char *charset = sw_preferred_charset(bsmsg);
+            insert_string_data *insert_data = g_new(insert_string_data, 1);
+
+            insert_data->bsmsg   = g_object_ref(bsmsg);
+            insert_data->string  = g_strdup(string);
+            insert_data->len     = len;
+            insert_data->charset = g_strdup(charset);
+            insert_data->fname   = g_strdup(path);
+
+            g_thread_unref(g_thread_new("insert-string", do_insert_string_thread, insert_data));
+        }
+        g_free(string);
 
-        g_free(fname);
+        /* Use the same folder as for attachments. */
+        g_free(balsa_app.attach_dir);
+        file = gtk_file_chooser_get_current_folder(chooser);
+        balsa_app.attach_dir = g_file_get_path(file);
+        g_object_unref(file);
+    } else {
+        balsa_information_parented(GTK_WINDOW(bsmsg->window),
+                                   LIBBALSA_INFORMATION_WARNING,
+                                   _("Cannot read the file “%s”: %s"),
+                                   path, error->message);
+        g_error_free(error);
     }
 
-    gtk_widget_destroy(selector);
+    g_free(path);
+
+    gtk_window_destroy(GTK_WINDOW(selector));
 }
 
 static void
@@ -4851,11 +5185,16 @@ sw_include_file_activated(GSimpleAction * action,
     libbalsa_macosx_menu_for_parent(file_selector, GTK_WINDOW(bsmsg->window));
 #endif
     gtk_window_set_destroy_with_parent(GTK_WINDOW(file_selector), TRUE);
+
     /* Use the same folder as for attachments. */
-    if (balsa_app.attach_dir)
-        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER
-                                            (file_selector),
-                                            balsa_app.attach_dir);
+    if (balsa_app.attach_dir != NULL) {
+        GFile *file;
+
+        file = g_file_new_for_path(balsa_app.attach_dir);
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_selector), file, NULL);
+        g_object_unref(file);
+    }
+
     g_signal_connect(file_selector, "response",
                      G_CALLBACK(insert_file_response), bsmsg);
 
@@ -5113,31 +5452,49 @@ bsmsg2message(BalsaSendmsg * bsmsg)
 }
 
 /* ask the user for a subject */
+
+typedef struct {
+    BalsaSendmsg *bsmsg;
+    GMutex lock;
+    GCond cond;
+
+    int choice;
+    gboolean warn_mp;
+    gboolean warn_html_sign;
+    GtkWidget *subj_entry;
+} send_message_data;
+
+static void
+send_message_thread_subject_response(GtkDialog *dialog,
+                                     int        response_id,
+                                     gpointer   user_data)
+{
+    send_message_data *data = user_data;
+
+    /* always set the current string in the subject entry */
+    gtk_editable_set_text(GTK_EDITABLE(data->bsmsg->subject[1]),
+                          gtk_editable_get_text(GTK_EDITABLE(data->subj_entry)));
+
+    g_mutex_lock(&data->lock);
+    data->choice = response_id;
+    g_cond_signal(&data->cond);
+    g_mutex_unlock(&data->lock);
+
+    gtk_window_destroy(GTK_WINDOW(dialog));
+}
+
 static gboolean
-subject_not_empty(BalsaSendmsg * bsmsg)
+send_message_thread_subject_idle(gpointer user_data)
 {
-    const gchar *subj;
+    send_message_data *data = user_data;
+    BalsaSendmsg *bsmsg = data->bsmsg;
     GtkWidget *no_subj_dialog;
     GtkWidget *dialog_vbox;
     GtkWidget *hbox;
     GtkWidget *image;
     GtkWidget *vbox;
-    gchar *text_str;
+    char *text_str;
     GtkWidget *label;
-    GtkWidget *subj_entry;
-    gint response;
-
-    /* read the subject widget and verify that it is contains something else
-       than spaces */
-    subj = gtk_entry_get_text(GTK_ENTRY(bsmsg->subject[1]));
-    if (subj) {
-       const gchar *p = subj;
-
-       while (*p && g_unichar_isspace(g_utf8_get_char(p)))
-           p = g_utf8_next_char(p);
-       if (*p != '\0')
-           return TRUE;
-    }
 
     /* build the dialog */
     no_subj_dialog =
@@ -5148,115 +5505,166 @@ subject_not_empty(BalsaSendmsg * bsmsg)
                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     _("_Send"),   GTK_RESPONSE_OK,
                                     NULL);
-    gtk_container_set_border_width (GTK_CONTAINER (no_subj_dialog), 6);
-    gtk_window_set_resizable (GTK_WINDOW (no_subj_dialog), FALSE);
-    gtk_window_set_type_hint (GTK_WINDOW (no_subj_dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    gtk_widget_set_margin_top(no_subj_dialog, 6);
+    gtk_widget_set_margin_bottom(no_subj_dialog, 6);
+    gtk_widget_set_margin_start(no_subj_dialog, 6);
+    gtk_widget_set_margin_end(no_subj_dialog, 6);
+
+    gtk_window_set_resizable(GTK_WINDOW(no_subj_dialog), FALSE);
 
     dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(no_subj_dialog));
 
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
     gtk_widget_set_vexpand(hbox, TRUE);
     gtk_widget_set_valign(hbox, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(dialog_vbox), hbox);
-    gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+    gtk_box_append(GTK_BOX(dialog_vbox), hbox);
 
-    image = gtk_image_new_from_icon_name("dialog-question",
-                                         GTK_ICON_SIZE_DIALOG);
-    gtk_container_add(GTK_CONTAINER (hbox), image);
+    gtk_widget_set_margin_top(hbox, 6);
+    gtk_widget_set_margin_bottom(hbox, 6);
+    gtk_widget_set_margin_start(hbox, 6);
+    gtk_widget_set_margin_end(hbox, 6);
+
+    image = gtk_image_new_from_icon_name("dialog-question");
+    gtk_box_append(GTK_BOX (hbox), image);
     gtk_widget_set_valign(image, GTK_ALIGN_START);
 
     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
     gtk_widget_set_hexpand(vbox, TRUE);
     gtk_widget_set_halign(vbox, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(hbox), vbox);
+    gtk_box_append(GTK_BOX(hbox), vbox);
 
     text_str = g_strdup_printf("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
                               _("You did not specify a subject for this message"),
                               _("If you would like to provide one, enter it below."));
     label = libbalsa_create_wrap_label(text_str, TRUE);
     g_free(text_str);
-    gtk_container_add(GTK_CONTAINER (vbox), label);
+    gtk_box_append(GTK_BOX (vbox), label);
     gtk_widget_set_valign(label, GTK_ALIGN_START);
 
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
-    gtk_container_add(GTK_CONTAINER (vbox), hbox);
+    gtk_box_append(GTK_BOX (vbox), hbox);
 
     label = gtk_label_new (_("Subject:"));
-    gtk_container_add(GTK_CONTAINER (hbox), label);
+    gtk_box_append(GTK_BOX (hbox), label);
 
-    subj_entry = gtk_entry_new ();
-    gtk_entry_set_text(GTK_ENTRY(subj_entry), _("(no subject)"));
-    gtk_widget_set_hexpand(subj_entry, TRUE);
-    gtk_widget_set_halign(subj_entry, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(hbox), subj_entry);
+    data->subj_entry = gtk_entry_new ();
+    gtk_editable_set_text(GTK_EDITABLE(data->subj_entry), _("(no subject)"));
+    gtk_widget_set_hexpand(data->subj_entry, TRUE);
+    gtk_widget_set_halign(data->subj_entry, GTK_ALIGN_FILL);
+    gtk_box_append(GTK_BOX(hbox), data->subj_entry);
 
-    gtk_entry_set_activates_default (GTK_ENTRY (subj_entry), TRUE);
+    gtk_entry_set_activates_default(GTK_ENTRY(data->subj_entry), TRUE);
     gtk_dialog_set_default_response(GTK_DIALOG (no_subj_dialog),
                                     GTK_RESPONSE_OK);
 
-    gtk_widget_grab_focus (subj_entry);
-    gtk_editable_select_region(GTK_EDITABLE(subj_entry), 0, -1);
-    gtk_widget_show_all(dialog_vbox);
+    gtk_widget_grab_focus (data->subj_entry);
+    gtk_editable_select_region(GTK_EDITABLE(data->subj_entry), 0, -1);
 
-    response = gtk_dialog_run(GTK_DIALOG(no_subj_dialog));
+    g_signal_connect(no_subj_dialog, "response",
+                     G_CALLBACK(send_message_thread_subject_response), data);
+    gtk_widget_show(no_subj_dialog);
 
-    /* always set the current string in the subject entry */
-    gtk_entry_set_text(GTK_ENTRY(bsmsg->subject[1]),
-                      gtk_entry_get_text(GTK_ENTRY(subj_entry)));
-    gtk_widget_destroy(no_subj_dialog);
+    return G_SOURCE_REMOVE;
+}
+
+/*
+ * Encryption and autocrypt suggestions
+ */
+
+typedef struct {
+    BalsaSendmsg *bsmsg;
+    GMutex lock;
+    GCond cond;
+
+    int choice;
+    const char *secondary_msg;
+    int default_button;
+} check_encrypt_data;
 
-    return response == GTK_RESPONSE_OK;
+static void
+config_dlg_button(GtkDialog *dialog, int response_id, const char *icon_id)
+{
+    GtkWidget *button;
+
+    button = gtk_dialog_get_widget_for_response(dialog, response_id);
+    if (button != NULL)
+        gtk_button_set_icon_name(GTK_BUTTON(button), icon_id);
 }
 
 static void
-config_dlg_button(GtkDialog *dialog, gint response_id, const gchar *icon_id)
+run_check_encrypt_dialog_response(GtkDialog *dialog,
+                                  int        response_id,
+                                  gpointer   user_data)
 {
-       GtkWidget *button;
+    check_encrypt_data *data = user_data;
 
-       button = gtk_dialog_get_widget_for_response(dialog, response_id);
-       if (button != NULL) {
-               GtkWidget *image;
+    if (response_id == GTK_RESPONSE_YES)
+        bsmsg_setup_gpg_ui_by_mode(data->bsmsg, data->bsmsg->gpg_mode | LIBBALSA_PROTECT_ENCRYPT);
 
-               image = gtk_image_new_from_icon_name(icon_id, GTK_ICON_SIZE_BUTTON);
-               gtk_button_set_image(GTK_BUTTON(button), image);
-       }
+    g_mutex_lock(&data->lock);
+    data->choice = response_id;
+    g_cond_signal(&data->cond);
+    g_mutex_unlock(&data->lock);
+
+    gtk_window_destroy(GTK_WINDOW(dialog));
 }
 
 static gboolean
-run_check_encrypt_dialog(BalsaSendmsg *bsmsg, const gchar *secondary_msg, gint default_button)
-{
-       GtkWidget *dialog;
-       gboolean result = TRUE;
-       gint choice;
-
-       dialog = gtk_message_dialog_new(GTK_WINDOW(bsmsg->window),
-            GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL | libbalsa_dialog_flags(),
-            GTK_MESSAGE_QUESTION,
-            GTK_BUTTONS_NONE,
-                _("Message could be encrypted"));
-       gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(dialog), "%s", secondary_msg);
-       gtk_dialog_add_buttons(GTK_DIALOG(dialog),
-               _("Send _encrypted"), GTK_RESPONSE_YES,
-               _("Send _unencrypted"), GTK_RESPONSE_NO,
-               _("_Cancel"), GTK_RESPONSE_CANCEL,
-               NULL);
-       gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_button);
-
-       /* add button images */
-       config_dlg_button(GTK_DIALOG(dialog), GTK_RESPONSE_YES, balsa_icon_id(BALSA_PIXMAP_GPG_ENCRYPT));
-       config_dlg_button(GTK_DIALOG(dialog), GTK_RESPONSE_NO, balsa_icon_id(BALSA_PIXMAP_SEND));
-
-       choice = gtk_dialog_run(GTK_DIALOG(dialog));
-       gtk_widget_destroy(dialog);
-       if (choice == GTK_RESPONSE_YES) {
-           bsmsg_setup_gpg_ui_by_mode(bsmsg, bsmsg->gpg_mode | LIBBALSA_PROTECT_ENCRYPT);
-       } else if ((choice == GTK_RESPONSE_CANCEL) || (choice == GTK_RESPONSE_DELETE_EVENT)) {
-           result = FALSE;
-    } else {
-       /* nothing to do */
-    }
+run_check_encrypt_dialog_idle(gpointer user_data)
+{
+    check_encrypt_data *data = user_data;
+    GtkWidget *dialog;
 
-       return result;
+    dialog = gtk_message_dialog_new(GTK_WINDOW(data->bsmsg->window),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL |
+                                    libbalsa_dialog_flags(), GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+                                    _("Message could be encrypted"));
+    gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(dialog), "%s", data->secondary_msg);
+    gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+                           _("Send _encrypted"), GTK_RESPONSE_YES,
+                           _("Send _unencrypted"), GTK_RESPONSE_NO,
+                           _("_Cancel"), GTK_RESPONSE_CANCEL, NULL);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), data->default_button);
+
+    /* add button images */
+    config_dlg_button(GTK_DIALOG(dialog), GTK_RESPONSE_YES,
+                      balsa_icon_id(BALSA_PIXMAP_GPG_ENCRYPT));
+    config_dlg_button(GTK_DIALOG(dialog), GTK_RESPONSE_NO, balsa_icon_id(BALSA_PIXMAP_SEND));
+
+    g_signal_connect(dialog, "response", G_CALLBACK(run_check_encrypt_dialog_response), data);
+    gtk_widget_show(dialog);
+
+    return G_SOURCE_REMOVE;
+}
+
+static gboolean
+run_check_encrypt_dialog(BalsaSendmsg *bsmsg, const char *secondary_msg, int default_button)
+{
+    check_encrypt_data data;
+    gboolean result = TRUE;
+
+    g_mutex_init(&data.lock);
+    g_cond_init(&data.cond);
+
+    data.bsmsg = bsmsg;
+    data.secondary_msg = secondary_msg;
+    data.default_button = default_button;
+
+    g_mutex_lock(&data.lock);
+    g_idle_add(run_check_encrypt_dialog_idle, &data);
+    data.choice = 0;
+    while (data.choice == 0)
+        g_cond_wait(&data.cond, &data.lock);
+    g_mutex_unlock(&data.lock);
+
+    g_mutex_clear(&data.lock);
+    g_cond_clear(&data.cond);
+
+    if ((data.choice == GTK_RESPONSE_CANCEL) || (data.choice == GTK_RESPONSE_DELETE_EVENT))
+        result = FALSE;
+
+    return result;
 }
 
 static gboolean
@@ -5268,6 +5676,8 @@ check_suggest_encryption(BalsaSendmsg * bsmsg)
     gint len;
     gboolean result = TRUE;
 
+    g_assert(libbalsa_am_i_subthread());
+
     /* check if the user wants to see the message */
     if (bsmsg->ident == NULL ||
         !libbalsa_identity_get_warn_send_plain(bsmsg->ident))
@@ -5351,6 +5761,8 @@ check_autocrypt_recommendation(BalsaSendmsg *bsmsg)
     GError *error = NULL;
     gboolean result;
 
+    g_assert(libbalsa_am_i_subthread());
+
     /* check if autocrypt is enabled, use the non-Autocrypt approach if not */
     if ((bsmsg->ident == NULL) ||
         (libbalsa_identity_get_autocrypt_mode(bsmsg->ident) == AUTOCRYPT_DISABLE)) {
@@ -5476,15 +5888,6 @@ check_autocrypt_recommendation(BalsaSendmsg *bsmsg)
  * blocking the main thread.
  */
 
-typedef struct {
-    GMutex lock;
-    GCond cond;
-    int choice;
-    GtkWindow *window;
-    gboolean warn_mp;
-    gboolean warn_html_sign;
-} send_message_data;
-
 static void
 send_message_thread_response(GtkDialog *dialog,
                              int        response_id,
@@ -5497,12 +5900,12 @@ send_message_thread_response(GtkDialog *dialog,
     g_cond_signal(&data->cond);
     g_mutex_unlock(&data->lock);
 
-    gtk_widget_destroy(GTK_WIDGET(dialog));
+    gtk_window_destroy(GTK_WINDOW(dialog));
 }
 
 
 static gboolean
-send_message_thread_idle(gpointer user_data)
+send_message_thread_warn_idle(gpointer user_data)
 {
     send_message_data *data = user_data;
     GtkWidget *dialog;
@@ -5521,12 +5924,12 @@ send_message_thread_idle(gpointer user_data)
                     _("You should select MIME mode if the complete "
                       "message shall be protected. Do you really want to proceed?"));
     dialog =
-        gtk_message_dialog_new(data->window,
+        gtk_message_dialog_new(GTK_WINDOW(data->bsmsg->window),
                                GTK_DIALOG_DESTROY_WITH_PARENT |
                                GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                                GTK_BUTTONS_OK_CANCEL, "%s", string->str);
 #if HAVE_MACOSX_DESKTOP
-    libbalsa_macosx_menu_for_parent(dialog, data->window);
+    libbalsa_macosx_menu_for_parent(dialog, GTK_WINDOW(data->bsmsg->window));
 #endif
     g_string_free(string, TRUE);
 
@@ -5540,6 +5943,7 @@ static gpointer
 send_message_thread(gpointer data)
 {
     BalsaSendmsg *bsmsg = data;
+    send_message_data send_data;
     LibBalsaMsgCreateResult result;
     LibBalsaMessage *message;
     LibBalsaMailbox *fcc;
@@ -5556,7 +5960,24 @@ send_message_thread(gpointer data)
     }
 #endif /* ENABLE_AUTOCRYPT */
 
-    if ((bsmsg->gpg_mode & LIBBALSA_PROTECT_OPENPGP) != 0) {
+    send_data.choice = GTK_RESPONSE_OK;
+
+    g_mutex_init(&send_data.lock);
+    g_cond_init(&send_data.cond);
+
+    send_data.bsmsg = bsmsg;
+
+    if (!bsmsg->has_subject) {
+        g_mutex_lock(&send_data.lock);
+        g_idle_add(send_message_thread_subject_idle, &send_data);
+        send_data.choice = 0;
+        while (send_data.choice == 0)
+            g_cond_wait(&send_data.cond, &send_data.lock);
+        g_mutex_unlock(&send_data.lock);
+    }
+
+    if (send_data.choice == GTK_RESPONSE_OK &&
+        (bsmsg->gpg_mode & LIBBALSA_PROTECT_OPENPGP) != 0) {
         gboolean warn_mp;
         gboolean warn_html_sign;
 
@@ -5567,31 +5988,26 @@ send_message_thread(gpointer data)
             bsmsg->send_mp_alt;
 
         if (warn_mp || warn_html_sign) {
-            send_message_data data;
             /* we are going to RFC2440 sign/encrypt a multipart, or to
              * RFC2440 sign a multipart/alternative... */
-            data.window = GTK_WINDOW(bsmsg->window);
-            data.warn_mp = warn_mp;
-            data.warn_html_sign = warn_html_sign;
-            g_idle_add(send_message_thread_idle, info);
-
-            g_mutex_init(&data.lock);
-            g_cond_init(&data.cond);
-
-            g_mutex_lock(&data.lock);
-            data.choice = 0;
-            while (data.choice == 0)
-                g_cond_wait(&data.cond, &data.lock);
-            g_mutex_unlock(&data.lock);
-
-            g_mutex_clear(&data.lock);
-            g_cond_clear(&data.cond);
-
-            if (data.choice != GTK_RESPONSE_OK)
-                return NULL;
+            send_data.warn_mp = warn_mp;
+            send_data.warn_html_sign = warn_html_sign;
+
+            g_mutex_lock(&send_data.lock);
+            g_idle_add(send_message_thread_warn_idle, &send_data);
+            send_data.choice = 0;
+            while (send_data.choice == 0)
+                g_cond_wait(&send_data.cond, &send_data.lock);
+            g_mutex_unlock(&send_data.lock);
         }
     }
 
+    g_mutex_clear(&send_data.lock);
+    g_cond_clear(&send_data.cond);
+
+    if (send_data.choice != GTK_RESPONSE_OK)
+        return NULL;
+
     message = bsmsg2message(bsmsg);
     fcc = balsa_find_mailbox_by_url(bsmsg->fcc_url);
 
@@ -5653,7 +6069,7 @@ send_message_thread(gpointer data)
        return NULL;
     }
 
-    gtk_widget_destroy(bsmsg->window);
+    gtk_window_destroy(GTK_WINDOW(bsmsg->window));
 
     return NULL;
 }
@@ -5661,16 +6077,26 @@ send_message_thread(gpointer data)
 static void
 send_message_handler(BalsaSendmsg * bsmsg)
 {
-    GThread *thread;
+    const char *subj;
 
     if (!bsmsg->ready_to_send)
        return;
 
-    if(!subject_not_empty(bsmsg))
-       return;
+    /* read the subject widget and verify that it is contains something else
+       than spaces */
+    subj = gtk_editable_get_text(GTK_EDITABLE(bsmsg->subject[1]));
+    bsmsg->has_subject = FALSE;
+    if (subj != NULL) {
+       const gchar *p = subj;
+       const gchar *end = subj + strlen(subj);
+
+       while (p < end && g_unichar_isspace(g_utf8_get_char(p)))
+           p = g_utf8_next_char(p);
+       if (p < end)
+           bsmsg->has_subject = TRUE;
+    }
 
-    thread = g_thread_new("send-message", send_message_thread, bsmsg);
-    g_thread_unref(thread);
+    g_thread_unref(g_thread_new("send-message", send_message_thread, bsmsg));
 }
 
 
@@ -5787,7 +6213,7 @@ sw_postpone_activated(GSimpleAction * action,
             balsa_information_parented(GTK_WINDOW(bsmsg->window),
                                        LIBBALSA_INFORMATION_MESSAGE,
                                        _("Message postponed."));
-            gtk_widget_destroy(bsmsg->window);
+            gtk_window_destroy(GTK_WINDOW(bsmsg->window));
         } else {
             balsa_information_parented(GTK_WINDOW(bsmsg->window),
                                        LIBBALSA_INFORMATION_MESSAGE,
@@ -6209,8 +6635,8 @@ sw_entry_helper(GSimpleAction      * action,
                 GtkWidget    * entry[])
 {
     if (g_variant_get_boolean(state)) {
-        gtk_widget_show_all(entry[0]);
-        gtk_widget_show_all(entry[1]);
+        gtk_widget_show(entry[0]);
+        gtk_widget_show(entry[1]);
         gtk_widget_grab_focus(entry[1]);
     } else {
         gtk_widget_hide(entry[0]);
@@ -6523,7 +6949,7 @@ sw_spell_check_activated(GSimpleAction * action,
         } else
             /* A spell checker was created, but not shown because of
              * errors; we'll destroy it, and create a new one. */
-            gtk_widget_destroy(bsmsg->spell_checker);
+            gtk_window_destroy(GTK_WINDOW(bsmsg->spell_checker));
     }
 
     sw_buffer_signals_disconnect(bsmsg);
@@ -6562,7 +6988,6 @@ sendmsg_window_new_from_list(LibBalsaMailbox * mailbox,
 {
     BalsaSendmsg *bsmsg;
     LibBalsaMessage *message;
-    GtkTextBuffer *buffer;
     guint i;
     guint msgno;
 
@@ -6585,8 +7010,6 @@ sendmsg_window_new_from_list(LibBalsaMailbox * mailbox,
     }
     g_object_unref(message);
 
-    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
-
     for (i = 1; i < selected->len; i++) {
        msgno = g_array_index(selected, guint, i);
         message = libbalsa_mailbox_get_message(mailbox, msgno);
@@ -6595,12 +7018,9 @@ sendmsg_window_new_from_list(LibBalsaMailbox * mailbox,
 
         if (type == SEND_FORWARD_ATTACH)
             attach_message(bsmsg, message);
-        else if (type == SEND_FORWARD_INLINE) {
-            GString *body =
-                quote_message_body(bsmsg, message, QUOTE_NOPREFIX);
-            gtk_text_buffer_insert_at_cursor(buffer, body->str, body->len);
-            g_string_free(body, TRUE);
-        }
+        else if (type == SEND_FORWARD_INLINE)
+            sw_insert_message(bsmsg, message, QUOTE_NOPREFIX);
+
         g_object_unref(message);
     }
 
@@ -6751,7 +7171,7 @@ sendmsg_window_set_title(BalsaSendmsg * bsmsg)
     g_object_unref(list);
 
     title = g_strdup_printf(title_format, to_string ? to_string : "",
-                            gtk_entry_get_text(GTK_ENTRY(bsmsg->subject[1])));
+                            gtk_editable_get_text(GTK_EDITABLE(bsmsg->subject[1])));
     g_free(to_string);
     gtk_window_set_title(GTK_WINDOW(bsmsg->window), title);
     g_free(title);
@@ -6885,13 +7305,36 @@ sendmsg_window_add_action_entries(GActionMap * action_map)
 }
 
 static void
-sw_menubar_foreach(GtkWidget *widget, gpointer data)
+set_language_menu(GtkWidget *menubar, GMenu *language_menu)
 {
-    GtkWidget **lang_menu = data;
-    GtkMenuItem *item = GTK_MENU_ITEM(widget);
+    GMenuModel *model;
+    int i;
+    int n;
+    int lang_position = -1;
+    GMenuItem *item;
+
+    model = gtk_popover_menu_bar_get_menu_model(GTK_POPOVER_MENU_BAR(menubar));
+    n = g_menu_model_get_n_items(model);
+    for (i = 0; i < n; i++) {
+        const char *label;
+
+        if (g_menu_model_get_item_attribute(model, i, "label", "s", &label) &&
+            strcmp(label, "_Language") == 0) {
+            lang_position = i;
+            break;
+        }
+    }
+
+    g_return_if_fail(lang_position >= 0);
 
-    if (strcmp(gtk_menu_item_get_label(item), _("_Language")) == 0)
-        *lang_menu = widget;
+    item = g_menu_item_new_from_model(model, lang_position);
+
+    g_menu_item_set_submenu(item, G_MENU_MODEL(language_menu));
+
+    /* Replace the existing submenu */
+    g_menu_remove(G_MENU(model), lang_position);
+    g_menu_insert_item(G_MENU(model), lang_position, item);
+    g_object_unref(item);
 }
 
 static BalsaSendmsg*
@@ -6908,49 +7351,32 @@ sendmsg_window_new()
     GtkWidget *menubar;
     GtkWidget *paned;
     const gchar resource_path[] = "/org/desktop/Balsa/sendmsg-window.ui";
+    GMenu *language_menu;
     const gchar *current_locale;
 
-    bsmsg = g_malloc(sizeof(BalsaSendmsg));
-    bsmsg->in_reply_to = NULL;
-    bsmsg->references = NULL;
-    bsmsg->spell_check_lang = NULL;
-    bsmsg->fcc_url  = NULL;
-    bsmsg->insert_mark = NULL;
+    bsmsg = g_new0(BalsaSendmsg, 1);
 
     bsmsg->ident = g_object_ref(balsa_app.current_ident);
 
-    bsmsg->update_config = FALSE;
-    bsmsg->state = SENDMSG_STATE_CLEAN;
-
     bsmsg->window = window =
         gtk_application_window_new(balsa_app.application);
     geometry_manager_attach(GTK_WINDOW(window), "SendMsgWindow");
 
-    gtk_window_set_role(GTK_WINDOW(window), "compose");
+    gtk_box_append(GTK_BOX(window), main_box);
+    gtk_widget_show(window);
 
-    gtk_container_add(GTK_CONTAINER(window), main_box);
-    gtk_widget_show_all(window);
-
-    bsmsg->type = SEND_NORMAL;
-    bsmsg->is_continue = FALSE;
-#if !HAVE_GTKSPELL && !HAVE_GSPELL
-    bsmsg->spell_checker = NULL;
-#endif                          /* HAVE_GTKSPELL */
     bsmsg->gpg_mode = LIBBALSA_PROTECT_RFC3156;
-    bsmsg->attach_pubkey = FALSE;
     bsmsg->autosave_timeout_id = /* autosave every 5 minutes */
         g_timeout_add_seconds(60*5, (GSourceFunc)sw_autosave_timeout_cb, bsmsg);
 
-    bsmsg->draft_message = NULL;
-    bsmsg->parent_message = NULL;
-    g_signal_connect(window, "delete-event",
-                    G_CALLBACK(delete_event_cb), bsmsg);
+    g_signal_connect(window, "close-request",
+                    G_CALLBACK(sw_close_request_cb), bsmsg);
     g_signal_connect(window, "destroy",
                     G_CALLBACK(destroy_event_cb), bsmsg);
     /* If any compose windows are open when Balsa is closed, we want
      * them also to be closed. */
     g_object_weak_ref(G_OBJECT(balsa_app.main_window),
-                      (GWeakNotify) gtk_widget_destroy, window);
+                      (GWeakNotify) gtk_window_destroy, window);
 
     /* Set up the GMenu structures */
     menubar = libbalsa_window_get_menu_bar(GTK_APPLICATION_WINDOW(window),
@@ -6962,32 +7388,29 @@ sendmsg_window_new()
         g_error_free(error);
         return NULL;
     }
-    gtk_widget_show(menubar);
 
 #if HAVE_MACOSX_DESKTOP
     libbalsa_macosx_menu(window, GTK_MENU_SHELL(menubar));
 #else
-    gtk_container_add(GTK_CONTAINER(main_box), menubar);
+    gtk_box_append(GTK_BOX(main_box), menubar);
 #endif
 
     /*
      * Set up the spell-checker language menu
      */
-    bsmsg->text = NULL;
-    gtk_container_foreach(GTK_CONTAINER(menubar), sw_menubar_foreach,
-                          &bsmsg->current_language_menu);
-    current_locale = create_lang_menu(bsmsg->current_language_menu, bsmsg);
-    if (current_locale == NULL) {
+    language_menu = create_lang_menu(bsmsg, &current_locale);
+    set_language_menu(menubar, language_menu);
+    g_object_unref(language_menu);
+
+    if (current_locale == NULL)
         sw_action_set_enabled(bsmsg, "spell-check", FALSE);
-    }
 
     model = sendmsg_window_get_toolbar_model();
     bsmsg->toolbar = balsa_toolbar_new(model, G_ACTION_MAP(window));
-    gtk_container_add(GTK_CONTAINER(main_box), bsmsg->toolbar);
+    gtk_box_append(GTK_BOX(main_box), bsmsg->toolbar);
 
     bsmsg->flow = !balsa_app.wordwrap;
     sw_action_set_enabled(bsmsg, "reflow", bsmsg->flow);
-    bsmsg->send_mp_alt = FALSE;
 
     sw_action_set_enabled(bsmsg, "select-ident",
                      balsa_app.identities->next != NULL);
@@ -6999,10 +7422,6 @@ sendmsg_window_new()
     sw_buffer_set_undo(bsmsg, TRUE, FALSE);
 #endif                          /* HAVE_GTKSOURCEVIEW */
 
-    /* set options */
-    bsmsg->req_mdn = FALSE;
-    bsmsg->req_dsn = FALSE;
-
     sw_action_set_active(bsmsg, "flowed", bsmsg->flow);
     sw_action_set_active(bsmsg, "send-html",
                          libbalsa_identity_get_send_mp_alternative(bsmsg->ident));
@@ -7015,15 +7434,13 @@ sendmsg_window_new()
     bsmsg->paned = paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
     gtk_widget_set_vexpand(paned, TRUE);
     gtk_widget_set_valign(paned, GTK_ALIGN_FILL);
-    gtk_container_add(GTK_CONTAINER(main_box), paned);
-    gtk_widget_show(paned);
+    gtk_box_append(GTK_BOX(main_box), paned);
 
     /* create the top portion with the to, from, etc in it */
-    gtk_paned_add1(GTK_PANED(paned), create_info_pane(bsmsg));
-    bsmsg->tree_view = NULL;
+    gtk_paned_set_start_child(GTK_PANED(paned), create_info_pane(bsmsg));
 
     /* create text area for the message */
-    gtk_paned_add2(GTK_PANED(paned), create_text_area(bsmsg));
+    gtk_paned_set_end_child(GTK_PANED(paned), create_text_area(bsmsg));
 
     /* set the menus - and language index */
     init_menus(bsmsg);
@@ -7046,8 +7463,8 @@ sendmsg_window_new()
     bsmsg->update_config = TRUE;
 
     bsmsg->delete_sig_id =
-       g_signal_connect(balsa_app.main_window, "delete-event",
-                        G_CALLBACK(delete_event_cb), bsmsg);
+       g_signal_connect(balsa_app.main_window, "close-request",
+                        G_CALLBACK(sw_close_request_cb), bsmsg);
 
     setup_headers_from_identity(bsmsg, bsmsg->ident);
 
@@ -7144,8 +7561,7 @@ sendmsg_window_reply_embedded(LibBalsaMessageBody *part,
     BalsaSendmsg *bsmsg = sendmsg_window_new();
     LibBalsaMessageHeaders *headers;
 
-    g_assert(part);
-    g_return_val_if_fail(part->embhdrs, bsmsg);
+    g_assert(libbalsa_am_i_subthread());
 
     switch(reply_type) {
     case SEND_REPLY:
@@ -7154,12 +7570,13 @@ sendmsg_window_reply_embedded(LibBalsaMessageBody *part,
         bsmsg->type = reply_type;       break;
     default: g_error("reply_type: %d", reply_type);
     }
+
     bsm_prepare_for_setup(g_object_ref(part->message));
     headers = part->embhdrs;
     /* To: */
     set_to(bsmsg, headers);
 
-    if(part->embhdrs) {
+    if (part->embhdrs != NULL) {
         const gchar *message_id =
             libbalsa_message_header_get_one(part->embhdrs, "Message-Id");
         const gchar *in_reply_to =
@@ -7227,19 +7644,15 @@ sendmsg_window_forward(LibBalsaMailbox *mailbox, guint msgno,
 BalsaSendmsg*
 sendmsg_window_continue(LibBalsaMailbox * mailbox, guint msgno)
 {
-    LibBalsaMessage *message =
-        libbalsa_mailbox_get_message(mailbox, msgno);
+    LibBalsaMessage *message = libbalsa_mailbox_get_message(mailbox, msgno);
     BalsaSendmsg *bsmsg;
     const gchar *postpone_hdr;
     GList *list, *refs = NULL;
     GList *in_reply_to;
 
-    g_assert(message);
-
-    if ((bsmsg = g_object_get_data(G_OBJECT(message),
-                                   BALSA_SENDMSG_WINDOW_KEY))) {
-        gtk_window_present_with_time(GTK_WINDOW(bsmsg->window),
-                                     gtk_get_current_event_time());
+    bsmsg = g_object_get_data(G_OBJECT(message), BALSA_SENDMSG_WINDOW_KEY);
+    if (bsmsg != NULL) {
+        gtk_window_present(GTK_WINDOW(bsmsg->window));
         return NULL;
     }
 
@@ -7247,8 +7660,7 @@ sendmsg_window_continue(LibBalsaMailbox * mailbox, guint msgno)
     bsmsg->is_continue = TRUE;
     bsm_prepare_for_setup(message);
     bsmsg->draft_message = message;
-    g_object_set_data(G_OBJECT(bsmsg->draft_message),
-                      BALSA_SENDMSG_WINDOW_KEY, bsmsg);
+    g_object_set_data(G_OBJECT(bsmsg->draft_message), BALSA_SENDMSG_WINDOW_KEY, bsmsg);
     set_identity(bsmsg, message);
     setup_headers_from_message(bsmsg, message);
 
@@ -7257,35 +7669,32 @@ sendmsg_window_continue(LibBalsaMailbox * mailbox, guint msgno)
                                         libbalsa_message_get_headers(message)->reply_to);
     in_reply_to = libbalsa_message_get_in_reply_to(message);
     if (in_reply_to != NULL)
-        bsmsg->in_reply_to =
-            g_strconcat("<", in_reply_to->data, ">", NULL);
+        bsmsg->in_reply_to = g_strconcat("<", in_reply_to->data, ">", NULL);
 
-    if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-Crypto")))
+    if ((postpone_hdr = libbalsa_message_get_user_header(message, "X-Balsa-Crypto")) != NULL)
         bsmsg_setup_gpg_ui_by_mode(bsmsg, atoi(postpone_hdr));
     postpone_hdr = libbalsa_message_get_user_header(message, "X-Balsa-Att-Pubkey");
     if (postpone_hdr != NULL) {
        sw_action_set_active(bsmsg, "attpubkey", atoi(postpone_hdr) != 0);
     }
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-MDN")))
+         libbalsa_message_get_user_header(message, "X-Balsa-MDN")) != NULL)
         sw_action_set_active(bsmsg, "request-mdn", atoi(postpone_hdr) != 0);
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-DSN")))
+         libbalsa_message_get_user_header(message, "X-Balsa-DSN")) != NULL)
         sw_action_set_active(bsmsg, "request-dsn", atoi(postpone_hdr) != 0);
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-Lang"))) {
-        g_action_change_state(bsmsg->set_language_action,
-                              g_variant_new_string(postpone_hdr));
+         libbalsa_message_get_user_header(message, "X-Balsa-Lang")) != NULL) {
+        g_action_change_state(bsmsg->set_language_action, g_variant_new_string(postpone_hdr));
     }
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-Format")))
+         libbalsa_message_get_user_header(message, "X-Balsa-Format")) != NULL)
         sw_action_set_active(bsmsg, "flowed", strcmp(postpone_hdr, "Fixed"));
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-MP-Alt")))
+         libbalsa_message_get_user_header(message, "X-Balsa-MP-Alt")) != NULL)
         sw_action_set_active(bsmsg, "send-html", !strcmp(postpone_hdr, "yes"));
     if ((postpone_hdr =
-         libbalsa_message_get_user_header(message, "X-Balsa-Send-Type")))
+         libbalsa_message_get_user_header(message, "X-Balsa-Send-Type")) != NULL)
         bsmsg->type = atoi(postpone_hdr);
 
     for (list = libbalsa_message_get_references(message); list != NULL; list = list->next)
diff --git a/src/sendmsg-window.h b/src/sendmsg-window.h
index 9092ce5ee..190179327 100644
--- a/src/sendmsg-window.h
+++ b/src/sendmsg-window.h
@@ -46,6 +46,12 @@ G_BEGIN_DECLS
         SENDMSG_STATE_AUTO_SAVED
     } SendmsgState;
 
+    typedef enum {
+        QUOTE_HEADERS,
+        QUOTE_ALL,
+        QUOTE_NOPREFIX
+    } QuoteType;
+
 #define VIEW_MENU_LENGTH 5
 
     typedef struct _BalsaSendmsg BalsaSendmsg;
@@ -106,6 +112,24 @@ G_BEGIN_DECLS
 
         gboolean ready_to_send;
         gboolean queue_only;    /* Set when about to send */
+
+        /* Set when attaching files, so we know when to close the dialog */
+        unsigned n_attachments;
+        GtkWidget *attach_dialog;
+
+        /* Set when inserting text */
+        GString         *body;
+        LibBalsaMessage *message;
+        QuoteType        quote_type;
+
+        /* Set when quoting message or part */
+        LibBalsaMessageHeaders *headers;
+        char                   *message_id;
+        GList                  *fill_references;
+        LibBalsaMessageBody    *root;
+
+        /* Set when sending */
+        gboolean has_subject;
     };
 
     BalsaSendmsg *sendmsg_window_compose(void);
@@ -124,10 +148,10 @@ G_BEGIN_DECLS
     void sendmsg_window_set_field(BalsaSendmsg *bsmsg, const gchar* key,
                                   const gchar* val, gboolean from_command_line);
 
-    gboolean add_attachment(BalsaSendmsg * bsmsg,
-                            const gchar *filename, 
-                            gboolean is_a_tmp_file, 
-                            const gchar *forced_mime_type);
+    void add_attachment(BalsaSendmsg * bsmsg,
+                        const char *filename,
+                        gboolean is_a_tmp_file,
+                        const char *forced_mime_type);
 
     void sendmsg_window_process_url(BalsaSendmsg *bsmsg,
                                     const char *url,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]