diff options
author | Tomas Popela <tpopela@redhat.com> | 2014-06-25 21:54:38 +0800 |
---|---|---|
committer | Tomas Popela <tpopela@redhat.com> | 2014-06-25 22:06:19 +0800 |
commit | d4b8a56865843be13e7fa6b8990f8915024994e0 (patch) | |
tree | e4a4c5a7937cd7916106614a45ca92ff43173f05 /e-util/e-html-editor-view.c | |
parent | 212adecd1a7f716aa6533712c604576cd9c2df4c (diff) | |
download | gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar.gz gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar.bz2 gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar.lz gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar.xz gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.tar.zst gsoc2013-evolution-d4b8a56865843be13e7fa6b8990f8915024994e0.zip |
EHTMLEditorView - Fix clipboard paste into quoted content
When pasting the text from clipboard into the quoted content in the composer
paste it into it and not into the body.
Diffstat (limited to 'e-util/e-html-editor-view.c')
-rw-r--r-- | e-util/e-html-editor-view.c | 1293 |
1 files changed, 665 insertions, 628 deletions
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c index 0afd7a3f78..80c952e585 100644 --- a/e-util/e-html-editor-view.c +++ b/e-util/e-html-editor-view.c @@ -2653,6 +2653,626 @@ e_html_editor_view_class_init (EHTMLEditorViewClass *class) } static gboolean +is_citation_node (WebKitDOMNode *node) +{ + char *value; + + if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) + return FALSE; + + value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); + + /* citation == <blockquote type='cite'> */ + if (g_strcmp0 (value, "cite") == 0) { + g_free (value); + return TRUE; + } else { + g_free (value); + return FALSE; + } +} + +static gchar * +get_quotation_for_level (gint quote_level) +{ + gint ii; + GString *output = g_string_new (""); + + for (ii = 0; ii < quote_level; ii++) { + g_string_append (output, "<span class=\"-x-evo-quote-character\">"); + g_string_append (output, QUOTE_SYMBOL); + g_string_append (output, " "); + g_string_append (output, "</span>"); + } + + return g_string_free (output, FALSE); +} + +static void +insert_quote_symbols (WebKitDOMHTMLElement *element, + gint quote_level, + gboolean skip_first, + gboolean insert_newline) +{ + gchar *text; + gint ii; + GString *output; + gchar *quotation; + + if (!WEBKIT_DOM_IS_HTML_ELEMENT (element)) + return; + + text = webkit_dom_html_element_get_inner_html (element); + output = g_string_new (""); + quotation = get_quotation_for_level (quote_level); + + if (g_strcmp0 (text, "\n") == 0) { + g_string_append (output, "<span class=\"-x-evo-quoted\">"); + g_string_append (output, quotation); + g_string_append (output, "</span>"); + g_string_append (output, "\n"); + } else { + gchar **lines; + + lines = g_strsplit (text, "\n", 0); + + for (ii = 0; lines[ii]; ii++) { + if (ii == 0 && skip_first) { + if (g_strv_length (lines) == 1) { + g_strfreev (lines); + goto exit; + } + g_string_append (output, lines[ii]); + g_string_append (output, "\n"); + } + + g_string_append (output, "<span class=\"-x-evo-quoted\">"); + g_string_append (output, quotation); + g_string_append (output, "</span>"); + + /* Insert line of text */ + g_string_append (output, lines[ii]); + if ((ii == g_strv_length (lines) - 1) && + !g_str_has_suffix (text, "\n") && !insert_newline) { + /* If we are on last line and node's text doesn't + * end with \n, don't insert it */ + break; + } + g_string_append (output, "\n"); + } + + g_strfreev (lines); + } + + webkit_dom_html_element_set_inner_html (element, output->str, NULL); + exit: + g_free (quotation); + g_free (text); + g_string_free (output, TRUE); +} + +static void +quote_node (WebKitDOMDocument *document, + WebKitDOMNode *node, + gint quote_level) +{ + gboolean skip_first = FALSE; + gboolean insert_newline = FALSE; + gboolean is_html_node = FALSE; + WebKitDOMElement *wrapper; + WebKitDOMNode *node_clone, *prev_sibling, *next_sibling; + + /* Don't quote when we are not in citation */ + if (quote_level == 0) + return; + + if (WEBKIT_DOM_IS_COMMENT (node)) + return; + + if (WEBKIT_DOM_IS_HTML_ELEMENT (node)) { + insert_quote_symbols ( + WEBKIT_DOM_HTML_ELEMENT (node), quote_level, FALSE, FALSE); + return; + } + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + next_sibling = webkit_dom_node_get_next_sibling (node); + + is_html_node = + !WEBKIT_DOM_IS_TEXT (prev_sibling) && + !WEBKIT_DOM_IS_COMMENT (prev_sibling) && ( + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling) || + element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "b") || + element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "i") || + element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "u")); + + if (prev_sibling && is_html_node) + skip_first = TRUE; + + /* Skip the BR between first blockquote and pre */ + if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling)) + return; + + if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) && + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (webkit_dom_node_get_next_sibling (next_sibling))) { + insert_newline = TRUE; + } + + /* Do temporary wrapper */ + wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); + webkit_dom_element_set_class_name (wrapper, "-x-evo-temp-text-wrapper"); + + node_clone = webkit_dom_node_clone_node (node, TRUE); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (wrapper), + node_clone, + NULL); + + insert_quote_symbols ( + WEBKIT_DOM_HTML_ELEMENT (wrapper), + quote_level, + skip_first, + insert_newline); + + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (wrapper), + node, + NULL); +} + +static void +insert_quote_symbols_before_node (WebKitDOMDocument *document, + WebKitDOMNode *node, + gint quote_level, + gboolean is_html_node) +{ + gchar *quotation; + WebKitDOMElement *element; + + quotation = get_quotation_for_level (quote_level); + element = webkit_dom_document_create_element (document, "SPAN", NULL); + element_add_class (element, "-x-evo-quoted"); + webkit_dom_html_element_set_inner_html ( + WEBKIT_DOM_HTML_ELEMENT (element), quotation, NULL); + + if (is_html_node) { + WebKitDOMElement *new_br; + + new_br = webkit_dom_document_create_element (document, "br", NULL); + element_add_class (new_br, "-x-evo-temp-br"); + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (new_br), + node, + NULL); + } + + webkit_dom_node_insert_before ( + webkit_dom_node_get_parent_node (node), + WEBKIT_DOM_NODE (element), + node, + NULL); + + if (is_html_node) + remove_node (node); + + g_free (quotation); +} + +static gboolean +element_is_selection_marker (WebKitDOMElement *element) +{ + gboolean is_marker = FALSE; + + is_marker = + element_has_id (element, "-x-evo-selection-start-marker") || + element_has_id (element, "-x-evo-selection-end-marker"); + + return is_marker; +} + +static gboolean +check_if_suppress_next_node (WebKitDOMNode *node) +{ + if (node && WEBKIT_DOM_IS_ELEMENT (node)) + if (element_is_selection_marker (WEBKIT_DOM_ELEMENT (node))) + if (!webkit_dom_node_get_previous_sibling (node)) + return FALSE; + + return TRUE; +} + +static void +quote_plain_text_recursive (WebKitDOMDocument *document, + WebKitDOMNode *node, + WebKitDOMNode *start_node, + gint quote_level) +{ + gboolean skip_node = FALSE; + gboolean move_next = FALSE; + gboolean suppress_next = FALSE; + gboolean is_html_node = FALSE; + WebKitDOMNode *next_sibling, *prev_sibling; + + node = webkit_dom_node_get_first_child (node); + + while (node) { + skip_node = FALSE; + move_next = FALSE; + is_html_node = FALSE; + + if (WEBKIT_DOM_IS_COMMENT (node)) + goto next_node; + + prev_sibling = webkit_dom_node_get_previous_sibling (node); + next_sibling = webkit_dom_node_get_next_sibling (node); + + if (WEBKIT_DOM_IS_TEXT (node)) { + /* Start quoting after we are in blockquote */ + if (quote_level > 0 && !suppress_next) { + /* When quoting text node, we are wrappering it and + * afterwards replacing it with that wrapper, thus asking + * for next_sibling after quoting will return NULL bacause + * that node don't exist anymore */ + quote_node (document, node, quote_level); + node = next_sibling; + skip_node = TRUE; + } else + suppress_next = FALSE; + + goto next_node; + } + + if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node))) + goto next_node; + + if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position")) { + if (quote_level > 0) + element_add_class ( + WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-quoting"); + + move_next = TRUE; + suppress_next = TRUE; + goto next_node; + } + + if (element_is_selection_marker (WEBKIT_DOM_ELEMENT (node))) { + move_next = TRUE; + /* If there is collapsed selection in the beginning of line + * we cannot suppress first text that is after the end of + * selection */ + suppress_next = check_if_suppress_next_node (prev_sibling); + goto next_node; + } + + if (WEBKIT_DOM_IS_HTML_META_ELEMENT (node)) { + goto next_node; + } + if (WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node)) { + move_next = TRUE; + goto next_node; + } + if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) { + move_next = TRUE; + goto next_node; + } + + if (webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0) + goto with_children; + + /* Even in plain text mode we can have some basic html element + * like anchor and others. When Forwaring e-mail as Quoted EMFormat + * generates header that contatains <b> tags (bold font). + * We have to treat these elements separately to avoid + * modifications of theirs inner texts */ + is_html_node = + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") || + element_has_tag (WEBKIT_DOM_ELEMENT (node), "u"); + + if (is_html_node) { + if (!prev_sibling) + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + + if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) + insert_quote_symbols_before_node ( + document, prev_sibling, quote_level, TRUE); + + move_next = TRUE; + goto next_node; + } + + /* If element doesn't have children, we can quote it */ + if (is_citation_node (node)) { + /* Citation with just text inside */ + quote_node (document, node, quote_level + 1); + /* Set citation as quoted */ + element_add_class ( + WEBKIT_DOM_ELEMENT (node), + "-x-evo-plaintext-quoted"); + + move_next = TRUE; + goto next_node; + } + + if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) + goto not_br; + + if (!prev_sibling) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (node); + + /* BR in the beginning of the citation */ + if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent)) + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + } + + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (next_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) { + /* Situation when anchors are alone on line */ + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (prev_sibling); + + if (g_str_has_suffix (text_content, "\n")) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + remove_node (node); + g_free (text_content); + node = next_sibling; + skip_node = TRUE; + goto next_node; + } + g_free (text_content); + } + + if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) { + gchar *quotation, *content; + + quotation = get_quotation_for_level (quote_level); + + content = g_strconcat ( + "<span class=\"-x-evo-quoted\">", + quotation, + "</span><br class=\"-x-evo-temp-br\">", + NULL); + + webkit_dom_html_element_set_outer_html ( + WEBKIT_DOM_HTML_ELEMENT (node), + content, + NULL); + + g_free (content); + g_free (quotation); + + node = next_sibling; + skip_node = TRUE; + goto next_node; + } + + if (!prev_sibling && !next_sibling) { + WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); + + if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + } + } + + if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && + element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) { + gchar *text_content; + + text_content = webkit_dom_node_get_text_content (prev_sibling); + if (g_strcmp0 (text_content, "") == 0) + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + + g_free (text_content); + } + + if (is_citation_node (prev_sibling)) { + insert_quote_symbols_before_node ( + document, node, quote_level, FALSE); + } + not_br: + if (g_strcmp0 (webkit_dom_node_get_text_content (node), "") == 0) { + move_next = TRUE; + goto next_node; + } + + quote_node (document, node, quote_level); + + move_next = TRUE; + goto next_node; + + with_children: + if (is_citation_node (node)) { + /* Go deeper and increase level */ + quote_plain_text_recursive ( + document, node, start_node, quote_level + 1); + /* set citation as quoted */ + element_add_class ( + WEBKIT_DOM_ELEMENT (node), + "-x-evo-plaintext-quoted"); + move_next = TRUE; + } else { + quote_plain_text_recursive ( + document, node, start_node, quote_level); + move_next = TRUE; + } + next_node: + if (!skip_node) { + /* Move to next node */ + if (!move_next && webkit_dom_node_has_child_nodes (node)) { + node = webkit_dom_node_get_first_child (node); + } else if (webkit_dom_node_get_next_sibling (node)) { + node = webkit_dom_node_get_next_sibling (node); + } else { + return; + } + } + } +} + +WebKitDOMElement * +e_html_editor_view_quote_plain_text_element (EHTMLEditorView *view, + WebKitDOMElement *element) +{ + WebKitDOMDocument *document; + WebKitDOMNode *element_clone; + WebKitDOMNodeList *list; + gint ii, length, level; + + document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); + + element_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE); + level = get_citation_level (WEBKIT_DOM_NODE (element), TRUE); + + /* Remove old quote characters if the exists */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) + remove_node (webkit_dom_node_list_item (list, ii)); + + quote_plain_text_recursive ( + document, element_clone, element_clone, level); + + /* Replace old element with one, that is quoted */ + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), + element_clone, + WEBKIT_DOM_NODE (element), + NULL); + + return WEBKIT_DOM_ELEMENT (element_clone); +} + +/** + * e_html_editor_view_quote_plain_text: + * @view: an #EHTMLEditorView + * + * Quote text inside citation blockquotes in plain text mode. + * + * As this function is cloning and replacing all citation blockquotes keep on + * mind that any pointers to nodes inside these blockquotes will be invalidated. + */ +WebKitDOMElement * +e_html_editor_view_quote_plain_text (EHTMLEditorView *view) +{ + WebKitDOMDocument *document; + WebKitDOMHTMLElement *body; + WebKitDOMNode *body_clone; + WebKitDOMNamedNodeMap *attributes; + WebKitDOMNodeList *list; + WebKitDOMElement *element; + gint ii, length; + gulong attributes_length; + + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); + + /* Check if the document is already quoted */ + element = webkit_dom_document_query_selector ( + document, ".-x-evo-plaintext-quoted", NULL); + if (element) + return NULL; + + body = webkit_dom_document_get_body (document); + body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE); + + /* Clean unwanted spaces before and after blockquotes */ + list = webkit_dom_element_query_selector_all ( + WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL); + length = webkit_dom_node_list_get_length (list); + for (ii = 0; ii < length; ii++) { + WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii); + WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote); + WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote); + + if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) + remove_node (prev_sibling); + + if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) + remove_node (next_sibling); + + if (webkit_dom_node_has_child_nodes (blockquote)) { + WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote); + if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) + remove_node (child); + } + } + + quote_plain_text_recursive (document, body_clone, body_clone, 0); + + /* Copy attributes */ + attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); + attributes_length = webkit_dom_named_node_map_get_length (attributes); + for (ii = 0; ii < attributes_length; ii++) { + gchar *name, *value; + WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); + + name = webkit_dom_node_get_local_name (node); + value = webkit_dom_node_get_node_value (node); + + webkit_dom_element_set_attribute ( + WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL); + + g_free (name); + g_free (value); + } + + /* Replace old BODY with one, that is quoted */ + webkit_dom_node_replace_child ( + webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), + body_clone, + WEBKIT_DOM_NODE (body), + NULL); + + return WEBKIT_DOM_ELEMENT (body_clone); +} + +/** + * e_html_editor_view_dequote_plain_text: + * @view: an #EHTMLEditorView + * + * Dequote already quoted plain text in editor. + * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise + * it's not working. + */ +void +e_html_editor_view_dequote_plain_text (EHTMLEditorView *view) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *paragraphs; + gint length, ii; + + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); + + paragraphs = webkit_dom_document_query_selector_all ( + document, "blockquote.-x-evo-plaintext-quoted", NULL); + length = webkit_dom_node_list_get_length (paragraphs); + for (ii = 0; ii < length; ii++) { + WebKitDOMElement *element; + + element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii)); + + if (is_citation_node (WEBKIT_DOM_NODE (element))) { + element_remove_class (element, "-x-evo-plaintext-quoted"); + remove_quoting_from_element (element); + } + } +} + +static gboolean create_anchor_for_link (const GMatchInfo *info, GString *res, gpointer data) @@ -2695,7 +3315,6 @@ create_anchor_for_link (const GMatchInfo *info, if (address_surrounded) g_string_append (res, ">"); - g_warning ("%s", res->str); g_free (match); return FALSE; @@ -3255,9 +3874,11 @@ static void html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view, WebKitDOMDocument *document_convertor) { + EHTMLEditorSelection *selection = e_html_editor_view_get_selection (view); gchar *inner_text, *inner_html; + gint citation_level; WebKitDOMDocument *document; - WebKitDOMElement *element; + WebKitDOMElement *caret, *element; WebKitDOMHTMLElement *convertor_body; document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); @@ -3271,20 +3892,56 @@ html_editor_view_insert_converted_html_into_selection (EHTMLEditorView *view, inner_html = webkit_dom_html_element_get_inner_html ( WEBKIT_DOM_HTML_ELEMENT (element)); - parse_html_into_paragraphs ( - view, document, element, inner_html, FALSE); - + parse_html_into_paragraphs (view, document, element, inner_html, FALSE); g_free (inner_html); + caret = e_html_editor_selection_save_caret_position (selection); + citation_level = get_citation_level (WEBKIT_DOM_NODE (caret), FALSE); + /* Pasting into citation */ + if (citation_level > 0) { + WebKitDOMNode *parent; + + parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (caret)); + /* Pasting into empty line in citation */ + if (is_citation_node (parent)) { + quote_plain_text_recursive ( + document, + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (element), + citation_level); + + webkit_dom_node_insert_before ( + parent, + WEBKIT_DOM_NODE (element), + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (caret)), + NULL); + + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (caret), + NULL); + + remove_node ( + webkit_dom_node_get_next_sibling ( + WEBKIT_DOM_NODE (element))); + + e_html_editor_selection_restore_caret_position (selection); + + goto out; + } + } + + e_html_editor_selection_clear_caret_position_marker (selection); + inner_html = webkit_dom_html_element_get_inner_html ( WEBKIT_DOM_HTML_ELEMENT (element)); - e_html_editor_view_exec_command ( view, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html); - + g_free (inner_html); + out: e_html_editor_view_force_spell_check (view); - g_free (inner_html); g_free (inner_text); } @@ -3627,626 +4284,6 @@ e_html_editor_view_set_changed (EHTMLEditorView *view, g_object_notify (G_OBJECT (view), "changed"); } -static gboolean -is_citation_node (WebKitDOMNode *node) -{ - char *value; - - if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) - return FALSE; - - value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type"); - - /* citation == <blockquote type='cite'> */ - if (g_strcmp0 (value, "cite") == 0) { - g_free (value); - return TRUE; - } else { - g_free (value); - return FALSE; - } -} - -static gchar * -get_quotation_for_level (gint quote_level) -{ - gint ii; - GString *output = g_string_new (""); - - for (ii = 0; ii < quote_level; ii++) { - g_string_append (output, "<span class=\"-x-evo-quote-character\">"); - g_string_append (output, QUOTE_SYMBOL); - g_string_append (output, " "); - g_string_append (output, "</span>"); - } - - return g_string_free (output, FALSE); -} - -static void -insert_quote_symbols (WebKitDOMHTMLElement *element, - gint quote_level, - gboolean skip_first, - gboolean insert_newline) -{ - gchar *text; - gint ii; - GString *output; - gchar *quotation; - - if (!WEBKIT_DOM_IS_HTML_ELEMENT (element)) - return; - - text = webkit_dom_html_element_get_inner_html (element); - output = g_string_new (""); - quotation = get_quotation_for_level (quote_level); - - if (g_strcmp0 (text, "\n") == 0) { - g_string_append (output, "<span class=\"-x-evo-quoted\">"); - g_string_append (output, quotation); - g_string_append (output, "</span>"); - g_string_append (output, "\n"); - } else { - gchar **lines; - - lines = g_strsplit (text, "\n", 0); - - for (ii = 0; lines[ii]; ii++) { - if (ii == 0 && skip_first) { - if (g_strv_length (lines) == 1) { - g_strfreev (lines); - goto exit; - } - g_string_append (output, lines[ii]); - g_string_append (output, "\n"); - } - - g_string_append (output, "<span class=\"-x-evo-quoted\">"); - g_string_append (output, quotation); - g_string_append (output, "</span>"); - - /* Insert line of text */ - g_string_append (output, lines[ii]); - if ((ii == g_strv_length (lines) - 1) && - !g_str_has_suffix (text, "\n") && !insert_newline) { - /* If we are on last line and node's text doesn't - * end with \n, don't insert it */ - break; - } - g_string_append (output, "\n"); - } - - g_strfreev (lines); - } - - webkit_dom_html_element_set_inner_html (element, output->str, NULL); - exit: - g_free (quotation); - g_free (text); - g_string_free (output, TRUE); -} - -static void -quote_node (WebKitDOMDocument *document, - WebKitDOMNode *node, - gint quote_level) -{ - gboolean skip_first = FALSE; - gboolean insert_newline = FALSE; - gboolean is_html_node = FALSE; - WebKitDOMElement *wrapper; - WebKitDOMNode *node_clone, *prev_sibling, *next_sibling; - - /* Don't quote when we are not in citation */ - if (quote_level == 0) - return; - - if (WEBKIT_DOM_IS_COMMENT (node)) - return; - - if (WEBKIT_DOM_IS_HTML_ELEMENT (node)) { - insert_quote_symbols ( - WEBKIT_DOM_HTML_ELEMENT (node), quote_level, FALSE, FALSE); - return; - } - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - next_sibling = webkit_dom_node_get_next_sibling (node); - - is_html_node = - !WEBKIT_DOM_IS_TEXT (prev_sibling) && - !WEBKIT_DOM_IS_COMMENT (prev_sibling) && ( - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling) || - element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "b") || - element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "i") || - element_has_tag (WEBKIT_DOM_ELEMENT (prev_sibling), "u")); - - if (prev_sibling && is_html_node) - skip_first = TRUE; - - /* Skip the BR between first blockquote and pre */ - if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling)) - return; - - if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling) && - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (webkit_dom_node_get_next_sibling (next_sibling))) { - insert_newline = TRUE; - } - - /* Do temporary wrapper */ - wrapper = webkit_dom_document_create_element (document, "SPAN", NULL); - webkit_dom_element_set_class_name (wrapper, "-x-evo-temp-text-wrapper"); - - node_clone = webkit_dom_node_clone_node (node, TRUE); - - webkit_dom_node_append_child ( - WEBKIT_DOM_NODE (wrapper), - node_clone, - NULL); - - insert_quote_symbols ( - WEBKIT_DOM_HTML_ELEMENT (wrapper), - quote_level, - skip_first, - insert_newline); - - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (wrapper), - node, - NULL); -} - -static void -insert_quote_symbols_before_node (WebKitDOMDocument *document, - WebKitDOMNode *node, - gint quote_level, - gboolean is_html_node) -{ - gchar *quotation; - WebKitDOMElement *element; - - quotation = get_quotation_for_level (quote_level); - element = webkit_dom_document_create_element (document, "SPAN", NULL); - element_add_class (element, "-x-evo-quoted"); - webkit_dom_html_element_set_inner_html ( - WEBKIT_DOM_HTML_ELEMENT (element), quotation, NULL); - - if (is_html_node) { - WebKitDOMElement *new_br; - - new_br = webkit_dom_document_create_element (document, "br", NULL); - element_add_class (new_br, "-x-evo-temp-br"); - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (new_br), - node, - NULL); - } - - webkit_dom_node_insert_before ( - webkit_dom_node_get_parent_node (node), - WEBKIT_DOM_NODE (element), - node, - NULL); - - if (is_html_node) - remove_node (node); - - g_free (quotation); -} - -static gboolean -element_is_selection_marker (WebKitDOMElement *element) -{ - gboolean is_marker = FALSE; - - is_marker = - element_has_id (element, "-x-evo-selection-start-marker") || - element_has_id (element, "-x-evo-selection-end-marker"); - - return is_marker; -} - -static gboolean -check_if_suppress_next_node (WebKitDOMNode *node) -{ - if (node && WEBKIT_DOM_IS_ELEMENT (node)) - if (element_is_selection_marker (WEBKIT_DOM_ELEMENT (node))) - if (!webkit_dom_node_get_previous_sibling (node)) - return FALSE; - - return TRUE; -} - -static void -quote_plain_text_recursive (WebKitDOMDocument *document, - WebKitDOMNode *node, - WebKitDOMNode *start_node, - gint quote_level) -{ - gboolean skip_node = FALSE; - gboolean move_next = FALSE; - gboolean suppress_next = FALSE; - gboolean is_html_node = FALSE; - WebKitDOMNode *next_sibling, *prev_sibling; - - node = webkit_dom_node_get_first_child (node); - - while (node) { - skip_node = FALSE; - move_next = FALSE; - is_html_node = FALSE; - - if (WEBKIT_DOM_IS_COMMENT (node)) - goto next_node; - - prev_sibling = webkit_dom_node_get_previous_sibling (node); - next_sibling = webkit_dom_node_get_next_sibling (node); - - if (WEBKIT_DOM_IS_TEXT (node)) { - /* Start quoting after we are in blockquote */ - if (quote_level > 0 && !suppress_next) { - /* When quoting text node, we are wrappering it and - * afterwards replacing it with that wrapper, thus asking - * for next_sibling after quoting will return NULL bacause - * that node don't exist anymore */ - quote_node (document, node, quote_level); - node = next_sibling; - skip_node = TRUE; - } else - suppress_next = FALSE; - - goto next_node; - } - - if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node))) - goto next_node; - - if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-position")) { - if (quote_level > 0) - element_add_class ( - WEBKIT_DOM_ELEMENT (node), "-x-evo-caret-quoting"); - - move_next = TRUE; - suppress_next = TRUE; - goto next_node; - } - - if (element_is_selection_marker (WEBKIT_DOM_ELEMENT (node))) { - move_next = TRUE; - /* If there is collapsed selection in the beginning of line - * we cannot suppress first text that is after the end of - * selection */ - suppress_next = check_if_suppress_next_node (prev_sibling); - goto next_node; - } - - if (WEBKIT_DOM_IS_HTML_META_ELEMENT (node)) { - goto next_node; - } - if (WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node)) { - move_next = TRUE; - goto next_node; - } - if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) { - move_next = TRUE; - goto next_node; - } - - if (webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0) - goto with_children; - - /* Even in plain text mode we can have some basic html element - * like anchor and others. When Forwaring e-mail as Quoted EMFormat - * generates header that contatains <b> tags (bold font). - * We have to treat these elements separately to avoid - * modifications of theirs inner texts */ - is_html_node = - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") || - element_has_tag (WEBKIT_DOM_ELEMENT (node), "u"); - - if (is_html_node) { - if (!prev_sibling) - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) - insert_quote_symbols_before_node ( - document, prev_sibling, quote_level, TRUE); - - move_next = TRUE; - goto next_node; - } - - /* If element doesn't have children, we can quote it */ - if (is_citation_node (node)) { - /* Citation with just text inside */ - quote_node (document, node, quote_level + 1); - /* Set citation as quoted */ - element_add_class ( - WEBKIT_DOM_ELEMENT (node), - "-x-evo-plaintext-quoted"); - - move_next = TRUE; - goto next_node; - } - - if (!WEBKIT_DOM_IS_HTMLBR_ELEMENT (node)) - goto not_br; - - if (!prev_sibling) { - WebKitDOMNode *parent; - - parent = webkit_dom_node_get_parent_node (node); - - /* BR in the beginning of the citation */ - if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent)) - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - } - - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (next_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) { - /* Situation when anchors are alone on line */ - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (prev_sibling); - - if (g_str_has_suffix (text_content, "\n")) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - remove_node (node); - g_free (text_content); - node = next_sibling; - skip_node = TRUE; - goto next_node; - } - g_free (text_content); - } - - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) { - gchar *quotation, *content; - - quotation = get_quotation_for_level (quote_level); - - content = g_strconcat ( - "<span class=\"-x-evo-quoted\">", - quotation, - "</span><br class=\"-x-evo-temp-br\">", - NULL); - - webkit_dom_html_element_set_outer_html ( - WEBKIT_DOM_HTML_ELEMENT (node), - content, - NULL); - - g_free (content); - g_free (quotation); - - node = next_sibling; - skip_node = TRUE; - goto next_node; - } - - if (!prev_sibling && !next_sibling) { - WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node); - - if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent)) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - } - } - - if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) && - element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-temp-text-wrapper")) { - gchar *text_content; - - text_content = webkit_dom_node_get_text_content (prev_sibling); - if (g_strcmp0 (text_content, "") == 0) - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - - g_free (text_content); - } - - if (is_citation_node (prev_sibling)) { - insert_quote_symbols_before_node ( - document, node, quote_level, FALSE); - } - not_br: - if (g_strcmp0 (webkit_dom_node_get_text_content (node), "") == 0) { - move_next = TRUE; - goto next_node; - } - - quote_node (document, node, quote_level); - - move_next = TRUE; - goto next_node; - - with_children: - if (is_citation_node (node)) { - /* Go deeper and increase level */ - quote_plain_text_recursive ( - document, node, start_node, quote_level + 1); - /* set citation as quoted */ - element_add_class ( - WEBKIT_DOM_ELEMENT (node), - "-x-evo-plaintext-quoted"); - move_next = TRUE; - } else { - quote_plain_text_recursive ( - document, node, start_node, quote_level); - move_next = TRUE; - } - next_node: - if (!skip_node) { - /* Move to next node */ - if (!move_next && webkit_dom_node_has_child_nodes (node)) { - node = webkit_dom_node_get_first_child (node); - } else if (webkit_dom_node_get_next_sibling (node)) { - node = webkit_dom_node_get_next_sibling (node); - } else { - return; - } - } - } -} - -WebKitDOMElement * -e_html_editor_view_quote_plain_text_element (EHTMLEditorView *view, - WebKitDOMElement *element) -{ - WebKitDOMDocument *document; - WebKitDOMNode *element_clone; - WebKitDOMNodeList *list; - gint ii, length, level; - - document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)); - - element_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), TRUE); - level = get_citation_level (WEBKIT_DOM_NODE (element), TRUE); - - /* Remove old quote characters if the exists */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) - remove_node (webkit_dom_node_list_item (list, ii)); - - quote_plain_text_recursive ( - document, element_clone, element_clone, level); - - /* Replace old element with one, that is quoted */ - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), - element_clone, - WEBKIT_DOM_NODE (element), - NULL); - - return WEBKIT_DOM_ELEMENT (element_clone); -} - -/** - * e_html_editor_view_quote_plain_text: - * @view: an #EHTMLEditorView - * - * Quote text inside citation blockquotes in plain text mode. - * - * As this function is cloning and replacing all citation blockquotes keep on - * mind that any pointers to nodes inside these blockquotes will be invalidated. - */ -WebKitDOMElement * -e_html_editor_view_quote_plain_text (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMHTMLElement *body; - WebKitDOMNode *body_clone; - WebKitDOMNamedNodeMap *attributes; - WebKitDOMNodeList *list; - WebKitDOMElement *element; - gint ii, length; - gulong attributes_length; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - /* Check if the document is already quoted */ - element = webkit_dom_document_query_selector ( - document, ".-x-evo-plaintext-quoted", NULL); - if (element) - return NULL; - - body = webkit_dom_document_get_body (document); - body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE); - - /* Clean unwanted spaces before and after blockquotes */ - list = webkit_dom_element_query_selector_all ( - WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL); - length = webkit_dom_node_list_get_length (list); - for (ii = 0; ii < length; ii++) { - WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii); - WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote); - WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote); - - if (prev_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (prev_sibling)) - remove_node (prev_sibling); - - if (next_sibling && WEBKIT_DOM_IS_HTMLBR_ELEMENT (next_sibling)) - remove_node (next_sibling); - - if (webkit_dom_node_has_child_nodes (blockquote)) { - WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote); - if (WEBKIT_DOM_IS_HTMLBR_ELEMENT (child)) - remove_node (child); - } - } - - quote_plain_text_recursive (document, body_clone, body_clone, 0); - - /* Copy attributes */ - attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body)); - attributes_length = webkit_dom_named_node_map_get_length (attributes); - for (ii = 0; ii < attributes_length; ii++) { - gchar *name, *value; - WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii); - - name = webkit_dom_node_get_local_name (node); - value = webkit_dom_node_get_node_value (node); - - webkit_dom_element_set_attribute ( - WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL); - - g_free (name); - g_free (value); - } - - /* Replace old BODY with one, that is quoted */ - webkit_dom_node_replace_child ( - webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)), - body_clone, - WEBKIT_DOM_NODE (body), - NULL); - - return WEBKIT_DOM_ELEMENT (body_clone); -} - -/** - * e_html_editor_view_dequote_plain_text: - * @view: an #EHTMLEditorView - * - * Dequote already quoted plain text in editor. - * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise - * it's not working. - */ -void -e_html_editor_view_dequote_plain_text (EHTMLEditorView *view) -{ - WebKitDOMDocument *document; - WebKitDOMNodeList *paragraphs; - gint length, ii; - - document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)); - - paragraphs = webkit_dom_document_query_selector_all ( - document, "blockquote.-x-evo-plaintext-quoted", NULL); - length = webkit_dom_node_list_get_length (paragraphs); - for (ii = 0; ii < length; ii++) { - WebKitDOMElement *element; - - element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii)); - - if (is_citation_node (WEBKIT_DOM_NODE (element))) { - element_remove_class (element, "-x-evo-plaintext-quoted"); - remove_quoting_from_element (element); - } - } -} - /** * e_html_editor_view_get_html_mode: * @view: an #EHTMLEditorView |