[evolution/webkit-composer: 30/210] Make inserting a auto-replacing emoticons work



commit e6be3315dc5abb41d00eec40d615537faf84654d
Author: Dan Vrátil <dvratil redhat com>
Date:   Thu Aug 9 10:28:59 2012 +0200

    Make inserting a auto-replacing emoticons work
    
    The algorithm for magic smileys is taken from GtkHTML
    (I admit I don't 100% understand how it works), because
    it's quick and known to work quite well.
    
    TODO: It would be nice to automagically replace emoticons
    in pasted and inserted text as well.

 e-util/e-editor-actions.c        |   46 +++-------
 e-util/e-editor-widget.c         |  175 +++++++++++++++++++++++++++++++++++++-
 e-util/e-emoticon-chooser-menu.c |    2 +-
 e-util/e-emoticon-chooser.c      |    2 +-
 e-util/e-emoticon-chooser.h      |    4 +-
 e-util/e-emoticon-tool-button.c  |    2 +-
 e-util/e-emoticon.c              |   25 ++++++
 e-util/e-emoticon.h              |    2 +
 8 files changed, 219 insertions(+), 39 deletions(-)
---
diff --git a/e-util/e-editor-actions.c b/e-util/e-editor-actions.c
index 0e6fdee..c09c63d 100644
--- a/e-util/e-editor-actions.c
+++ b/e-util/e-editor-actions.c
@@ -30,6 +30,7 @@
 #include "e-editor-private.h"
 #include "e-editor-widgets.h"
 #include "e-emoticon-action.h"
+#include "e-emoticon-chooser.h"
 #include "e-image-chooser-dialog.h"
 
 static void
@@ -569,45 +570,24 @@ action_indent_cb (GtkAction *action,
 
 static void
 action_insert_emoticon_cb (GtkAction *action,
-      EEditor *editor)
+                          EEditor *editor)
 {
-       /* FIXME WEBKIT
-       GtkHTML *html;
-       HTMLObject *image;
-       GtkIconInfo *icon_info;
-       GtkIconTheme *icon_theme;
-       GtkhtmlFaceChooser *chooser;
-       GtkhtmlFace *face;
-       const gchar *filename;
+       EEditorWidget *widget;
+       EEditorSelection *selection;
+       EEmoticonChooser *chooser;
+       EEmoticon *emoticon;
        gchar *uri = NULL;
 
-       html = gtkhtml_editor_get_html (editor);
+       chooser = E_EMOTICON_CHOOSER (action);
+       emoticon = e_emoticon_chooser_get_current_emoticon (chooser);
+       g_return_if_fail (emoticon != NULL);
 
-       chooser = GTKHTML_FACE_CHOOSER (action);
-       face = gtkhtml_face_chooser_get_current_face (chooser);
-       g_return_if_fail (face != NULL);
-
-       icon_theme = gtk_icon_theme_get_default ();
-       icon_info = gtk_icon_theme_lookup_icon (
-               icon_theme, face->icon_name, 16, 0);
-       g_return_if_fail (icon_info != NULL);
-
-       filename = gtk_icon_info_get_filename (icon_info);
-       if (filename != NULL)
-               uri = g_filename_to_uri (filename, NULL, NULL);
-       gtk_icon_info_free (icon_info);
-       g_return_if_fail (uri != NULL);
-
-       image = html_image_new (
-               html_engine_get_image_factory (html->engine),
-               uri, NULL, NULL, -1, -1, FALSE, FALSE, 0, NULL,
-               HTML_VALIGN_MIDDLE, FALSE);
-       html_image_set_alt (HTML_IMAGE (image), face->text_face);
-       html_engine_paste_object (
-               html->engine, image, html_object_get_length (image));
+       uri = e_emoticon_get_uri (emoticon);
+       widget = e_editor_get_editor_widget (editor);
+       selection = e_editor_widget_get_selection (widget);
+       e_editor_selection_insert_image (selection, uri);
 
        g_free (uri);
-       */
 }
 
 static void
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 4b55200..f5fed8c 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -22,8 +22,10 @@
 
 #include "e-editor-widget.h"
 #include "e-editor.h"
+#include "e-emoticon-chooser.h"
 
 #include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
 
 struct _EEditorWidgetPrivate {
        gint changed            : 1;
@@ -47,8 +49,7 @@ struct _EEditorWidgetPrivate {
 G_DEFINE_TYPE (
        EEditorWidget,
        e_editor_widget,
-       WEBKIT_TYPE_WEB_VIEW
-);
+       WEBKIT_TYPE_WEB_VIEW);
 
 enum {
        PROP_0,
@@ -65,6 +66,24 @@ enum {
        PROP_CAN_UNDO
 };
 
+static WebKitDOMRange *
+editor_widget_get_dom_range (EEditorWidget *widget)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *window;
+       WebKitDOMDOMSelection *selection;
+
+       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);
+
+       if (webkit_dom_dom_selection_get_range_count (selection) < 1) {
+               return NULL;
+       }
+
+       return webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+}
+
 static void
 editor_widget_strip_formatting (EEditorWidget *widget)
 {
@@ -129,6 +148,154 @@ editor_widget_selection_changed_cb (EEditorWidget *widget,
        }
 }
 
+/* Based on original use_pictograms() from GtkHTML */
+static const gchar *emoticons_chars =
+       /*  0 */ "DO)(|/PQ*!"
+       /* 10 */ "S\0:-\0:\0:-\0"
+       /* 20 */ ":\0:;=-\"\0:;"
+       /* 30 */ "B\"|\0:-'\0:X"
+       /* 40 */ "\0:\0:-\0:\0:-"
+       /* 50 */ "\0:\0:-\0:\0:-"
+       /* 60 */ "\0:\0:\0:-\0:\0"
+       /* 70 */ ":-\0:\0:-\0:\0";
+static gint emoticons_states[] = {
+       /*  0 */  12,  17,  22,  34,  43,  48,  53,  58,  65,  70,
+       /* 10 */  75,   0, -15,  15,   0, -15,   0, -17,  20,   0,
+       /* 20 */ -17,   0, -14, -20, -14,  28,  63,   0, -14, -20,
+       /* 30 */  -3,  63, -18,   0, -12,  38,  41,   0, -12,  -2,
+       /* 40 */   0,  -4,   0, -10,  46,   0, -10,   0, -19,  51,
+       /* 50 */   0, -19,   0, -11,  56,   0, -11,   0, -13,  61,
+       /* 60 */   0, -13,   0,  -6,   0,  68,  -7,   0,  -7,   0,
+       /* 70 */ -16,  73,   0, -16,   0, -21,  78,   0, -21,   0 };
+static const gchar *emoticons_icon_names[] = {
+       "face-angel",
+       "face-angry",
+       "face-cool",
+       "face-crying",
+       "face-devilish",
+       "face-embarrassed",
+       "face-kiss",
+       "face-laugh",           /* not used */
+       "face-monkey",          /* not used */
+       "face-plain",
+       "face-raspberry",
+       "face-sad",
+       "face-sick",
+       "face-smile",
+       "face-smile-big",
+       "face-smirk",
+       "face-surprise",
+       "face-tired",
+       "face-uncertain",
+       "face-wink",
+       "face-worried"
+};
+
+static void
+editor_widget_check_magic_smileys (EEditorWidget *widget,
+                                  WebKitDOMRange *range)
+{
+       gint pos;
+       gint state;
+       gint relative;
+       gint start;
+       gchar *node_text;
+       gunichar uc;
+       WebKitDOMNode *node;
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (!webkit_dom_node_get_node_type (node) == 3) {
+               return;
+       }
+
+       node_text = webkit_dom_text_get_whole_text ((WebKitDOMText *) node);
+       start = webkit_dom_range_get_end_offset (range, NULL) - 1;
+       pos = start;
+       state = 0;
+       while (pos >= 0) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos));
+               relative = 0;
+               while (emoticons_chars[state + relative]) {
+                       if (emoticons_chars[state + relative] == uc)
+                               break;
+                       relative++;
+               }
+               state = emoticons_states[state + relative];
+               /* 0 .. not found, -n .. found n-th */
+               if (state <= 0)
+                       break;
+               pos--;
+       }
+
+       /* Special case needed to recognize angel and devilish. */
+       if (pos > 0 && state == -14) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+               if (uc == 'O') {
+                       state = -1;
+                       pos--;
+               } else if (uc == '>') {
+                       state = -5;
+                       pos--;
+               }
+       }
+
+       if (state < 0) {
+               GtkIconInfo *icon_info;
+               const gchar *filename;
+               gchar *filename_uri;
+               WebKitDOMDocument *document;
+               WebKitDOMDOMWindow *window;
+               WebKitDOMDOMSelection *selection;
+
+               if (pos > 0) {
+                       uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+                       if (uc != ' ' && uc != '\t')
+                               return;
+               }
+
+               /* Select the text-smiley and replace it by <img> */
+               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);
+               webkit_dom_dom_selection_set_base_and_extent (
+                       selection, webkit_dom_range_get_end_container (range, NULL),
+                       pos, webkit_dom_range_get_end_container (range, NULL),
+                       start + 1, NULL);
+
+               /* Convert a named icon to a file URI. */
+               icon_info = gtk_icon_theme_lookup_icon (
+                       gtk_icon_theme_get_default (),
+                       emoticons_icon_names[-state - 1], 16, 0);
+               g_return_if_fail (icon_info != NULL);
+               filename = gtk_icon_info_get_filename (icon_info);
+               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);
+
+               g_free (filename_uri);
+               gtk_icon_info_free (icon_info);
+       }
+}
+
+static gboolean
+editor_widget_key_release_event (GtkWidget *gtk_widget,
+                                GdkEventKey *event)
+{
+       WebKitDOMRange *range;
+       EEditorWidget *widget = E_EDITOR_WIDGET (gtk_widget);
+
+       range = editor_widget_get_dom_range (widget);
+
+       if (widget->priv->magic_smileys) {
+               editor_widget_check_magic_smileys (widget, range);
+       }
+
+       /* Propagate the event to WebKit */
+       return FALSE;
+}
+
 static void
 e_editor_widget_get_property (GObject *object,
                              guint property_id,
@@ -251,6 +418,7 @@ static void
 e_editor_widget_class_init (EEditorWidgetClass *klass)
 {
        GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
 
        g_type_class_add_private (klass, sizeof (EEditorWidgetPrivate));
 
@@ -259,6 +427,9 @@ e_editor_widget_class_init (EEditorWidgetClass *klass)
        object_class->set_property = e_editor_widget_set_property;
        object_class->finalize = e_editor_widget_finalize;
 
+       widget_class = GTK_WIDGET_CLASS (klass);
+       widget_class->key_release_event = editor_widget_key_release_event;
+
        g_object_class_install_property (
                object_class,
                PROP_MODE,
diff --git a/e-util/e-emoticon-chooser-menu.c b/e-util/e-emoticon-chooser-menu.c
index 619bd21..7bf6184 100644
--- a/e-util/e-emoticon-chooser-menu.c
+++ b/e-util/e-emoticon-chooser-menu.c
@@ -133,7 +133,7 @@ emoticon_chooser_menu_init (EEmoticonChooserMenu *chooser_menu)
        GList *list, *iter;
 
        chooser = E_EMOTICON_CHOOSER (chooser_menu);
-       list = e_emoticon_chooser_get_items (chooser);
+       list = e_emoticon_chooser_get_items ();
 
        for (iter = list; iter != NULL; iter = iter->next) {
                EEmoticon *face = iter->data;
diff --git a/e-util/e-emoticon-chooser.c b/e-util/e-emoticon-chooser.c
index c8a92b0..8136f2c 100644
--- a/e-util/e-emoticon-chooser.c
+++ b/e-util/e-emoticon-chooser.c
@@ -168,7 +168,7 @@ e_emoticon_chooser_item_activated (EEmoticonChooser *chooser)
 }
 
 GList *
-e_emoticon_chooser_get_items (EEmoticonChooser *chooser)
+e_emoticon_chooser_get_items (void)
 {
        GList *list = NULL;
        gint ii;
diff --git a/e-util/e-emoticon-chooser.h b/e-util/e-emoticon-chooser.h
index 7390da3..a134c7e 100644
--- a/e-util/e-emoticon-chooser.h
+++ b/e-util/e-emoticon-chooser.h
@@ -55,6 +55,7 @@ struct _EEmoticonChooserIface {
 };
 
 GType          e_emoticon_chooser_get_type     (void);
+
 EEmoticon *    e_emoticon_chooser_get_current_emoticon
                                                (EEmoticonChooser *chooser);
 void           e_emoticon_chooser_set_current_emoticon
@@ -62,7 +63,8 @@ void          e_emoticon_chooser_set_current_emoticon
                                                 EEmoticon *emoticon);
 void           e_emoticon_chooser_item_activated
                                                (EEmoticonChooser *chooser);
-GList *                e_emoticon_chooser_get_items    (EEmoticonChooser *chooser);
+
+GList *                e_emoticon_chooser_get_items    (void);
 
 G_END_DECLS
 
diff --git a/e-util/e-emoticon-tool-button.c b/e-util/e-emoticon-tool-button.c
index dc0d0dd..eaf2105 100644
--- a/e-util/e-emoticon-tool-button.c
+++ b/e-util/e-emoticon-tool-button.c
@@ -601,7 +601,7 @@ emoticon_tool_button_init (EEmoticonToolButton *button)
        container = widget;
 
        chooser = E_EMOTICON_CHOOSER (button);
-       list = e_emoticon_chooser_get_items (chooser);
+       list = e_emoticon_chooser_get_items ();
        g_assert (g_list_length (list) <= NUM_ROWS * NUM_COLS);
 
        for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c
index 1d394e6..d5b69c6 100644
--- a/e-util/e-emoticon.c
+++ b/e-util/e-emoticon.c
@@ -17,6 +17,8 @@
 
 #include "e-emoticon.h"
 
+#include <gtk/gtk.h>
+
 static EEmoticon *
 emoticon_copy (EEmoticon *emoticon)
 {
@@ -87,3 +89,26 @@ e_emoticon_free (EEmoticon *emoticon)
 {
        g_boxed_free (E_TYPE_EMOTICON, emoticon);
 }
+
+gchar *
+e_emoticon_get_uri(EEmoticon *emoticon)
+{
+       GtkIconInfo *icon_info;
+       GtkIconTheme *icon_theme;
+       const gchar *filename;
+       gchar *uri = NULL;
+
+       icon_theme = gtk_icon_theme_get_default ();
+       icon_info = gtk_icon_theme_lookup_icon (
+               icon_theme, emoticon->icon_name, 16, 0);
+       g_return_val_if_fail (icon_info != NULL, NULL);
+
+       filename = gtk_icon_info_get_filename (icon_info);
+       if (filename != NULL) {
+               uri = g_filename_to_uri (filename, NULL, NULL);
+       }
+       gtk_icon_info_free (icon_info);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       return uri;
+}
diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h
index cf270ef..17995d5 100644
--- a/e-util/e-emoticon.h
+++ b/e-util/e-emoticon.h
@@ -43,6 +43,8 @@ gboolean      e_emoticon_equal                (EEmoticon *emoticon_a,
 EEmoticon *    e_emoticon_copy                 (EEmoticon *emoticon);
 void           e_emoticon_free                 (EEmoticon *emoticon);
 
+gchar *                e_emoticon_get_uri              (EEmoticon *emoticon);
+
 G_END_DECLS
 
 #endif /* E_EMOTICON_H */


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