[evolution/wip/webkit-composer: 124/372] Improve HTML -> plain text mode switching



commit 95155bf0230430cc2f8c625e02a881f6899cec8f
Author: Dan Vrátil <dvratil redhat com>
Date:   Wed Aug 22 15:29:28 2012 +0200

    Improve HTML -> plain text mode switching
    
     - smiley images are replaced by their text representation
     - solves WebKit runtime warnings immediatelly after the switch

 e-util/e-editor-selection.c |    7 ++-
 e-util/e-editor-widget.c    |  198 +++++++++++++++++++++++++++++++++++++++----
 e-util/e-editor-widget.h    |    2 +
 e-util/e-emoticon-chooser.c |   17 ++++
 e-util/e-emoticon-chooser.h |    4 +
 5 files changed, 210 insertions(+), 18 deletions(-)
---
diff --git a/e-util/e-editor-selection.c b/e-util/e-editor-selection.c
index 7ff4d10..4a09baa 100644
--- a/e-util/e-editor-selection.c
+++ b/e-util/e-editor-selection.c
@@ -86,9 +86,14 @@ editor_selection_get_current_range (EEditorSelection *selection)
 
        document = webkit_web_view_get_dom_document (selection->priv->webview);
        window = webkit_dom_document_get_default_view (document);
+       if (!window) {
+               return NULL;
+       }
+
        dom_selection = webkit_dom_dom_window_get_selection (window);
-       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
                return NULL;
+       }
 
        return webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
 }
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 6d2d7c7..10de3dc 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -40,6 +40,7 @@ struct _EEditorWidgetPrivate {
        gint can_paste          : 1;
        gint can_redo           : 1;
        gint can_undo           : 1;
+       gint reload_in_progress : 1;
 
        EEditorSelection *selection;
 
@@ -50,6 +51,8 @@ struct _EEditorWidgetPrivate {
 
        GSettings *font_settings;
        GSettings *aliasing_settings;
+
+       GQueue *postreload_operations;
 };
 
 G_DEFINE_TYPE (
@@ -77,8 +80,38 @@ enum {
        LAST_SIGNAL
 };
 
+typedef void (*PostReloadOperationFunc)        (EEditorWidget *widget,
+                                        gpointer data);
+
+typedef struct  {
+       PostReloadOperationFunc func;
+       gpointer                data;
+       GDestroyNotify          data_free_func;
+} PostReloadOperation;
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static void
+editor_widget_queue_postreload_operation (EEditorWidget *widget,
+                                         PostReloadOperationFunc func,
+                                         gpointer data,
+                                         GDestroyNotify data_free_func)
+{
+       PostReloadOperation *op;
+
+       g_return_if_fail (func != NULL);
+
+       if (widget->priv->postreload_operations == NULL)
+               widget->priv->postreload_operations = g_queue_new ();
+
+       op = g_new0 (PostReloadOperation, 1);
+       op->func = func;
+       op->data = data;
+       op->data_free_func = data_free_func;
+
+       g_queue_push_head (widget->priv->postreload_operations, op);
+}
+
 static WebKitDOMRange *
 editor_widget_get_dom_range (EEditorWidget *widget)
 {
@@ -100,19 +133,11 @@ editor_widget_get_dom_range (EEditorWidget *widget)
 static void
 editor_widget_strip_formatting (EEditorWidget *widget)
 {
-       gchar *plain, *html;
-       GRegex *regex;
+       gchar *plain;
 
        plain = e_editor_widget_get_text_plain (widget);
-
-       /* Convert \n to <br> */
-       regex = g_regex_new ("\n", 0, 0, NULL);
-       html = g_regex_replace (regex, plain, strlen (plain), 0, "<br>", 0, NULL);
-
-       e_editor_widget_set_text_html (widget, html);
-
+       e_editor_widget_set_text_plain (widget, plain);
        g_free (plain);
-       g_free (html);
 }
 
 static void
@@ -142,6 +167,14 @@ editor_widget_selection_changed_cb (EEditorWidget *widget,
 {
        gboolean can_copy, can_cut, can_paste;
 
+       /* When the webview is being (re)loaded, the document is in inconsitant
+        * state and there is no selection, so don't propagate the signal further
+        * to EEditorSelection and others and wait until the load is finished */
+       if (widget->priv->reload_in_progress) {
+               g_signal_stop_emission_by_name (widget, "selection-changed");
+               return;
+       }
+
        can_copy = webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (widget));
        if ((widget->priv->can_copy ? TRUE : FALSE) != (can_copy ? TRUE : FALSE)) {
                widget->priv->can_copy = can_copy;
@@ -168,6 +201,53 @@ editor_widget_should_show_delete_interface_for_element (EEditorWidget *widget,
        return FALSE;
 }
 
+static void
+editor_widget_load_status_changed (EEditorWidget *widget)
+{
+       WebKitLoadStatus status;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+       WebKitDOMRange *range;
+
+       status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (widget));
+       if (status != WEBKIT_LOAD_FINISHED) {
+               return;
+       }
+
+       widget->priv->reload_in_progress = FALSE;
+
+       /* After reload, there is no selection in the document. Fix it. */
+       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+       window = webkit_dom_document_get_default_view (document);
+       selection = webkit_dom_dom_window_get_selection (window);
+
+       range = webkit_dom_document_create_range (document);
+       webkit_dom_range_set_start (
+               range, WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)),
+               0, NULL);
+       webkit_dom_range_set_end (
+               range, WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)),
+               0, NULL);
+
+       webkit_dom_dom_selection_add_range (selection, range);
+
+       /* Dispatch queued operations */
+       while (widget->priv->postreload_operations &&
+              !g_queue_is_empty (widget->priv->postreload_operations)) {
+
+               PostReloadOperation *op;
+
+               op = g_queue_pop_head (widget->priv->postreload_operations);
+
+               op->func (widget, op->data);
+
+               if (op->data_free_func)
+                       op->data_free_func (op->data);
+               g_free (op);
+       }
+}
+
 static gboolean
 editor_widget_button_press_event (GtkWidget *gtk_widget,
                                  GdkEventButton *event)
