[evolution/webkit-composer] Implement plain text quoting in composer.



commit 4e0c0c6ea280d87c6da70d4f51f51a95776faf3b
Author: Tomas Popela <tpopela redhat com>
Date:   Wed Sep 18 15:24:37 2013 +0200

    Implement plain text quoting in composer.

 composer/e-composer-private.c |    6 +
 e-util/e-editor-widget.c      |  455 +++++++++++++++++++++++++++++++++++++++--
 e-util/e-editor-widget.h      |   13 +-
 3 files changed, 452 insertions(+), 22 deletions(-)
---
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index ec795dd..43108a9 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -839,6 +839,9 @@ composer_move_caret (EMsgComposer *composer)
 
        if (start_bottom) {
                if (webkit_dom_node_list_get_length (blockquotes) != 0) {
+                       if (!e_editor_widget_get_html_mode (editor_widget))
+                               e_editor_widget_quote_plain_text (editor_widget);
+
                        if (!has_paragraphs_in_body) {
                                webkit_dom_node_insert_before (
                                        WEBKIT_DOM_NODE (body),
@@ -898,6 +901,9 @@ composer_move_caret (EMsgComposer *composer)
                        if (webkit_dom_node_list_get_length (blockquotes) != 0) {
                                WebKitDOMElement *br = webkit_dom_document_create_element (document, "BR", 
NULL);
 
+                               if (!e_editor_widget_get_html_mode (editor_widget))
+                                       e_editor_widget_quote_plain_text (editor_widget);
+
                                webkit_dom_node_insert_before (
                                        WEBKIT_DOM_NODE (body),
                                        WEBKIT_DOM_NODE (br),
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 076070f..031f31d 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -1577,6 +1577,367 @@ e_editor_widget_set_changed (EEditorWidget *widget,
        g_object_notify (G_OBJECT (widget), "changed");
 }
 
+static gboolean
+is_citation_node (WebKitDOMNode *node)
+{
+       const gchar *value;
+
+       if (node && !WEBKIT_DOM_IS_HTML_ELEMENT (node))
+               return FALSE;
+
+       if (!element_has_tag (WEBKIT_DOM_ELEMENT (node), "blockquote"))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+       /* citation == <blockquote type='cite'> */
+       if (g_strcmp0 (value, "cite") == 0)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+static void
+insert_quote_symbols (WebKitDOMHTMLElement *element,
+                      gint quote_level,
+                      gboolean force_insert)
+{
+       const gchar *text;
+       gint ii;
+       GString *output;
+       gchar *indent;
+
+       if (!WEBKIT_DOM_IS_HTML_ELEMENT (element))
+               return;
+
+       text = webkit_dom_html_element_get_inner_html (element);
+       output = g_string_new ("");
+       indent = g_strnfill (quote_level, '>');
+
+       if (g_strcmp0 (text, "\n") == 0) {
+               g_string_append (output, "<span class=\"-x-evo-quoted\">");
+               g_string_append (output, indent);
+               g_string_append (output, " </span>");
+               g_string_append (output, "\n");
+       } else {
+               gchar **lines;
+
+               lines = g_strsplit (text, "\n", 0);
+
+               if (g_str_has_prefix (text, "\n") &&
+                   !g_str_has_suffix (text, "\n") &&
+                   g_strv_length (lines) == 2) {
+                       /* Text node on the beginning of line with another element */
+                       g_string_append (output, "<span class=\"-x-evo-quoted\">");
+                       g_string_append (output, indent);
+                       g_string_append (output, " </span>");
+                       g_string_append (output, lines[1]);
+               } else if (!g_str_has_prefix (text, "\n") &&
+                          g_str_has_suffix (text, "\n") &&
+                          g_strv_length (lines) == 2) {
+                       /* Text node on the end of line with another element */
+                       if (force_insert) {
+                               /* Workaround for first line of citation: On xx/xx/xxxx ... */
+                               g_string_append (output, "<span class=\"-x-evo-quoted\">");
+                               g_string_append (output, indent);
+                               g_string_append (output, " </span>");
+                       }
+                       g_string_append (output, lines[0]);
+                       g_string_append (output, "\n");
+               } else {
+                       for (ii = 0; lines[ii]; ii++) {
+                               if (ii == 0 && g_str_has_prefix (text, "\n")) {
+                                       /* Skip first line if is empty (if node's text starts with \n) */
+                                       continue;
+                               }
+                               if ((ii == g_strv_length (lines) - 1) &&
+                                   g_str_has_suffix (text, "\n")) {
+                                       /* Skip last line if is empty (if node's text ends with \n) */
+                                       break;
+                               }
+                               if (!(ii == 0 && (g_strcmp0 (lines[ii], "&gt;") == 0) &&
+                                   g_str_has_prefix (text, "&gt;"))) {
+                                       g_string_append (output, "<span class=\"-x-evo-quoted\">");
+                                       g_string_append (output, indent);
+                                       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")) {
+                                       /* 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);
+
+       g_free (indent);
+       g_string_free (output, TRUE);
+}
+
+static void
+quote_node (WebKitDOMDocument *document,
+           WebKitDOMNode *node,
+           gint quote_level)
+{
+       /* Don't quote when we are not in citation */
+       if (quote_level == 0)
+               return;
+
+       if (WEBKIT_DOM_IS_TEXT (node)) {
+               WebKitDOMElement *wrapper;
+               WebKitDOMNode *parent;
+               WebKitDOMNode *node_clone;
+               WebKitDOMNode *prev_sibling;
+               const gchar *text_content;
+               gboolean force_insert = FALSE;
+
+               text_content = webkit_dom_node_get_text_content (node);
+               parent = webkit_dom_node_get_parent_node (node);
+
+               /* FIXME find where we take the "\n" nodes */
+               if (g_strcmp0 (text_content, "\n") == 0) {
+                       webkit_dom_node_remove_child (
+                               parent,
+                               node,
+                               NULL);
+                       return;
+               }
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+               if (!prev_sibling) {
+                       /* Workaround for first line of citation: On xx/xx/xxxx ... */
+                       WebKitDOMNode *parent;
+
+                       parent = webkit_dom_node_get_parent_node (node);
+                       if (element_has_tag (WEBKIT_DOM_ELEMENT (parent), "pre"))
+                               force_insert = 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,
+                       force_insert);
+
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (wrapper),
+                       node,
+                       NULL);
+       } else if (WEBKIT_DOM_IS_HTML_ELEMENT (node))
+               insert_quote_symbols (WEBKIT_DOM_HTML_ELEMENT (node), quote_level, FALSE);
+}
+
+static void
+quote_plain_text_recursive (WebKitDOMDocument *document,
+                           WebKitDOMNode *node,
+                           WebKitDOMNode *start_node,
+                           gint quote_level)
+{
+       gboolean skip_node = FALSE;
+       gboolean move_next = FALSE;
+
+       node = webkit_dom_node_get_first_child (node);
+
+       while (node) {
+               skip_node = FALSE;
+               move_next = FALSE;
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       /* Start quoting after we are in blockquote */
+                       if (quote_level > 0) {
+                               WebKitDOMNode *next_sibling;
+
+                               /* 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 */
+                               next_sibling = webkit_dom_node_get_next_sibling (node);
+                               quote_node (document, node, quote_level);
+                               node = next_sibling;
+                               skip_node = TRUE;
+                       }
+
+                       goto next_node;
+               }
+
+               if (WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node)) {
+                       if (webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) == 0) {
+                               /* We have to treat anchors separately to avoid
+                                * modifications * of it's inner text */
+                               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                                       WebKitDOMNode *next_sibling;
+                                       const gchar *text_content;
+
+                                       next_sibling = webkit_dom_node_get_next_sibling (node);
+                                       text_content = webkit_dom_node_get_text_content (next_sibling);
+
+                                       /* If anchor is on the end of the line, we have
+                                        * to insert "\n" text node after it, because
+                                        * we are skipping the anchor and in next
+                                        * node we won't have this information */
+                                       if (g_str_has_prefix (text_content, "\n")) {
+                                               WebKitDOMText *text;
+                                               /* Create new "\n" text node */
+                                               text = webkit_dom_document_create_text_node (document, "\n");
+                                               /* Insert it and move to it */
+                                               node = webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (node),
+                                                       WEBKIT_DOM_NODE (text),
+                                                       next_sibling, NULL);
+                                       }
+                                       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");
+                               } else
+                                       quote_node (document, node, quote_level);
+
+                               move_next = TRUE;
+                       } else {
+                               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;
+                       }
+               }
+       }
+}
+
+/**
+ * e_editor_widget_quote_plain_text:
+ * @widget: an #EEditorWidget
+ *
+ * Quote plain text in editor.
+ */
+void
+e_editor_widget_quote_plain_text (EEditorWidget *widget)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNode *body_clone;
+       WebKitDOMElement *element;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+       /* Check if the document is already quoted */
+       element = webkit_dom_document_query_selector (document, ".-x-evo-plaintext-quoted", NULL);
+       if (element)
+               return;
+
+       body = webkit_dom_document_get_body (document);
+       body_clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), TRUE);
+
+       quote_plain_text_recursive (
+               document,
+               body_clone,
+               body_clone,
+               0);
+
+       /* Replace old BODY with one, that is quoted */
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)),
+               WEBKIT_DOM_NODE (body_clone),
+               WEBKIT_DOM_NODE (body),
+               NULL);
+}
+
+/**
+ * e_editor_widget_dequote_plain_text:
+ * @widget: an #EEditorWidget
+ *
+ * Dequote already quoted plain text in editor.
+ * Editor have to be quoted with e_editor_widget_quote_plain_text otherwise
+ * it's working.
+ */
+void
+e_editor_widget_dequote_plain_text (EEditorWidget *widget)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list;
+       gint ii;
+
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+       list = webkit_dom_document_query_selector_all (document, "blockquote.-x-evo-plaintext-quoted", NULL);
+
+       for (ii = 0; ii < webkit_dom_node_list_get_length (list); ii++) {
+               WebKitDOMNodeList *gt_list;
+               WebKitDOMElement *element;
+               gint jj;
+
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, ii));
+
+               if (is_citation_node (WEBKIT_DOM_NODE (element))) {
+                       element_remove_class (element, "-x-evo-plaintext-quoted");
+
+                       gt_list = webkit_dom_element_query_selector_all (element, "span.-x-evo-quoted", NULL);
+
+                       for (jj = 0; jj < webkit_dom_node_list_get_length (gt_list); jj++) {
+                               WebKitDOMNode *node = webkit_dom_node_list_item (gt_list, jj);
+
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       node,
+                                       NULL);
+                       }
+               }
+       }
+}
+
 /**
  * e_editor_widget_get_html_mode:
  * @widget: an #EEditorWidget
@@ -1629,9 +1990,10 @@ process_elements (WebKitDOMNode *node,
 
        /* Skip signature */
        if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evolution-signature")) {
