[gnome-terminal] all: Add support for OSC 8 hyperlinks (HTML-like anchors)
- From: Egmont Koblinger <egmontkob src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal] all: Add support for OSC 8 hyperlinks (HTML-like anchors)
- Date: Mon, 24 Apr 2017 23:54:30 +0000 (UTC)
commit 1c6f8db736efc62d9a9b38bfbc43ec03c8544696
Author: Egmont Koblinger <egmont gmail com>
Date: Tue Apr 25 01:50:08 2017 +0200
all: Add support for OSC 8 hyperlinks (HTML-like anchors)
The escape sequences
OSC 8 ; params ; URI BEL
OSC 8 ; params ; URI ST
turn subsequent characters into an HTML-like anchor to the given address.
Terminate the hyperlink with the same escape sequence with an empty URI.
Params is an optional colon-separated list of key=value assignments. Cells
having the same URI and "id" parameter, or cells lacking an "id" that were
printed in a single OSC 8 run are underlined together on mouseover.
For further details, see
https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda.
https://bugzilla.gnome.org/show_bug.cgi?id=779734
src/terminal-screen.c | 41 ++++++++++++++++++++++++--
src/terminal-screen.h | 1 +
src/terminal-util.c | 47 ++++++++++++++++++++++++++++++
src/terminal-util.h | 2 +
src/terminal-window.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++--
src/terminal.xml | 3 ++
6 files changed, 161 insertions(+), 8 deletions(-)
---
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index 9df4fc7..f6cc4a5 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -152,6 +152,8 @@ static void terminal_screen_icon_title_changed (VteTerminal *vte_terminal
static void update_color_scheme (TerminalScreen *screen);
+static char* terminal_screen_check_hyperlink (TerminalScreen *screen,
+ GdkEvent *event);
static void terminal_screen_check_extra (TerminalScreen *screen,
GdkEvent *event,
char **number_info);
@@ -358,6 +360,8 @@ terminal_screen_init (TerminalScreen *screen)
priv->child_pid = -1;
+ vte_terminal_set_allow_hyperlink (terminal, TRUE);
+
for (i = 0; i < n_url_regexes; ++i)
{
TagData *tag_data;
@@ -1509,6 +1513,7 @@ terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info)
g_object_unref (info->screen);
g_weak_ref_clear (&info->window_weak_ref);
+ g_free (info->hyperlink);
g_free (info->url);
g_free (info->number_info);
g_slice_free (TerminalScreenPopupInfo, info);
@@ -1547,6 +1552,7 @@ terminal_screen_popup_menu (GtkWidget *widget)
static void
terminal_screen_do_popup (TerminalScreen *screen,
GdkEventButton *event,
+ char *hyperlink,
char *url,
int url_flavor,
char *number_info)
@@ -1557,6 +1563,7 @@ terminal_screen_do_popup (TerminalScreen *screen,
info->button = event->button;
info->state = event->state & gtk_accelerator_get_default_mod_mask ();
info->timestamp = event->time;
+ info->hyperlink = hyperlink; /* adopted */
info->url = url; /* adopted */
info->url_flavor = url_flavor;
info->number_info = number_info; /* adopted */
@@ -1572,6 +1579,7 @@ terminal_screen_button_press (GtkWidget *widget,
TerminalScreen *screen = TERMINAL_SCREEN (widget);
gboolean (* button_press_event) (GtkWidget*, GdkEventButton*) =
GTK_WIDGET_CLASS (terminal_screen_parent_class)->button_press_event;
+ gs_free char *hyperlink = NULL;
gs_free char *url = NULL;
int url_flavor = 0;
gs_free char *number_info = NULL;
@@ -1579,9 +1587,25 @@ terminal_screen_button_press (GtkWidget *widget,
state = event->state & gtk_accelerator_get_default_mod_mask ();
+ hyperlink = terminal_screen_check_hyperlink (screen, (GdkEvent*)event);
url = terminal_screen_check_match (screen, (GdkEvent*)event, &url_flavor);
terminal_screen_check_extra (screen, (GdkEvent*)event, &number_info);
+ if (hyperlink != NULL &&
+ (event->button == 1 || event->button == 2) &&
+ (state & GDK_CONTROL_MASK))
+ {
+ gboolean handled = FALSE;
+
+ g_signal_emit (screen, signals[MATCH_CLICKED], 0,
+ hyperlink,
+ FLAVOR_AS_IS,
+ state,
+ &handled);
+ if (handled)
+ return TRUE; /* don't do anything else such as select with the click */
+ }
+
if (url != NULL &&
(event->button == 1 || event->button == 2) &&
(state & GDK_CONTROL_MASK))
@@ -1606,16 +1630,18 @@ terminal_screen_button_press (GtkWidget *widget,
if (button_press_event && button_press_event (widget, event))
return TRUE;
- terminal_screen_do_popup (screen, event, url, url_flavor, number_info);
- url = NULL; /* adopted to the popup info */
+ terminal_screen_do_popup (screen, event, hyperlink, url, url_flavor, number_info);
+ hyperlink = NULL; /* adopted to the popup info */
+ url = NULL; /* ditto */
number_info = NULL; /* ditto */
return TRUE;
}
else if (!(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
{
/* do popup on shift+right-click */
- terminal_screen_do_popup (screen, event, url, url_flavor, number_info);
- url = NULL; /* adopted to the popup info */
+ terminal_screen_do_popup (screen, event, hyperlink, url, url_flavor, number_info);
+ hyperlink = NULL; /* adopted to the popup info */
+ url = NULL; /* ditto */
number_info = NULL; /* ditto */
return TRUE;
}
@@ -1954,6 +1980,13 @@ terminal_screen_get_cell_size (TerminalScreen *screen,
}
static char*
+terminal_screen_check_hyperlink (TerminalScreen *screen,
+ GdkEvent *event)
+{
+ return vte_terminal_hyperlink_check_event (VTE_TERMINAL (screen), event);
+}
+
+static char*
terminal_screen_check_match (TerminalScreen *screen,
GdkEvent *event,
int *flavor)
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index f3b4013..2e8f6dc 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -142,6 +142,7 @@ struct _TerminalScreenPopupInfo {
TerminalScreen *screen;
char *url;
TerminalURLFlavor url_flavor;
+ char *hyperlink;
char *number_info;
guint button;
guint state;
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 9e63070..b8676a2 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -20,6 +20,7 @@
*/
#include "config.h"
+#define _GNU_SOURCE /* for strchrnul */
#include <string.h>
#include <stdlib.h>
@@ -1140,3 +1141,49 @@ terminal_util_uri_is_allowed (const char *uri,
}
return TRUE;
}
+
+/**
+ * terminal_util_hyperlink_uri_label:
+ * @uri: a URI
+ *
+ * Formats @uri to be displayed in a tooltip.
+ * Performs URI-decoding and converts IDN hostname to UTF-8.
+ *
+ * Returns: (transfer full): The human readable URI as plain text
+ */
+char *terminal_util_hyperlink_uri_label (const char *uri)
+{
+ char *unesc = NULL;
+ gboolean replace_hostname;
+
+ if (uri == NULL)
+ return NULL;
+
+ unesc = g_uri_unescape_string(uri, NULL);
+ if (unesc == NULL)
+ unesc = g_strdup(uri);
+
+ if (g_ascii_strncasecmp(unesc, "ftp://", 6) == 0 ||
+ g_ascii_strncasecmp(unesc, "http://", 7) == 0 ||
+ g_ascii_strncasecmp(unesc, "https://", 8) == 0) {
+ gs_free char *unidn = NULL;
+ char *hostname = strchr(unesc, '/') + 2;
+ char *hostname_end = strchrnul(hostname, '/');
+ char save = *hostname_end;
+ *hostname_end = '\0';
+ unidn = g_hostname_to_unicode(hostname);
+ replace_hostname = unidn != NULL && g_ascii_strcasecmp(unidn, hostname) != 0;
+ *hostname_end = save;
+ if (replace_hostname) {
+ char *new_unesc = g_strdup_printf("%.*s%s%s",
+ (int) (hostname - unesc),
+ unesc,
+ unidn,
+ hostname_end);
+ g_free(unesc);
+ unesc = new_unesc;
+ }
+ }
+
+ return unesc;
+}
diff --git a/src/terminal-util.h b/src/terminal-util.h
index 581a8f7..278096a 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -98,6 +98,8 @@ char *terminal_util_number_info (const char *str);
gboolean terminal_util_uri_is_allowed (const char *uri,
GError **error);
+char *terminal_util_hyperlink_uri_label (const char *str);
+
G_END_DECLS
#endif /* TERMINAL_UTIL_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index 2921c02..abd73d7 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -1951,6 +1951,38 @@ handle_tab_droped_on_desktop (GtkNotebook *source_notebook,
/* Terminal screen popup menu handling */
static void
+popup_open_hyperlink_callback (GtkAction *action,
+ TerminalWindow *window)
+{
+ TerminalWindowPrivate *priv = window->priv;
+ TerminalScreenPopupInfo *info = priv->popup_info;
+
+ if (info == NULL)
+ return;
+
+ terminal_util_open_url (GTK_WIDGET (window), info->hyperlink, FLAVOR_AS_IS,
+ gtk_get_current_event_time ());
+}
+
+static void
+popup_copy_hyperlink_callback (GtkAction *action,
+ TerminalWindow *window)
+{
+ TerminalWindowPrivate *priv = window->priv;
+ TerminalScreenPopupInfo *info = priv->popup_info;
+ GtkClipboard *clipboard;
+
+ if (info == NULL)
+ return;
+
+ if (info->hyperlink == NULL)
+ return;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, info->hyperlink, -1);
+}
+
+static void
popup_open_url_callback (GtkAction *action,
TerminalWindow *window)
{
@@ -2054,7 +2086,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
TerminalScreen *screen = info->screen;
GtkWidget *popup_menu;
GtkAction *action;
- gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_number_info;
+ gboolean can_paste, can_paste_uris, show_hyperlink, show_link, show_email_link, show_call_link,
show_number_info;
window = terminal_screen_popup_info_ref_window (info);
if (window == NULL ||
@@ -2072,11 +2104,17 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
can_paste = targets != NULL && gtk_targets_include_text (targets, n_targets);
can_paste_uris = targets != NULL && gtk_targets_include_uri (targets, n_targets);
- show_link = info->url != NULL && (info->url_flavor == FLAVOR_AS_IS || info->url_flavor ==
FLAVOR_DEFAULT_TO_HTTP);
- show_email_link = info->url != NULL && info->url_flavor == FLAVOR_EMAIL;
- show_call_link = info->url != NULL && info->url_flavor == FLAVOR_VOIP_CALL;
+ show_hyperlink = info->hyperlink != NULL;
+ show_link = !show_hyperlink && info->url != NULL && (info->url_flavor == FLAVOR_AS_IS || info->url_flavor
== FLAVOR_DEFAULT_TO_HTTP);
+ show_email_link = !show_hyperlink && info->url != NULL && info->url_flavor == FLAVOR_EMAIL;
+ show_call_link = !show_hyperlink && info->url != NULL && info->url_flavor == FLAVOR_VOIP_CALL;
show_number_info = info->number_info != NULL;
+ action = gtk_action_group_get_action (priv->action_group, "PopupOpenHyperlink");
+ gtk_action_set_visible (action, show_hyperlink);
+ action = gtk_action_group_get_action (priv->action_group, "PopupCopyHyperlinkAddress");
+ gtk_action_set_visible (action, show_hyperlink);
+
action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail");
gtk_action_set_visible (action, show_email_link);
action = gtk_action_group_get_action (priv->action_group, "PopupCopyEmailAddress");
@@ -2089,6 +2127,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
gtk_action_set_visible (action, show_link);
action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress");
gtk_action_set_visible (action, show_link);
+
action = gtk_action_group_get_action (priv->action_group, "PopupNumberInfo");
gtk_action_set_label (action, info->number_info);
gtk_action_set_sensitive (action, FALSE);
@@ -2562,6 +2601,12 @@ terminal_window_init (TerminalWindow *window)
G_CALLBACK (help_inspector_callback) },
/* Popup menu */
+ { "PopupOpenHyperlink", NULL, N_("_Open Hyperlink"), NULL,
+ NULL,
+ G_CALLBACK (popup_open_hyperlink_callback) },
+ { "PopupCopyHyperlinkAddress", NULL, N_("_Copy Hyperlink Address"), NULL,
+ NULL,
+ G_CALLBACK (popup_copy_hyperlink_callback) },
{ "PopupSendEmail", NULL, N_("_Send Mail To…"), NULL,
NULL,
G_CALLBACK (popup_open_url_callback) },
@@ -3082,6 +3127,22 @@ screen_font_any_changed_cb (TerminalScreen *screen,
terminal_window_update_size (window);
}
+static void
+screen_hyperlink_hover_uri_changed (TerminalScreen *screen,
+ const char *uri,
+ const GdkRectangle *bbox G_GNUC_UNUSED,
+ TerminalWindow *window G_GNUC_UNUSED)
+{
+ gs_free char *label = NULL;
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (screen)))
+ return;
+
+ label = terminal_util_hyperlink_uri_label (uri);
+
+ gtk_widget_set_tooltip_text (GTK_WIDGET (screen), label);
+}
+
/* MDI container callbacks */
static void
@@ -3444,6 +3505,8 @@ mdi_screen_added_cb (TerminalMdiContainer *container,
G_CALLBACK (screen_font_any_changed_cb), window);
g_signal_connect (screen, "selection-changed",
G_CALLBACK (terminal_window_update_copy_sensitivity), window);
+ g_signal_connect (screen, "hyperlink-hover-uri-changed",
+ G_CALLBACK (screen_hyperlink_hover_uri_changed), window);
g_signal_connect (screen, "show-popup-menu",
G_CALLBACK (screen_show_popup_menu_callback), window);
@@ -3527,6 +3590,10 @@ mdi_screen_removed_cb (TerminalMdiContainer *container,
G_CALLBACK (terminal_window_update_copy_sensitivity),
window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
+ G_CALLBACK (screen_hyperlink_hover_uri_changed),
+ window);
+
g_signal_handlers_disconnect_by_func (screen,
G_CALLBACK (screen_show_popup_menu_callback),
window);
diff --git a/src/terminal.xml b/src/terminal.xml
index d830149..e72e595 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -78,6 +78,9 @@
</menubar>
<popup name="Popup" action="Popup">
+ <menuitem action="PopupOpenHyperlink" />
+ <menuitem action="PopupCopyHyperlinkAddress" />
+ <separator />
<menuitem action="PopupSendEmail" />
<menuitem action="PopupCopyEmailAddress" />
<menuitem action="PopupCall" />
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]