package net.momodalo.app.vimtouch;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Handler;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import java.lang.reflect.Field;
import jackpal.androidterm.emulatorview.ColorScheme;
import jackpal.androidterm.emulatorview.EmulatorView;
import jackpal.androidterm.emulatorview.TermSession;
public class TermView extends EmulatorView implements
GestureDetector.OnGestureListener {
private TermSession mSession;
private int mTopRow = 0; //we don't use termnial scroll
private GestureDetector mGestureDetector;
private ScaleDetectorCompat mScaleDetector;
private boolean mSingleTapESC;
private boolean mTouchGesture;
VimSettings mSettings;
private boolean mInserted = false;
private Runnable mCheckRunnable;
private Handler mCheckHandler;
private int mCheckCount = 0;
private VimInputConnection mInputConnection = null;
private boolean mIMEComposing = false;
private boolean mZoomBottom = true;
private int mFontSize = 0;
private static final int FLING_REFRESH_PERIOD = 50;
private static final int SCREEN_CHECK_PERIOD = 1000;
private static final int CURSOR_BLINK_PERIOD = 1000;
private static final int VISUAL_MODE_PERIOD = 1000;
public interface OnZoomListener {
void onZoom(boolean on);
}
private OnZoomListener mZoomListener = null;
public void setOnZoomListener(OnZoomListener listen) {
mZoomListener = listen;
}
public boolean checkInsertMode() {
boolean b = Exec.isInsertMode();
if (b != mInserted){
Context context = getContext();
InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.restartInput(TermView.this);
mInserted = b;
return true;
}
return false;
}
public TermView(Context context, TermSession session, DisplayMetrics metrics) {
super(context, session, metrics);
mSession = session;
mGestureDetector = new GestureDetector(context, this);
mScaleDetector = null;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { // 2.2+
mScaleDetector = new ScaleDetectorCompat(this);
}
mCheckRunnable = new Runnable() {
public void run() {
if(!checkInsertMode() && mCheckCount < 10)
mCheckHandler.postDelayed(mCheckRunnable, 100);
mCheckCount++;
}
};
mCheckHandler = new Handler();
}
public void updatePrefs(VimSettings settings, ColorScheme scheme) {
if (scheme == null) {
scheme = new ColorScheme(settings.getColorScheme());
}
if(mFontSize != settings.getFontSize()){
setTextSize(settings.getFontSize());
mFontSize = settings.getFontSize();
}
setCursorStyle(settings.getCursorStyle(), settings.getCursorBlink());
setUseCookedIME(settings.useCookedIME());
mIMEComposing = settings.useCookedIME();
setColorScheme(scheme);
setBackKeyCharacter(settings.getBackKeyCharacter());
setControlKeyCode(settings.getControlKeyCode());
setFnKeyCode(settings.getFnKeyCode());
mZoomBottom = settings.getZoomBottom();
mSingleTapESC = settings.getSingleTapESC();
mTouchGesture = settings.getTouchGesture();
mSettings = settings;
}
public void updatePrefs(VimSettings settings) {
updatePrefs(settings, null);
}
// Begin GestureDetector.OnGestureListener methods
/**
* Our message handler class. Implements a periodic callback.
*/
private final Handler mHandler = new Handler();
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
/*
distanceY += mScrollRemainder;
int deltaRows = (int) (distanceY / getCharacterHeight());
mScrollRemainder = distanceY - deltaRows * getCharacterHeight();
mTopRow =
Math.min(0, Math.max(-(mTranscriptScreen
.getActiveTranscriptRows()), mTopRow + deltaRows));
*/
invalidate();
return true;
}
float mScaleSpan = -1.0f;
public boolean onSingleTapUp(MotionEvent ev) {
Exec.mouseDown( mDownY, mDownX);
Exec.mouseUp( mDownY, mDownX);
if(mSingleTapESC)mSession.write(27);
return true;
}
private float mVelocity = 0;
private Runnable mFlingRun = new Runnable() {
public void run() {
Exec.scrollBy((int)mVelocity);
if(mVelocity > 0){
mVelocity -= mVelocity>2?2:mVelocity;
}else{
mVelocity -= mVelocity<-2?-2:mVelocity;
}
if(mVelocity == 0){
if(mInputConnection!=null)mInputConnection.notifyTextChange();
return;
}
mHandler.postDelayed(this, FLING_REFRESH_PERIOD);
}
};
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
mVelocity = -velocityY/(10*getCharacterHeight());
mHandler.postDelayed(mFlingRun, FLING_REFRESH_PERIOD);
return true;
}
int mDownX;
int mDownY;
public boolean onDown(MotionEvent ev) {
float y = ev.getY();
float x = ev.getX();
int fingers = ev.getPointerCount();
mLastY = y;
mHandler.removeCallbacks(mFlingRun);
return true;
}
// End GestureDetector.OnGestureListener methods
float mLastY = -1;
float mLastX = -1;
int mVisualMode = 0;
private void toggleVisualMode() {
if(!Exec.isInsertMode()){
switch(mVisualMode){
case 0:
mVisualMode = 1;
mSession.write("v");
break;
case 1:
mVisualMode = 2;
mSession.write((char) ('v' - 'a' + '\001'));
break;
case 2:
mVisualMode = 1;
mSession.write("v");
break;
default:
mVisualMode = 1;
mSession.write("v");
break;
}
}
}
private Runnable mVisualRun = new Runnable() {
public void run() {
toggleVisualMode();
}
};
boolean mZoom = false;
float mZoomX = 0.0f;
float mZoomY = 0.0f;
public void setZoom(boolean z){
mZoom = z;
if(mZoomListener != null) mZoomListener.onZoom(z);
}
public boolean getZoom(){
return mZoom;
}
public void onLongPress(MotionEvent ev) {
float y = ev.getY();
float x = ev.getX();
setZoom(true);
mZoomX = x;
mZoomY = y;
mLastY = -1;
Exec.mouseDown( (int)(y/getCharacterHeight()), (int)(x/getCharacterWidth()));
Exec.mouseUp( (int)(y/getCharacterHeight()), (int)(x/getCharacterWidth()));
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
checkInsertMode();
mHandler.removeCallbacks(mVisualRun);
if(getSelectingText())
return super.onTouchEvent(ev);
float y = ev.getY();
float x = ev.getX();
int action = ev.getAction();
int fingers = ev.getPointerCount();
if(action == MotionEvent.ACTION_DOWN && fingers == 1){
mLastY = y;
mLastX = x;
mDownX = (int)(x/getCharacterWidth());
mDownY = (int)(y/getCharacterHeight());
}else if (action == MotionEvent.ACTION_MOVE && fingers == 1 && mScaleSpan < 0.0){
mZoomX = x;
mZoomY = y;
if(mLastX != -1 && Math.abs(x-mLastX) > getCharacterWidth() * 5 && !getZoom()){
setZoom(true);
mLastY = -1;
int cursorX = (int)(x/getCharacterWidth());
int cursorY = (int)(y/getCharacterHeight());
Exec.setCursorPos(cursorY, cursorX);
} else if(mLastY != -1 && ( mLastX == -1 || (Math.abs(y-mLastY) > getCharacterHeight() * 2)) && !getZoom()){
int scrolls = (int)((mLastY - y)/getCharacterHeight());
if(mTouchGesture){
if(mLastX != -1){
int cursorX = (int)(x/getCharacterWidth());
int cursorY = (int)(y/getCharacterHeight());
Exec.setCursorPos(cursorY, cursorX);
}
//Exec.mouseDown( mDownY, mDownX);
if(scrolls != 0)Exec.scrollBy(scrolls);
if(mInputConnection!=null)mInputConnection.notifyTextChange();
}
if(scrolls != 0)mLastY = y;
mLastX = -1;
}else if (getZoom()){
long time = ev.getEventTime();
int state = Exec.getState();
if(mVisualMode > 0){
Exec.mouseDrag( (int)(y/getCharacterHeight()), (int)(x/getCharacterWidth()));
Exec.mouseDown( (int)(y/getCharacterHeight()), (int)(x/getCharacterWidth()));
}else{
int cursorX = (int)(x/getCharacterWidth());
int cursorY = (int)(y/getCharacterHeight());
Exec.setCursorPos(cursorY, cursorX);
}
Exec.updateScreen();
mHandler.postDelayed(mVisualRun, VISUAL_MODE_PERIOD);
}
}else if(action == MotionEvent.ACTION_UP){
if(getZoom())
Exec.mouseUp( (int)(y/getCharacterHeight()), (int)(x/getCharacterWidth()));
mLastY = -1;
mLastX = -1;
setZoom(false);
invalidate();
mVisualMode = 0;
if(mInputConnection!=null)mInputConnection.notifyTextChange();
}
if (mTouchGesture && mScaleSpan < 0.0)
mGestureDetector.onTouchEvent(ev);
if (null != mScaleDetector) { //
return mScaleDetector.onTouchEvent(ev);
}
return true;
}
public boolean dispatchKeyEvent(KeyEvent event){
boolean b = super.dispatchKeyEvent(event);
lateCheckInserted();
return b;
}
public void lateCheckInserted(){
//FIXME check the vim State change lately
mCheckHandler.removeCallbacks(mCheckRunnable);
if(!checkInsertMode()){
mCheckCount = 0;
mCheckHandler.postDelayed(mCheckRunnable, 100);
}
}
public InputConnection onCreateInputConnection (EditorInfo outAttrs) {
if(!Exec.isInsertMode() || !mIMEComposing){
mInputConnection = null;
return super.onCreateInputConnection(outAttrs);
}
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
mInputConnection = new VimInputConnection(this);
return mInputConnection;
}
public boolean toggleIMEComposing(){
mIMEComposing = mIMEComposing?false:true;
Context context = getContext();
InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.restartInput(TermView.this);
return mIMEComposing;
}
/*
public boolean onCheckIsTextEditor () {
return true;
}*/
private float mScaleX = 1.0f;
private float mScaleY = 1.0f;
private float mScalePX;
private float mScalePY;
public void setScale(float sx, float sy, float px, float py){
mScaleX = sx;
mScaleY = sy;
mScalePX = px;
mScalePY = py;
}
protected void onDraw(Canvas canvas) {
if(mScaleX != 1.0 || mScaleY != 1.0){
canvas.scale(mScaleX,mScaleY,mScalePX,mScalePY);
}
super.onDraw(canvas);
if(mZoom){
float tx = 2 * getCharacterWidth() * ( 1 - 2 * mZoomX / getVisibleWidth());
float h = getCharacterHeight()*2;
canvas.scale(3.0f,3.0f,mZoomX, mZoomY);
if(mZoomY > getVisibleHeight()/2){
canvas.translate( tx, (-mZoomY)/3 + h);
}else{
canvas.translate( tx, (getVisibleHeight()-mZoomY)/3 - h);
}
canvas.clipRect(mZoomX - mZoomX/3-5, mZoomY-h, mZoomX+(getVisibleWidth()-mZoomX)/3+5,mZoomY+h);
super.onDraw(canvas);
}
if(mZoomBottom && Exec.isCmdLine()){
float cx = getVisibleWidth() - (Exec.getCursorCol()+2) * getCharacterWidth() * 3.0f;
canvas.scale(3.0f,3.0f, cx < 0.0f? cx : 0.0f, getVisibleHeight() - getCharacterHeight());
canvas.translate( cx < 0.0f? cx : 0.0f, (- getVisibleHeight() + getCharacterHeight() * 2)/3);
super.onDraw(canvas);
}
}
public float getCharacterWidth(){
try {
Field privateField =
EmulatorView.class.getDeclaredField("mCharacterWidth");
privateField.setAccessible(true);
return privateField.getFloat(this);
}catch(Exception e){
return 0.0f;
}
}
public float getCharacterHeight(){
try{
Field privateField =
EmulatorView.class.getDeclaredField("mCharacterHeight");
privateField.setAccessible(true);
return (float) privateField.getInt(this);
}catch(Exception e){
return 0.0f;
}
}
}