-               if (process_nodes)
+               if (process_nodes) {
                        g_string_append (buffer, "\n");
-               else
+                       element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evolution-signature");
+               } else
                        return;
        }
 
@@ -1682,6 +2044,51 @@ process_elements (WebKitDOMNode *node,
                                        g_string_append (buffer, content);
                                        g_free (content);
                                        skip_node = TRUE;
+                               } else {
+                                       WebKitDOMNodeList *list;
+                                       int jj, length;
+
+                                       /* First replace wrappers */
+                                       list = webkit_dom_element_query_selector_all (
+                                                       WEBKIT_DOM_ELEMENT (child),
+                                                       "span.-x-evo-temp-text-wrapper",
+                                                       NULL);
+
+                                       length = webkit_dom_node_list_get_length (list);
+
+                                       for (jj = 0; jj < length; jj++) {
+                                               WebKitDOMNode *quoted_node;
+                                               const gchar *text_content;
+
+                                               quoted_node = webkit_dom_node_list_item (list, jj);
+                                               text_content = webkit_dom_node_get_text_content (quoted_node);
+
+                                               webkit_dom_html_element_set_outer_html (
+                                                       WEBKIT_DOM_HTML_ELEMENT (quoted_node),
+                                                       text_content,
+                                                       NULL);
+                                       }
+
+                                       /* Afterwards replace quote nodes with symbols */
+                                       list = webkit_dom_element_query_selector_all (
+                                                       WEBKIT_DOM_ELEMENT (child),
+                                                       "span.-x-evo-quoted",
+                                                       NULL);
+
+                                       length = webkit_dom_node_list_get_length (list);
+
+                                       for (jj = 0; jj < length; jj++) {
+                                               WebKitDOMNode *quoted_node;
+                                               const gchar *text_content;
+
+                                               quoted_node = webkit_dom_node_list_item (list, jj);
+                                               text_content = webkit_dom_node_get_text_content (quoted_node);
+
+                                               webkit_dom_html_element_set_outer_html (
+                                                       WEBKIT_DOM_HTML_ELEMENT (quoted_node),
+                                                       text_content,
+                                                       NULL);
+                                       }
                                }
                        }
 
