[evolution/449-support-markdown-in-composer: 10/15] EHTMLEditor: Make it possible to switch between different editors for different modes
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/449-support-markdown-in-composer: 10/15] EHTMLEditor: Make it possible to switch between different editors for different modes
- Date: Tue, 8 Feb 2022 09:51:57 +0000 (UTC)
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]