ArrowKeyMovementMethod.java revision 1c9aefd471cec85f905bea4099f4a641f347e0a0
1/* 2 * Copyright (C) 2006 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 17package android.text.method; 18 19import android.util.Log; 20import android.view.KeyEvent; 21import android.graphics.Rect; 22import android.text.*; 23import android.widget.TextView; 24import android.view.View; 25import android.view.MotionEvent; 26 27// XXX this doesn't extend MetaKeyKeyListener because the signatures 28// don't match. Need to figure that out. Meanwhile the meta keys 29// won't work in fields that don't take input. 30 31public class 32ArrowKeyMovementMethod 33implements MovementMethod 34{ 35 private boolean up(TextView widget, Spannable buffer) { 36 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 37 KeyEvent.META_SHIFT_ON) == 1) || 38 (MetaKeyKeyListener.getMetaState(buffer, 39 MetaKeyKeyListener.META_SELECTING) != 0); 40 boolean alt = MetaKeyKeyListener.getMetaState(buffer, 41 KeyEvent.META_ALT_ON) == 1; 42 Layout layout = widget.getLayout(); 43 44 if (cap) { 45 if (alt) { 46 Selection.extendSelection(buffer, 0); 47 return true; 48 } else { 49 return Selection.extendUp(buffer, layout); 50 } 51 } else { 52 if (alt) { 53 Selection.setSelection(buffer, 0); 54 return true; 55 } else { 56 return Selection.moveUp(buffer, layout); 57 } 58 } 59 } 60 61 private boolean down(TextView widget, Spannable buffer) { 62 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 63 KeyEvent.META_SHIFT_ON) == 1) || 64 (MetaKeyKeyListener.getMetaState(buffer, 65 MetaKeyKeyListener.META_SELECTING) != 0); 66 boolean alt = MetaKeyKeyListener.getMetaState(buffer, 67 KeyEvent.META_ALT_ON) == 1; 68 Layout layout = widget.getLayout(); 69 70 if (cap) { 71 if (alt) { 72 Selection.extendSelection(buffer, buffer.length()); 73 return true; 74 } else { 75 return Selection.extendDown(buffer, layout); 76 } 77 } else { 78 if (alt) { 79 Selection.setSelection(buffer, buffer.length()); 80 return true; 81 } else { 82 return Selection.moveDown(buffer, layout); 83 } 84 } 85 } 86 87 private boolean left(TextView widget, Spannable buffer) { 88 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 89 KeyEvent.META_SHIFT_ON) == 1) || 90 (MetaKeyKeyListener.getMetaState(buffer, 91 MetaKeyKeyListener.META_SELECTING) != 0); 92 boolean alt = MetaKeyKeyListener.getMetaState(buffer, 93 KeyEvent.META_ALT_ON) == 1; 94 Layout layout = widget.getLayout(); 95 96 if (cap) { 97 if (alt) { 98 return Selection.extendToLeftEdge(buffer, layout); 99 } else { 100 return Selection.extendLeft(buffer, layout); 101 } 102 } else { 103 if (alt) { 104 return Selection.moveToLeftEdge(buffer, layout); 105 } else { 106 return Selection.moveLeft(buffer, layout); 107 } 108 } 109 } 110 111 private boolean right(TextView widget, Spannable buffer) { 112 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 113 KeyEvent.META_SHIFT_ON) == 1) || 114 (MetaKeyKeyListener.getMetaState(buffer, 115 MetaKeyKeyListener.META_SELECTING) != 0); 116 boolean alt = MetaKeyKeyListener.getMetaState(buffer, 117 KeyEvent.META_ALT_ON) == 1; 118 Layout layout = widget.getLayout(); 119 120 if (cap) { 121 if (alt) { 122 return Selection.extendToRightEdge(buffer, layout); 123 } else { 124 return Selection.extendRight(buffer, layout); 125 } 126 } else { 127 if (alt) { 128 return Selection.moveToRightEdge(buffer, layout); 129 } else { 130 return Selection.moveRight(buffer, layout); 131 } 132 } 133 } 134 135 public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 136 if (executeDown(widget, buffer, keyCode)) { 137 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 138 MetaKeyKeyListener.resetLockedMeta(buffer); 139 return true; 140 } 141 142 return false; 143 } 144 145 private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { 146 boolean handled = false; 147 148 switch (keyCode) { 149 case KeyEvent.KEYCODE_DPAD_UP: 150 handled |= up(widget, buffer); 151 break; 152 153 case KeyEvent.KEYCODE_DPAD_DOWN: 154 handled |= down(widget, buffer); 155 break; 156 157 case KeyEvent.KEYCODE_DPAD_LEFT: 158 handled |= left(widget, buffer); 159 break; 160 161 case KeyEvent.KEYCODE_DPAD_RIGHT: 162 handled |= right(widget, buffer); 163 break; 164 165 case KeyEvent.KEYCODE_DPAD_CENTER: 166 if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { 167 if (widget.showContextMenu()) { 168 handled = true; 169 } 170 } 171 } 172 173 if (handled) { 174 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 175 MetaKeyKeyListener.resetLockedMeta(buffer); 176 } 177 178 return handled; 179 } 180 181 public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 182 return false; 183 } 184 185 public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { 186 int code = event.getKeyCode(); 187 if (code != KeyEvent.KEYCODE_UNKNOWN 188 && event.getAction() == KeyEvent.ACTION_MULTIPLE) { 189 int repeat = event.getRepeatCount(); 190 boolean handled = false; 191 while ((--repeat) > 0) { 192 handled |= executeDown(view, text, code); 193 } 194 return handled; 195 } 196 return false; 197 } 198 199 public boolean onTrackballEvent(TextView widget, Spannable text, 200 MotionEvent event) { 201 return false; 202 } 203 204 public boolean onTouchEvent(TextView widget, Spannable buffer, 205 MotionEvent event) { 206 int initialScrollX = -1, initialScrollY = -1; 207 if (event.getAction() == MotionEvent.ACTION_UP) { 208 initialScrollX = Touch.getInitialScrollX(widget, buffer); 209 initialScrollY = Touch.getInitialScrollY(widget, buffer); 210 } 211 212 boolean handled = Touch.onTouchEvent(widget, buffer, event); 213 214 if (widget.isFocused() && !widget.didTouchFocusSelect()) { 215 if (event.getAction() == MotionEvent.ACTION_UP) { 216 // If we have scrolled, then the up shouldn't move the cursor, 217 // but we do need to make sure the cursor is still visible at 218 // the current scroll offset to avoid the scroll jumping later 219 // to show it. 220 if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) || 221 (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) { 222 widget.moveCursorToVisibleOffset(); 223 return true; 224 } 225 226 int x = (int) event.getX(); 227 int y = (int) event.getY(); 228 229 x -= widget.getTotalPaddingLeft(); 230 y -= widget.getTotalPaddingTop(); 231 232 // Clamp the position to inside of the view. 233 if (x < 0) { 234 x = 0; 235 } else if (x >= (widget.getWidth()-widget.getTotalPaddingRight())) { 236 x = widget.getWidth()-widget.getTotalPaddingRight() - 1; 237 } 238 if (y < 0) { 239 y = 0; 240 } else if (y >= (widget.getHeight()-widget.getTotalPaddingBottom())) { 241 y = widget.getHeight()-widget.getTotalPaddingBottom() - 1; 242 } 243 244 x += widget.getScrollX(); 245 y += widget.getScrollY(); 246 247 Layout layout = widget.getLayout(); 248 int line = layout.getLineForVertical(y); 249 250 int off = layout.getOffsetForHorizontal(line, x); 251 252 // XXX should do the same adjust for x as we do for the line. 253 254 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 255 KeyEvent.META_SHIFT_ON) == 1) || 256 (MetaKeyKeyListener.getMetaState(buffer, 257 MetaKeyKeyListener.META_SELECTING) != 0); 258 259 if (cap) { 260 Selection.extendSelection(buffer, off); 261 } else { 262 Selection.setSelection(buffer, off); 263 } 264 265 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 266 MetaKeyKeyListener.resetLockedMeta(buffer); 267 268 return true; 269 } 270 } 271 272 return handled; 273 } 274 275 public boolean canSelectArbitrarily() { 276 return true; 277 } 278 279 public void initialize(TextView widget, Spannable text) { 280 Selection.setSelection(text, 0); 281 } 282 283 public void onTakeFocus(TextView view, Spannable text, int dir) { 284 if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) { 285 Layout layout = view.getLayout(); 286 287 if (layout == null) { 288 /* 289 * This shouldn't be null, but do something sensible if it is. 290 */ 291 Selection.setSelection(text, text.length()); 292 } else { 293 /* 294 * Put the cursor at the end of the first line, which is 295 * either the last offset if there is only one line, or the 296 * offset before the first character of the second line 297 * if there is more than one line. 298 */ 299 if (layout.getLineCount() == 1) { 300 Selection.setSelection(text, text.length()); 301 } else { 302 Selection.setSelection(text, layout.getLineStart(1) - 1); 303 } 304 } 305 } else { 306 Selection.setSelection(text, text.length()); 307 } 308 } 309 310 public static MovementMethod getInstance() { 311 if (sInstance == null) 312 sInstance = new ArrowKeyMovementMethod(); 313 314 return sInstance; 315 } 316 317 private static ArrowKeyMovementMethod sInstance; 318} 319