summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Stark <[email protected]>2023-01-31 12:37:30 +0300
committerraveit65 <[email protected]>2023-08-31 14:25:41 +0200
commit649935a610d4f97ad99f98fed2facffdd5be5265 (patch)
tree195fdd0ec69ba6cfa30b714e8be3c81cdd3b4959
parenta060d180f0e5e44ab0d0972c42dc4e392f458892 (diff)
downloadmate-terminal-649935a610d4f97ad99f98fed2facffdd5be5265.tar.bz2
mate-terminal-649935a610d4f97ad99f98fed2facffdd5be5265.tar.xz
Add support for OSC 8 hyperlinks (HTML-like anchors)
backport of 1c6f8db736efc62d9a9b38bfbc43ec03c8544696 from gnome-terminal
-rw-r--r--configure.ac2
-rw-r--r--src/terminal-screen.c50
-rw-r--r--src/terminal-screen.h1
-rw-r--r--src/terminal-util.c50
-rw-r--r--src/terminal-util.h2
-rw-r--r--src/terminal-window.c78
-rw-r--r--src/terminal.xml3
7 files changed, 173 insertions, 13 deletions
diff --git a/configure.ac b/configure.ac
index 8c489b4..f048c6d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ AM_PROG_LIBTOOL
GLIB_REQUIRED=2.50.0
GIO_REQUIRED=2.50.0
GTK_REQUIRED=3.22.0
-VTE_REQUIRED=0.48
+VTE_REQUIRED=0.49.1
DCONF_REQUIRED=0.13.4
PKG_CHECK_MODULES([TERM],
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index defe083..7282f6d 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -137,6 +137,9 @@ 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 gboolean terminal_screen_format_title (TerminalScreen *screen, const char *raw_title, char **old_cooked_title);
static void terminal_screen_cook_title (TerminalScreen *screen);
@@ -353,6 +356,8 @@ terminal_screen_init (TerminalScreen *screen)
vte_terminal_set_bold_is_bright (VTE_TERMINAL (screen), TRUE);
#endif
+ vte_terminal_set_allow_hyperlink (VTE_TERMINAL (screen), TRUE);
+
priv->child_pid = -1;
priv->font_scale = PANGO_SCALE_MEDIUM;
@@ -1680,6 +1685,7 @@ terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info)
return;
g_object_unref (info->screen);
+ g_free (info->hyperlink);
g_free (info->url);
g_slice_free (TerminalScreenPopupInfo, info);
}
@@ -1707,24 +1713,39 @@ 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;
+ char *hyperlink;
char *url;
- int url_flavor = 0;
+ int url_flavor = FLAVOR_AS_IS;
guint state;
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);
- if (url != NULL &&
- (event->button == 1 || event->button == 2) &&
- (state & GDK_CONTROL_MASK))
+ // left or middle button with Ctrl
+ if ((event->button == 1 || event->button == 2) &&
+ (state & GDK_CONTROL_MASK))
{
gboolean handled = FALSE;
+ if (hyperlink != NULL)
+ g_signal_emit (screen, signals[MATCH_CLICKED], 0,
+ hyperlink,
+ FLAVOR_AS_IS,
+ state,
+ &handled);
+
+ if (handled) {
+ g_free (url);
+ g_free (hyperlink);
+ return TRUE; /* don't do anything else such as select with the click */
+ }
+
#ifdef ENABLE_SKEY
if (url_flavor != FLAVOR_SKEY ||
terminal_profile_get_property_boolean (screen->priv->profile, TERMINAL_PROFILE_USE_SKEY))
#endif
+ if (url != NULL)
{
g_signal_emit (screen, signals[MATCH_CLICKED], 0,
url,
@@ -1733,12 +1754,14 @@ terminal_screen_button_press (GtkWidget *widget,
&handled);
}
- g_free (url);
-
- if (handled)
+ if (handled) {
+ g_free (url);
+ g_free (hyperlink);
return TRUE; /* don't do anything else such as select with the click */
+ }
}
+ // right button with no Ctrl, Alt or Shift
if (event->button == 3 &&
(state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0)
{
@@ -1749,6 +1772,7 @@ terminal_screen_button_press (GtkWidget *widget,
info->state = state;
info->timestamp = event->time;
info->url = url; /* adopted */
+ info->hyperlink = hyperlink; /* adopted */
info->flavor = url_flavor;
g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info);
@@ -1757,6 +1781,9 @@ terminal_screen_button_press (GtkWidget *widget,
return TRUE;
}
+ g_free (url);
+ g_free (hyperlink);
+
/* default behavior is to let the terminal widget deal with it */
if (button_press_event)
return button_press_event (widget, event);
@@ -2381,6 +2408,13 @@ terminal_screen_url_match_remove (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 c5cdb5d..ba1c218 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -148,6 +148,7 @@ struct _TerminalScreenPopupInfo
TerminalWindow *window;
TerminalScreen *screen;
char *url;
+ char *hyperlink;
TerminalURLFlavor flavor;
guint button;
guint state;
diff --git a/src/terminal-util.c b/src/terminal-util.c
index ede72da..d0f1864 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -22,6 +22,7 @@
#include <config.h>
+#define _GNU_SOURCE /* for strchrnul */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@@ -707,6 +708,55 @@ terminal_util_add_proxy_env (GHashTable *env_table)
g_object_unref (settings_socks);
}
+/**
+ * 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)
+ {
+ 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;
+ }
+ g_free(unidn);
+ }
+ return unesc;
+}
+
/* Bidirectional object/widget binding */
typedef struct
diff --git a/src/terminal-util.h b/src/terminal-util.h
index 190672f..f1ccd43 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -104,6 +104,8 @@ void terminal_util_bind_object_property_to_widget (GObject *object,
void terminal_util_x11_clear_demands_attention (GdkWindow *window);
+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 d23fd87..6517d8d 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -1366,6 +1366,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)
{
@@ -1476,7 +1508,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_input_method_menu;
+ gboolean can_paste, can_paste_uris, show_hyperlink, show_link, show_email_link, show_call_link, show_input_method_menu;
int n_pages;
if (!gtk_widget_get_realized (GTK_WIDGET (screen)))
@@ -1494,11 +1526,16 @@ 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->flavor == FLAVOR_AS_IS || info->flavor == FLAVOR_DEFAULT_TO_HTTP);
- show_email_link = info->url != NULL && info->flavor == FLAVOR_EMAIL;
- show_call_link = info->url != NULL && info->flavor == FLAVOR_VOIP_CALL;
+ show_hyperlink = info->hyperlink != NULL;
+ show_link = !show_hyperlink && info->url != NULL && (info->flavor == FLAVOR_AS_IS || info->flavor == FLAVOR_DEFAULT_TO_HTTP);
+ show_email_link = !show_hyperlink && info->url != NULL && info->flavor == FLAVOR_EMAIL;
+ show_call_link = !show_hyperlink && info->url != NULL && info->flavor == FLAVOR_VOIP_CALL;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ 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");
@@ -2117,6 +2154,16 @@ terminal_window_init (TerminalWindow *window)
/* 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)
@@ -2596,6 +2643,23 @@ sync_screen_icon_title_set (TerminalScreen *screen,
/* Re-setting the right title will be done by the notify::title handler which comes after this one */
}
+static void
+screen_hyperlink_hover_uri_changed (TerminalScreen *screen,
+ const char *uri,
+ const GdkRectangle *bbox G_GNUC_UNUSED,
+ TerminalWindow *window G_GNUC_UNUSED)
+{
+ 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);
+ g_free(label);
+}
+
/* Notebook callbacks */
static void
@@ -3209,6 +3273,8 @@ notebook_page_added_callback (GtkWidget *notebook,
G_CALLBACK (sync_screen_icon_title_set), 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);
@@ -3289,6 +3355,10 @@ notebook_page_removed_callback (GtkWidget *notebook,
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 263dce4..281dc90 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -83,6 +83,9 @@
</menubar>
<popup name="Popup" action="Popup">
+ <menuitem action="PopupOpenHyperlink" />
+ <menuitem action="PopupCopyHyperlinkAddress" />
+ <separator />
<menuitem action="PopupSendEmail" />
<menuitem action="PopupCopyEmailAddress" />
<menuitem action="PopupCall" />