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