[evolution] I#1923 - Composer: Add "Copy/Open Link" into the context menu



commit 1ee1bbe2a496524f96bb91da46cb0bbf792cbad7
Author: Milan Crha <mcrha redhat com>
Date:   Mon Jun 13 14:10:32 2022 +0200

    I#1923 - Composer: Add "Copy/Open Link" into the context menu
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1923

 data/webkit/e-editor.js                     | 11 ++++--
 po/POTFILES.in                              |  1 +
 src/e-util/e-content-editor.c               | 15 ++++++++
 src/e-util/e-content-editor.h               |  5 ++-
 src/e-util/e-html-editor-actions.c          | 49 +++++++++++++++++++++++-
 src/e-util/e-html-editor-actions.h          |  4 ++
 src/e-util/e-html-editor-manager.ui         |  2 +
 src/e-util/e-html-editor-private.h          |  2 +
 src/e-util/e-html-editor.c                  | 26 ++++++++++---
 src/e-util/e-html-editor.h                  |  3 +-
 src/modules/webkit-editor/e-webkit-editor.c | 59 ++++++++++++++++++++++++++---
 11 files changed, 159 insertions(+), 18 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 80f950865b..48b3c7565c 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -6434,12 +6434,16 @@ EvoEditor.onContextMenu = function(event)
 
        EvoEditor.contextMenuNode = node;
 
-       var nodeFlags = EvoEditor.E_CONTENT_EDITOR_NODE_UNKNOWN, res;
+       var nodeFlags = EvoEditor.E_CONTENT_EDITOR_NODE_UNKNOWN, res, anchorHref = "";
 
        while (node && node.tagName != "BODY") {
-               if (node.tagName == "A")
+               if (node.tagName == "A") {
                        nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_ANCHOR;
-               else if (node.tagName == "HR")
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT)
+                               anchorHref = node.innerText;
+                       else
+                               anchorHref = node.href;
+               } else if (node.tagName == "HR")
                        nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_H_RULE;
                else if (node.tagName == "IMG")
                        nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_IMAGE;
@@ -6461,6 +6465,7 @@ EvoEditor.onContextMenu = function(event)
 
        res["nodeFlags"] = nodeFlags;
        res["caretWord"] = EvoEditor.GetCaretWord();
+       res["anchorHref"] = anchorHref;
 
        window.webkit.messageHandlers.contextMenuRequested.postMessage(res);
 }
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c3b5e1b29a..eed67030f2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -494,6 +494,7 @@ src/modules/text-highlight/languages.c
 src/modules/vcard-inline/e-mail-formatter-vcard.c
 src/modules/vcard-inline/e-mail-parser-vcard.c
 src/modules/vcard-inline/e-mail-part-vcard.c
+src/modules/webkit-editor/e-webkit-editor.c
 src/modules/webkit-inspector/evolution-webkit-inspector.c
 src/plugins/attachment-reminder/attachment-reminder.c
 src/plugins/attachment-reminder/org-gnome-attachment-reminder.error.xml
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index f8b1722639..8d2aeca84e 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -3855,6 +3855,21 @@ e_content_editor_spell_check_prev_word (EContentEditor *editor,
        return iface->spell_check_prev_word (editor, word);
 }
 
+const gchar *
+e_content_editor_get_hover_uri (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       if (!iface->get_hover_uri)
+               return NULL;
+
+       return iface->get_hover_uri (editor);
+}
+
 void
 e_content_editor_emit_load_finished (EContentEditor *editor)
 {
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index 8f376e6b2b..0ae7ea1665 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -430,9 +430,10 @@ struct _EContentEditorInterface {
                                                         EContentEditorMode mode);
        void            (*grab_focus)                   (EContentEditor *editor);
        gboolean        (*is_focus)                     (EContentEditor *editor);
+       const gchar *   (*get_hover_uri)                (EContentEditor *editor);
 
        /* padding for future expansion */
-       gpointer reserved[17];
+       gpointer reserved[16];
 };
 
 /* Properties */
@@ -991,6 +992,8 @@ void                e_content_editor_spell_check_replace_all
 void           e_content_editor_delete_h_rule  (EContentEditor *editor);
 void           e_content_editor_delete_image   (EContentEditor *editor);
 
