ArrowKeyMovementMethod.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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.view.KeyEvent;
20import android.text.*;
21import android.widget.TextView;
22import android.view.View;
23import android.view.MotionEvent;
24
25// XXX this doesn't extend MetaKeyKeyListener because the signatures
26// don't match.  Need to figure that out.  Meanwhile the meta keys
27// won't work in fields that don't take input.
28
29public class
30ArrowKeyMovementMethod
31implements MovementMethod
32{
33    private boolean up(TextView widget, Spannable buffer) {
34        boolean cap = MetaKeyKeyListener.getMetaState(buffer,
35                        KeyEvent.META_SHIFT_ON) == 1;
36        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
37                        KeyEvent.META_ALT_ON) == 1;
38        Layout layout = widget.getLayout();
39
40        if (cap) {
41            if (alt) {
42                Selection.extendSelection(buffer, 0);
43                return true;
44            } else {
45                return Selection.extendUp(buffer, layout);
46            }
47        } else {
48            if (alt) {
49                Selection.setSelection(buffer, 0);
50                return true;
51            } else {
52                return Selection.moveUp(buffer, layout);
53            }
54        }
55    }
56
57    private boolean down(TextView widget, Spannable buffer) {
58        boolean cap = MetaKeyKeyListener.getMetaState(buffer,
59                        KeyEvent.META_SHIFT_ON) == 1;
60        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
61                        KeyEvent.META_ALT_ON) == 1;
62        Layout layout = widget.getLayout();
63
64        if (cap) {
65            if (alt) {
66                Selection.extendSelection(buffer, buffer.length());
67                return true;
68            } else {
69                return Selection.extendDown(buffer, layout);
70            }
71        } else {
72            if (alt) {
73                Selection.setSelection(buffer, buffer.length());
74                return true;
75            } else {
76                return Selection.moveDown(buffer, layout);
77            }
78        }
79    }
80
81    private boolean left(TextView widget, Spannable buffer) {
82        boolean cap = MetaKeyKeyListener.getMetaState(buffer,
83                        KeyEvent.META_SHIFT_ON) == 1;
84        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
85                        KeyEvent.META_ALT_ON) == 1;
86        Layout layout = widget.getLayout();
87
88        if (cap) {
89            if (alt) {
90                return Selection.extendToLeftEdge(buffer, layout);
91            } else {
92                return Selection.extendLeft(buffer, layout);
93            }
94        } else {
95            if (alt) {
96                return Selection.moveToLeftEdge(buffer, layout);
97            } else {
98                return Selection.moveLeft(buffer, layout);
99            }
100        }
101    }
102
103    private boolean right(TextView widget, Spannable buffer) {
104        boolean cap = MetaKeyKeyListener.getMetaState(buffer,
105                        KeyEvent.META_SHIFT_ON) == 1;
106        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
107                        KeyEvent.META_ALT_ON) == 1;
108        Layout layout = widget.getLayout();
109
110        if (cap) {
111            if (alt) {
112                return Selection.extendToRightEdge(buffer, layout);
113            } else {
114                return Selection.extendRight(buffer, layout);
115            }
116        } else {
117            if (alt) {
118                return Selection.moveToRightEdge(buffer, layout);
119            } else {
120                return Selection.moveRight(buffer, layout);
121            }
122        }
123    }
124
125    public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
126        boolean handled = false;
127
128        switch (keyCode) {
129        case KeyEvent.KEYCODE_DPAD_UP:
130            handled |= up(widget, buffer);
131            break;
132
133        case KeyEvent.KEYCODE_DPAD_DOWN:
134            handled |= down(widget, buffer);
135            break;
136
137        case KeyEvent.KEYCODE_DPAD_LEFT:
138            handled |= left(widget, buffer);
139            break;
140
141        case KeyEvent.KEYCODE_DPAD_RIGHT:
142            handled |= right(widget, buffer);
143            break;
144        }
145
146        if (handled) {
147            MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
148            MetaKeyKeyListener.resetLockedMeta(buffer);
149        }
150
151        return handled;
152    }
153
154    public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
155        return false;
156    }
157
158    public boolean onTrackballEvent(TextView widget, Spannable buffer,
159                                    MotionEvent event) {
160        boolean handled = false;
161        int x = (int) event.getX();
162        int y = (int) event.getY();
163
164        for (; y < 0; y++) {
165            handled |= up(widget, buffer);
166        }
167        for (; y > 0; y--) {
168            handled |= down(widget, buffer);
169        }
170
171        for (; x < 0; x++) {
172            handled |= left(widget, buffer);
173        }
174        for (; x > 0; x--) {
175            handled |= right(widget, buffer);
176        }
177
178        if (handled) {
179            MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
180            MetaKeyKeyListener.resetLockedMeta(buffer);
181        }
182
183        return handled;
184    }
185
186    public boolean onTouchEvent(TextView widget, Spannable buffer,
187                                MotionEvent event) {
188        boolean handled = Touch.onTouchEvent(widget, buffer, event);
189
190        if (widget.isFocused()) {
191            if (event.getAction() == MotionEvent.ACTION_UP) {
192                int x = (int) event.getX();
193                int y = (int) event.getY();
194
195                x -= widget.getTotalPaddingLeft();
196                y -= widget.getTotalPaddingTop();
197
198                x += widget.getScrollX();
199                y += widget.getScrollY();
200
201                Layout layout = widget.getLayout();
202                int line = layout.getLineForVertical(y);
203                int off = layout.getOffsetForHorizontal(line, x);
204
205                boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0;
206
207                if (cap) {
208                    Selection.extendSelection(buffer, off);
209                } else {
210                    Selection.setSelection(buffer, off);
211                }
212
213                MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
214                MetaKeyKeyListener.resetLockedMeta(buffer);
215
216                return true;
217            }
218        }
219
220        return handled;
221    }
222
223    public boolean canSelectArbitrarily() {
224        return true;
225    }
226
227    public void initialize(TextView widget, Spannable text) {
228        Selection.setSelection(text, 0);
229    }
230
231    public void onTakeFocus(TextView view, Spannable text, int dir) {
232        if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
233            Layout layout = view.getLayout();
234
235            if (layout == null) {
236                /*
237                 * This shouldn't be null, but do something sensible if it is.
238                 */
239                Selection.setSelection(text, text.length());
240            } else {
241                /*
242                 * Put the cursor at the end of the first line, which is
243                 * either the last offset if there is only one line, or the
244                 * offset before the first character of the second line
245                 * if there is more than one line.
246                 */
247                if (layout.getLineCount() == 1) {
248                    Selection.setSelection(text, text.length());
249                } else {
250                    Selection.setSelection(text, layout.getLineStart(1) - 1);
251                }
252            }
253        } else {
254            Selection.setSelection(text, text.length());
255        }
256    }
257
258    public static MovementMethod getInstance() {
259        if (sInstance == null)
260            sInstance = new ArrowKeyMovementMethod();
261
262        return sInstance;
263    }
264
265    private static ArrowKeyMovementMethod sInstance;
266}
267