@@ -284,10 +364,11 @@ editor_widget_check_magic_smileys (EEditorWidget *widget,
        if (state < 0) {
                GtkIconInfo *icon_info;
                const gchar *filename;
-               gchar *filename_uri;
+               gchar *filename_uri, *html;
                WebKitDOMDocument *document;
                WebKitDOMDOMWindow *window;
                WebKitDOMDOMSelection *selection;
+               const EEmoticon *emoticon;
 
                if (pos > 0) {
                        uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
@@ -306,6 +387,9 @@ editor_widget_check_magic_smileys (EEditorWidget *widget,
                        pos, webkit_dom_range_get_end_container (range, NULL),
                        start + 1, NULL);
 
+               emoticon = e_emoticon_chooser_lookup_emoticon (
+                               emoticons_icon_names[-state - 1]);
+
                /* Convert a named icon to a file URI. */
                icon_info = gtk_icon_theme_lookup_icon (
                        gtk_icon_theme_get_default (),
@@ -315,9 +399,15 @@ editor_widget_check_magic_smileys (EEditorWidget *widget,
                g_return_if_fail (filename != NULL);
                filename_uri = g_filename_to_uri (filename, NULL, NULL);
 
-               e_editor_selection_insert_image (
-                       widget->priv->selection, filename_uri);
+               html = g_strdup_printf (
+                       "<img src=\"%s\" alt=\"%s\" x-evo-smiley=\"%s\" />",
+                       filename_uri, emoticon ? emoticon->text_face : "",
+                       emoticons_icon_names[-state - 1]);
 
+               e_editor_selection_insert_html (
+                       widget->priv->selection, html);
+
+               g_free (html);
                g_free (filename_uri);
                gtk_icon_info_free (icon_info);
        }
@@ -782,6 +872,8 @@ e_editor_widget_init (EEditorWidget *editor)
                G_CALLBACK (editor_widget_selection_changed_cb), NULL);
        g_signal_connect (editor, "should-show-delete-interface-for-element",
                G_CALLBACK (editor_widget_should_show_delete_interface_for_element), NULL);
+       g_signal_connect (editor, "notify::load-status",
+               G_CALLBACK (editor_widget_load_status_changed), NULL);
 
        editor->priv->selection = e_editor_selection_new (
                                        WEBKIT_WEB_VIEW (editor));
@@ -991,18 +1083,90 @@ gchar *
 e_editor_widget_get_text_plain (EEditorWidget *widget)
 {
        WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *element;
+       WebKitDOMNode *body;
+       WebKitDOMNodeList *imgs;
+       gulong ii, length;
 
        document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
-       element = webkit_dom_document_get_body (document);
-       return webkit_dom_html_element_get_inner_text (element);
+       body = (WebKitDOMNode *) webkit_dom_document_get_body (document);
+       body = webkit_dom_node_clone_node (body, TRUE);
+
+       imgs = webkit_dom_element_get_elements_by_tag_name (
+                       (WebKitDOMElement *) body, "IMG");
+       length = webkit_dom_node_list_get_length (imgs);
+
+       /* Replace all smiley images with their text representation */
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *img;
+
+               img = webkit_dom_node_list_item (imgs, ii);
+
+               if (webkit_dom_element_has_attribute (
+                               WEBKIT_DOM_ELEMENT (img), "x-evo-smiley")) {
+
+                       gchar *name;
+                       const EEmoticon *emoticon;
+
+                       name = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (img), "x-evo-smiley");
+                       emoticon = e_emoticon_chooser_lookup_emoticon (name);
+                       if (emoticon) {
+                               WebKitDOMText *text;
+
+                               text = webkit_dom_document_create_text_node (
+                                       document, emoticon->text_face);
+
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (img),
+                                       WEBKIT_DOM_NODE (text), img, NULL);
+                               webkit_dom_node_remove_child (
+                                       webkit_dom_node_get_parent_node (img),
+                                       img, NULL);
+                       }
+
+                       g_free (name);
+               }
+       }
+
+       return webkit_dom_html_element_get_inner_text ((WebKitDOMHTMLElement *) body);
 }
 
 void
 e_editor_widget_set_text_html (EEditorWidget *widget,
                               const gchar *text)
 {
-       webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (widget), text, NULL);
+       webkit_web_view_load_html_string (
+               WEBKIT_WEB_VIEW (widget), text, "file://");
+}
+
+static void
+do_set_text_plain (EEditorWidget *widget,
+                  gpointer data)
+{
+       WebKitDOMDocument *document;
+
+       document =
+               webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+       webkit_dom_document_exec_command (
+               document, "insertText", FALSE, data);
+}
+
+void
+e_editor_widget_set_text_plain (EEditorWidget *widget,
+                               const gchar *text)
+{
+       widget->priv->reload_in_progress = TRUE;
+
+       webkit_web_view_load_html_string (
+               WEBKIT_WEB_VIEW (widget), "", "file://");
+
+       /* webkit_web_view_load_html_string() is actually performed
+        * when this functions returns, so the operation below would get
+        * overwritten. Instead queue the insert operation and insert the
+        * actual text when the webview is reloaded */
+       editor_widget_queue_postreload_operation (
+               widget, do_set_text_plain, g_strdup (text), g_free);
 }
 
 void
