From 74047473d334c3c5fb87fd37816b22693b7c260f Mon Sep 17 00:00:00 2001 From: Tomas Popela Date: Thu, 19 Jun 2014 15:02:29 +0200 Subject: Bug 731766 - [webkit-composer] Emoticon cannot be inserted at the end of the email Rework how the emoticons are inserted into composer. There were certain situations where the previous version failed from various reasons like wrongly saved caret position. Together with previous commits (preventing the selection markers to be saved into body) this change fixes the insertion of emoticons. --- e-util/e-html-editor-view.c | 177 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 137 insertions(+), 40 deletions(-) diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c index 85555147b5..ba10b1bc13 100644 --- a/e-util/e-html-editor-view.c +++ b/e-util/e-html-editor-view.c @@ -1037,15 +1037,17 @@ emoticon_read_async_cb (GFile *file, EHTMLEditorView *view = load_context->view; EEmoticon *emoticon = load_context->emoticon; GError *error = NULL; - gchar *html, *node_text = NULL, *mime_type; + gboolean misplaced_selection = FALSE, empty = FALSE; + gchar *html, *node_text = NULL, *mime_type, *content; gchar *base64_encoded, *output, *data; const gchar *emoticon_start; GFileInputStream *input_stream; GOutputStream *output_stream; gssize size; WebKitDOMDocument *document; - WebKitDOMElement *span, *caret_position; - WebKitDOMNode *node; + WebKitDOMElement *span, *selection_start_marker, *selection_end_marker; + WebKitDOMNode *node, *insert_before, *prev_sibling, *next_sibling; + WebKitDOMNode *selection_end_marker_parent; WebKitDOMRange *range; input_stream = g_file_read_finish (file, result, &error); @@ -1060,48 +1062,98 @@ emoticon_read_async_cb (GFile *file, if (error || (size == -1)) goto out; - caret_position = e_html_editor_selection_save_caret_position ( - e_html_editor_view_get_selection (view)); - - if (caret_position) { - WebKitDOMNode *parent; + e_html_editor_selection_save (e_html_editor_view_get_selection (view)); - parent = webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (caret_position)); - - /* Situation when caret is restored in body and not in paragraph */ - if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) { - caret_position = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_remove_child ( - WEBKIT_DOM_NODE (parent), - WEBKIT_DOM_NODE (caret_position), - NULL)); + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); + selection_start_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-start-marker"); + selection_end_marker = webkit_dom_document_get_element_by_id ( + document, "-x-evo-selection-end-marker"); + + /* If the selection was not saved, move it into the first child of body */ + if (!selection_start_marker || !selection_end_marker) { + WebKitDOMHTMLElement *body; + + body = webkit_dom_document_get_body (document); + selection_start_marker = webkit_dom_document_create_element ( + document, "SPAN", NULL); + webkit_dom_element_set_id ( + selection_start_marker, "-x-evo-selection-start-marker"); + webkit_dom_node_insert_before ( + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_first_child ( + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (body))), + NULL); + selection_end_marker = webkit_dom_document_create_element ( + document, "SPAN", NULL); + webkit_dom_element_set_id ( + selection_end_marker, "-x-evo-selection-end-marker"); + webkit_dom_node_insert_before ( + webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_first_child ( + webkit_dom_node_get_first_child ( + WEBKIT_DOM_NODE (body))), + NULL); + } - caret_position = WEBKIT_DOM_ELEMENT ( - webkit_dom_node_insert_before ( - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (parent)), - WEBKIT_DOM_NODE (caret_position), - webkit_dom_node_get_first_child ( - webkit_dom_node_get_first_child ( - WEBKIT_DOM_NODE (parent))), - NULL)); - } + /* Sometimes selection end marker is in body. Move it into next sibling */ + selection_end_marker_parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker)); + if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (selection_end_marker_parent)) { + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_start_marker)), + WEBKIT_DOM_NODE (selection_end_marker), + WEBKIT_DOM_NODE (selection_start_marker), + NULL); } + selection_end_marker_parent = webkit_dom_node_get_parent_node ( + WEBKIT_DOM_NODE (selection_end_marker)); + + /* Determine before what node we have to insert the smiley */ + insert_before = WEBKIT_DOM_NODE (selection_start_marker); + prev_sibling = webkit_dom_node_get_previous_sibling ( + WEBKIT_DOM_NODE (selection_start_marker)); + if (prev_sibling) { + if (webkit_dom_node_is_same_node ( + prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { + insert_before = WEBKIT_DOM_NODE (selection_end_marker); + } else { + prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling); + if (prev_sibling && + webkit_dom_node_is_same_node ( + prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) { + insert_before = WEBKIT_DOM_NODE (selection_end_marker); + } + } + } else + insert_before = WEBKIT_DOM_NODE (selection_start_marker); + + /* Look if selection is misplaced - that means that the selection was + * restored before the previously inserted smiley in situations when we + * are writing more smileys in a row */ + next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)); + if (next_sibling && WEBKIT_DOM_IS_ELEMENT (next_sibling)) + if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-smiley-wrapper")) + misplaced_selection = TRUE; mime_type = g_content_type_get_mime_type (load_context->content_type); - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); range = html_editor_view_get_dom_range (view); node = webkit_dom_range_get_end_container (range, NULL); if (WEBKIT_DOM_IS_TEXT (node)) node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node)); - span = webkit_dom_document_create_element (document, "SPAN", NULL); data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream)); - base64_encoded = g_base64_encode ((const guchar *) data, size); output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL); + content = webkit_dom_node_get_text_content (selection_end_marker_parent); + empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0); + g_free (content); + /* Insert span with image representation and another one with text * represetation and hide/show them dependant on active composer mode */ /* ​ == UNICODE_ZERO_WIDTH_SPACE */ @@ -1110,17 +1162,39 @@ emoticon_read_async_cb (GFile *file, "\"%s\"" "%s" - "​", + "%s", output, emoticon ? emoticon->text_face : "", emoticon->icon_name, - load_context->name, emoticon ? emoticon->text_face : ""); + load_context->name, emoticon ? emoticon->text_face : "", + empty ? "​" : ""); + + span = webkit_dom_document_create_element (document, "SPAN", NULL); - span = WEBKIT_DOM_ELEMENT ( + if (misplaced_selection) { + /* Insert smiley and selection markers after it */ webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node ( - WEBKIT_DOM_NODE (caret_position)), - WEBKIT_DOM_NODE (span), - WEBKIT_DOM_NODE (caret_position), - NULL)); + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (selection_start_marker), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (selection_end_marker), + webkit_dom_node_get_next_sibling (next_sibling), + NULL); + span = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (span), + webkit_dom_node_get_next_sibling (next_sibling), + NULL)); + } else { + span = WEBKIT_DOM_ELEMENT ( + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (insert_before), + WEBKIT_DOM_NODE (span), + insert_before, + NULL)); + } webkit_dom_html_element_set_outer_html ( WEBKIT_DOM_HTML_ELEMENT (span), html, NULL); @@ -1137,7 +1211,7 @@ emoticon_read_async_cb (GFile *file, } } - e_html_editor_selection_restore_caret_position ( + e_html_editor_selection_restore ( e_html_editor_view_get_selection (view)); e_html_editor_view_set_changed (view, TRUE); @@ -1772,6 +1846,27 @@ html_editor_view_key_press_event (GtkWidget *widget, key_press_event (widget, event); } +static void +fix_paragraph_structure_after_pressing_enter_after_smiley (EHTMLEditorSelection *selection, + WebKitDOMDocument *document) +{ + WebKitDOMElement *element; + + element = webkit_dom_document_query_selector ( + document, "span.-x-evo-smiley-wrapper > br", NULL); + + if (element) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)); + webkit_dom_html_element_set_inner_html ( + WEBKIT_DOM_HTML_ELEMENT ( + webkit_dom_node_get_parent_node (parent)), + UNICODE_ZERO_WIDTH_SPACE, + NULL); + } +} + static void mark_node_as_paragraph_after_ending_list (EHTMLEditorSelection *selection, WebKitDOMDocument *document) @@ -1855,6 +1950,8 @@ html_editor_view_key_release_event (GtkWidget *widget, if (is_return_key (event) || (event->keyval == GDK_KEY_space)) { html_editor_view_check_magic_links (view, range, FALSE, event); mark_node_as_paragraph_after_ending_list (selection, document); + if (view->priv->html_mode) + fix_paragraph_structure_after_pressing_enter_after_smiley (selection, document); } else { WebKitDOMNode *node; -- cgit v1.2.3