Advertisement
Guest User

emulatorview

a guest
Dec 20th, 2012
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 42.16 KB | None | 0 0
  1. /*
  2.  * Copyright (C) 2007 The Android Open Source Project
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. package jackpal.androidterm.emulatorview;
  18.  
  19. import java.io.IOException;
  20.  
  21. import android.content.Context;
  22. import android.graphics.Canvas;
  23. import android.graphics.Paint;
  24. import android.os.Build;
  25. import android.os.Bundle;
  26. import android.os.Handler;
  27. import android.text.ClipboardManager;
  28. import android.util.AttributeSet;
  29. import android.util.DisplayMetrics;
  30. import android.util.Log;
  31. import android.view.GestureDetector;
  32. import android.view.KeyEvent;
  33. import android.view.MotionEvent;
  34. import android.view.View;
  35. import android.view.inputmethod.BaseInputConnection;
  36. import android.view.inputmethod.CompletionInfo;
  37. import android.view.inputmethod.CorrectionInfo;
  38. import android.view.inputmethod.EditorInfo;
  39. import android.view.inputmethod.ExtractedText;
  40. import android.view.inputmethod.ExtractedTextRequest;
  41. import android.view.inputmethod.InputConnection;
  42. import android.widget.Scroller;
  43.  
  44. /**
  45.  * A view on a {@link TermSession}.  Displays the terminal emulator's screen,
  46.  * provides access to its scrollback buffer, and passes input through to the
  47.  * terminal emulator.
  48.  * <p>
  49.  * If this view is inflated from an XML layout, you need to call {@link
  50.  * #attachSession attachSession} and {@link #setDensity setDensity} before using
  51.  * the view.  If creating this view from code, use the {@link
  52.  * #EmulatorView(Context, TermSession, DisplayMetrics)} constructor, which will
  53.  * take care of this for you.
  54.  */
  55. public class EmulatorView extends View implements GestureDetector.OnGestureListener {
  56.     private final String TAG = "EmulatorView";
  57.     private final boolean LOG_KEY_EVENTS = false;
  58.     private final boolean LOG_IME = false;
  59.  
  60.     /**
  61.      * We defer some initialization until we have been layed out in the view
  62.      * hierarchy. The boolean tracks when we know what our size is.
  63.      */
  64.     private boolean mKnownSize;
  65.  
  66.     // Set if initialization was deferred because a TermSession wasn't attached
  67.     private boolean mDeferInit = false;
  68.  
  69.     private int mVisibleWidth;
  70.     private int mVisibleHeight;
  71.  
  72.     private TermSession mTermSession;
  73.  
  74.     /**
  75.      * Our transcript. Contains the screen and the transcript.
  76.      */
  77.     private TranscriptScreen mTranscriptScreen;
  78.  
  79.     /**
  80.      * Total width of each character, in pixels
  81.      */
  82.     private float mCharacterWidth;
  83.  
  84.     /**
  85.      * Total height of each character, in pixels
  86.      */
  87.     private int mCharacterHeight;
  88.  
  89.     /**
  90.      * Top-of-screen margin
  91.      */
  92.     private int mTopOfScreenMargin;
  93.  
  94.     /**
  95.      * Used to render text
  96.      */
  97.     private TextRenderer mTextRenderer;
  98.  
  99.     /**
  100.      * Text size. Zero means 4 x 8 font.
  101.      */
  102.     private int mTextSize = 10;
  103.  
  104.     private int mCursorStyle;
  105.     private int mCursorBlink;
  106.  
  107.     /**
  108.      * Color scheme (default foreground/background colors).
  109.      */
  110.     private ColorScheme mColorScheme = BaseTextRenderer.defaultColorScheme;
  111.  
  112.     /**
  113.      * Used to paint the cursor
  114.      */
  115.     private Paint mCursorPaint;
  116.  
  117.     private Paint mForegroundPaint;
  118.  
  119.     private Paint mBackgroundPaint;
  120.  
  121.     private boolean mUseCookedIme;
  122.  
  123.     /**
  124.      * Our terminal emulator. We use this to get the current cursor position.
  125.      */
  126.     private TerminalEmulator mEmulator;
  127.  
  128.     /**
  129.      * The number of rows of text to display.
  130.      */
  131.     private int mRows;
  132.  
  133.     /**
  134.      * The number of columns of text to display.
  135.      */
  136.     private int mColumns;
  137.  
  138.     /**
  139.      * The number of columns that are visible on the display.
  140.      */
  141.  
  142.     private int mVisibleColumns;
  143.  
  144.     /**
  145.      * The top row of text to display. Ranges from -activeTranscriptRows to 0
  146.      */
  147.     private int mTopRow;
  148.  
  149.     private int mLeftColumn;
  150.  
  151.     private static final int CURSOR_BLINK_PERIOD = 1000;
  152.  
  153.     private boolean mCursorVisible = true;
  154.  
  155.     private boolean mIsSelectingText = false;
  156.  
  157.     private boolean mBackKeySendsCharacter = false;
  158.     private int mControlKeyCode;
  159.     private int mFnKeyCode;
  160.     private boolean mIsControlKeySent = false;
  161.     private boolean mIsFnKeySent = false;
  162.  
  163.     private float mDensity;
  164.  
  165.     private float mScaledDensity;
  166.     private static final int SELECT_TEXT_OFFSET_Y = -40;
  167.     private int mSelXAnchor = -1;
  168.     private int mSelYAnchor = -1;
  169.     private int mSelX1 = -1;
  170.     private int mSelY1 = -1;
  171.     private int mSelX2 = -1;
  172.     private int mSelY2 = -1;
  173.  
  174.     private boolean mIsActive = false;
  175.  
  176.     /**
  177.      * Routing alt and meta keyCodes away from the IME allows Alt key processing to work on
  178.      * the Asus Transformer TF101.
  179.      * It doesn't seem to harm anything else, but it also doesn't seem to be
  180.      * required on other platforms.
  181.      *
  182.      * This test should be refined as we learn more.
  183.      */
  184.     private final static boolean sTrapAltAndMeta = Build.MODEL.contains("Transformer TF101");
  185.  
  186.     private Runnable mBlinkCursor = new Runnable() {
  187.         public void run() {
  188.             if (mCursorBlink != 0) {
  189.                 mCursorVisible = ! mCursorVisible;
  190.                 mHandler.postDelayed(this, CURSOR_BLINK_PERIOD);
  191.             } else {
  192.                 mCursorVisible = true;
  193.             }
  194.             // Perhaps just invalidate the character with the cursor.
  195.             invalidate();
  196.         }
  197.     };
  198.  
  199.     private GestureDetector mGestureDetector;
  200.     private GestureDetector.OnGestureListener mExtGestureListener;
  201.     private Scroller mScroller;
  202.     private Runnable mFlingRunner = new Runnable() {
  203.         public void run() {
  204.             if (mScroller.isFinished()) {
  205.                 return;
  206.             }
  207.  
  208.             boolean more = mScroller.computeScrollOffset();
  209.             int newTopRow = mScroller.getCurrY();
  210.             if (newTopRow != mTopRow) {
  211.                 mTopRow = newTopRow;
  212.                 invalidate();
  213.             }
  214.  
  215.             if (more) {
  216.                 post(this);
  217.             }
  218.  
  219.         }
  220.     };
  221.     private float mScrollRemainder;
  222.     private TermKeyListener mKeyListener;
  223.  
  224.     private String mImeBuffer = "";
  225.  
  226.     /**
  227.      * Our message handler class. Implements a periodic callback.
  228.      */
  229.     private final Handler mHandler = new Handler();
  230.  
  231.     /**
  232.      * Called by the TermSession when the contents of the view need updating
  233.      */
  234.     private UpdateCallback mUpdateNotify = new UpdateCallback() {
  235.         public void onUpdate() {
  236.             if ( mIsSelectingText ) {
  237.                 int rowShift = mEmulator.getScrollCounter();
  238.                 mSelY1 -= rowShift;
  239.                 mSelY2 -= rowShift;
  240.                 mSelYAnchor -= rowShift;
  241.             }
  242.             mEmulator.clearScrollCounter();
  243.             ensureCursorVisible();
  244.             invalidate();
  245.         }
  246.     };
  247.  
  248.     /**
  249.      * Create an <code>EmulatorView</code> for a {@link TermSession}.
  250.      *
  251.      * @param context The {@link Context} for the view.
  252.      * @param session The {@link TermSession} this view will be displaying.
  253.      * @param metrics The {@link DisplayMetrics} of the screen on which the view
  254.      *                will be displayed.
  255.      */
  256.     public EmulatorView(Context context, TermSession session, DisplayMetrics metrics) {
  257.         super(context);
  258.         attachSession(session);
  259.         setDensity(metrics);
  260.         commonConstructor(context);
  261.     }
  262.  
  263.     /**
  264.      * Constructor called when inflating this view from XML.
  265.      * <p>
  266.      * You should call {@link #attachSession attachSession} and {@link
  267.      * #setDensity setDensity} before using an <code>EmulatorView</code> created
  268.      * using this constructor.
  269.      */
  270.     public EmulatorView(Context context, AttributeSet attrs) {
  271.         super(context, attrs);
  272.         commonConstructor(context);
  273.     }
  274.  
  275.     /**
  276.      * Constructor called when inflating this view from XML with a
  277.      * default style set.
  278.      * <p>
  279.      * You should call {@link #attachSession attachSession} and {@link
  280.      * #setDensity setDensity} before using an <code>EmulatorView</code> created
  281.      * using this constructor.
  282.      */
  283.     public EmulatorView(Context context, AttributeSet attrs, int defStyle) {
  284.         super(context, attrs, defStyle);
  285.         commonConstructor(context);
  286.     }
  287.  
  288.     private void commonConstructor(Context context) {
  289.         // TODO: See if we want to use the API level 11 constructor to get new flywheel feature.
  290.         mScroller = new Scroller(context);
  291.     }
  292.  
  293.     /**
  294.      * Attach a {@link TermSession} to this view.
  295.      *
  296.      * @param session The {@link TermSession} this view will be displaying.
  297.      */
  298.     public void attachSession(TermSession session) {
  299.         mTextRenderer = null;
  300.         mCursorPaint = new Paint();
  301.         mCursorPaint.setARGB(255,128,128,128);
  302.         mForegroundPaint = new Paint();
  303.         mBackgroundPaint = new Paint();
  304.         mTopRow = 0;
  305.         mLeftColumn = 0;
  306.         mGestureDetector = new GestureDetector(this);
  307.         // mGestureDetector.setIsLongpressEnabled(false);
  308.         setVerticalScrollBarEnabled(true);
  309.         setFocusable(true);
  310.         setFocusableInTouchMode(true);
  311.  
  312.         mTermSession = session;
  313.  
  314.         mKeyListener = new TermKeyListener(session);
  315.  
  316.         // Do init now if it was deferred until a TermSession was attached
  317.         if (mDeferInit) {
  318.             mDeferInit = false;
  319.             mKnownSize = true;
  320.             initialize();
  321.         }
  322.     }
  323.  
  324.     /**
  325.      * Update the screen density for the screen on which the view is displayed.
  326.      *
  327.      * @param metrics The {@link DisplayMetrics} of the screen.
  328.      */
  329.     public void setDensity(DisplayMetrics metrics) {
  330.         if (mDensity == 0) {
  331.             // First time we've known the screen density, so update font size
  332.             mTextSize = (int) (mTextSize * metrics.density);
  333.         }
  334.         mDensity = metrics.density;
  335.         mScaledDensity = metrics.scaledDensity;
  336.     }
  337.  
  338.     /**
  339.      * Inform the view that it is now visible on screen.
  340.      */
  341.     public void onResume() {
  342.         mIsActive = true;
  343.         updateSize(false);
  344.         if (mCursorBlink != 0) {
  345.             mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
  346.         }
  347.     }
  348.  
  349.     /**
  350.      * Inform the view that it is no longer visible on the screen.
  351.      */
  352.     public void onPause() {
  353.         if (mCursorBlink != 0) {
  354.             mHandler.removeCallbacks(mBlinkCursor);
  355.         }
  356.         mIsActive = false;
  357.     }
  358.  
  359.     /**
  360.      * Set this <code>EmulatorView</code>'s color scheme.
  361.      *
  362.      * @param scheme The {@link ColorScheme} to use (use null for the default
  363.      *               scheme).
  364.      * @see TermSession#setColorScheme
  365.      * @see ColorScheme
  366.      */
  367.     public void setColorScheme(ColorScheme scheme) {
  368.         if (scheme == null) {
  369.             mColorScheme = BaseTextRenderer.defaultColorScheme;
  370.         } else {
  371.             mColorScheme = scheme;
  372.         }
  373.         updateText();
  374.     }
  375.  
  376.     @Override
  377.     public boolean onCheckIsTextEditor() {
  378.         return true;
  379.     }
  380.  
  381.     @Override
  382.     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
  383.         outAttrs.inputType = mUseCookedIme ?
  384.                 EditorInfo.TYPE_CLASS_TEXT :
  385.                 EditorInfo.TYPE_NULL;
  386.         return new BaseInputConnection(this, true) {
  387.             /**
  388.              * Used to handle composing text requests
  389.              */
  390.             private int mCursor;
  391.             private int mComposingTextStart;
  392.             private int mComposingTextEnd;
  393.             private int mSelectedTextStart;
  394.             private int mSelectedTextEnd;
  395.  
  396.             private void sendText(CharSequence text) {
  397.                 int n = text.length();
  398.                 char c;
  399.                 try {
  400.                     for(int i = 0; i < n; i++) {
  401.                         c = text.charAt(i);
  402.                         if (Character.isHighSurrogate(c)) {
  403.                             int codePoint;
  404.                             if (++i < n) {
  405.                                 codePoint = Character.toCodePoint(c, text.charAt(i));
  406.                             } else {
  407.                                 // Unicode Replacement Glyph, aka white question mark in black diamond.
  408.                                 codePoint = '\ufffd';
  409.                             }
  410.                             mapAndSend(codePoint);
  411.                         } else {
  412.                             mapAndSend(c);
  413.                         }
  414.                     }
  415.                 } catch (IOException e) {
  416.                     Log.e(TAG, "error writing ", e);
  417.                 }
  418.             }
  419.  
  420.             private void mapAndSend(int c) throws IOException {
  421.                 int result = mKeyListener.mapControlChar(c);
  422.                 if (result < TermKeyListener.KEYCODE_OFFSET) {
  423.                     mTermSession.write(result);
  424.                 } else {
  425.                     mKeyListener.handleKeyCode(result - TermKeyListener.KEYCODE_OFFSET, getKeypadApplicationMode());
  426.                 }
  427.                 clearSpecialKeyStatus();
  428.             }
  429.  
  430.             public boolean beginBatchEdit() {
  431.                 if (LOG_IME) {
  432.                     Log.w(TAG, "beginBatchEdit");
  433.                 }
  434.                 setImeBuffer("");
  435.                 mCursor = 0;
  436.                 mComposingTextStart = 0;
  437.                 mComposingTextEnd = 0;
  438.                 return true;
  439.             }
  440.  
  441.             public boolean clearMetaKeyStates(int arg0) {
  442.                 if (LOG_IME) {
  443.                     Log.w(TAG, "clearMetaKeyStates " + arg0);
  444.                 }
  445.                 return false;
  446.             }
  447.  
  448.             public boolean commitCompletion(CompletionInfo arg0) {
  449.                 if (LOG_IME) {
  450.                     Log.w(TAG, "commitCompletion " + arg0);
  451.                 }
  452.                 return false;
  453.             }
  454.  
  455.             public boolean endBatchEdit() {
  456.                 if (LOG_IME) {
  457.                     Log.w(TAG, "endBatchEdit");
  458.                 }
  459.                 return true;
  460.             }
  461.  
  462.             public boolean finishComposingText() {
  463.                 if (LOG_IME) {
  464.                     Log.w(TAG, "finishComposingText");
  465.                 }
  466.                 sendText(mImeBuffer);
  467.                 setImeBuffer("");
  468.                 mComposingTextStart = 0;
  469.                 mComposingTextEnd = 0;
  470.                 mCursor = 0;
  471.                 return true;
  472.             }
  473.  
  474.             public int getCursorCapsMode(int arg0) {
  475.                 if (LOG_IME) {
  476.                     Log.w(TAG, "getCursorCapsMode(" + arg0 + ")");
  477.                 }
  478.                 return 0;
  479.             }
  480.  
  481.             public ExtractedText getExtractedText(ExtractedTextRequest arg0,
  482.                     int arg1) {
  483.                 if (LOG_IME) {
  484.                     Log.w(TAG, "getExtractedText" + arg0 + "," + arg1);
  485.                 }
  486.                 return null;
  487.             }
  488.  
  489.             public CharSequence getTextAfterCursor(int n, int flags) {
  490.                 if (LOG_IME) {
  491.                     Log.w(TAG, "getTextAfterCursor(" + n + "," + flags + ")");
  492.                 }
  493.                 int len = Math.min(n, mImeBuffer.length() - mCursor);
  494.                 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
  495.                     return "";
  496.                 }
  497.                 return mImeBuffer.substring(mCursor, mCursor + len);
  498.             }
  499.  
  500.             public CharSequence getTextBeforeCursor(int n, int flags) {
  501.                 if (LOG_IME) {
  502.                     Log.w(TAG, "getTextBeforeCursor(" + n + "," + flags + ")");
  503.                 }
  504.                 int len = Math.min(n, mCursor);
  505.                 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
  506.                     return "";
  507.                 }
  508.                 return mImeBuffer.substring(mCursor-len, mCursor);
  509.             }
  510.  
  511.             public boolean performContextMenuAction(int arg0) {
  512.                 if (LOG_IME) {
  513.                     Log.w(TAG, "performContextMenuAction" + arg0);
  514.                 }
  515.                 return true;
  516.             }
  517.  
  518.             public boolean performPrivateCommand(String arg0, Bundle arg1) {
  519.                 if (LOG_IME) {
  520.                     Log.w(TAG, "performPrivateCommand" + arg0 + "," + arg1);
  521.                 }
  522.                 return true;
  523.             }
  524.  
  525.             public boolean reportFullscreenMode(boolean arg0) {
  526.                 if (LOG_IME) {
  527.                     Log.w(TAG, "reportFullscreenMode" + arg0);
  528.                 }
  529.                 return true;
  530.             }
  531.  
  532.             public boolean commitCorrection (CorrectionInfo correctionInfo) {
  533.                 if (LOG_IME) {
  534.                     Log.w(TAG, "commitCorrection");
  535.                 }
  536.                 return true;
  537.             }
  538.  
  539.             public boolean commitText(CharSequence text, int newCursorPosition) {
  540.                 if (LOG_IME) {
  541.                     Log.w(TAG, "commitText(\"" + text + "\", " + newCursorPosition + ")");
  542.                 }
  543.                 clearComposingText();
  544.                 sendText(text);
  545.                 setImeBuffer("");
  546.                 mCursor = 0;
  547.                 return true;
  548.             }
  549.  
  550.             private void clearComposingText() {
  551.                 int len = mImeBuffer.length();
  552.                 if (mComposingTextStart > len || mComposingTextEnd > len) {
  553.                     mComposingTextEnd = mComposingTextStart = 0;
  554.                     return;
  555.                 }
  556.                 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
  557.                     mImeBuffer.substring(mComposingTextEnd));
  558.                 if (mCursor < mComposingTextStart) {
  559.                     // do nothing
  560.                 } else if (mCursor < mComposingTextEnd) {
  561.                     mCursor = mComposingTextStart;
  562.                 } else {
  563.                     mCursor -= mComposingTextEnd - mComposingTextStart;
  564.                 }
  565.                 mComposingTextEnd = mComposingTextStart = 0;
  566.             }
  567.  
  568.             public boolean deleteSurroundingText(int leftLength, int rightLength) {
  569.                 if (LOG_IME) {
  570.                     Log.w(TAG, "deleteSurroundingText(" + leftLength +
  571.                             "," + rightLength + ")");
  572.                 }
  573.                 if (leftLength > 0) {
  574.                     for (int i = 0; i < leftLength; i++) {
  575.                         sendKeyEvent(
  576.                             new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
  577.                     }
  578.                 } else if ((leftLength == 0) && (rightLength == 0)) {
  579.                     // Delete key held down / repeating
  580.                     sendKeyEvent(
  581.                         new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
  582.                 }
  583.                 // TODO: handle forward deletes.
  584.                 return true;
  585.             }
  586.  
  587.             public boolean performEditorAction(int actionCode) {
  588.                 if (LOG_IME) {
  589.                     Log.w(TAG, "performEditorAction(" + actionCode + ")");
  590.                 }
  591.                 if (actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
  592.                     // The "return" key has been pressed on the IME.
  593.                     sendText("\r");
  594.                 }
  595.                 return true;
  596.             }
  597.  
  598.             public boolean sendKeyEvent(KeyEvent event) {
  599.                 if (LOG_IME) {
  600.                     Log.w(TAG, "sendKeyEvent(" + event + ")");
  601.                 }
  602.                 // Some keys are sent here rather than to commitText.
  603.                 // In particular, del and the digit keys are sent here.
  604.                 // (And I have reports that the HTC Magic also sends Return here.)
  605.                 // As a bit of defensive programming, handle every key.
  606.                 dispatchKeyEvent(event);
  607.                 return true;
  608.             }
  609.  
  610.             public boolean setComposingText(CharSequence text, int newCursorPosition) {
  611.                 if (LOG_IME) {
  612.                     Log.w(TAG, "setComposingText(\"" + text + "\", " + newCursorPosition + ")");
  613.                 }
  614.                 int len = mImeBuffer.length();
  615.                 if (mComposingTextStart > len || mComposingTextEnd > len) {
  616.                     return false;
  617.                 }
  618.                 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
  619.                     text + mImeBuffer.substring(mComposingTextEnd));
  620.                 mComposingTextEnd = mComposingTextStart + text.length();
  621.                 mCursor = newCursorPosition > 0 ? mComposingTextEnd + newCursorPosition - 1
  622.                         : mComposingTextStart - newCursorPosition;
  623.                 return true;
  624.             }
  625.  
  626.             public boolean setSelection(int start, int end) {
  627.                 if (LOG_IME) {
  628.                     Log.w(TAG, "setSelection" + start + "," + end);
  629.                 }
  630.                 int length = mImeBuffer.length();
  631.                 if (start == end && start > 0 && start < length) {
  632.                     mSelectedTextStart = mSelectedTextEnd = 0;
  633.                     mCursor = start;
  634.                 } else if (start < end && start > 0 && end < length) {
  635.                     mSelectedTextStart = start;
  636.                     mSelectedTextEnd = end;
  637.                     mCursor = start;
  638.                 }
  639.                 return true;
  640.             }
  641.  
  642.             public boolean setComposingRegion(int start, int end) {
  643.                 if (LOG_IME) {
  644.                     Log.w(TAG, "setComposingRegion " + start + "," + end);
  645.                 }
  646.                 if (start < end && start > 0 && end < mImeBuffer.length()) {
  647.                     clearComposingText();
  648.                     mComposingTextStart = start;
  649.                     mComposingTextEnd = end;
  650.                 }
  651.                 return true;
  652.             }
  653.  
  654.             public CharSequence getSelectedText(int flags) {
  655.                 if (LOG_IME) {
  656.                     Log.w(TAG, "getSelectedText " + flags);
  657.                 }
  658.                 int len = mImeBuffer.length();
  659.                 if (mSelectedTextEnd >= len || mSelectedTextStart > mSelectedTextEnd) {
  660.                     return "";
  661.                 }
  662.                 return mImeBuffer.substring(mSelectedTextStart, mSelectedTextEnd+1);
  663.             }
  664.  
  665.         };
  666.     }
  667.  
  668.     private void setImeBuffer(String buffer) {
  669.         if (!buffer.equals(mImeBuffer)) {
  670.             invalidate();
  671.         }
  672.         mImeBuffer = buffer;
  673.     }
  674.  
  675.     /**
  676.      * Get the terminal emulator's keypad application mode.
  677.      */
  678.     public boolean getKeypadApplicationMode() {
  679.         return mEmulator.getKeypadApplicationMode();
  680.     }
  681.  
  682.     /**
  683.      * Set a {@link android.view.GestureDetector.OnGestureListener
  684.      * GestureDetector.OnGestureListener} to receive gestures performed on this
  685.      * view.  Can be used to implement additional
  686.      * functionality via touch gestures or override built-in gestures.
  687.      *
  688.      * @param listener The {@link
  689.      *                 android.view.GestureDetector.OnGestureListener
  690.      *                 GestureDetector.OnGestureListener} which will receive
  691.      *                 gestures.
  692.      */
  693.     public void setExtGestureListener(GestureDetector.OnGestureListener listener) {
  694.         mExtGestureListener = listener;
  695.     }
  696.  
  697.     /**
  698.      * Compute the vertical range that the vertical scrollbar represents.
  699.      */
  700.     @Override
  701.     protected int computeVerticalScrollRange() {
  702.         return mTranscriptScreen.getActiveRows();
  703.     }
  704.  
  705.     /**
  706.      * Compute the vertical extent of the horizontal scrollbar's thumb within
  707.      * the vertical range. This value is used to compute the length of the thumb
  708.      * within the scrollbar's track.
  709.      */
  710.     @Override
  711.     protected int computeVerticalScrollExtent() {
  712.         return mRows;
  713.     }
  714.  
  715.     /**
  716.      * Compute the vertical offset of the vertical scrollbar's thumb within the
  717.      * horizontal range. This value is used to compute the position of the thumb
  718.      * within the scrollbar's track.
  719.      */
  720.     @Override
  721.     protected int computeVerticalScrollOffset() {
  722.         return mTranscriptScreen.getActiveRows() + mTopRow - mRows;
  723.     }
  724.  
  725.     /**
  726.      * Call this to initialize the view.
  727.      */
  728.     private void initialize() {
  729.         TermSession session = mTermSession;
  730.  
  731.         updateText();
  732.  
  733.         mTranscriptScreen = session.getTranscriptScreen();
  734.         mEmulator = session.getEmulator();
  735.         session.setUpdateCallback(mUpdateNotify);
  736.  
  737.         requestFocus();
  738.     }
  739.  
  740.     /**
  741.      * Get the {@link TermSession} corresponding to this view.
  742.      *
  743.      * @return The {@link TermSession} object for this view.
  744.      */
  745.     public TermSession getTermSession() {
  746.         return mTermSession;
  747.     }
  748.  
  749.     /**
  750.      * Get the width of the visible portion of this view.
  751.      *
  752.      * @return The width of the visible portion of this view, in pixels.
  753.      */
  754.     public int getVisibleWidth() {
  755.         return mVisibleWidth;
  756.     }
  757.  
  758.     /**
  759.      * Get the height of the visible portion of this view.
  760.      *
  761.      * @return The height of the visible portion of this view, in pixels.
  762.      */
  763.     public int getVisibleHeight() {
  764.         return mVisibleHeight;
  765.     }
  766.  
  767.     /**
  768.      * Page the terminal view (scroll it up or down by <code>delta</code>
  769.      * screenfuls).
  770.      *
  771.      * @param delta The number of screens to scroll. Positive means scroll down,
  772.      *        negative means scroll up.
  773.      */
  774.     public void page(int delta) {
  775.         mTopRow =
  776.                 Math.min(0, Math.max(-(mTranscriptScreen
  777.                         .getActiveTranscriptRows()), mTopRow + mRows * delta));
  778.         invalidate();
  779.     }
  780.  
  781.     /**
  782.      * Page the terminal view horizontally.
  783.      *
  784.      * @param deltaColumns the number of columns to scroll. Positive scrolls to
  785.      *        the right.
  786.      */
  787.     public void pageHorizontal(int deltaColumns) {
  788.         mLeftColumn =
  789.                 Math.max(0, Math.min(mLeftColumn + deltaColumns, mColumns
  790.                         - mVisibleColumns));
  791.         invalidate();
  792.     }
  793.  
  794.     /**
  795.      * Sets the text size, which in turn sets the number of rows and columns.
  796.      *
  797.      * @param fontSize the new font size, in density-independent pixels.
  798.      */
  799.     public void setTextSize(int fontSize) {
  800.         mTextSize = (int) (fontSize * mDensity);
  801.         updateText();
  802.     }
  803.  
  804.     /**
  805.      * Sets style information about the cursor.
  806.      *
  807.      * @param style The style of the cursor.
  808.      * @param blink Whether the cursor should blink.
  809.      */
  810.     public void setCursorStyle(int style, int blink) {
  811.         mCursorStyle = style;
  812.         if (blink != 0 && mCursorBlink == 0) {
  813.             mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
  814.         } else if (blink == 0 && mCursorBlink != 0) {
  815.             mHandler.removeCallbacks(mBlinkCursor);
  816.         }
  817.         mCursorBlink = blink;
  818.     }
  819.  
  820.     /**
  821.      * Sets the IME mode ("cooked" or "raw").
  822.      *
  823.      * @param useCookedIME Whether the IME should be used in cooked mode.
  824.      */
  825.     public void setUseCookedIME(boolean useCookedIME) {
  826.         mUseCookedIme = useCookedIME;
  827.     }
  828.  
  829.     // Begin GestureDetector.OnGestureListener methods
  830.  
  831.     public boolean onSingleTapUp(MotionEvent e) {
  832.         if (mExtGestureListener != null && mExtGestureListener.onSingleTapUp(e)) {
  833.             return true;
  834.         }
  835.         requestFocus();
  836.         return true;
  837.     }
  838.  
  839.     public void onLongPress(MotionEvent e) {
  840.         // XXX hook into external gesture listener
  841.         showContextMenu();
  842.     }
  843.  
  844.     public boolean onScroll(MotionEvent e1, MotionEvent e2,
  845.             float distanceX, float distanceY) {
  846.         if (mExtGestureListener != null && mExtGestureListener.onScroll(e1, e2, distanceX, distanceY)) {
  847.             return true;
  848.         }
  849.         distanceY += mScrollRemainder;
  850.         int deltaRows = (int) (distanceY / mCharacterHeight);
  851.         mScrollRemainder = distanceY - deltaRows * mCharacterHeight;
  852.         mTopRow =
  853.             Math.min(0, Math.max(-(mTranscriptScreen
  854.                     .getActiveTranscriptRows()), mTopRow + deltaRows));
  855.         invalidate();
  856.  
  857.         return true;
  858.     }
  859.  
  860.     public void onSingleTapConfirmed(MotionEvent e) {
  861.     }
  862.  
  863.     public boolean onJumpTapDown(MotionEvent e1, MotionEvent e2) {
  864.        // Scroll to bottom
  865.        mTopRow = 0;
  866.        invalidate();
  867.        return true;
  868.     }
  869.  
  870.     public boolean onJumpTapUp(MotionEvent e1, MotionEvent e2) {
  871.         // Scroll to top
  872.         mTopRow = -mTranscriptScreen.getActiveTranscriptRows();
  873.         invalidate();
  874.         return true;
  875.     }
  876.  
  877.     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  878.             float velocityY) {
  879.         if (mExtGestureListener != null && mExtGestureListener.onFling(e1, e2, velocityX, velocityY)) {
  880.             return true;
  881.         }
  882.         float SCALE = 0.25f;
  883.         mScroller.fling(0, mTopRow,
  884.                 -(int) (velocityX * SCALE), -(int) (velocityY * SCALE),
  885.                 0, 0,
  886.                 -mTranscriptScreen.getActiveTranscriptRows(), 0);
  887.         mScrollRemainder = 0.0f;
  888.         // onScroll(e1, e2, 0.1f * velocityX, -0.1f * velocityY);
  889.         post(mFlingRunner);
  890.         return true;
  891.     }
  892.  
  893.     public void onShowPress(MotionEvent e) {
  894.         if (mExtGestureListener != null) {
  895.             mExtGestureListener.onShowPress(e);
  896.         }
  897.     }
  898.  
  899.     public boolean onDown(MotionEvent e) {
  900.         if (mExtGestureListener != null && mExtGestureListener.onDown(e)) {
  901.             return true;
  902.         }
  903.         mScrollRemainder = 0.0f;
  904.         return true;
  905.     }
  906.  
  907.     // End GestureDetector.OnGestureListener methods
  908.  
  909.     @Override
  910.     public boolean onTouchEvent(MotionEvent ev) {
  911.         if (mIsSelectingText) {
  912.             return onTouchEventWhileSelectingText(ev);
  913.         } else {
  914.             return mGestureDetector.onTouchEvent(ev);
  915.         }
  916.     }
  917.  
  918.     @SuppressWarnings("deprecation")
  919.     private boolean onTouchEventWhileSelectingText(MotionEvent ev) {
  920.         int action = ev.getAction();
  921.         int cx = (int)(ev.getX() / mCharacterWidth);
  922.         int cy = Math.max(0,
  923.                 (int)((ev.getY() + SELECT_TEXT_OFFSET_Y * mScaledDensity)
  924.                         / mCharacterHeight) + mTopRow);
  925.         switch (action) {
  926.         case MotionEvent.ACTION_DOWN:
  927.             mSelXAnchor = cx;
  928.             mSelYAnchor = cy;
  929.             mSelX1 = cx;
  930.             mSelY1 = cy;
  931.             mSelX2 = mSelX1;
  932.             mSelY2 = mSelY1;
  933.             break;
  934.         case MotionEvent.ACTION_MOVE:
  935.         case MotionEvent.ACTION_UP:
  936.             int minx = Math.min(mSelXAnchor, cx);
  937.             int maxx = Math.max(mSelXAnchor, cx);
  938.             int miny = Math.min(mSelYAnchor, cy);
  939.             int maxy = Math.max(mSelYAnchor, cy);
  940.             mSelX1 = minx;
  941.             mSelY1 = miny;
  942.             mSelX2 = maxx;
  943.             mSelY2 = maxy;
  944.             if (action == MotionEvent.ACTION_UP) {
  945.                 ClipboardManager clip = (ClipboardManager)
  946.                      getContext().getApplicationContext()
  947.                          .getSystemService(Context.CLIPBOARD_SERVICE);
  948.                 clip.setText(getSelectedText().trim());
  949.                 toggleSelectingText();
  950.             }
  951.             invalidate();
  952.             break;
  953.         default:
  954.             toggleSelectingText();
  955.             invalidate();
  956.             break;
  957.         }
  958.         return true;
  959.     }
  960.  
  961.     /**
  962.      * Called when a key is pressed in the view.
  963.      *
  964.      * @param keyCode The keycode of the key which was pressed.
  965.      * @param event A {@link KeyEvent} describing the event.
  966.      * @return Whether the event was handled.
  967.      */
  968.     @Override
  969.     public boolean onKeyDown(int keyCode, KeyEvent event) {
  970.         if (LOG_KEY_EVENTS) {
  971.             Log.w(TAG, "onKeyDown " + keyCode);
  972.         }
  973.         if (handleControlKey(keyCode, true)) {
  974.             return true;
  975.         } else if (handleFnKey(keyCode, true)) {
  976.             return true;
  977.         } else if (isSystemKey(keyCode, event)) {
  978.             if (! isInterceptedSystemKey(keyCode) ) {
  979.                 // Don't intercept the system keys
  980.                 return super.onKeyDown(keyCode, event);
  981.             }
  982.         }
  983.  
  984.         // Translate the keyCode into an ASCII character.
  985.  
  986.         try {
  987.             mKeyListener.keyDown(keyCode, event, getKeypadApplicationMode(),
  988.                     TermKeyListener.isEventFromToggleDevice(event));
  989.         } catch (IOException e) {
  990.             // Ignore I/O exceptions
  991.         }
  992.         return true;
  993.     }
  994.  
  995.     /** Do we want to intercept this system key? */
  996.     private boolean isInterceptedSystemKey(int keyCode) {
  997.         return keyCode == KeyEvent.KEYCODE_BACK && mBackKeySendsCharacter;
  998.     }
  999.  
  1000.     /**
  1001.      * Called when a key is released in the view.
  1002.      *
  1003.      * @param keyCode The keycode of the key which was released.
  1004.      * @param event A {@link KeyEvent} describing the event.
  1005.      * @return Whether the event was handled.
  1006.      */
  1007.     @Override
  1008.     public boolean onKeyUp(int keyCode, KeyEvent event) {
  1009.         if (LOG_KEY_EVENTS) {
  1010.             Log.w(TAG, "onKeyUp " + keyCode);
  1011.         }
  1012.         if (handleControlKey(keyCode, false)) {
  1013.             return true;
  1014.         } else if (handleFnKey(keyCode, false)) {
  1015.             return true;
  1016.         } else if (isSystemKey(keyCode, event)) {
  1017.             // Don't intercept the system keys
  1018.             if ( ! isInterceptedSystemKey(keyCode) ) {
  1019.                 return super.onKeyUp(keyCode, event);
  1020.             }
  1021.         }
  1022.  
  1023.         mKeyListener.keyUp(keyCode, event);
  1024.         clearSpecialKeyStatus();
  1025.         return true;
  1026.     }
  1027.  
  1028.     @Override
  1029.     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
  1030.         if (sTrapAltAndMeta) {
  1031.             boolean altEsc = mKeyListener.getAltSendsEsc();
  1032.             boolean altOn = (event.getMetaState() & KeyEvent.META_ALT_ON) != 0;
  1033.             boolean metaOn = (event.getMetaState() & KeyEvent.META_META_ON) != 0;
  1034.             boolean altPressed = (keyCode == KeyEvent.KEYCODE_ALT_LEFT)
  1035.                     || (keyCode == KeyEvent.KEYCODE_ALT_RIGHT);
  1036.             boolean altActive = mKeyListener.isAltActive();
  1037.             if (altEsc && (altOn || altPressed || altActive || metaOn)) {
  1038.                 if (event.getAction() == KeyEvent.ACTION_DOWN) {
  1039.                     return onKeyDown(keyCode, event);
  1040.                 } else {
  1041.                     return onKeyUp(keyCode, event);
  1042.                 }
  1043.             }
  1044.         }
  1045.  
  1046.         return super.onKeyPreIme(keyCode, event);
  1047.     };
  1048.  
  1049.     private boolean handleControlKey(int keyCode, boolean down) {
  1050.         if (keyCode == mControlKeyCode) {
  1051.             if (LOG_KEY_EVENTS) {
  1052.                 Log.w(TAG, "handleControlKey " + keyCode);
  1053.             }
  1054.             mKeyListener.handleControlKey(down);
  1055.             return true;
  1056.         }
  1057.         return false;
  1058.     }
  1059.  
  1060.     private boolean handleFnKey(int keyCode, boolean down) {
  1061.         if (keyCode == mFnKeyCode) {
  1062.             if (LOG_KEY_EVENTS) {
  1063.                 Log.w(TAG, "handleFnKey " + keyCode);
  1064.             }
  1065.             mKeyListener.handleFnKey(down);
  1066.             return true;
  1067.         }
  1068.         return false;
  1069.     }
  1070.  
  1071.     private boolean isSystemKey(int keyCode, KeyEvent event) {
  1072.         return event.isSystem();
  1073.     }
  1074.  
  1075.     private void clearSpecialKeyStatus() {
  1076.         if (mIsControlKeySent) {
  1077.             mIsControlKeySent = false;
  1078.             mKeyListener.handleControlKey(false);
  1079.         }
  1080.         if (mIsFnKeySent) {
  1081.             mIsFnKeySent = false;
  1082.             mKeyListener.handleFnKey(false);
  1083.         }
  1084.     }
  1085.  
  1086.     private void updateText() {
  1087.         ColorScheme scheme = mColorScheme;
  1088.         if (mTextSize > 0) {
  1089.             mTextRenderer = new PaintRenderer(mTextSize, scheme);
  1090.         }
  1091.         else {
  1092.             mTextRenderer = new Bitmap4x8FontRenderer(getResources(), scheme);
  1093.         }
  1094.  
  1095.         mForegroundPaint.setColor(scheme.getForeColor());
  1096.         mBackgroundPaint.setColor(scheme.getBackColor());
  1097.         mCharacterWidth = mTextRenderer.getCharacterWidth();
  1098.         mCharacterHeight = mTextRenderer.getCharacterHeight();
  1099.  
  1100.         updateSize(true);
  1101.     }
  1102.  
  1103.     /**
  1104.      * This is called during layout when the size of this view has changed. If
  1105.      * you were just added to the view hierarchy, you're called with the old
  1106.      * values of 0.
  1107.      */
  1108.     @Override
  1109.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  1110.         if (mTermSession == null) {
  1111.             // Not ready, defer until TermSession is attached
  1112.             mDeferInit = true;
  1113.             return;
  1114.         }
  1115.  
  1116.         if (!mKnownSize) {
  1117.             mKnownSize = true;
  1118.             initialize();
  1119.         } else {
  1120.             updateSize(false);
  1121.         }
  1122.     }
  1123.  
  1124.     private void updateSize(int w, int h) {
  1125.         mColumns = Math.max(1, (int) (((float) w) / mCharacterWidth));
  1126.         mVisibleColumns = (int) (((float) mVisibleWidth) / mCharacterWidth);
  1127.  
  1128.         mTopOfScreenMargin = mTextRenderer.getTopMargin();
  1129.         mRows = Math.max(1, (h - mTopOfScreenMargin) / mCharacterHeight);
  1130.         mTermSession.updateSize(mColumns, mRows);
  1131.  
  1132.         // Reset our paging:
  1133.         mTopRow = 0;
  1134.         mLeftColumn = 0;
  1135.  
  1136.         invalidate();
  1137.     }
  1138.  
  1139.     /**
  1140.      * Update the view's idea of its size.
  1141.      *
  1142.      * @param force Whether a size adjustment should be performed even if the
  1143.      *              view's size has not changed.
  1144.      */
  1145.     public void updateSize(boolean force) {
  1146.         if (mKnownSize) {
  1147.             int w = getWidth();
  1148.             int h = getHeight();
  1149.             // Log.w("Term", "(" + w + ", " + h + ")");
  1150.             if (force || w != mVisibleWidth || h != mVisibleHeight) {
  1151.                 mVisibleWidth = w;
  1152.                 mVisibleHeight = h;
  1153.                 updateSize(mVisibleWidth, mVisibleHeight);
  1154.             }
  1155.         }
  1156.     }
  1157.  
  1158.     /**
  1159.      * Draw the view to the provided {@link Canvas}.
  1160.      *
  1161.      * @param canvas The {@link Canvas} to draw the view to.
  1162.      */
  1163.     @Override
  1164.     protected void onDraw(Canvas canvas) {
  1165.         updateSize(false);
  1166.  
  1167.         if (mEmulator == null) {
  1168.             // Not ready yet
  1169.             return;
  1170.         }
  1171.  
  1172.         int w = getWidth();
  1173.         int h = getHeight();
  1174.  
  1175.         boolean reverseVideo = mEmulator.getReverseVideo();
  1176.         mTextRenderer.setReverseVideo(reverseVideo);
  1177.  
  1178.         Paint backgroundPaint =
  1179.                 reverseVideo ? mForegroundPaint : mBackgroundPaint;
  1180.         canvas.drawRect(0, 0, w, h, backgroundPaint);
  1181.         float x = -mLeftColumn * mCharacterWidth;
  1182.         float y = mCharacterHeight + mTopOfScreenMargin;
  1183.         int endLine = mTopRow + mRows;
  1184.         int cx = mEmulator.getCursorCol();
  1185.         int cy = mEmulator.getCursorRow();
  1186.         for (int i = mTopRow; i < endLine; i++) {
  1187.             int cursorX = -1;
  1188.             if (i == cy && mCursorVisible) {
  1189.                 cursorX = cx;
  1190.             }
  1191.             int selx1 = -1;
  1192.             int selx2 = -1;
  1193.             if ( i >= mSelY1 && i <= mSelY2 ) {
  1194.                 if ( i == mSelY1 ) {
  1195.                     selx1 = mSelX1;
  1196.                 }
  1197.                 if ( i == mSelY2 ) {
  1198.                     selx2 = mSelX2;
  1199.                 } else {
  1200.                     selx2 = mColumns;
  1201.                 }
  1202.             }
  1203.             mTranscriptScreen.drawText(i, canvas, x, y, mTextRenderer, cursorX, selx1, selx2, mImeBuffer);
  1204.             y += mCharacterHeight;
  1205.         }
  1206.     }
  1207.  
  1208.     private void ensureCursorVisible() {
  1209.         mTopRow = 0;
  1210.         if (mVisibleColumns > 0) {
  1211.             int cx = mEmulator.getCursorCol();
  1212.             int visibleCursorX = mEmulator.getCursorCol() - mLeftColumn;
  1213.             if (visibleCursorX < 0) {
  1214.                 mLeftColumn = cx;
  1215.             } else if (visibleCursorX >= mVisibleColumns) {
  1216.                 mLeftColumn = (cx - mVisibleColumns) + 1;
  1217.             }
  1218.         }
  1219.     }
  1220.  
  1221.     /**
  1222.      * Toggle text selection mode in the view.
  1223.      */
  1224.     public void toggleSelectingText() {
  1225.         mIsSelectingText = ! mIsSelectingText;
  1226.         setVerticalScrollBarEnabled( ! mIsSelectingText );
  1227.         if ( ! mIsSelectingText ) {
  1228.             mSelX1 = -1;
  1229.             mSelY1 = -1;
  1230.             mSelX2 = -1;
  1231.             mSelY2 = -1;
  1232.         }
  1233.     }
  1234.  
  1235.     /**
  1236.      * Whether the view is currently in text selection mode.
  1237.      */
  1238.     public boolean getSelectingText() {
  1239.         return mIsSelectingText;
  1240.     }
  1241.  
  1242.     /**
  1243.      * Get selected text.
  1244.      *
  1245.      * @return A {@link String} with the selected text.
  1246.      */
  1247.     public String getSelectedText() {
  1248.         return mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2);
  1249.     }
  1250.  
  1251.     /**
  1252.      * Send a Ctrl key event to the terminal.
  1253.      */
  1254.     public void sendControlKey() {
  1255.         mIsControlKeySent = true;
  1256.         mKeyListener.handleControlKey(true);
  1257.     }
  1258.  
  1259.     /**
  1260.      * Send an Fn key event to the terminal.  The Fn modifier key can be used to
  1261.      * generate various special characters and escape codes.
  1262.      */
  1263.     public void sendFnKey() {
  1264.         mIsFnKeySent = true;
  1265.         mKeyListener.handleFnKey(true);
  1266.     }
  1267.  
  1268.     /**
  1269.      * Set the key code to be sent when the Back key is pressed.
  1270.      */
  1271.     public void setBackKeyCharacter(int keyCode) {
  1272.         mKeyListener.setBackKeyCharacter(keyCode);
  1273.         mBackKeySendsCharacter = (keyCode != 0);
  1274.     }
  1275.  
  1276.     /**
  1277.      * Set whether to prepend the ESC keycode to the character when when pressing
  1278.      * the ALT Key.
  1279.      * @param flag
  1280.      */
  1281.     public void setAltSendsEsc(boolean flag) {
  1282.         mKeyListener.setAltSendsEsc(flag);
  1283.     }
  1284.  
  1285.     /**
  1286.      * Set the keycode corresponding to the Ctrl key.
  1287.      */
  1288.     public void setControlKeyCode(int keyCode) {
  1289.         mControlKeyCode = keyCode;
  1290.     }
  1291.  
  1292.     /**
  1293.      * Set the keycode corresponding to the Fn key.
  1294.      */
  1295.     public void setFnKeyCode(int keyCode) {
  1296.         mFnKeyCode = keyCode;
  1297.     }
  1298. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement