Skip to content

Commit af16e79

Browse files
Merge pull request #2146 from trygveaa/click-on-url
Added: Allow users to directly open URL links in terminal transcript when clicked or tapped The user can add `terminal-onclick-url-open=true` entry to `termux.properties` file to enable opening of URL links in terminal transcript when clicked or tapped. The default value is `false`. Running `termux-reload-settings` command will also update the behaviour instantaneously if changed. Implemented in #2146
2 parents da6174e + 54bb83d commit af16e79

File tree

5 files changed

+108
-30
lines changed

5 files changed

+108
-30
lines changed

app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.termux.shared.data.UrlUtils;
2626
import com.termux.shared.file.FileUtils;
2727
import com.termux.shared.interact.MessageDialogUtils;
28+
import com.termux.shared.interact.ShareUtils;
2829
import com.termux.shared.shell.ShellUtils;
2930
import com.termux.shared.terminal.TermuxTerminalViewClientBase;
3031
import com.termux.shared.terminal.io.extrakeys.SpecialButton;
@@ -42,6 +43,7 @@
4243
import com.termux.shared.view.KeyboardUtils;
4344
import com.termux.shared.view.ViewUtils;
4445
import com.termux.terminal.KeyHandler;
46+
import com.termux.terminal.TerminalBuffer;
4547
import com.termux.terminal.TerminalEmulator;
4648
import com.termux.terminal.TerminalSession;
4749

@@ -172,10 +174,26 @@ public float onScale(float scale) {
172174

173175
@Override
174176
public void onSingleTapUp(MotionEvent e) {
175-
if (!KeyboardUtils.areDisableSoftKeyboardFlagsSet(mActivity))
176-
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
177-
else
178-
Logger.logVerbose(LOG_TAG, "Not showing soft keyboard onSingleTapUp since its disabled");
177+
TerminalEmulator term = mActivity.getCurrentSession().getEmulator();
178+
179+
if (mActivity.getProperties().shouldOpenTerminalTranscriptURLOnClick()) {
180+
int[] columnAndRow = mActivity.getTerminalView().getColumnAndRow(e, true);
181+
String wordAtTap = term.getScreen().getWordAtLocation(columnAndRow[0], columnAndRow[1]);
182+
LinkedHashSet<CharSequence> urlSet = UrlUtils.extractUrls(wordAtTap);
183+
184+
if (!urlSet.isEmpty()) {
185+
String url = (String) urlSet.iterator().next();
186+
ShareUtils.openURL(mActivity, url);
187+
return;
188+
}
189+
}
190+
191+
if (!term.isMouseTrackingActive() && !e.isFromSource(InputDevice.SOURCE_MOUSE)) {
192+
if (!KeyboardUtils.areDisableSoftKeyboardFlagsSet(mActivity))
193+
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
194+
else
195+
Logger.logVerbose(LOG_TAG, "Not showing soft keyboard onSingleTapUp since its disabled");
196+
}
179197
}
180198

181199
@Override
@@ -670,13 +688,7 @@ public void showUrlSelection() {
670688
lv.setOnItemLongClickListener((parent, view, position, id) -> {
671689
dialog.dismiss();
672690
String url = (String) urls[position];
673-
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
674-
try {
675-
mActivity.startActivity(i, null);
676-
} catch (ActivityNotFoundException e) {
677-
// If no applications match, Android displays a system message.
678-
mActivity.startActivity(Intent.createChooser(i, null));
679-
}
691+
ShareUtils.openURL(mActivity, url);
680692
return true;
681693
});
682694
});

terminal-emulator/src/main/java/com/termux/terminal/TerminalBuffer.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,45 @@ public String getSelectedText(int selX1, int selY1, int selX2, int selY2, boolea
102102
return builder.toString();
103103
}
104104

105+
public String getWordAtLocation(int x, int y) {
106+
// Set y1 and y2 to the lines where the wrapped line starts and ends.
107+
// I.e. if a line that is wrapped to 3 lines starts at line 4, and this
108+
// is called with y=5, then y1 would be set to 4 and y2 would be set to 6.
109+
int y1 = y;
110+
int y2 = y;
111+
while (y1 > 0 && !getSelectedText(0, y1 - 1, mColumns, y, true, true).contains("\n")) {
112+
y1--;
113+
}
114+
while (y2 < mScreenRows && !getSelectedText(0, y, mColumns, y2 + 1, true, true).contains("\n")) {
115+
y2++;
116+
}
117+
118+
// Get the text for the whole wrapped line
119+
String text = getSelectedText(0, y1, mColumns, y2, true, true);
120+
// The index of x in text
121+
int textOffset = (y - y1) * mColumns + x;
122+
123+
if (textOffset >= text.length()) {
124+
// The click was to the right of the last word on the line, so
125+
// there's no word to return
126+
return "";
127+
}
128+
129+
// Set x1 and x2 to the indices of the last space before x and the
130+
// first space after x in text respectively
131+
int x1 = text.lastIndexOf(' ', textOffset);
132+
int x2 = text.indexOf(' ', textOffset);
133+
if (x2 == -1) {
134+
x2 = text.length();
135+
}
136+
137+
if (x1 == x2) {
138+
// The click was on a space, so there's no word to return
139+
return "";
140+
}
141+
return text.substring(x1 + 1, x2);
142+
}
143+
105144
public int getActiveTranscriptRows() {
106145
return mActiveTranscriptRows;
107146
}

