[evolution/449-support-markdown-in-composer: 10/15] EHTMLEditor: Make it possible to switch between different editors for different modes




commit 0c6bf3a4fef44664d4bf1c856590fa0816198788
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 3 17:25:04 2022 +0100

    EHTMLEditor: Make it possible to switch between different editors for different modes
    
    Since there can be multiple editors for different modes, let the code
    switch between different editors on demand.

 data/org.gnome.evolution.mail.gschema.xml.in |   4 +-
 src/composer/e-composer-private.c            |   7 +-
 src/e-util/e-content-editor.c                |  24 +++
 src/e-util/e-content-editor.h                |   7 +-
 src/e-util/e-html-editor-actions.c           |   9 +
 src/e-util/e-html-editor-private.h           |   9 +-
 src/e-util/e-html-editor.c                   | 264 +++++++++++++++++++++------
 src/e-util/e-html-editor.h                   |   1 +
 src/modules/webkit-editor/e-webkit-editor.c  |  11 ++
 9 files changed, 266 insertions(+), 70 deletions(-)
---
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index 11e233f982..33e02ee0bb 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -57,8 +57,8 @@
     </key>
     <key name="composer-editor" type="s">
       <default>''</default>
-      <_summary>Name of the editor to prefer in the message composer</_summary>
-      <_description>If the name doesn’t correspond to any known editor, then the built-in WebKit editor is 
used.</_description>
+      <_summary>Comma-separated names of the editor to prefer in the message composer for which 
modes</_summary>
+      <_description>If the name doesn’t correspond to any known editor, then the built-in WebKit editor is 
used. The mode is optional, in which case the editor is used for all the modes it supports. Modes are “plain” 
and “html”. Example values: “webkit” (to use WebKit for plain and html), 
“plain:first-editor,html:second-editor” (to use “first-editor” for the “plain” and “second-editor” for 
“html”)</_description>
     </key>
     <key name="composer-gallery-path" type="s">
       <default>''</default>
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index 23055d561f..f7cc48e177 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -349,12 +349,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->gallery_scrolled_window = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       widget = GTK_WIDGET (cnt_editor);
-       if (GTK_IS_SCROLLABLE (cnt_editor)) {
-               /* Scrollables are packed in a scrolled window */
-               widget = gtk_widget_get_parent (widget);
-               g_warn_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
-       }
+       widget = e_html_editor_get_content_box (editor);
        gtk_widget_reparent (widget, container);
 
        /* Construct the picture gallery. */
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index 3f2d8083a0..73c9b7018c 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -642,6 +642,30 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                G_TYPE_STRING);
 }
 
+/**
+ * e_content_editor_supports_mode:
+ * @editor: an #EContentEditor
+ * @mode: an #EContentEditorMode to check
+ *
+ * Returns: whether the @editor supports @mode
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_content_editor_supports_mode (EContentEditor *editor,
+                               EContentEditorMode mode)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+
+       return iface->supports_mode != NULL &&
+               iface->supports_mode (editor, mode);
+}
+
 ESpellChecker *
 e_content_editor_ref_spell_checker (EContentEditor *editor)
 {
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index 630d5c398c..193dd4a164 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -426,14 +426,19 @@ struct _EContentEditorInterface {
        void            (*delete_h_rule)                (EContentEditor *editor);
        void            (*delete_image)                 (EContentEditor *editor);
 
+       gboolean        (*supports_mode)                (EContentEditor *editor,
+                                                        EContentEditorMode mode);
+
        /* padding for future expansion */
-       gpointer reserved[20];
+       gpointer reserved[19];
 };
 
 /* Properties */
 
 ESpellChecker *        e_content_editor_ref_spell_checker
                                                (EContentEditor *editor);