diff --git a/e-util/e-editor-widget.h b/e-util/e-editor-widget.h
index 1f08c4b..dcdf549 100644
--- a/e-util/e-editor-widget.h
+++ b/e-util/e-editor-widget.h
@@ -120,6 +120,8 @@ gchar *             e_editor_widget_get_text_html   (EEditorWidget *widget);
 gchar *                e_editor_widget_get_text_plain  (EEditorWidget *widget);
 void           e_editor_widget_set_text_html   (EEditorWidget *widget,
                                                 const gchar *text);
+void           e_editor_widget_set_text_plain  (EEditorWidget *widget,
+                                                const gchar *text);
 
 void           e_editor_widget_paste_clipboard_quoted
                                                (EEditorWidget *widget);
diff --git a/e-util/e-emoticon-chooser.c b/e-util/e-emoticon-chooser.c
index 8136f2c..4c894a4 100644
--- a/e-util/e-emoticon-chooser.c
+++ b/e-util/e-emoticon-chooser.c
@@ -178,3 +178,20 @@ e_emoticon_chooser_get_items (void)
 
        return g_list_reverse (list);
 }
+
+const EEmoticon *
+e_emoticon_chooser_lookup_emoticon (const gchar *icon_name)
+{
+       gint ii;
+
+       g_return_val_if_fail (icon_name && *icon_name, NULL);
+
+       for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++) {
+               if (strcmp (available_emoticons[ii].icon_name, icon_name) == 0) {
+                       return (const EEmoticon *) &available_emoticons[ii];
+               }
+       }
+
+       return NULL;
+}
+
diff --git a/e-util/e-emoticon-chooser.h b/e-util/e-emoticon-chooser.h
index a134c7e..f0e3c15 100644
--- a/e-util/e-emoticon-chooser.h
+++ b/e-util/e-emoticon-chooser.h
@@ -66,6 +66,10 @@ void         e_emoticon_chooser_item_activated
 
 GList *                e_emoticon_chooser_get_items    (void);
 
+const EEmoticon *
+               e_emoticon_chooser_lookup_emoticon
+                                               (const gchar *icon_name);
+
 G_END_DECLS
 
 #endif /* E_EMOTICON_CHOOSER_H */


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