terminal-emulator/src/test/java/com/termux/terminal/ScreenBufferTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,21 @@ public void testGetSelectedTextJoinFullLines() {
4545
withTerminalSized(5, 3).enterString("ABC\r\nFG");
4646
assertEquals("ABC\nFG", mTerminal.getScreen().getSelectedText(0, 0, 1, 1, true, true));
4747
}
48+
49+
public void testGetWordAtLocation() {
50+
withTerminalSized(5, 3).enterString("ABCDEFGHIJ\r\nKLMNO");
51+
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(0, 0));
52+
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(4, 1));
53+
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(4, 2));
54+
55+
withTerminalSized(5, 3).enterString("ABC DEF GHI ");
56+
assertEquals("ABC", mTerminal.getScreen().getWordAtLocation(0, 0));
57+
assertEquals("", mTerminal.getScreen().getWordAtLocation(3, 0));
58+
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(4, 0));
59+
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(0, 1));
60+
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(1, 1));
61+
assertEquals("GHI", mTerminal.getScreen().getWordAtLocation(0, 2));
62+
assertEquals("", mTerminal.getScreen().getWordAtLocation(1, 2));
63+
assertEquals("", mTerminal.getScreen().getWordAtLocation(2, 2));
64+
}
4865
}

terminal-view/src/main/java/com/termux/view/TerminalView.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unuse
9494
@Override
9595
public boolean onUp(MotionEvent event) {
9696
mScrollRemainder = 0.0f;
97-
if (mEmulator != null && mEmulator.isMouseTrackingActive() && !isSelectingText() && !scrolledWithFinger) {
97+
if (mEmulator != null && mEmulator.isMouseTrackingActive() && !event.isFromSource(InputDevice.SOURCE_MOUSE) && !isSelectingText() && !scrolledWithFinger) {
9898
// Quick event processing when mouse tracking is active - do not wait for check of double tapping
9999
// for zooming.
100100
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, true);
@@ -114,13 +114,8 @@ public boolean onSingleTapUp(MotionEvent event) {
114114
return true;
115115
}
116116
requestFocus();
117-
if (!mEmulator.isMouseTrackingActive()) {
118-
if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
119-
mClient.onSingleTapUp(event);
120-
return true;
121-
}
122-
}
123-
return false;
117+
mClient.onSingleTapUp(event);
118+
return true;
124119
}
125120

126121
@Override
@@ -471,10 +466,31 @@ public boolean isOpaque() {
471466
return true;
472467
}
473468

469+
/**
470+
* Get the zero indexed column and row of the terminal view for the
471+
* position of the event.
472+
*
473+
* @param event The event with the position to get the column and row for.
474+
* @param relativeToScroll If true the column number will take the scroll
475+
* position into account. E.g. if scrolled 3 lines up and the event
476+
* position is in the top left, column will be -3 if relativeToScroll is
477+
* true and 0 if relativeToScroll is false.
478+
* @return Array with the column and row.
479+
*/
480+
public int[] getColumnAndRow(MotionEvent event, boolean relativeToScroll) {
481+
int column = (int) (event.getX() / mRenderer.mFontWidth);
482+
int row = (int) ((event.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing);
483+
if (relativeToScroll) {
484+
row += mTopRow;
485+
}
486+
return new int[] { column, row };
487+
}
488+
474489
/** Send a single mouse event code to the terminal. */
475490
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
476-
int x = (int) (e.getX() / mRenderer.mFontWidth) + 1;
477-
int y = (int) ((e.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing) + 1;
491+
int[] columnAndRow = getColumnAndRow(e, false);
492+
int x = columnAndRow[0] + 1;
493+
int y = columnAndRow[1] + 1;
478494
if (pressed && (button == TerminalEmulator.MOUSE_WHEELDOWN_BUTTON || button == TerminalEmulator.MOUSE_WHEELUP_BUTTON)) {
479495
if (mMouseStartDownTime == e.getDownTime()) {
480496
x = mMouseScrollStartX;
@@ -550,7 +566,6 @@ public boolean onTouchEvent(MotionEvent event) {
550566
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON_MOVED, true);
551567
break;
552568
}
553-
return true;
554569
}
555570
}
556571

terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,9 @@ public void render() {
8989
}
9090

9191
public void setInitialTextSelectionPosition(MotionEvent event) {
92-
int cx = (int) (event.getX() / terminalView.mRenderer.getFontWidth());
93-
final boolean eventFromMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
94-
// Offset for finger:
95-
final int SELECT_TEXT_OFFSET_Y = eventFromMouse ? 0 : -40;
96-
int cy = (int) ((event.getY() + SELECT_TEXT_OFFSET_Y) / terminalView.mRenderer.getFontLineSpacing()) + terminalView.getTopRow();
97-
98-
mSelX1 = mSelX2 = cx;
99-
mSelY1 = mSelY2 = cy;
92+
int[] columnAndRow = terminalView.getColumnAndRow(event, true);
93+
mSelX1 = mSelX2 = columnAndRow[0];
94+
mSelY1 = mSelY2 = columnAndRow[1];
10095

10196
TerminalBuffer screen = terminalView.mEmulator.getScreen();
10297
if (!" ".equals(screen.getSelectedText(mSelX1, mSelY1, mSelX1, mSelY1))) {

0 commit comments

Comments
 (0)