+gboolean       e_content_editor_supports_mode  (EContentEditor *editor,
+                                                EContentEditorMode mode);
 gboolean       e_content_editor_is_malfunction (EContentEditor *editor);
 gboolean       e_content_editor_can_cut        (EContentEditor *editor);
 gboolean       e_content_editor_can_copy       (EContentEditor *editor);
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index 20061ca9a1..3ecab0e7ee 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -2303,6 +2303,7 @@ editor_actions_bind (EHTMLEditor *editor)
        GtkAction *action;
        GtkActionGroup *action_group;
        EContentEditor *cnt_editor;
+       guint ii;
 
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
@@ -2315,6 +2316,14 @@ editor_actions_bind (EHTMLEditor *editor)
                action, "current-value",
                G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
+       for (ii = 0; ii < G_N_ELEMENTS (core_mode_entries); ii++) {
+               action = gtk_action_group_get_action (action_group, core_mode_entries[ii].name);
+
+               gtk_action_set_visible (action, e_html_editor_has_editor_for_mode (editor, 
core_mode_entries[ii].value));
+       }
+
+       e_action_combo_box_update_model (E_ACTION_COMBO_BOX (editor->priv->mode_combo_box));
+
        /* Synchronize widget mode with the buttons */
        e_html_editor_set_mode (editor, E_CONTENT_EDITOR_MODE_HTML);
 
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index 50bbdc158d..fda8d02e51 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -36,6 +36,7 @@
 #include <e-util/e-html-editor-spell-check-dialog.h>
 #include <e-util/e-html-editor-table-dialog.h>
 #include <e-util/e-html-editor-text-dialog.h>
+#include <e-util/e-util-enumtypes.h>
 
 #ifdef HAVE_XFREE
 #include <X11/XF86keysym.h>
@@ -49,6 +50,8 @@ G_BEGIN_DECLS
 struct _EHTMLEditorPrivate {
        EContentEditorMode mode;
 
+       GtkWidget *content_editors_box;
+
        GtkUIManager *manager;
        GtkActionGroup *core_actions;
        GtkActionGroup *core_editor_actions;
@@ -90,7 +93,8 @@ struct _EHTMLEditorPrivate {
        GtkWidget *emoji_chooser;
 
        GHashTable *cid_parts; /* gchar *cid: URI ~> CamelMimePart * */
-       GHashTable *content_editors;
+       GHashTable *content_editors; /* gchar *name ~> EContentEditor * */
+       GHashTable *content_editors_for_mode; /* EContentEditorMode ~> EContentEditor *; pointers borrowed 
from content_editors */
        EContentEditor *use_content_editor;
 
        gchar *filename;
@@ -114,6 +118,9 @@ GtkWidget * e_html_editor_util_create_font_name_combo
                                                (void);
 gchar *                e_html_editor_util_dup_font_id  (GtkComboBox *combo_box,
                                                 const gchar *font_name);
+gboolean       e_html_editor_has_editor_for_mode
+                                               (EHTMLEditor *editor,
+                                                EContentEditorMode mode);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index dc16262cb6..47aa6de7f3 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -836,6 +836,18 @@ html_editor_constructed (GObject *object)
 
        e_extensible_load_extensions (E_EXTENSIBLE (object));
 
+       /* Construct the main editing area. */
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "visible", TRUE,
+               NULL);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
+       priv->content_editors_box = widget;
+
        editor_actions_init (editor);
        priv->editor_layout_row = 2;
 
@@ -876,31 +888,10 @@ html_editor_constructed (GObject *object)
        priv->alert_bar = g_object_ref (widget);
        /* EAlertBar controls its own visibility. */
 
-       /* Construct the main editing area. */
+       /* Have the default editor added (it's done inside the function) */
        widget = GTK_WIDGET (e_html_editor_get_content_editor (editor));
-
-       /* Pack editors which implement GtkScrollable in a scrolled window */
-       if (GTK_IS_SCROLLABLE (widget)) {
-               GtkWidget *scrolled_window;
-
-               scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
-                       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-               gtk_widget_show (scrolled_window);
-
-               gtk_grid_attach (GTK_GRID (editor), scrolled_window, 0, 4, 1, 1);
-
-               gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
-       } else {
-               gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
-       }
-
        gtk_widget_show (widget);
 
-       g_signal_connect (
-               widget, "context-menu-requested",
-               G_CALLBACK (html_editor_context_menu_requested_cb), editor);
-
        /* Add some combo boxes to the "edit" toolbar. */
 
        toolbar = GTK_TOOLBAR (priv->edit_toolbar);
@@ -1026,6 +1017,7 @@ html_editor_finalize (GObject *object)
 
        g_hash_table_destroy (editor->priv->cid_parts);
        g_hash_table_destroy (editor->priv->content_editors);
+       g_hash_table_destroy (editor->priv->content_editors_for_mode);
 
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_html_editor_parent_class)->finalize (object);
@@ -1147,6 +1139,7 @@ e_html_editor_init (EHTMLEditor *editor)
        priv->suggestion_actions = gtk_action_group_new ("suggestion");
        priv->cid_parts = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, 
