Skip to content

Commit

Permalink
Initial implementation of #904
Browse files Browse the repository at this point in the history
  • Loading branch information
gnunn1 committed May 10, 2017
1 parent af2605f commit 30f394a
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 19 deletions.
88 changes: 88 additions & 0 deletions source/gx/tilix/terminal/exvte.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module gx.tilix.terminal.exvte;
import std.algorithm;
import std.experimental.logger;

import gdk.Event;

import gobject.Signals;

import glib.Str;
Expand Down Expand Up @@ -172,6 +174,79 @@ public:
}
}

protected class OnHyperlinkHoverUriDelegateWrapper
{
void delegate(string, Terminal) dlg;
gulong handlerId;
ConnectFlags flags;
this(void delegate(string, Terminal) dlg, gulong handlerId, ConnectFlags flags)
{
this.dlg = dlg;
this.handlerId = handlerId;
this.flags = flags;
}
}

protected OnHyperlinkHoverUriDelegateWrapper[] onHyperlinkHoverUriListeners;

/**
* Emitted to track a hyperlink uri change
*
* Params:
* uri = The URI of the hyperlink
*/
gulong addOnHyperlinkHoverUri(void delegate(string, Terminal) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
{
if (Signals.lookup("hyperlink-hover-uri", getType()) != 0) {
onHyperlinkHoverUriListeners ~= new OnHyperlinkHoverUriDelegateWrapper(dlg, 0, connectFlags);
onHyperlinkHoverUriListeners[onHyperlinkHoverUriListeners.length - 1].handlerId = Signals.connectData(
this,
"hyperlink-hover-uri",
cast(GCallback)&callBackHyperlinkHoverUri,
cast(void*)onHyperlinkHoverUriListeners[onHyperlinkHoverUriListeners.length - 1],
cast(GClosureNotify)&callBackHyperlinkHoverUriDestroy,
connectFlags);
return onHyperlinkHoverUriListeners[onHyperlinkHoverUriListeners.length - 1].handlerId;
} else {
return 0;
}
}

extern(C) static void callBackHyperlinkHoverUri(VteTerminal* terminalStruct, char* uri, OnHyperlinkHoverUriDelegateWrapper wrapper)
{
wrapper.dlg(Str.toString(uri), wrapper.outer);
}

extern(C) static void callBackHyperlinkHoverUriDestroy(OnHyperlinkHoverUriDelegateWrapper wrapper, GClosure* closure)
{
wrapper.outer.internalRemoveOnHyperlinkHoverUri(wrapper);
}

protected void internalRemoveOnHyperlinkHoverUri(OnHyperlinkHoverUriDelegateWrapper source)
{
foreach(index, wrapper; onHyperlinkHoverUriListeners)
{
if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId)
{
onHyperlinkHoverUriListeners[index] = null;
onHyperlinkHoverUriListeners = std.algorithm.remove(onHyperlinkHoverUriListeners, index);
break;
}
}
}

public bool getAllowHyperlink() {
return vte_terminal_get_allow_hyperlink(vteTerminal) != 0;
}

public void setAllowHyperlink(bool enabled) {
vte_terminal_set_allow_hyperlink(vteTerminal, enabled);
}

public string hyperlinkCheckEvent(Event event) {
return Str.toString(vte_terminal_hyperlink_check_event(vteTerminal, (event is null) ? null : event.getEventStruct()));
}

public bool getDisableBGDraw() {
return vte_terminal_get_disable_bg_draw(vteTerminal) != 0;
}
Expand All @@ -189,12 +264,25 @@ import gtkc.paths;
__gshared extern(C) {
int function(VteTerminal* terminal) c_vte_terminal_get_disable_bg_draw;
void function(VteTerminal* terminal, int isAudible) c_vte_terminal_set_disable_bg_draw;

// Custom hyperlink
int function(VteTerminal *terminal) c_vte_terminal_get_allow_hyperlink;
void function(VteTerminal *terminal, int allow_hyperlink) c_vte_terminal_set_allow_hyperlink;
char* function (VteTerminal *terminal, GdkEvent *event) c_vte_terminal_hyperlink_check_event;
}

alias c_vte_terminal_get_disable_bg_draw vte_terminal_get_disable_bg_draw;
alias c_vte_terminal_set_disable_bg_draw vte_terminal_set_disable_bg_draw;

alias c_vte_terminal_get_allow_hyperlink vte_terminal_get_allow_hyperlink;
alias c_vte_terminal_set_allow_hyperlink vte_terminal_set_allow_hyperlink;
alias c_vte_terminal_hyperlink_check_event vte_terminal_hyperlink_check_event;

