package net.momodalo.app.vimtouch; import android.content.Context; import android.text.Editable; import android.text.Selection; import android.text.Spanned; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputMethodManager; public class VimInputConnection extends BaseInputConnection { private boolean DEBUG = false; protected static final String LOGTAG = "VimInputConnection"; private TermView mView; private boolean mBatchMode; public VimInputConnection(TermView view) { super(view, true); mView = view; initEditable(Exec.getCurrentLine(0)); } @Override public boolean beginBatchEdit() { mBatchMode = true; return true; } @Override public boolean endBatchEdit() { mBatchMode = false; return true; } @Override public boolean sendKeyEvent(KeyEvent event) { mView.dispatchKeyEvent(event); mView.lateCheckInserted(); return true; } private void syncEditable() { Editable editable = getEditable(); if(!editable.toString().equals(Exec.getCurrentLine(0))) { setEditable(Exec.getCurrentLine(0)); } int end = Selection.getSelectionEnd(editable); int col = Exec.getCursorCol()>0?Exec.getCurrentLine(Exec.getCursorCol()).length():0; if(col != end){ setSelection(col,col); } } @Override public boolean setComposingText(CharSequence text, int newCursorPosition) { if(DEBUG) Log.e(LOGTAG, "setComposingText " + text); if(!Exec.isInsertMode()){ resetIME(); return true; } Editable editable = getEditable(); syncEditable(); int start = getComposingSpanStart(editable); int end = getComposingSpanEnd(editable); if (start < 0 || end < 0) { int col = Exec.getCursorCol()>0?Exec.getCurrentLine(Exec.getCursorCol()).length():0; setSelection(col,col); //start = Selection.getSelectionStart(editable); //end = Selection.getSelectionEnd(editable); start = col; end = col; } if (end < start) { int temp = end; end = start; start = temp; } super.setComposingText(text, newCursorPosition); Exec.lineReplace(editable.toString()); end = Selection.getSelectionEnd(editable); int wide = end>0?editable.subSequence(0,end-1).toString().getBytes().length:0; Exec.setCursorCol(wide+1); Exec.updateScreen(); return true; } @Override public boolean commitText(CharSequence text, int newCursorPosition) { if(DEBUG) Log.e(LOGTAG, "commitText " + text); if(!Exec.isInsertMode()) { resetIME(); return true; } setComposingText(text, newCursorPosition); finishComposingText(); return true; } @Override public boolean deleteSurroundingText(int leftLength, int rightLength) { if(DEBUG) Log.e(LOGTAG, "deleteSurroundingText " + leftLength +" "+ rightLength); if(!Exec.isInsertMode()){ resetIME(); return true; } Editable editable = getEditable(); syncEditable(); boolean res = super.deleteSurroundingText(leftLength, rightLength); Exec.lineReplace(editable.toString()); int end = Selection.getSelectionEnd(editable); int wide = end>0?editable.subSequence(0,end).toString().getBytes().length:0; Exec.setCursorCol(wide+1); Exec.updateScreen(); return res; } public void setEditable(String contents) { Editable editable = getEditable(); editable.removeSpan(this); editable.replace(0, editable.length(), contents); editable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); Selection.setSelection(editable, contents.length()); } public void initEditable(String contents) { Editable editable = getEditable(); editable.replace(0, editable.length(), contents); editable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); Selection.setSelection(editable, contents.length()); } @Override public boolean setSelection(int start, int end) { boolean result = super.setSelection(start, end); return result; } @Override public boolean setComposingRegion(int start, int end) { boolean result = super.setComposingRegion(start, end); return result; } private ExtractedTextRequest mUpdateRequest; private final ExtractedText mUpdateExtract = new ExtractedText(); @Override public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) { if(DEBUG) Log.e(LOGTAG, "getExtractedText"); if(!Exec.isInsertMode()) { resetIME(); return null; } if (req == null) return null; final Editable content = getEditable(); if (content == null) return null; if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) mUpdateRequest = req; syncEditable(); ExtractedText extract = new ExtractedText(); extract.flags = 0; extract.partialStartOffset = -1; extract.partialEndOffset = -1; extract.selectionStart = Selection.getSelectionStart(content); extract.selectionEnd = Selection.getSelectionEnd(content); extract.startOffset = 0; try { extract.text = content.toString(); } catch (IndexOutOfBoundsException iob) { Log.d(LOGTAG, "IndexOutOfBoundsException thrown from getExtractedText(). start: " + Selection.getSelectionStart(content) + " end: " + Selection.getSelectionEnd(content)); return null; } if(DEBUG) Log.e(LOGTAG, "getExtractedText result " + extract); return extract; } public void resetIME() { Context context = mView.getContext(); InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); imm.restartInput(mView); } public void notifyTextChange() { String text = Exec.getCurrentLine(0); if (!mBatchMode) { if (!text.contentEquals(getEditable())) { setEditable(text); } } Editable content = getEditable(); Context context = mView.getContext(); InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); int newEnd = Exec.getCursorCol()>0?Exec.getCurrentLine(Exec.getCursorCol()).length():0; int end = Selection.getSelectionEnd(content); if(newEnd != end){ setSelection( newEnd, newEnd); } if (mUpdateRequest == null){ resetIME(); return; } mUpdateExtract.flags = 0; mUpdateExtract.partialStartOffset = 0; mUpdateExtract.partialEndOffset = 1; // Faster to not query for selection mUpdateExtract.selectionStart = newEnd; mUpdateExtract.selectionEnd = newEnd; mUpdateExtract.text = text; mUpdateExtract.startOffset = 0; imm.updateExtractedText(mView, mUpdateRequest.token, mUpdateExtract); } public CharSequence getTextBeforeCursor(int length, int flags) { if(DEBUG) Log.e(LOGTAG, "getTextBeforeCursor " + length); if(!Exec.isInsertMode()){ resetIME(); return null; } syncEditable(); int col = Exec.getCursorCol(); if(col == 0) return ""; String line = Exec.getCurrentLine(col); if(line.length() == 0) return ""; if(DEBUG) Log.e(LOGTAG, "getTextBeforeCursor result " + line); if(length > line.length()) return line; else return line.subSequence(line.length() - length, line.length()); } public CharSequence getTextAfterCursor(int length, int flags) { if(DEBUG) Log.e(LOGTAG, "getTextAfterCursor " + length); if(!Exec.isInsertMode()){ resetIME(); return null; } syncEditable(); String line = Exec.getCurrentLine(0); if(line.length() == 0) return ""; String before = Exec.getCurrentLine(Exec.getCursorCol()); String after = line.substring(before.length()); if(DEBUG) Log.e(LOGTAG, "getTextAfterCursor result " + after); if(length > after.length()) return after; return after.subSequence(0 , length); } }