aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2009-01-21 06:50:10 +0000
committerKenny Root <kenny@the-b.org>2009-01-21 06:50:10 +0000
commitc8a5ca406a7c6776f6becbb62ab3edfdbf5adcf8 (patch)
treea5699fa3d3264eb5e27e09d99312cad8c8948898 /src
parent5be990cb0339abf90f35afe83f73726e801ebf7f (diff)
downloadconnectbot-c8a5ca406a7c6776f6becbb62ab3edfdbf5adcf8.tar.gz
connectbot-c8a5ca406a7c6776f6becbb62ab3edfdbf5adcf8.tar.bz2
connectbot-c8a5ca406a7c6776f6becbb62ab3edfdbf5adcf8.zip
Allow use of directional pad to select area of text to copy to clipboard
Diffstat (limited to 'src')
-rw-r--r--src/org/connectbot/ConsoleActivity.java87
-rw-r--r--src/org/connectbot/TerminalView.java22
-rw-r--r--src/org/connectbot/bean/SelectionArea.java190
-rw-r--r--src/org/connectbot/service/TerminalBridge.java107
4 files changed, 329 insertions, 77 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index 00b1543..c0b045e 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -20,6 +20,7 @@ package org.connectbot;
import org.connectbot.bean.HostBean;
import org.connectbot.bean.PortForwardBean;
+import org.connectbot.bean.SelectionArea;
import org.connectbot.service.PromptHelper;
import org.connectbot.service.TerminalBridge;
import org.connectbot.service.TerminalManager;
@@ -97,8 +98,7 @@ public class ConsoleActivity extends Activity {
private MenuItem disconnect, copy, paste, portForward, resize;
- protected boolean copying = false;
- protected TerminalView copySource = null;
+ protected TerminalBridge copySource = null;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
@@ -413,7 +413,8 @@ public class ConsoleActivity extends Activity {
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// if copying, then ignore
- if(copying) return false;
+ if (copySource != null && copySource.isSelectingForCopy())
+ return false;
// if releasing then reset total scroll
if(e2.getAction() == MotionEvent.ACTION_UP) {
@@ -470,63 +471,46 @@ public class ConsoleActivity extends Activity {
public boolean onTouch(View v, MotionEvent event) {
// when copying, highlight the area
- if(copying) {
- if(copySource == null) return false;
- float row = event.getY() / copySource.bridge.charHeight;
- float col = event.getX() / copySource.bridge.charWidth;
+ if (copySource != null && copySource.isSelectingForCopy()) {
+ int row = (int)Math.floor(event.getY() / copySource.charHeight);
+ int col = (int)Math.floor(event.getX() / copySource.charWidth);
+
+ SelectionArea area = copySource.getSelectionArea();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// recording starting area
- copySource.top = (int) Math.floor(row);
- copySource.left = (int) Math.floor(col);
+ area.setTop(row);
+ area.setLeft(col);
+ copySource.redraw();
return false;
case MotionEvent.ACTION_MOVE:
// update selected area
- copySource.bottom = (int) Math.ceil(row);
- copySource.right = (int) Math.ceil(col);
- copySource.invalidate();
+ area.setBottom(row);
+ area.setRight(col);
+ copySource.redraw();
return false;
case MotionEvent.ACTION_UP:
- // copy selected area to clipboard
- int adjust = 0; //copySource.bridge.buffer.windowBase - copySource.bridge.buffer.screenBase;
- int top = Math.min(copySource.top, copySource.bottom) + adjust,
- bottom = Math.max(copySource.top, copySource.bottom) + adjust,
- left = Math.min(copySource.left, copySource.right),
- right = Math.max(copySource.left, copySource.right);
-
- // perform actual buffer copy
- int size = (right - left) * (bottom - top);
- StringBuffer buffer = new StringBuffer(size);
- for(int y = top; y < bottom; y++) {
- int lastNonSpace = buffer.length();
-
- for(int x = left; x < right; x++) {
- // only copy printable chars
- char c = copySource.bridge.buffer.getChar(x, y);
- if(c < 32 || c >= 127) c = ' ';
- if (c != ' ')
- lastNonSpace = buffer.length();
- buffer.append(c);
- }
-
- // Don't leave a bunch of spaces in our copy buffer.
- if (buffer.length() > lastNonSpace)
- buffer.delete(lastNonSpace + 1, buffer.length());
-
- if (y != bottom)
- buffer.append("\n");
+ /* If they didn't move their finger, maybe they meant to
+ * select the rest of the text with the directional pad.
+ */
+ if (area.getLeft() == area.getRight() &&
+ area.getTop() == area.getBottom()) {
+ return true;
}
- clipboard.setText(buffer.toString());
- Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, buffer.length()), Toast.LENGTH_LONG).show();
+ // copy selected area to clipboard
+ String copiedText = area.copyFrom(copySource.buffer);
+
+ clipboard.setText(copiedText);
+ Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, copiedText.length()), Toast.LENGTH_LONG).show();
// fall through to clear state
case MotionEvent.ACTION_CANCEL:
// make sure we clear any highlighted area
- copySource.resetSelected();
- copySource.invalidate();
- copying = false;
+ area.reset();
+ copySource.setSelectingForCopy(false);
+ copySource.redraw();
return true;
}
@@ -579,9 +563,16 @@ public class ConsoleActivity extends Activity {
copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// mark as copying and reset any previous bounds
- copying = true;
- copySource = (TerminalView)view;
- copySource.resetSelected();
+ copySource = ((TerminalView)view).bridge;
+
+ SelectionArea area = copySource.getSelectionArea();
+ area.reset();
+ area.setBounds(copySource.buffer.getColumns(), copySource.buffer.getRows());
+
+ copySource.setSelectingForCopy(true);
+
+ // Make sure we show the initial selection
+ copySource.redraw();
Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show();
return true;
diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java
index 5003190..3feebd5 100644
--- a/src/org/connectbot/TerminalView.java
+++ b/src/org/connectbot/TerminalView.java
@@ -18,6 +18,7 @@
package org.connectbot;
+import org.connectbot.bean.SelectionArea;
import org.connectbot.service.TerminalBridge;
import android.app.Activity;
@@ -45,15 +46,6 @@ public class TerminalView extends View {
private Toast notification = null;
private String lastNotification = null;
-
- protected int top = -1, bottom = -1, left = -1, right = -1;
-
- public void resetSelected() {
- top = -1;
- bottom = -1;
- left = -1;
- right = -1;
- }
public TerminalView(Context context, TerminalBridge bridge) {
super(context);
@@ -109,9 +101,15 @@ public class TerminalView extends View {
}
// draw any highlighted area
- if(top >= 0 && bottom >= 0 && left >= 0 && right >= 0) {
- canvas.drawRect(left * bridge.charWidth, top * bridge.charHeight,
- right * bridge.charWidth, bottom * bridge.charHeight, cursorPaint);
+ if (bridge.isSelectingForCopy()) {
+ SelectionArea area = bridge.getSelectionArea();
+ canvas.drawRect(
+ area.getLeft() * bridge.charWidth,
+ area.getTop() * bridge.charHeight,
+ (area.getRight() + 1) * bridge.charWidth,
+ (area.getBottom() + 1) * bridge.charHeight,
+ cursorPaint
+ );
}
}
}
diff --git a/src/org/connectbot/bean/SelectionArea.java b/src/org/connectbot/bean/SelectionArea.java
new file mode 100644
index 0000000..6f13193
--- /dev/null
+++ b/src/org/connectbot/bean/SelectionArea.java
@@ -0,0 +1,190 @@
+/*
+ ConnectBot: simple, powerful, open-source SSH client for Android
+ Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.connectbot.bean;
+
+import de.mud.terminal.VDUBuffer;
+
+/**
+ * @author Kenny Root
+ * Keep track of a selection area for the terminal copying mechanism.
+ * If the orientation is flipped one way, swap the bottom and top or
+ * left and right to keep it in the correct orientation.
+ */
+public class SelectionArea {
+ private int top;
+ private int bottom;
+ private int left;
+ private int right;
+ private int maxColumns;
+ private int maxRows;
+ private boolean selectingOrigin;
+
+ public SelectionArea() {
+ reset();
+ }
+
+ public void reset() {
+ top = left = bottom = right = 0;
+ selectingOrigin = true;
+ }
+
+ /**
+ * @param columns
+ * @param rows
+ */
+ public void setBounds(int columns, int rows) {
+ maxColumns = columns - 1;
+ maxRows = rows - 1;
+ }
+
+ private int checkBounds(int value, int max) {
+ if (value < 0)
+ return 0;
+ else if (value > max)
+ return max;
+ else
+ return value;
+ }
+
+ public boolean isSelectingOrigin() {
+ return selectingOrigin;
+ }
+
+ public void finishSelectingOrigin() {
+ selectingOrigin = false;
+ }
+
+ public void setTop(int top) {
+ this.top = bottom = checkBounds(top, maxRows);
+ }
+
+ public void decrementTop() {
+ setTop(--top);
+ }
+
+ public void incrementTop() {
+ setTop(++top);
+ }
+
+ public int getTop() {
+ return Math.min(top, bottom);
+ }
+
+ public void setBottom(int bottom) {
+ this.bottom = checkBounds(bottom, maxRows);
+ }
+
+ public void decrementBottom() {
+ setBottom(--bottom);
+ }
+
+ public void incrementBottom() {
+ setBottom(++bottom);
+ }
+
+ public int getBottom() {
+ return Math.max(top, bottom);
+ }
+
+ public void setLeft(int left) {
+ this.left = right = checkBounds(left, maxColumns);
+ }
+
+ public void decrementLeft() {
+ setLeft(--left);
+ }
+
+ public void incrementLeft() {
+ setLeft(++left);
+ }
+
+ public int getLeft() {
+ return Math.min(left, right);
+ }
+
+ public void setRight(int right) {
+ this.right = checkBounds(right, maxColumns);
+ }
+
+ public void decrementRight() {
+ setRight(--right);
+ }
+
+ public void incrementRight() {
+ setRight(++right);
+ }
+
+ public int getRight() {
+ return Math.max(left, right);
+ }
+
+ public String copyFrom(VDUBuffer vb) {
+ int size = (getRight() - getLeft() + 1) * (getBottom() - getTop() + 1);
+
+ StringBuffer buffer = new StringBuffer(size);
+
+ for(int y = getTop(); y <= getBottom(); y++) {
+ int lastNonSpace = buffer.length();
+
+ for (int x = getLeft(); x <= getRight(); x++) {
+ // only copy printable chars
+ char c = vb.getChar(x, y);
+
+ if (!Character.isDefined(c) ||
+ (Character.isISOControl(c) && c != '\t'))
+ c = ' ';
+
+ if (c != ' ')
+ lastNonSpace = buffer.length();
+
+ buffer.append(c);
+ }
+
+ // Don't leave a bunch of spaces in our copy buffer.
+ if (buffer.length() > lastNonSpace)
+ buffer.delete(lastNonSpace + 1, buffer.length());
+
+ if (y != bottom)
+ buffer.append("\n");
+ }
+
+ return buffer.toString();
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append("SelectionArea[top=");
+ buffer.append(top);
+ buffer.append(", bottom=");
+ buffer.append(bottom);
+ buffer.append(", left=");
+ buffer.append(left);
+ buffer.append(", right=");
+ buffer.append(right);
+ buffer.append(", maxColumns=");
+ buffer.append(maxColumns);
+ buffer.append(", maxRows=");
+ buffer.append(maxRows);
+ buffer.append(", isSelectingOrigin=");
+ buffer.append(isSelectingOrigin());
+ buffer.append("]");
+
+ return buffer.toString();
+ }
+}
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index 8024c88..2522caa 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -39,10 +39,12 @@ import org.connectbot.TerminalView;
import org.connectbot.bean.HostBean;
import org.connectbot.bean.PortForwardBean;
import org.connectbot.bean.PubkeyBean;
+import org.connectbot.bean.SelectionArea;
import org.connectbot.util.HostDatabase;
import org.connectbot.util.PubkeyDatabase;
import org.connectbot.util.PubkeyUtils;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -50,13 +52,13 @@ import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.FontMetricsInt;
+import android.os.Vibrator;
+import android.text.ClipboardManager;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
-import android.os.Vibrator;
-import android.content.Context;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.Connection;
@@ -148,6 +150,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
private int termHeight;
private String keymode = null;
+
+ private boolean selectingForCopy = false;
+ private SelectionArea selectionArea;
+ private ClipboardManager clipboard;
protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
@@ -285,6 +291,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
buffer.setBufferSize(scrollback);
resetColors();
buffer.setDisplay(this);
+
+ selectionArea = new SelectionArea();
// TODO Change this when hosts are beans as well
portForwards = manager.hostdb.getPortForwardsForHost(host);
@@ -940,32 +948,84 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
case KeyEvent.KEYCODE_DEL: stdin.write(0x08); return true;
case KeyEvent.KEYCODE_ENTER: ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', event.getMetaState()); return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
- ((vt320)buffer).keyPressed(vt320.KEY_LEFT, ' ', event.getMetaState());
- this.tryKeyVibrate();
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.decrementLeft();
+ else
+ selectionArea.decrementRight();
+ redraw();
+ } else {
+ ((vt320)buffer).keyPressed(vt320.KEY_LEFT, ' ', event.getMetaState());
+ tryKeyVibrate();
+ }
return true;
case KeyEvent.KEYCODE_DPAD_UP:
- ((vt320)buffer).keyPressed(vt320.KEY_UP, ' ', event.getMetaState());
- this.tryKeyVibrate();
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.decrementTop();
+ else
+ selectionArea.decrementBottom();
+ redraw();
+ } else {
+ ((vt320)buffer).keyPressed(vt320.KEY_UP, ' ', event.getMetaState());
+ tryKeyVibrate();
+ }
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
- ((vt320)buffer).keyPressed(vt320.KEY_DOWN, ' ', event.getMetaState());
- this.tryKeyVibrate();
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.incrementTop();
+ else
+ selectionArea.incrementBottom();
+ redraw();
+ } else {
+ ((vt320)buffer).keyPressed(vt320.KEY_DOWN, ' ', event.getMetaState());
+ tryKeyVibrate();
+ }
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- ((vt320)buffer).keyPressed(vt320.KEY_RIGHT, ' ', event.getMetaState());
- this.tryKeyVibrate();
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.incrementLeft();
+ else
+ selectionArea.incrementRight();
+ redraw();
+ } else {
+ ((vt320)buffer).keyPressed(vt320.KEY_RIGHT, ' ', event.getMetaState());
+ tryKeyVibrate();
+ }
return true;
case KeyEvent.KEYCODE_DPAD_CENTER:
- // TODO: Add some visual indication of Ctrl state
- if (ctrlPressed) {
- ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
- ctrlPressed = false;
- } else
- ctrlPressed = true;
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.finishSelectingOrigin();
+ else {
+ if (parent != null && clipboard != null) {
+ // copy selected area to clipboard
+ String copiedText = selectionArea.copyFrom(buffer);
+
+ clipboard.setText(copiedText);
+ parent.notifyUser(parent.getContext().getString(R.string.console_copy_done,
+ copiedText.length()));
+
+ selectingForCopy = false;
+ selectionArea.reset();
+ }
+ }
+
+ redraw();
+ } else {
+ // TODO: Add some visual indication of Ctrl state
+ if (ctrlPressed) {
+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
+ ctrlPressed = false;
+ } else
+ ctrlPressed = true;
+ }
return true;
}
@@ -986,6 +1046,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
return false;
}
+ public void setSelectingForCopy(boolean selectingForCopy) {
+ this.selectingForCopy = selectingForCopy;
+ }
+
+ public boolean isSelectingForCopy() {
+ return selectingForCopy;
+ }
+
+ public SelectionArea getSelectionArea() {
+ return selectionArea;
+ }
+
public synchronized void tryKeyVibrate() {
if (bumpyArrows && vibrator != null)
vibrator.vibrate(VIBRATE_DURATION);
@@ -1028,7 +1100,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
bumpyArrows = manager.prefs.getBoolean(manager.res.getString(R.string.pref_bumpyarrows), true);
vibrator = (Vibrator) parent.getContext().getSystemService(Context.VIBRATOR_SERVICE);
-
+ clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+
if (!forcedSize) {
// recalculate buffer size
int newTermWidth, newTermHeight;