+const gchar *  e_content_editor_get_hover_uri  (EContentEditor *editor);
+
 /* Signal helpers */
 
 void           e_content_editor_emit_load_finished
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index aa6f664597..f56e560e32 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -140,9 +140,42 @@ insert_text_file_ready_cb (GFile *file,
  * Action Callbacks
  *****************************************************************************/
 
+static void
+action_copy_link_cb (GtkAction *action,
+                    EHTMLEditor *editor)
+{
+       GtkClipboard *clipboard;
+
+       if (!editor->priv->context_hover_uri)
+               return;
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+       gtk_clipboard_set_text (clipboard, editor->priv->context_hover_uri, -1);
+       gtk_clipboard_store (clipboard);
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       gtk_clipboard_set_text (clipboard, editor->priv->context_hover_uri, -1);
+       gtk_clipboard_store (clipboard);
+}
+
+static void
+action_open_link_cb (GtkAction *action,
+                    EHTMLEditor *editor)
+{
+       gpointer parent;
+
+       if (!editor->priv->context_hover_uri)
+               return;
+
+       parent = gtk_widget_get_toplevel (GTK_WIDGET (editor));
+       parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+       e_show_uri (parent, editor->priv->context_hover_uri);
+}
+
 static void
 action_context_delete_cell_contents_cb (GtkAction *action,
-                               EHTMLEditor *editor)
+                                       EHTMLEditor *editor)
 {
        EContentEditor *cnt_editor;
 
@@ -1667,6 +1700,20 @@ static GtkRadioActionEntry html_size_entries[] = {
 
 static GtkActionEntry context_entries[] = {
 
+       { "context-copy-link",
+         "edit-copy",
+         N_("Copy _Link Location"),
+         NULL,
+         N_("Copy the link to the clipboard"),
+         G_CALLBACK (action_copy_link_cb) },
+
+       { "context-open-link",
+         "emblem-web",
+         N_("_Open Link in Browser"),
+         NULL,
+         N_("Open the link in a web browser"),
+         G_CALLBACK (action_open_link_cb) },
+
        { "context-delete-cell",
          NULL,
          N_("Cell Contents"),
diff --git a/src/e-util/e-html-editor-actions.h b/src/e-util/e-html-editor-actions.h
index f66eb584aa..0adde4c3d9 100644
--- a/src/e-util/e-html-editor-actions.h
+++ b/src/e-util/e-html-editor-actions.h
@@ -29,6 +29,8 @@
 
 #define E_HTML_EDITOR_ACTION_BOLD(editor) \
        E_HTML_EDITOR_ACTION ((editor), "bold")
+#define E_HTML_EDITOR_ACTION_CONTEXT_COPY_LINK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-copy-link")
 #define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_CELL(editor) \
        E_HTML_EDITOR_ACTION ((editor), "context-delete-cell")
 #define E_HTML_EDITOR_ACTION_CONTEXT_DELETE_COLUMN(editor) \
@@ -51,6 +53,8 @@
        E_HTML_EDITOR_ACTION ((editor), "context-insert-row-above")
 #define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \
        E_HTML_EDITOR_ACTION ((editor), "context-insert-row-below")
+#define E_HTML_EDITOR_ACTION_CONTEXT_OPEN_LINK(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "context-open-link")
 #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \
        E_HTML_EDITOR_ACTION ((editor), "context-properties-cell")
 #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \
diff --git a/src/e-util/e-html-editor-manager.ui b/src/e-util/e-html-editor-manager.ui
index 885108bde2..4592ea63a8 100644
--- a/src/e-util/e-html-editor-manager.ui
+++ b/src/e-util/e-html-editor-manager.ui
@@ -158,6 +158,8 @@
     <menuitem action='paste-quote'/>
     <menuitem action='paste-as-text'/>
     <separator/>
+    <menuitem action='context-copy-link'/>
+    <menuitem action='context-open-link'/>
     <menuitem action='context-insert-link'/>
     <menuitem action='context-remove-link'/>
     <separator/>
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index 909a603011..aeb6208b46 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -111,6 +111,8 @@ struct _EHTMLEditorPrivate {
        gint editor_layout_row;
 
        gboolean paste_plain_prefer_pre;
+
+       gchar *context_hover_uri;
 };
 
 void           e_html_editor_actions_init      (EHTMLEditor *editor);
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index 6e26ef4166..23f8b18ef2 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -457,7 +457,8 @@ action_set_visible_and_sensitive (GtkAction *action,
 static void
 html_editor_update_actions (EHTMLEditor *editor,
                            EContentEditorNodeFlags flags,
-                           const gchar *caret_word)
+                           const gchar *caret_word,
+                           const gchar *hover_uri)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -472,17 +473,23 @@ html_editor_update_actions (EHTMLEditor *editor,
        cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (camel_debug ("webkit:editor"))
-               printf ("%s: flags:%d(%x)\n", G_STRFUNC, flags, flags);
+               printf ("%s: flags:%d(%x) caret-word:'%s' hover_uri:'%s'\n", G_STRFUNC, flags, flags, 
caret_word, hover_uri);
+
+       g_clear_pointer (&editor->priv->context_hover_uri, g_free);
+       editor->priv->context_hover_uri = hover_uri && *hover_uri ? g_strdup (hover_uri) : NULL;
 
        visible = (flags & E_CONTENT_EDITOR_NODE_IS_IMAGE);
        action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
        action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_IMAGE), visible);
 
        visible = (flags & E_CONTENT_EDITOR_NODE_IS_ANCHOR);
-       if (visible)
-               action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_LINK), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_LINK), !visible);
        action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_LINK), visible);
 
