diff options
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 |