167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown/* 267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Copyright (C) 2010 The Android Open Source Project 367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * you may not use this file except in compliance with the License. 667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * You may obtain a copy of the License at 767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * http://www.apache.org/licenses/LICENSE-2.0 967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 1067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Unless required by applicable law or agreed to in writing, software 1167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 1267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * See the License for the specific language governing permissions and 1467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * limitations under the License. 1567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 1667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 1767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownpackage android.text.method; 1867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 1967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownimport android.text.Layout; 2067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownimport android.text.Spannable; 218f34567c71003505456a9b1a0d461a4e62883d70Jeff Brownimport android.view.InputDevice; 2267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownimport android.view.KeyEvent; 2367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownimport android.view.MotionEvent; 2467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownimport android.widget.TextView; 2567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 2667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown/** 2767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Base classes for movement methods. 2867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 2967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownpublic class BaseMovementMethod implements MovementMethod { 3067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 3167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean canSelectArbitrarily() { 3267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 3367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 3467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 3567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 3667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public void initialize(TextView widget, Spannable text) { 3767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 3867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 3967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 4067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean onKeyDown(TextView widget, Spannable text, int keyCode, KeyEvent event) { 4167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown final int movementMetaState = getMovementMetaState(text, event); 4267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown boolean handled = handleMovementKey(widget, text, keyCode, movementMetaState, event); 4367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (handled) { 4467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown MetaKeyKeyListener.adjustMetaAfterKeypress(text); 4567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown MetaKeyKeyListener.resetLockedMeta(text); 4667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 4767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return handled; 4867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 4967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 5067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 5167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean onKeyOther(TextView widget, Spannable text, KeyEvent event) { 5267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown final int movementMetaState = getMovementMetaState(text, event); 5367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown final int keyCode = event.getKeyCode(); 5467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (keyCode != KeyEvent.KEYCODE_UNKNOWN 5567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown && event.getAction() == KeyEvent.ACTION_MULTIPLE) { 5667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown final int repeat = event.getRepeatCount(); 5767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown boolean handled = false; 5867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown for (int i = 0; i < repeat; i++) { 5967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (!handleMovementKey(widget, text, keyCode, movementMetaState, event)) { 6067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 6167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 6267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown handled = true; 6367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 6467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (handled) { 6567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown MetaKeyKeyListener.adjustMetaAfterKeypress(text); 6667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown MetaKeyKeyListener.resetLockedMeta(text); 6767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 6867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return handled; 6967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 7067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 7167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 7267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 7367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 7467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean onKeyUp(TextView widget, Spannable text, int keyCode, KeyEvent event) { 7567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 7667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 7767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 7867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 7967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public void onTakeFocus(TextView widget, Spannable text, int direction) { 8067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 8167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 8267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 8367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean onTouchEvent(TextView widget, Spannable text, MotionEvent event) { 8467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 8567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 8667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 8767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown @Override 8867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { 8967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 9067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 9167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 928f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown @Override 938f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown public boolean onGenericMotionEvent(TextView widget, Spannable text, MotionEvent event) { 948f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 958f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown switch (event.getAction()) { 968f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown case MotionEvent.ACTION_SCROLL: { 978f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final float vscroll; 988f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final float hscroll; 998f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { 1008f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown vscroll = 0; 1018f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1028f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } else { 1038f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); 1048f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 1058f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1068f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 1078f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown boolean handled = false; 1088f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (hscroll < 0) { 1098f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown handled |= scrollLeft(widget, text, (int)Math.ceil(-hscroll)); 1108f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } else if (hscroll > 0) { 1118f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown handled |= scrollRight(widget, text, (int)Math.ceil(hscroll)); 1128f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1138f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (vscroll < 0) { 1148f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown handled |= scrollUp(widget, text, (int)Math.ceil(-vscroll)); 1158f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } else if (vscroll > 0) { 1168f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown handled |= scrollDown(widget, text, (int)Math.ceil(vscroll)); 1178f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1188f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return handled; 1198f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1208f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1218f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1228f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 1238f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 1248f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 12567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 12667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Gets the meta state used for movement using the modifiers tracked by the text 12767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * buffer as well as those present in the key event. 12867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 12967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * The movement meta state excludes the state of locked modifiers or the SHIFT key 13067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * since they are not used by movement actions (but they may be used for selection). 13167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 13267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 13367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param event The key event. 13467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return The keyboard meta states used for movement. 13567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 13667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected int getMovementMetaState(Spannable buffer, KeyEvent event) { 13767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown // We ignore locked modifiers and SHIFT. 13814f10e5d5f51bc54ca2a45ee62d3cfb6debd3af0Raph Levien int metaState = MetaKeyKeyListener.getMetaState(buffer, event) 13967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown & ~(MetaKeyKeyListener.META_ALT_LOCKED | MetaKeyKeyListener.META_SYM_LOCKED); 14067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return KeyEvent.normalizeMetaState(metaState) & ~KeyEvent.META_SHIFT_MASK; 14167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 14267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 14367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 14467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a movement key action. 14567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * The default implementation decodes the key down and invokes movement actions 14667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * such as {@link #down} and {@link #up}. 14767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * {@link #onKeyDown(TextView, Spannable, int, KeyEvent)} calls this method once 14867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * to handle an {@link KeyEvent#ACTION_DOWN}. 14967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * {@link #onKeyOther(TextView, Spannable, KeyEvent)} calls this method repeatedly 15067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * to handle each repetition of an {@link KeyEvent#ACTION_MULTIPLE}. 15167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 15267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 15367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 15467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param event The key event. 15567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param keyCode The key code. 15667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param movementMetaState The keyboard meta states used for movement. 15767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param event The key event. 15867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 15967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 16067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean handleMovementKey(TextView widget, Spannable buffer, 16167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown int keyCode, int movementMetaState, KeyEvent event) { 16267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown switch (keyCode) { 16367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_DPAD_LEFT: 16467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 16567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return left(widget, buffer); 16667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 167e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey KeyEvent.META_CTRL_ON)) { 168e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return leftWord(widget, buffer); 169e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 17067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 17167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return lineStart(widget, buffer); 17267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 17367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 17467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 17567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_DPAD_RIGHT: 17667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 17767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return right(widget, buffer); 17867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 179e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey KeyEvent.META_CTRL_ON)) { 180e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return rightWord(widget, buffer); 181e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 18267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 18367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return lineEnd(widget, buffer); 18467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 18567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 18667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 18767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_DPAD_UP: 18867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 18967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return up(widget, buffer); 19067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 19167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 19267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return top(widget, buffer); 19367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 19467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 19567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 19667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_DPAD_DOWN: 19767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 19867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return down(widget, buffer); 19967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 20067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 20167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return bottom(widget, buffer); 20267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 20367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 20467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 20567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_PAGE_UP: 20667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 20767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return pageUp(widget, buffer); 20867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 20967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 21067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return top(widget, buffer); 21167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 21267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 21367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 21467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_PAGE_DOWN: 21567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 21667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return pageDown(widget, buffer); 21767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 21867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown KeyEvent.META_ALT_ON)) { 21967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return bottom(widget, buffer); 22067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 22167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 22267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 22367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_MOVE_HOME: 22467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 22567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return home(widget, buffer); 226e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 227e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey KeyEvent.META_CTRL_ON)) { 228e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return top(widget, buffer); 22967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 23067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 23167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 23267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown case KeyEvent.KEYCODE_MOVE_END: 23367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { 23467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return end(widget, buffer); 235e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } else if (KeyEvent.metaStateHasModifiers(movementMetaState, 236e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey KeyEvent.META_CTRL_ON)) { 237e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return bottom(widget, buffer); 23867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 23967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown break; 24067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 24167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 24267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 24367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 24467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 24567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a left movement action. 24667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls left by one character. 24767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 24867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 24967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 25067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 25167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 25267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean left(TextView widget, Spannable buffer) { 25367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 25467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 25567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 25667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 25767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a right movement action. 25867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls right by one character. 25967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 26067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 26167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 26267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 26367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 26467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean right(TextView widget, Spannable buffer) { 26567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 26667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 26767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 26867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 26967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs an up movement action. 27067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls up by one line. 27167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 27267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 27367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 27467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 27567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 27667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean up(TextView widget, Spannable buffer) { 27767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 27867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 27967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 28067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 28167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a down movement action. 28267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls down by one line. 28367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 28467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 28567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 28667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 28767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 28867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean down(TextView widget, Spannable buffer) { 28967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 29067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 29167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 29267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 29367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a page-up movement action. 29467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls up by one page. 29567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 29667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 29767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 29867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 29967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 30067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean pageUp(TextView widget, Spannable buffer) { 30167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 30267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 30367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 30467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 30567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a page-down movement action. 30667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls down by one page. 30767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 30867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 30967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 31067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 31167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 31267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean pageDown(TextView widget, Spannable buffer) { 31367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 31467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 31567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 31667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 31767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a top movement action. 31867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the top of the buffer. 31967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 32067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 32167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 32267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 32367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 32467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean top(TextView widget, Spannable buffer) { 32567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 32667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 32767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 32867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 32967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a bottom movement action. 33067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the bottom of the buffer. 33167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 33267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 33367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 33467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 33567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 33667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean bottom(TextView widget, Spannable buffer) { 33767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 33867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 33967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 34067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 34167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a line-start movement action. 34267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the start of the line. 34367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 34467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 34567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 34667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 34767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 34867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean lineStart(TextView widget, Spannable buffer) { 34967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 35067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 35167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 35267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 353f76a50ce8fdc6aea22cabc77b2977a1a15a79630Ken Wakasa * Performs a line-end movement action. 35467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the end of the line. 35567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 35667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 35767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 35867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 35967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 36067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean lineEnd(TextView widget, Spannable buffer) { 36167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 36267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 36367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 364e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey /** {@hide} */ 365e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey protected boolean leftWord(TextView widget, Spannable buffer) { 366e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return false; 367e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } 368e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey 369e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey /** {@hide} */ 370e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey protected boolean rightWord(TextView widget, Spannable buffer) { 371e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey return false; 372e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey } 373e982dfc1bae36620f67371efc7b0a0f8adc9450dJeff Sharkey 37467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 37567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs a home movement action. 37667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the start of the line or to the top of the 37767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * document depending on whether the insertion point is being moved or 37867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * the document is being scrolled. 37967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 38067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 38167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 38267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 38367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 38467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean home(TextView widget, Spannable buffer) { 38567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 38667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 38767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown 38867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown /** 38967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Performs an end movement action. 39067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Moves the cursor or scrolls to the start of the line or to the top of the 39167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * document depending on whether the insertion point is being moved or 39267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * the document is being scrolled. 39367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * 39467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param widget The text view. 39567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @param buffer The text buffer. 39667b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * @return True if the event was handled. 39767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */ 39867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown protected boolean end(TextView widget, Spannable buffer) { 39967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown return false; 40067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown } 4018f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4028f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getTopLine(TextView widget) { 4038f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return widget.getLayout().getLineForVertical(widget.getScrollY()); 4048f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4058f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4068f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getBottomLine(TextView widget) { 4078f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return widget.getLayout().getLineForVertical(widget.getScrollY() + getInnerHeight(widget)); 4088f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4098f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4108f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getInnerWidth(TextView widget) { 4118f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return widget.getWidth() - widget.getTotalPaddingLeft() - widget.getTotalPaddingRight(); 4128f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4138f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4148f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getInnerHeight(TextView widget) { 4158f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return widget.getHeight() - widget.getTotalPaddingTop() - widget.getTotalPaddingBottom(); 4168f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4178f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4188f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getCharacterWidth(TextView widget) { 4198f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return (int) Math.ceil(widget.getPaint().getFontSpacing()); 4208f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4218f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4228f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getScrollBoundsLeft(TextView widget) { 4238f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 4248f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int topLine = getTopLine(widget); 4258f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int bottomLine = getBottomLine(widget); 4268f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (topLine > bottomLine) { 4278f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return 0; 4288f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4298f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int left = Integer.MAX_VALUE; 4308f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown for (int line = topLine; line <= bottomLine; line++) { 4318f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int lineLeft = (int) Math.floor(layout.getLineLeft(line)); 4328f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (lineLeft < left) { 4338f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown left = lineLeft; 4348f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4358f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4368f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return left; 4378f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4388f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4398f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown private int getScrollBoundsRight(TextView widget) { 4408f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 4418f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int topLine = getTopLine(widget); 4428f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int bottomLine = getBottomLine(widget); 4438f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (topLine > bottomLine) { 4448f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return 0; 4458f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4468f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int right = Integer.MIN_VALUE; 4478f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown for (int line = topLine; line <= bottomLine; line++) { 4488f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int lineRight = (int) Math.ceil(layout.getLineRight(line)); 4498f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (lineRight > right) { 4508f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown right = lineRight; 4518f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4528f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4538f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return right; 4548f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4558f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4568f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 4578f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll left action. 4588f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls left by the specified number of characters. 4598f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 4608f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 4618f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 4628f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param amount The number of characters to scroll by. Must be at least 1. 4638f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 4648f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 4658f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 4668f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollLeft(TextView widget, Spannable buffer, int amount) { 4678f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int minScrollX = getScrollBoundsLeft(widget); 4688f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int scrollX = widget.getScrollX(); 4698f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (scrollX > minScrollX) { 4708f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown scrollX = Math.max(scrollX - getCharacterWidth(widget) * amount, minScrollX); 4718f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown widget.scrollTo(scrollX, widget.getScrollY()); 4728f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 4738f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4748f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 4758f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4768f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4778f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 4788f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll right action. 4798f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls right by the specified number of characters. 4808f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 4818f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 4828f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 4838f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param amount The number of characters to scroll by. Must be at least 1. 4848f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 4858f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 4868f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 4878f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollRight(TextView widget, Spannable buffer, int amount) { 4888f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget); 4898f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int scrollX = widget.getScrollX(); 4908f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (scrollX < maxScrollX) { 4918f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown scrollX = Math.min(scrollX + getCharacterWidth(widget) * amount, maxScrollX); 4928f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown widget.scrollTo(scrollX, widget.getScrollY()); 4938f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 4948f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4958f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 4968f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 4978f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 4988f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 4998f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll up action. 5008f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls up by the specified number of lines. 5018f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 5028f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 5038f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 5048f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param amount The number of lines to scroll by. Must be at least 1. 5058f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 5068f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 5078f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 5088f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollUp(TextView widget, Spannable buffer, int amount) { 5098f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 5108f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int top = widget.getScrollY(); 5118f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int topLine = layout.getLineForVertical(top); 5128f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (layout.getLineTop(topLine) == top) { 5138f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown // If the top line is partially visible, bring it all the way 5148f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown // into view; otherwise, bring the previous line into view. 5158f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown topLine -= 1; 5168f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5178f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (topLine >= 0) { 5188f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown topLine = Math.max(topLine - amount + 1, 0); 5198f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine)); 5208f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 5218f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5228f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 5238f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5248f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 5258f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 5268f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll down action. 5278f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls down by the specified number of lines. 5288f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 5298f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 5308f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 5318f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param amount The number of lines to scroll by. Must be at least 1. 5328f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 5338f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 5348f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 5358f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollDown(TextView widget, Spannable buffer, int amount) { 5368f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 5378f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int innerHeight = getInnerHeight(widget); 5388f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int bottom = widget.getScrollY() + innerHeight; 5398f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int bottomLine = layout.getLineForVertical(bottom); 5408f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (layout.getLineTop(bottomLine + 1) < bottom + 1) { 5418f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown // Less than a pixel of this line is out of view, 5428f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown // so we must have tried to make it entirely in view 5438f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown // and now want the next line to be in view instead. 5448f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown bottomLine += 1; 5458f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5468f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int limit = layout.getLineCount() - 1; 5478f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (bottomLine <= limit) { 5488f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown bottomLine = Math.min(bottomLine + amount - 1, limit); 5498f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), 5508f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown layout.getLineTop(bottomLine + 1) - innerHeight); 5518f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 5528f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5538f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 5548f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5558f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 5568f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 5578f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll page up action. 5588f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls up by one page. 5598f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 5608f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 5618f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 5628f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 5638f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 5648f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 5658f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollPageUp(TextView widget, Spannable buffer) { 5668f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 5678f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int top = widget.getScrollY() - getInnerHeight(widget); 5688f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int topLine = layout.getLineForVertical(top); 5698f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (topLine >= 0) { 5708f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine)); 5718f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 5728f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5738f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 5748f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5758f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 5768f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 5778f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll page up action. 5788f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls down by one page. 5798f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 5808f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 5818f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 5828f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 5838f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 5848f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 5858f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollPageDown(TextView widget, Spannable buffer) { 5868f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 5878f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int innerHeight = getInnerHeight(widget); 5888f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int bottom = widget.getScrollY() + innerHeight + innerHeight; 5898f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int bottomLine = layout.getLineForVertical(bottom); 5908f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (bottomLine <= layout.getLineCount() - 1) { 5918f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), 5928f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown layout.getLineTop(bottomLine + 1) - innerHeight); 5938f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 5948f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5958f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 5968f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 5978f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 5988f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 5998f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll to top action. 6008f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls to the top of the document. 6018f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 6028f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 6038f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 6048f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 6058f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 6068f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 6078f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollTop(TextView widget, Spannable buffer) { 6088f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 6098f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (getTopLine(widget) >= 0) { 6108f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(0)); 6118f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 6128f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6138f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 6148f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6158f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 6168f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 6178f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll to bottom action. 6188f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls to the bottom of the document. 6198f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 6208f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 6218f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 6228f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 6238f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 6248f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 6258f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollBottom(TextView widget, Spannable buffer) { 6268f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final Layout layout = widget.getLayout(); 6278f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int lineCount = layout.getLineCount(); 6288f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (getBottomLine(widget) <= lineCount - 1) { 6298f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown Touch.scrollTo(widget, layout, widget.getScrollX(), 6308f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown layout.getLineTop(lineCount) - getInnerHeight(widget)); 6318f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 6328f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6338f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 6348f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6358f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 6368f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 6378f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll to line start action. 6388f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls to the start of the line. 6398f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 6408f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 6418f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 6428f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 6438f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 6448f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 6458f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollLineStart(TextView widget, Spannable buffer) { 6468f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int minScrollX = getScrollBoundsLeft(widget); 6478f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int scrollX = widget.getScrollX(); 6488f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (scrollX > minScrollX) { 6498f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown widget.scrollTo(minScrollX, widget.getScrollY()); 6508f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 6518f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6528f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 6538f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6548f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown 6558f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown /** 6568f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Performs a scroll to line end action. 6578f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * Scrolls to the end of the line. 6588f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * 6598f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param widget The text view. 6608f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @param buffer The text buffer. 6618f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @return True if the event was handled. 6628f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown * @hide 6638f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown */ 6648f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown protected boolean scrollLineEnd(TextView widget, Spannable buffer) { 6658f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget); 6668f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown int scrollX = widget.getScrollX(); 6678f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown if (scrollX < maxScrollX) { 6688f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown widget.scrollTo(maxScrollX, widget.getScrollY()); 6698f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return true; 6708f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 6718f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown return false; 6728f34567c71003505456a9b1a0d461a4e62883d70Jeff Brown } 67367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown} 674