g_object_unref);
        priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
+       priv->content_editors_for_mode = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
 
        filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
        if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
@@ -1253,6 +1246,23 @@ e_html_editor_new_finish (GAsyncResult *result,
        return e_simple_async_result_steal_user_data (eresult);
 }
 
+/**
+ * e_html_editor_get_content_box:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns: (transfer none): the content box, the content editors are
+ *    packed into.
+ *
+ * Since: 3.44
+ **/
+GtkWidget *
+e_html_editor_get_content_box (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       return editor->priv->content_editors_box;
+}
+
 static void
 e_html_editor_content_editor_notify_mode_cb (GObject *object,
                                             GParamSpec *param,
@@ -1272,55 +1282,175 @@ e_html_editor_content_editor_notify_mode_cb (GObject *object,
        }
 }
 
-/**
- * e_html_editor_get_content_editor:
- * @editor: an #EHTMLEditor
- *
- * Returns instance of #EContentEditor used in the @editor.
- */
-EContentEditor *
-e_html_editor_get_content_editor (EHTMLEditor *editor)
+static EContentEditor *
+e_html_editor_get_content_editor_for_mode (EHTMLEditor *editor,
+                                          EContentEditorMode mode)
 {
-       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+       EContentEditor *cnt_editor;
+       GSettings *settings;
+       const gchar *mode_name = NULL;
+       gchar *name;
 
-       if (!editor->priv->use_content_editor) {
-               GSettings *settings;
-               gchar *name;
+       if (!g_hash_table_size (editor->priv->content_editors))
+               return NULL;
+
+       cnt_editor = g_hash_table_lookup (editor->priv->content_editors_for_mode, GINT_TO_POINTER (mode));
+
+       if (cnt_editor)
+               return cnt_editor;
+
+       switch (mode) {
+       case E_CONTENT_EDITOR_MODE_PLAIN_TEXT:
+               mode_name = "plain";
+               break;
+       case E_CONTENT_EDITOR_MODE_HTML:
+               mode_name = "html";
+               break;
+       }
+
+       g_warn_if_fail (mode_name != NULL);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       name = g_settings_get_string (settings, "composer-editor");
+       g_clear_object (&settings);
+
+       if (name && *name && mode_name) {
+               gchar **split_names;
+               gint ii, mode_name_len = strlen (mode_name);
+
+               split_names = g_strsplit (name, ",", -1);
+
+               /* first round with the mode-specific overrides */
+               for (ii = 0; split_names && split_names[ii] && !cnt_editor; ii++) {
+                       const gchar *check_name = split_names[ii];
+
+                       if (g_ascii_strncasecmp (check_name, mode_name, mode_name_len) == 0 &&
+                           check_name[mode_name_len] == ':') {
+                               cnt_editor = g_hash_table_lookup (editor->priv->content_editors, check_name + 
mode_name_len + 1);
+
+                               if (cnt_editor && !e_content_editor_supports_mode (cnt_editor, mode))
+                                       cnt_editor = NULL;
+                       }
+               }
+
+               /* second round without the mode-specific overrides */
+               for (ii = 0; split_names && split_names[ii] && !cnt_editor; ii++) {
+                       const gchar *check_name = split_names[ii];
 
-               if (!g_hash_table_size (editor->priv->content_editors))
-                       return NULL;
+                       cnt_editor = g_hash_table_lookup (editor->priv->content_editors, check_name);
 
-               settings = e_util_ref_settings ("org.gnome.evolution.mail");
-               name = g_settings_get_string (settings, "composer-editor");
-               g_clear_object (&settings);
+                       if (cnt_editor && !e_content_editor_supports_mode (cnt_editor, mode))
+                               cnt_editor = NULL;
+               }
+
+               g_strfreev (split_names);
+       }
 
-               if (name)
-                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, name);
+       g_free (name);
 
-               g_free (name);
+       if (!cnt_editor)
+               cnt_editor = g_hash_table_lookup (editor->priv->content_editors, DEFAULT_CONTENT_EDITOR_NAME);
 
-               if (!editor->priv->use_content_editor)
-                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, DEFAULT_CONTENT_EDITOR_NAME);
+       if (!cnt_editor) {
+               GHashTableIter iter;
+               gpointer value;
+
+               g_hash_table_iter_init (&iter, editor->priv->content_editors);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       if (e_content_editor_supports_mode (value, editor->priv->mode)) {
+                               cnt_editor = value;
+                               break;
+                       }
+               }
+       }
 
