From f6c8e0402fcef5be47d309752428c3349bd0b8cb Mon Sep 17 00:00:00 2001 From: ketmar Date: Sun, 27 Jun 2021 13:24:30 +0000 Subject: [PATCH] open urls with mouse click FossilOrigin-Name: 287b448c5712f5a901e4838f717177f1a4631794df486d5b23ca44c72c8ef06b --- miri/textpane.d | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- xmiri.d | 8 ++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/miri/textpane.d b/miri/textpane.d index 4639af0..47d7b98 100644 --- a/miri/textpane.d +++ b/miri/textpane.d @@ -37,6 +37,39 @@ public class EventFocusIn : Event {} // ////////////////////////////////////////////////////////////////////////// // +string getBrowserCommand (bool forceOpera=false) { + __gshared string browser; + if (forceOpera) return "opera"; + if (browser.length == 0) { + import core.stdc.stdlib : getenv; + const(char)* evar = getenv("BROWSER"); + if (evar !is null && evar[0]) { + import std.string : fromStringz; + browser = evar.fromStringz.idup; + } else { + browser = "opera"; + } + } + return browser; +} + + +void openUrl (const(char)[] url, bool forceOpera=false) { + if (url.length) { + import std.stdio : File; + import std.process; + try { + auto frd = File("/dev/null"); + auto fwr = File("/dev/null", "w"); + spawnProcess([getBrowserCommand(forceOpera), url.idup], frd, fwr, fwr, null, Config.detached); + } catch (Exception e) { + //conwriteln("ERROR executing URL viewer (", e.msg, ")"); + } + } +} + + +// ////////////////////////////////////////////////////////////////////////// // // draw scrollbar in the given tty window public void drawScrollBar (XtWindow win, int cur, int total) { if (win.width < 1 || win.height < 1) return; // it won't be visible anyway @@ -502,7 +535,7 @@ public: if (obs != bottomSkip) wasLogMove = false; } - // drawing it from the bottom, lol + // draw it from the bottom void draw () { auto win = XtWindow(mX0, mY0, mWidth, mHeight); win.color = TextColor; @@ -552,4 +585,94 @@ public: win.drawScrollBar(lcc-bottomSkip, lcc); } } + + static bool strCheckPrev (const(char)[] s, usize pos, const(char)[] pfx) { + if (pfx.length == 0 || s.length == 0) return false; + if (pos < pfx.length) return false; + if (pos > s.length) return false; + pos -= pfx.length; + foreach (immutable c; 0..pfx.length) { + import iv.strex : tolower; + if (s[pos+c].tolower != pfx[c].tolower) return false; + } + return true; + } + + // x and y are relative to line, y is never out of bounds + void doClickAt (TextLine line, int x, int y) { + if (!line || !line.lines.length) return; + // open everything + string s = line.lines[0]; + if (line.lines.length > 1) { + foreach (immutable c; 1..line.lines.length) s ~= line.lines[c]; + } + import iv.strex; + while (s.length) { + auto np = s.indexOf("://"); + if (np <= 0) return; + int spos = -1; + if (strCheckPrev(s, np, "https")) spos = cast(int)np-5; + else if (strCheckPrev(s, np, "http")) spos = cast(int)np-4; + if (spos < 0) { + s = s[np+3..$]; + continue; + } + // remove everything before the protocol + s = s[spos..$]; + np = s.indexOf("://"); + assert(np > 0); + np += 3; + // skip host + while (np < s.length) { + immutable char ch = s[np]; + if (ch == '/') break; + if (ch <= 32 || ch >= 127) break; + ++np; + } + // skip path + if (np < s.length && s[np] == '/') { + while (np < s.length) { + immutable char ch = s[np]; + if (ch <= 32 || ch == 127) break; + ++np; + } + } + // get url + string url = s[0..np]; + s = s[np..$]; + while (url.length && url[$-1] == '.') url = url[0..$-1]; + if (url.length) { + openUrl(url); + ttyBeep(); + } + } + } + + // x and y are absoulte + // returns `false` if click wasn't eaten + bool clicked (int x, int y) { + x -= mX0; + y -= mY0; + if (x < 0 || y < 0 || x >= mWidth || y >= mHeight) return false; + // find the line + int cy = mHeight-1+bottomSkip; + int lnum = cast(int)text.length-1; + while (cy >= 0 && lnum >= 0) { + auto lnx = text[lnum--]; + // mark? + if (lnx is null) { + if (cy == y) return true; + --cy; + continue; + } + int y0 = cy-cast(int)lnx.lines.length+1; + if (y >= y0 && y <= cy) { + doClickAt(lnx, x, y-y0); + return true; + } + cy = y0-1; + } + //ttyBeep(); + return false; + } } diff --git a/xmiri.d b/xmiri.d index 143233e..f7ff4ab 100644 --- a/xmiri.d +++ b/xmiri.d @@ -1914,6 +1914,13 @@ bool keycb (TtyEvent key) { if (key == "^PageUp") { chatlist.goUp(); return true; } if (key == "^PageDown") { chatlist.goDown(); return true; } + if (key.mouse) { + if (key.key == TtyEvent.Key.MLeftUp) { + if (auto tp = chatlist.textpane) tp.clicked(key.x, key.y); + } + return true; + } + if (inputActive) { if (inputModeSingle) { if (key == "PageUp") { if (auto tp = chatlist.textpane) tp.doPageUp(); return true; } @@ -2122,6 +2129,7 @@ void main (string[] args) { if (ttyIsRedirected) assert(0, "no redirections, please"); xtInit(); + ttyEnableMouseReports(); if (ttyw < ChanTabWidth+NickTabWidth+30 || ttyh < 20) assert(0, "tty is too small"); TextPaneWidth = ttyw-ChanTabWidth-1-NickTabWidth-1; -- 2.11.4.GIT