+       visible = hover_uri && *hover_uri;
+       action_set_visible_and_sensitive (ACTION (CONTEXT_COPY_LINK), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_OPEN_LINK), visible);
+
        visible = (flags & E_CONTENT_EDITOR_NODE_IS_H_RULE);
        action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_RULE), visible);
        action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_HRULE), visible);
@@ -619,6 +626,7 @@ typedef struct _ContextMenuData {
        GWeakRef *editor_weakref; /* EHTMLEditor * */
        EContentEditorNodeFlags flags;
        gchar *caret_word;
+       gchar *hover_uri;
        GdkEvent *event;
 } ContextMenuData;
 
@@ -631,6 +639,7 @@ context_menu_data_free (gpointer ptr)
                g_clear_pointer (&cmd->event, gdk_event_free);
                e_weak_ref_free (cmd->editor_weakref);
                g_free (cmd->caret_word);
+               g_free (cmd->hover_uri);
                g_slice_free (ContextMenuData, cmd);
        }
 }
@@ -659,7 +668,7 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
 
                menu = e_html_editor_get_managed_widget (editor, "/context-menu");
 
-               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags, cmd->caret_word);
+               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags, cmd->caret_word, 
cmd->hover_uri);
 
                if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
                        gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
@@ -692,6 +701,7 @@ html_editor_context_menu_requested_cb (EContentEditor *cnt_editor,
        cmd->editor_weakref = e_weak_ref_new (editor);
        cmd->flags = flags;
        cmd->caret_word = g_strdup (caret_word);
+       cmd->hover_uri = g_strdup (e_content_editor_get_hover_uri (cnt_editor));
        cmd->event = gdk_event_copy (event);
 
        g_idle_add_full (G_PRIORITY_LOW, html_editor_show_context_menu_idle_cb,
@@ -1074,6 +1084,9 @@ html_editor_dispose (GObject *object)
 
        g_clear_object (&priv->mode_change_content_cancellable);
 
+       g_clear_pointer (&priv->filename, g_free);
+       g_clear_pointer (&priv->context_hover_uri, g_free);
+
        /* Do not unbind/disconnect signal handlers here, just free/unset them */
        g_slist_free_full (priv->content_editor_bindings, g_object_unref);
        priv->content_editor_bindings = NULL;
@@ -1171,8 +1184,9 @@ e_html_editor_class_init (EHTMLEditorClass *class)
                G_STRUCT_OFFSET (EHTMLEditorClass, update_actions),
                NULL, NULL,
                NULL,
-               G_TYPE_NONE, 2,
+               G_TYPE_NONE, 3,
                G_TYPE_UINT,
+               G_TYPE_STRING,
                G_TYPE_STRING);
 
        signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index b30250bdf3..1e65949b52 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -69,7 +69,8 @@ struct _EHTMLEditorClass {
 
        void            (*update_actions)       (EHTMLEditor *editor,
                                                 EContentEditorNodeFlags flags,
-                                                const gchar *caret_word);
+                                                const gchar *caret_word,
+                                                const gchar *hover_uri);
 
        void            (*spell_languages_changed)
                                                (EHTMLEditor *editor);
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 8b0b30717f..48db63df07 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -16,6 +16,8 @@
 
 #include "evolution-config.h"
 
+#include <glib/gi18n-lib.h>
+
 #include "e-webkit-editor.h"
 
 #include "e-util/e-util.h"
@@ -734,9 +736,11 @@ context_menu_requested_cb (WebKitUserContentManager *manager,
        g_return_if_fail (jsc_value_is_object (jsc_params));
 
        g_clear_pointer (&wk_editor->priv->context_menu_caret_word, g_free);
+       g_clear_pointer (&wk_editor->priv->last_hover_uri, g_free);
 
        wk_editor->priv->context_menu_node_flags = e_web_view_jsc_get_object_property_int32 (jsc_params, 
"nodeFlags", 0);
        wk_editor->priv->context_menu_caret_word = e_web_view_jsc_get_object_property_string (jsc_params, 
"caretWord", NULL);
+       wk_editor->priv->last_hover_uri = e_web_view_jsc_get_object_property_string (jsc_params, 
"anchorHref", NULL);
 }
 
 static gboolean
@@ -4432,8 +4436,7 @@ webkit_editor_finalize (GObject *object)
        g_clear_pointer (&priv->body_link_color, gdk_rgba_free);
        g_clear_pointer (&priv->body_vlink_color, gdk_rgba_free);
 
-       g_free (priv->last_hover_uri);
-       priv->last_hover_uri = NULL;
+       g_clear_pointer (&priv->last_hover_uri, g_free);
 
        g_clear_object (&priv->spell_checker);
        g_clear_object (&priv->cancellable);
@@ -5178,11 +5181,14 @@ webkit_editor_mouse_target_changed_cb (EWebKitEditor *wk_editor,
 {
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       g_free (wk_editor->priv->last_hover_uri);
-       wk_editor->priv->last_hover_uri = NULL;
+       g_clear_pointer (&wk_editor->priv->last_hover_uri, g_free);
 
-       if (webkit_hit_test_result_context_is_link (hit_test_result))
-               wk_editor->priv->last_hover_uri = g_strdup (webkit_hit_test_result_get_link_uri 
(hit_test_result));
+       if (webkit_hit_test_result_context_is_link (hit_test_result)) {
+               if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML)
+                       wk_editor->priv->last_hover_uri = g_strdup (webkit_hit_test_result_get_link_uri 
(hit_test_result));
+               else
+                       wk_editor->priv->last_hover_uri = g_strdup (webkit_hit_test_result_get_link_label 
(hit_test_result));
+       }
 }
 
 static gboolean
@@ -5457,6 +5463,40 @@ e_webkit_editor_cid_resolver_ref_part (ECidResolver *resolver,
        return e_content_editor_emit_ref_mime_part (E_CONTENT_EDITOR (resolver), cid_uri);
 }
 
+static const gchar *
+webkit_editor_get_hover_uri (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
+
+       return wk_editor->priv->last_hover_uri;
+}
+
+static gboolean
+webkit_editor_query_tooltip_cb (GtkWidget *widget,
+                               gint xx,
+                               gint yy,
+                               gboolean keyboard_mode,
+                               GtkTooltip *tooltip,
+                               gpointer user_data)
+{
+       EWebKitEditor *wk_editor;
+       gchar *str;
+
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (widget), FALSE);
+
+       wk_editor = E_WEBKIT_EDITOR (widget);
+
+       if (!wk_editor->priv->last_hover_uri || !*wk_editor->priv->last_hover_uri)
+               return FALSE;
+
+       /* Translators: The "%s" is replaced with a link, constructing a text like: "Ctrl-click to open a 
link “http://www.example.com”"; */
+       str = g_strdup_printf (_("Ctrl-click to open a link “%s”"), wk_editor->priv->last_hover_uri);
+       gtk_tooltip_set_text (tooltip, str);
+       g_free (str);
+
+       return TRUE;
+}
+
 static gboolean
 webkit_editor_button_press_event (GtkWidget *widget,
                                   GdkEventButton *event)
@@ -5799,6 +5839,12 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
                wk_editor, "state-flags-changed",
                G_CALLBACK (webkit_editor_style_updated_cb), NULL);
 
+       gtk_widget_set_has_tooltip (GTK_WIDGET (wk_editor), TRUE);
+
+       g_signal_connect (
+               wk_editor, "query-tooltip",
+               G_CALLBACK (webkit_editor_query_tooltip_cb), NULL);
+
        g_settings = e_util_ref_settings ("org.gnome.desktop.interface");
        g_signal_connect (
                g_settings, "changed::font-name",
@@ -5965,6 +6011,7 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        iface->spell_check_prev_word = webkit_editor_spell_check_prev_word;
        iface->delete_h_rule = webkit_editor_delete_h_rule;
        iface->delete_image = webkit_editor_delete_image;
+       iface->get_hover_uri = webkit_editor_get_hover_uri;
 }
 
 static void


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