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