@@ -1809,12 +2216,15 @@ e_editor_widget_set_html_mode (EEditorWidget *widget,
 
        if (widget->priv->html_mode) {
                /* FIXME WEBKIT: Process smileys! */
+               e_editor_widget_dequote_plain_text (widget);
        } else {
                gchar *plain;
 
                /* Save caret position -> it will be restored in e-composer-private.c */
                e_editor_selection_save_caret_position (e_editor_widget_get_selection (widget));
 
+               e_editor_widget_quote_plain_text (widget);
+
                plain = changing_composer_mode_get_text_plain (widget);
 
                if (*plain)
@@ -2185,7 +2595,16 @@ e_editor_widget_update_fonts (EEditorWidget *widget)
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] {\n"
+               "blockquote[type=cite] "
+               "{\n"
+               "  padding: 0.0ex 0ex;\n"
+               "  margin: 0ex;\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "{\n"
                "  padding: 0.4ex 1ex;\n"
                "  margin: 1ex;\n"
                "  border-width: 0px 2px 0px 2px;\n"
@@ -2197,45 +2616,45 @@ e_editor_widget_update_fonts (EEditorWidget *widget)
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
                "{\n"
                "  border-color: rgb(114,159,207);\n"  /* Sky Blue 1 */
                "}\n");
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
                "{\n"
                "  border-color: rgb(173,127,168);\n"  /* Plum 1 */
                "}\n");
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
                "{\n"
                "  border-color: rgb(138,226,52);\n"  /* Chameleon 1 */
                "}\n");
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
                "{\n"
                "  border-color: rgb(252,175,62);\n"  /* Orange 1 */
                "}\n");
 
        g_string_append_printf (
                stylesheet,
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
-               "blockquote[type=cite] "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
                "{\n"
                "  border-color: rgb(233,185,110);\n"  /* Chocolate 1 */
                "}\n");
diff --git a/e-util/e-editor-widget.h b/e-util/e-editor-widget.h
index 42c598b..9af32b5 100644
--- a/e-util/e-editor-widget.h
+++ b/e-util/e-editor-widget.h
@@ -107,11 +107,16 @@ void              e_editor_widget_set_text_plain  (EEditorWidget *widget,
 void           e_editor_widget_paste_clipboard_quoted
                                                (EEditorWidget *widget);
 void           e_editor_widget_update_fonts    (EEditorWidget *widget);
-
 WebKitDOMElement *
-               e_editor_widget_get_element_under_mouse_click (EEditorWidget *widget);
-void           e_editor_widget_check_magic_links (EEditorWidget *widget,
-                                                  gboolean while_typing);
+               e_editor_widget_get_element_under_mouse_click
+                                               (EEditorWidget *widget);
+void           e_editor_widget_check_magic_links
+                                               (EEditorWidget *widget,
+                                                gboolean while_typing);
+void           e_editor_widget_quote_plain_text
+                                               (EEditorWidget *widget);
+void           e_editor_widget_dequote_plain_text
+                                               (EEditorWidget *widget);
 G_END_DECLS
 
 #endif /* E_EDITOR_WIDGET_H */



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