-               if (!editor->priv->use_content_editor) {
-                       GHashTableIter iter;
-                       gpointer key, value;
+       if (cnt_editor) {
+               GHashTableIter iter;
+               gpointer value;
 
-                       g_hash_table_iter_init (&iter, editor->priv->content_editors);
-                       if (g_hash_table_iter_next (&iter, &key, &value)) {
-                               editor->priv->use_content_editor = value;
+               g_hash_table_iter_init (&iter, editor->priv->content_editors_for_mode);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       /* The editor can be used for multiple modes and it is already packed in the content 
box. */
+                       if (value == cnt_editor) {
+                               g_hash_table_insert (editor->priv->content_editors_for_mode, GINT_TO_POINTER 
(mode), cnt_editor);
+                               return cnt_editor;
                        }
                }
+       }
+
+       if (cnt_editor) {
+               e_content_editor_setup_editor (cnt_editor, editor);
+
+               g_signal_connect_swapped (cnt_editor, "ref-mime-part",
+                       G_CALLBACK (e_html_editor_ref_cid_part), editor);
+
+               e_signal_connect_notify (cnt_editor, "notify::mode",
+                       G_CALLBACK (e_html_editor_content_editor_notify_mode_cb), editor);
+
+               /* Pack editors which implement GtkScrollable in a scrolled window */
+               if (GTK_IS_SCROLLABLE (cnt_editor)) {
+                       GtkWidget *scrolled_window;
+
+                       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+                       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
-               if (editor->priv->use_content_editor) {
-                       e_content_editor_setup_editor (editor->priv->use_content_editor, editor);
+                       gtk_box_pack_start (GTK_BOX (editor->priv->content_editors_box), scrolled_window, 
TRUE, TRUE, 0);
 
-                       g_signal_connect_swapped (editor->priv->use_content_editor, "ref-mime-part",
-                               G_CALLBACK (e_html_editor_ref_cid_part), editor);
+                       gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (cnt_editor));
 
-                       e_signal_connect_notify (editor->priv->use_content_editor, "notify::mode",
-                               G_CALLBACK (e_html_editor_content_editor_notify_mode_cb), editor);
+                       e_binding_bind_property (cnt_editor, "visible",
+                               scrolled_window, "visible",
+                               G_BINDING_SYNC_CREATE);
+               } else {
+                       gtk_box_pack_start (GTK_BOX (editor->priv->content_editors_box), GTK_WIDGET 
(cnt_editor), TRUE, TRUE, 0);
                }
+
+               g_signal_connect (
+                       cnt_editor, "context-menu-requested",
+                       G_CALLBACK (html_editor_context_menu_requested_cb), editor);
+
+               g_hash_table_insert (editor->priv->content_editors_for_mode, GINT_TO_POINTER (mode), 
cnt_editor);
+       }
+
+       return cnt_editor;
+}
+
+gboolean
+e_html_editor_has_editor_for_mode (EHTMLEditor *editor,
+                                  EContentEditorMode mode)
+{
+       GHashTableIter iter;
+       gpointer value;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
+
+       g_hash_table_iter_init (&iter, editor->priv->content_editors);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               EContentEditor *cnt_editor = value;
+
+               if (e_content_editor_supports_mode (cnt_editor, mode))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+/**
+ * e_html_editor_get_content_editor:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns instance of #EContentEditor used in the @editor.
+ */
+EContentEditor *
+e_html_editor_get_content_editor (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       if (!editor->priv->use_content_editor) {
+               editor->priv->use_content_editor =
+                       e_html_editor_get_content_editor_for_mode (editor, editor->priv->mode);
        }
 
        return editor->priv->use_content_editor;
@@ -1399,21 +1529,35 @@ void
 e_html_editor_set_mode (EHTMLEditor *editor,
                        EContentEditorMode mode)
 {
-       EContentEditor *cnt_editor;
+       EContentEditor *cnt_editor, *old_cnt_editor;
 
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
        if (editor->priv->mode == mode)
                return;
 
-       editor->priv->mode = mode;
+       old_cnt_editor = editor->priv->use_content_editor;
+       editor->priv->use_content_editor = NULL;
 
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (cnt_editor)
+       if (cnt_editor) {
+               editor->priv->mode = mode;
+
                g_object_set (G_OBJECT (cnt_editor), "mode", mode, NULL);
 
-       g_object_notify (G_OBJECT (editor), "mode");
+               if (cnt_editor != old_cnt_editor) {
+                       if (old_cnt_editor)
+                               gtk_widget_hide (GTK_WIDGET (old_cnt_editor));
+
+                       if (cnt_editor)
+                               gtk_widget_show (GTK_WIDGET (cnt_editor));
+               }
+
+               g_object_notify (G_OBJECT (editor), "mode");
+       } else {
+               editor->priv->use_content_editor = old_cnt_editor;
+       }
 }
 
 /**
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index a2afa8c1d9..e79b1fb8e7 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -78,6 +78,7 @@ void          e_html_editor_new               (GAsyncReadyCallback callback,
                                                 gpointer user_data);
 GtkWidget *    e_html_editor_new_finish        (GAsyncResult *result,
                                                 GError **error);
+GtkWidget *    e_html_editor_get_content_box   (EHTMLEditor *editor);
 EContentEditor *
                e_html_editor_get_content_editor
                                                (EHTMLEditor *editor);
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 314a124e37..06563caab4 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -1135,6 +1135,16 @@ webkit_editor_show_inspector (EWebKitEditor *wk_editor)
        webkit_web_inspector_show (inspector);
 }
 
+static gboolean
+webkit_editor_supports_mode (EContentEditor *content_editor,
+                            EContentEditorMode mode)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (content_editor), FALSE);
+
+       return mode == E_CONTENT_EDITOR_MODE_PLAIN_TEXT ||
+               mode == E_CONTENT_EDITOR_MODE_HTML;
+}
+
 static void
 webkit_editor_initialize (EContentEditor *content_editor,
                           EContentEditorInitializedCallback callback,
@@ -5846,6 +5856,7 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
 static void
 e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
 {
+       iface->supports_mode = webkit_editor_supports_mode;
        iface->initialize = webkit_editor_initialize;
        iface->update_styles = webkit_editor_update_styles;
        iface->insert_content = webkit_editor_insert_content;


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