shared static this() {
Linker.link(vte_terminal_get_disable_bg_draw, "vte_terminal_get_disable_bg_draw", LIBRARY.VTE);
Linker.link(vte_terminal_set_disable_bg_draw, "vte_terminal_set_disable_bg_draw", LIBRARY.VTE);

Linker.link(vte_terminal_set_allow_hyperlink, "vte_terminal_get_allow_hyperlink", LIBRARY.VTE);
Linker.link(vte_terminal_set_allow_hyperlink, "vte_terminal_set_allow_hyperlink", LIBRARY.VTE);
Linker.link(vte_terminal_hyperlink_check_event, "vte_terminal_hyperlink_check_event", LIBRARY.VTE);
}
1 change: 1 addition & 0 deletions source/gx/tilix/terminal/regex.d
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ struct TerminalURLMatch {
TerminalURLFlavor flavor;
string match;
int tag;
bool uri;

void clear() {
flavor = TerminalURLFlavor.AS_IS;
Expand Down
76 changes: 57 additions & 19 deletions source/gx/tilix/terminal/terminal.d
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,10 @@ private:
vte.setVexpand(true);
//Search Properties
vte.searchSetWrapAround(gsSettings.getValue(SETTINGS_SEARCH_DEFAULT_WRAP_AROUND).getBoolean());
if (checkVTEVersionNumber(0, 49)) {
vte.setAllowHyperlink(true);
trace("Custom hyperlinks enabled for VTE 0.49");
}
//URL Regex Experessions
try {
if (checkVTEVersionNumber(0, 46)) {
Expand Down Expand Up @@ -1559,6 +1563,44 @@ private:
pmContext.bindModel(mmContext, null);
}

public void checkHyperlinkMatch(Event event) {
if (!checkVTEVersionNumber(0, 49)) return;
match.match = vte.hyperlinkCheckEvent(event);
if (match.match.length == 0) return;


match.flavor = TerminalURLFlavor.AS_IS;
// Check if it already has a URI, if not resolve to a file URI
// This code probably is going to need lot's of debugging and improvements
import std.uri: uriLength, emailLength;
import std.file: exists;
import std.path: buildPath;
if (uriLength(match.match) < 0 && emailLength(match.match) < 0) {
string filename = match.match;
if (gst.hasState(TerminalStateType.REMOTE)) {
// Does the filename have a path associated with it, if no add it.
if (filename.indexOf("/") < 0) {
filename = buildPath(gst.currentDirectory(), filename);
}
// is the file remote, if so add hostname
if (filename.indexOf(gst.currentHostname()) < 0) {
filename = gst.currentHostname() ~ "/" ~ filename;
}
filename = "file://" ~ filename;
} else {
// checks to see if file is qualified in order to determine
// whether to add the path.
if (!exists(filename) || filename.indexOf("/") < 0) {
string fqn = buildPath(gst.currentDirectory(), filename);
if (exists(fqn))
filename = fqn;
}
filename = "file:///" ~ filename;
}
match.match = filename;
}
}

/**
* Signal received when mouse button is pressed in terminal
*/
Expand All @@ -1568,7 +1610,11 @@ private:
void updateMatch(Event event) {
match.clear;
int tag;
match.match = vte.matchCheckEvent(event, tag);
checkHyperlinkMatch(event);
// Check standard hyperlink if new hyperlink feature returns nothing
if (!match.match) {
match.match = vte.matchCheckEvent(event, tag);
}
if (match.match) {
tracef("Match checked: %s for tag %d", match.match, tag);
if (tag in regexTag) {
Expand All @@ -1577,7 +1623,7 @@ private:
match.tag = tag;
trace("Found matching regex");
}
}
}
}

if (vte is null) return false;
Expand Down Expand Up @@ -1622,7 +1668,7 @@ private:
}

void openURI(TerminalURLMatch urlMatch) {
trace("Match clicked");
tracef("Match clicked %s", match.match);
string uri = urlMatch.match;
switch (urlMatch.flavor) {
case TerminalURLFlavor.DEFAULT_TO_HTTP:
Expand Down Expand Up @@ -1665,26 +1711,18 @@ private:
error(ge.msg);
}
}
/*
if (urlMatch.tag in regexTag) {
TerminalRegex tr = regexTag[urlMatch.tag];
auto regexMatch = matchAll(urlMatch.match, regex(tr.pattern, tr.caseless?"i":""));
string[] groups = [urlMatch.match];
foreach(group; regexMatch.captures) {
groups ~= group;
}
trace("Command: " ~ tr.command);
string command = replaceMatchTokens(tr.command, groups);
trace("Command: " ~ command);
string[string] env;
spawnShell(command, env, Config.none, currentLocalDirectory);
}
*/
return;
default:
break;
}
MountOperation.showUri(null, uri, Main.getCurrentEventTime());
try {
MountOperation.showUri(null, uri, Main.getCurrentEventTime());
} catch (Exception e) {
string message = format(_("Could not open match '%s'"), match.match);
showErrorDialog(cast(Window)getToplevel(), message, _("Error Opening Match"));
error(message);
error(e.msg);
}
}

/**
Expand Down

0 comments on commit 30f394a

Please sign in to comment.