talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit 95bd2a18ed7da8754ee4445ca24d6b196147031c
parent 3e3fa274fd7b3b9f0772c3141cd470deaaf65e37
Author: Colin Leroy <colin@colino.net>
Date:   Sun,  7 Jul 2019 10:43:45 +0200

Compose: handle pasted images and text/uri-list (files) and attach them

Diffstat:
Msrc/compose.c | 240++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 183 insertions(+), 57 deletions(-)

diff --git a/src/compose.c b/src/compose.c @@ -10897,58 +10897,196 @@ static void entry_copy_clipboard(GtkWidget *entry) gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); } +static void paste_text(Compose *compose, GtkWidget *entry, + gboolean wrap, GdkAtom clip, GtkTextIter *insert_place, + const gchar *contents) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)); + GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer); + GtkTextIter start_iter, end_iter; + gint start, end; + + if (contents == NULL) + return; + + /* we shouldn't delete the selection when middle-click-pasting, or we + * can't mid-click-paste our own selection */ + if (clip != GDK_SELECTION_PRIMARY) { + undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct); + gtk_text_buffer_delete_selection(buffer, FALSE, TRUE); + } + + if (insert_place == NULL) { + /* if insert_place isn't specified, insert at the cursor. + * used for Ctrl-V pasting */ + gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start); + start = gtk_text_iter_get_offset(&start_iter); + gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents)); + } else { + /* if insert_place is specified, paste here. + * used for mid-click-pasting */ + start = gtk_text_iter_get_offset(insert_place); + gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents)); + if (prefs_common.primary_paste_unselects) + gtk_text_buffer_select_range(buffer, insert_place, insert_place); + } + + if (!wrap) { + /* paste unwrapped: mark the paste so it's not wrapped later */ + end = start + strlen(contents); + gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); + gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); + gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter); + } else if (wrap && clip == GDK_SELECTION_PRIMARY) { + /* rewrap paragraph now (after a mid-click-paste) */ + mark_start = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start); + gtk_text_iter_backward_char(&start_iter); + compose_beautify_paragraph(compose, &start_iter, TRUE); + } + compose->modified = TRUE; +} + +static void attach_uri_list(Compose *compose, GtkSelectionData *data) +{ + GList *list, *tmp; + int att = 0; + gchar *warn_files = NULL; + + list = uri_list_extract_filenames( + (const gchar *)gtk_selection_data_get_data(data)); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data); + gchar *tmp_f = g_strdup_printf("%s%s\n", + warn_files?warn_files:"", + utf8_filename); + g_free(warn_files); + warn_files = tmp_f; + att++; + compose_attach_append + (compose, (const gchar *)tmp->data, + utf8_filename, NULL, NULL); + g_free(utf8_filename); + } + if (list) { + compose_changed_cb(NULL, compose); + alertpanel_notice(ngettext( + "The following file has been attached: \n%s", + "The following files have been attached: \n%s", att), warn_files); + g_free(warn_files); + } + list_free_strings_full(list); +} + +int attach_image(Compose *compose, GtkSelectionData *data, const gchar *subtype) +{ + FILE *fp; + const guchar *contents; + gchar *file; + gchar *type; + size_t len; + int r; + + cm_return_val_if_fail(data != NULL, -1); + + contents = gtk_selection_data_get_data(data); + len = gtk_selection_data_get_length(data); + + file = g_strconcat(get_tmp_file(), "-image.", subtype, NULL); + + debug_print("writing image to %s\n", file); + + if ((fp = claws_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "claws_fopen"); + return -1; + } + + if (claws_fwrite(contents, 1, len, fp) != len) { + FILE_OP_ERROR(file, "claws_fwrite"); + claws_fclose(fp); + claws_unlink(file); + return -1; + } + + r = claws_safe_fclose(fp); + + if (r == EOF) { + FILE_OP_ERROR(file, "claws_fclose"); + claws_unlink(file); + return -1; + } + + type = g_strconcat("image/", subtype, NULL); + + compose_attach_append(compose, (const gchar *)file, + (const gchar *)file, type, NULL); + + alertpanel_notice(_("The pasted image has been attached as: \n%s"), file); + + g_free(file); + g_free(type); + + return 0; +} + static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap, GdkAtom clip, GtkTextIter *insert_place) { if (GTK_IS_TEXT_VIEW(entry)) { - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)); - GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer); - GtkTextIter start_iter, end_iter; - gint start, end; - gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip)); + GdkAtom types = gdk_atom_intern ("TARGETS", FALSE); + GdkAtom *targets = NULL; + int n_targets = 0, i; + gboolean paste_done = FALSE; + GtkClipboard *clipboard = gtk_clipboard_get(clip); + + GtkSelectionData *contents = gtk_clipboard_wait_for_contents( + clipboard, types); + + if (contents != NULL) { + gtk_selection_data_get_targets(contents, &targets, &n_targets); + gtk_selection_data_free(contents); + } + + for (i = 0; i < n_targets; i++) { + GdkAtom atom = targets[i]; + gchar *atom_type = gdk_atom_name(atom); + + if (atom_type != NULL) { + GtkSelectionData *data = gtk_clipboard_wait_for_contents( + clipboard, atom); + debug_print("got contents of type %s\n", atom_type); + if (!strcmp(atom_type, "text/plain")) { + /* let the default text handler handle it */ + break; + } else if (!strcmp(atom_type, "text/uri-list")) { + attach_uri_list(compose, data); + + paste_done = TRUE; + break; + } else if (!strncmp(atom_type, "image/", strlen("image/"))) { + gchar *subtype = g_strdup((gchar *)(strstr(atom_type, "/")+1)); + debug_print("image of type %s\n", subtype); - if (contents == NULL) - return; - - /* we shouldn't delete the selection when middle-click-pasting, or we - * can't mid-click-paste our own selection */ - if (clip != GDK_SELECTION_PRIMARY) { - undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct); - gtk_text_buffer_delete_selection(buffer, FALSE, TRUE); - } - - if (insert_place == NULL) { - /* if insert_place isn't specified, insert at the cursor. - * used for Ctrl-V pasting */ - gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start); - start = gtk_text_iter_get_offset(&start_iter); - gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents)); - } else { - /* if insert_place is specified, paste here. - * used for mid-click-pasting */ - start = gtk_text_iter_get_offset(insert_place); - gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents)); - if (prefs_common.primary_paste_unselects) - gtk_text_buffer_select_range(buffer, insert_place, insert_place); + attach_image(compose, data, subtype); + g_free(subtype); + + paste_done = TRUE; + break; + } + } } - - if (!wrap) { - /* paste unwrapped: mark the paste so it's not wrapped later */ - end = start + strlen(contents); - gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); - gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); - gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter); - } else if (wrap && clip == GDK_SELECTION_PRIMARY) { - /* rewrap paragraph now (after a mid-click-paste) */ - mark_start = gtk_text_buffer_get_insert(buffer); - gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start); - gtk_text_iter_backward_char(&start_iter); - compose_beautify_paragraph(compose, &start_iter, TRUE); + if (!paste_done) { + gchar *def_text = gtk_clipboard_wait_for_text(clipboard); + paste_text(compose, entry, wrap, clip, + insert_place, def_text); + g_free(def_text); } - } else if (GTK_IS_EDITABLE(entry)) - gtk_editable_paste_clipboard (GTK_EDITABLE(entry)); + g_free(targets); - compose->modified = TRUE; + } else if (GTK_IS_EDITABLE(entry)) { + gtk_editable_paste_clipboard (GTK_EDITABLE(entry)); + compose->modified = TRUE; + } } static void entry_allsel(GtkWidget *entry) @@ -11608,25 +11746,13 @@ static void compose_attach_drag_received_cb (GtkWidget *widget, gpointer user_data) { Compose *compose = (Compose *)user_data; - GList *list, *tmp; GdkAtom type; type = gtk_selection_data_get_data_type(data); if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) && gtk_drag_get_source_widget(context) != summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) { - list = uri_list_extract_filenames( - (const gchar *)gtk_selection_data_get_data(data)); - for (tmp = list; tmp != NULL; tmp = tmp->next) { - gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data); - compose_attach_append - (compose, (const gchar *)tmp->data, - utf8_filename, NULL, NULL); - g_free(utf8_filename); - } - if (list) - compose_changed_cb(NULL, compose); - list_free_strings_full(list); + attach_uri_list(compose, data); } else if (gtk_drag_get_source_widget(context) == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) { /* comes from our summaryview */