|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 package javax.microedition.lcdui; |
|
18 |
|
19 import org.eclipse.ercp.swt.mobile.TextExtension; |
|
20 import org.eclipse.swt.SWT; |
|
21 import org.eclipse.swt.events.*; |
|
22 import org.eclipse.swt.graphics.Point; |
|
23 import org.eclipse.swt.widgets.Composite; |
|
24 import org.eclipse.swt.widgets.Control; |
|
25 import org.eclipse.swt.internal.extension.TextExtensionExtension; |
|
26 /** |
|
27 * Responsible for correct layout of TextField in a Form. |
|
28 */ |
|
29 class TextFieldLayouter extends ItemLayouter |
|
30 { |
|
31 |
|
32 /** |
|
33 * Key name for modify listener. |
|
34 */ |
|
35 private static final String MODIFY_LISTENER = "modify"; |
|
36 |
|
37 /** |
|
38 * Key name for key listener. |
|
39 */ |
|
40 private static final String KEY_LISTENER = "key"; |
|
41 |
|
42 /** |
|
43 * Key name for mouse listener. |
|
44 */ |
|
45 private static final String MOUSE_LISTENER = "mouse"; |
|
46 |
|
47 |
|
48 /** |
|
49 * Percentage of the whole screen. |
|
50 */ |
|
51 private static final int TOTAL_PERCENTAGE = 100; |
|
52 |
|
53 |
|
54 // private static Control[] staticControls = new Control[6]; |
|
55 |
|
56 private static boolean isCorrectText; |
|
57 |
|
58 /** |
|
59 * Constructor. |
|
60 * |
|
61 * @param aFormLayouter FormLayouter used for layouting. |
|
62 */ |
|
63 TextFieldLayouter(FormLayouter aFormLayouter) |
|
64 { |
|
65 super(aFormLayouter); |
|
66 } |
|
67 |
|
68 /** |
|
69 * Get static eSWT control (ConstraintText or TextExtension). |
|
70 * |
|
71 * @param constraint |
|
72 */ |
|
73 static Control eswtGetStaticTextControl(int constraint) |
|
74 { |
|
75 Control ret = null; |
|
76 |
|
77 /* |
|
78 int maskedConstraint = constraint & TextField.CONSTRAINT_MASK; |
|
79 |
|
80 if (staticControls[maskedConstraint] == null) { |
|
81 staticControls[maskedConstraint] = TextWrapper.eswtConstructText( |
|
82 eswtGetStaticShell(), SWT.MULTI | SWT.WRAP, constraint); |
|
83 ret = staticControls[maskedConstraint]; |
|
84 } |
|
85 */ |
|
86 |
|
87 if (constraint == TextField.NUMERIC) { |
|
88 constraint = TextExtensionExtension.NUMERIC; |
|
89 } |
|
90 else if (constraint == TextField.DECIMAL) { |
|
91 constraint = TextExtensionExtension.DECIMAL; |
|
92 } |
|
93 else if (constraint == TextField.PHONENUMBER) { |
|
94 constraint = TextExtensionExtension.PHONENUMBER; |
|
95 } |
|
96 else |
|
97 { |
|
98 // TODO: eSWT support required - text validation on EMAIL and URL constraints |
|
99 // default |
|
100 constraint = 0; |
|
101 } |
|
102 |
|
103 return new TextExtensionExtension(eswtGetStaticShell(), SWT.MULTI | SWT.WRAP,constraint); |
|
104 } |
|
105 |
|
106 /** |
|
107 * Check that text satisfies specified constraints. |
|
108 * |
|
109 * @param constraint TextField.NUMERIC etc. |
|
110 * @return true if text is correct for specified constraint. |
|
111 */ |
|
112 static boolean checkText(final int constraint, final String text) |
|
113 { |
|
114 isCorrectText = true; |
|
115 |
|
116 try |
|
117 { |
|
118 if(constraint == TextField.NUMERIC && !text.equals("")) |
|
119 { |
|
120 Integer.parseInt(text); |
|
121 } |
|
122 else if(constraint == TextField.DECIMAL && !text.equals("")) |
|
123 { |
|
124 Float.parseFloat(text); |
|
125 } |
|
126 } |
|
127 catch( NumberFormatException e ) |
|
128 { |
|
129 // Illegal text |
|
130 return false; |
|
131 } |
|
132 |
|
133 ESWTUIThreadRunner.syncExec(new Runnable() |
|
134 { |
|
135 public void run() |
|
136 { |
|
137 try |
|
138 { |
|
139 TextWrapper.eswtSetContent( |
|
140 eswtGetStaticTextControl(constraint), text); |
|
141 } |
|
142 catch(IllegalArgumentException e) |
|
143 { |
|
144 isCorrectText = false; |
|
145 } |
|
146 } |
|
147 }); |
|
148 return isCorrectText; |
|
149 } |
|
150 |
|
151 /** |
|
152 * eSWT specific calls to implement getControl. |
|
153 * |
|
154 * @param parent for the control. |
|
155 * @param item TextField item. |
|
156 */ |
|
157 Control eswtGetControl(Composite parent, Item item) |
|
158 { |
|
159 TextField textfield = (TextField) item; |
|
160 |
|
161 Control te = TextWrapper.eswtConstructText(parent, |
|
162 SWT.WRAP | SWT.MULTI, textfield.getConstraints()); |
|
163 TextWrapper.eswtSetMaxSize(te, textfield.getMaxSize()); |
|
164 TextWrapper.eswtSetContent(te, textfield.getString()); |
|
165 TextWrapper.eswtSetSelection(te, |
|
166 textfield.getCaretPosition(), textfield.getCaretPosition()); |
|
167 |
|
168 if(textfield.getInitialInputMode() != null) |
|
169 { |
|
170 eswtUpdateItem(textfield, te, TextField.UPDATE_INITIALINPUTMODE, |
|
171 null); |
|
172 } |
|
173 return te; |
|
174 } |
|
175 |
|
176 /** |
|
177 * Returns true if this eSWT control is suitable to be used for updating. |
|
178 * |
|
179 * @param item Item. |
|
180 * @param control eSWT control. |
|
181 * |
|
182 * @return true if this control is suitable for update |
|
183 */ |
|
184 boolean eswtIsSpecificControl(Item item, Control control) |
|
185 { |
|
186 return (control instanceof TextExtension); |
|
187 } |
|
188 |
|
189 /** |
|
190 * Updates the values of TextField. |
|
191 * |
|
192 * @param item Item. |
|
193 * @param control eSWT control. |
|
194 * @param reason reason to update. |
|
195 */ |
|
196 void eswtUpdateItem(Item item, Control control, int reason, Object param) |
|
197 { |
|
198 TextField textfield = (TextField) item; |
|
199 if(reason == TextField.UPDATE_INITIALINPUTMODE) |
|
200 { |
|
201 TextWrapper.eswtSetInputMode(control, |
|
202 textfield.getInitialInputMode(), |
|
203 textfield.getConstraints()); |
|
204 } |
|
205 else |
|
206 { |
|
207 TextWrapper.eswtSetContent(control, textfield.getString()); |
|
208 } |
|
209 } |
|
210 |
|
211 /** |
|
212 * Update size of TextField. |
|
213 * |
|
214 * @param item TextField. |
|
215 * @param control Control which represents TextField. |
|
216 * @param width which control must occupy. |
|
217 */ |
|
218 void eswtResizeControl(Item item, Control control, int width, int height) |
|
219 { |
|
220 super.eswtResizeControl(item, control, width, height); |
|
221 if(control instanceof TextExtension) |
|
222 { |
|
223 TextExtension te = (TextExtension) control; |
|
224 ((TextField) item).internalSetLinesCount(te.getLineCount()); |
|
225 } |
|
226 } |
|
227 |
|
228 /** |
|
229 * Returns true if that key was consumed by TextField. |
|
230 * |
|
231 * @param item TextField. |
|
232 * @param key keyCode. |
|
233 */ |
|
234 boolean eswtOfferKeyPressed(Item item, int key) |
|
235 { |
|
236 TextField tf = (TextField) item; |
|
237 if(item.hasLayout(Item.LAYOUT_SHRINK)) |
|
238 { |
|
239 if((key == SWT.ARROW_LEFT |
|
240 && tf.getCaretPosition() == 0) |
|
241 || (key == SWT.ARROW_RIGHT |
|
242 && tf.getCaretPosition() == tf.size())) |
|
243 { |
|
244 return false; |
|
245 } |
|
246 } |
|
247 if(((key == SWT.ARROW_UP) |
|
248 && (tf.getCaretPosition() == 0)) |
|
249 || ((key == SWT.ARROW_DOWN) |
|
250 && (tf.getCaretPosition() == tf.size()))) |
|
251 { |
|
252 return false; |
|
253 } |
|
254 return true; |
|
255 } |
|
256 |
|
257 /** |
|
258 * Responsible for reacting on focusGained event, and according to direction |
|
259 * from which that event comes sets the caret of the TextField. |
|
260 * |
|
261 * @param item TextField. |
|
262 * @param dir direction from which focus came, in case if it was set with |
|
263 * setCurrentItem() default direction is used (-1). |
|
264 */ |
|
265 void eswtFocusGained(Item item, int dir) |
|
266 { |
|
267 super.eswtFocusGained(item, dir); |
|
268 TextField tf = (TextField) item; |
|
269 // direction = dir; |
|
270 resetCaretPosition(tf, dir); |
|
271 Control control = eswtGetFirstSpecificControl(item); |
|
272 TextWrapper.eswtSetSelection(control, |
|
273 tf.getCaretPosition(), tf.getCaretPosition()); |
|
274 } |
|
275 |
|
276 /** |
|
277 * Returns the minimum area needed to display a TextField. |
|
278 * |
|
279 * @param textField TextField object |
|
280 * @return Minimum area needed to display TextField. |
|
281 */ |
|
282 static Point calculateMinimumBounds(final TextField textField) |
|
283 { |
|
284 final Point minSize = new Point(0, 0); |
|
285 ESWTUIThreadRunner.syncExec(new Runnable() |
|
286 { |
|
287 public void run() |
|
288 { |
|
289 TextExtension tempExt = (TextExtension) eswtGetStaticTextControl(TextField.ANY); |
|
290 tempExt.setText(ItemLayouter.MIN_TEXT); |
|
291 tempExt.pack(); |
|
292 minSize.x = tempExt.getSize().x; |
|
293 minSize.y = tempExt.getSize().y + Config.TEXTFIELD_MARGIN; |
|
294 applyMinMargins(textField, minSize); |
|
295 } |
|
296 }); |
|
297 return minSize; |
|
298 } |
|
299 |
|
300 /** |
|
301 * Returns the preferred area needed to display an Item. |
|
302 * |
|
303 * @param item Item. |
|
304 * @return Preferred area needed to display Item. x is width and y is |
|
305 * height. |
|
306 */ |
|
307 static Point calculatePreferredBounds(Item item) |
|
308 { |
|
309 final TextField textfield = (TextField) item; |
|
310 final Point prefSize = new Point(0, 0); |
|
311 ESWTUIThreadRunner.syncExec(new Runnable() |
|
312 { |
|
313 public void run() |
|
314 { |
|
315 TextExtension te = (TextExtension) eswtGetStaticTextControl(TextField.ANY); |
|
316 te.setText(textfield.getString()); |
|
317 |
|
318 int maxHeight = (formHeigh |
|
319 * Config.TEXTFIELD_MAX_SCREEN_PERCENTAGE / TOTAL_PERCENTAGE) |
|
320 - Config.TEXTFIELD_MARGIN; |
|
321 textfield.internalSetMaxVisibleLines(maxHeight |
|
322 / te.getLineHeight()); |
|
323 |
|
324 prefSize.x = getMaximumItemWidth(textfield); |
|
325 prefSize.y = Config.TEXTFIELD_MARGIN + Math.min( |
|
326 te.computeSize(prefSize.x, SWT.DEFAULT).y, maxHeight); |
|
327 // prefSize.y = Math.min(calc.y, maxHeight) + MARGIN; |
|
328 applyPrefMargins(textfield, prefSize); |
|
329 } |
|
330 }); |
|
331 return prefSize; |
|
332 } |
|
333 |
|
334 /** |
|
335 * Update caret position based on direction. |
|
336 * |
|
337 * @param textfield TextField for which to update caret position. |
|
338 * @param dir direction of scrolling. |
|
339 */ |
|
340 private void resetCaretPosition(TextField textfield, int dir) |
|
341 { |
|
342 switch(dir) |
|
343 { |
|
344 case SWT.ARROW_DOWN: |
|
345 textfield.internalSetCaretPosition(0); |
|
346 break; |
|
347 case SWT.ARROW_RIGHT: |
|
348 textfield.internalSetCaretPosition(0); |
|
349 break; |
|
350 case SWT.ARROW_UP: |
|
351 textfield.internalSetCaretPosition(textfield.size()); |
|
352 break; |
|
353 case SWT.ARROW_LEFT: |
|
354 textfield.internalSetCaretPosition(textfield.size()); |
|
355 break; |
|
356 default: |
|
357 break; |
|
358 } |
|
359 } |
|
360 |
|
361 /** |
|
362 * Add TextField listeners when form is activated. |
|
363 * |
|
364 * @param item TextField. |
|
365 * @param control Control which represents TextField. |
|
366 */ |
|
367 void eswtAddSpecificListeners(Item item, Control control) |
|
368 { |
|
369 super.eswtAddSpecificListeners(item, control); |
|
370 TextField textfield = (TextField) item; |
|
371 ModifyListener listener = new TextFieldModifyListener(textfield); |
|
372 TextWrapper.eswtAddModListener(control, listener); |
|
373 control.setData(MODIFY_LISTENER, listener); |
|
374 KeyListener listener2 = new TextFieldKeyListener(textfield); |
|
375 control.addKeyListener(listener2); |
|
376 control.setData(KEY_LISTENER, listener2); |
|
377 MouseListener listener4 = new AllMouseListener(textfield); |
|
378 control.addMouseListener(listener4); |
|
379 control.setData(MOUSE_LISTENER, listener4); |
|
380 } |
|
381 |
|
382 /** |
|
383 * Remove listeners from a TextField if the form goes to background. |
|
384 * |
|
385 * @param item TextField. |
|
386 * @param control Control which represents TextField. |
|
387 */ |
|
388 void eswtRemoveSpecificListeners(Item item, Control control) |
|
389 { |
|
390 super.eswtRemoveSpecificListeners(item, control); |
|
391 ModifyListener l1 = (ModifyListener) control.getData(MODIFY_LISTENER); |
|
392 if(l1 != null) |
|
393 { |
|
394 TextWrapper.eswtRemoveModListener(control, l1); |
|
395 control.setData(MODIFY_LISTENER, null); |
|
396 } |
|
397 KeyListener l2 = (KeyListener) control.getData(KEY_LISTENER); |
|
398 if(l2 != null) |
|
399 { |
|
400 control.removeKeyListener(l2); |
|
401 control.setData(KEY_LISTENER, null); |
|
402 } |
|
403 MouseListener l4 = (MouseListener) control.getData(MOUSE_LISTENER); |
|
404 if(l4 != null) |
|
405 { |
|
406 control.removeMouseListener(l4); |
|
407 control.setData(MOUSE_LISTENER, null); |
|
408 } |
|
409 } |
|
410 |
|
411 /** |
|
412 * Class that receives ModifyEvents from TextExtension and updates values of |
|
413 * TextField. |
|
414 */ |
|
415 class TextFieldModifyListener implements ModifyListener |
|
416 { |
|
417 |
|
418 private TextField textfield; |
|
419 |
|
420 TextFieldModifyListener(TextField textField) |
|
421 { |
|
422 this.textfield = textField; |
|
423 } |
|
424 |
|
425 private void handleLinesChange(TextExtension te) |
|
426 { |
|
427 int lines = te.getLineCount(); |
|
428 int visibleLines = te.getSize().y / te.getLineHeight(); |
|
429 if(lines != textfield.internalGetLinesCount()) |
|
430 { |
|
431 textfield.internalSetLinesCount(lines); |
|
432 Control control = eswtGetFirstControl(textfield); |
|
433 if(control.getSize().y + te.getLineHeight() |
|
434 + Config.TEXTFIELD_MARGIN <= formLayouter.getFormHeight()) |
|
435 { |
|
436 textfield.updateParent(Item.UPDATE_HEIGHT_CHANGED); |
|
437 } |
|
438 if(textfield.internalGetLinesCount() > lines) |
|
439 { |
|
440 if((te.getTopIndex() + visibleLines) > lines) |
|
441 { |
|
442 te.setTopIndex(Math.max(0, lines - visibleLines)); |
|
443 } |
|
444 if(visibleLines > lines) |
|
445 { |
|
446 textfield.updateParent(Item.UPDATE_HEIGHT_CHANGED); |
|
447 } |
|
448 } |
|
449 |
|
450 te.setTopIndex(Math.max(te.getCaretLineNumber() + 1 |
|
451 - textfield.internalGetMaxVisibleLines(), 0)); |
|
452 } |
|
453 } |
|
454 |
|
455 public void modifyText(ModifyEvent modifyEvent) |
|
456 { |
|
457 Control te = (Control) modifyEvent.widget; |
|
458 if(textfield.internalSetString(TextWrapper.eswtGetContent(te))) |
|
459 { |
|
460 Logger.method(textfield, "modify", modifyEvent); |
|
461 textfield.internalSetCaretPosition( |
|
462 TextWrapper.eswtGetCaretPosition(te)); |
|
463 if(te instanceof TextExtension) |
|
464 { |
|
465 handleLinesChange((TextExtension) te); |
|
466 } |
|
467 textfield.notifyStateChanged(); |
|
468 } |
|
469 } |
|
470 } |
|
471 |
|
472 /** |
|
473 * Class that receives KeyEvents from TextExtension and updates |
|
474 * caret position for TextField. |
|
475 */ |
|
476 class TextFieldKeyListener implements KeyListener |
|
477 { |
|
478 |
|
479 private TextField textfield; |
|
480 |
|
481 TextFieldKeyListener(TextField textField) |
|
482 { |
|
483 this.textfield = textField; |
|
484 } |
|
485 |
|
486 public void keyPressed(KeyEvent keyEvent) |
|
487 { |
|
488 Control te = (Control) keyEvent.widget; |
|
489 int caretPos = TextWrapper.eswtGetCaretPosition(te); |
|
490 int caretLine = TextWrapper.eswtGetCaretLine(te); |
|
491 |
|
492 if(keyEvent.keyCode == SWT.ARROW_UP && caretLine == 0) |
|
493 { |
|
494 caretPos = 0; |
|
495 TextWrapper.eswtSetSelection(te, caretPos, caretPos); |
|
496 } |
|
497 else if(keyEvent.keyCode == SWT.ARROW_DOWN |
|
498 && (caretLine == (TextWrapper.eswtGetLineCount(te) - 1))) |
|
499 { |
|
500 caretPos = textfield.size(); |
|
501 TextWrapper.eswtSetSelection(te, caretPos, caretPos); |
|
502 } |
|
503 |
|
504 textfield.internalSetCaretPosition(caretPos); |
|
505 } |
|
506 |
|
507 public void keyReleased(KeyEvent keyEvent) |
|
508 { |
|
509 // this is needed if focus was changed with touch. |
|
510 // so ne scrolling was done in DFI. |
|
511 if(!formLayouter.isItemFullyVisible(textfield)) |
|
512 { |
|
513 formLayouter.eswtScrollToItem(textfield); |
|
514 } |
|
515 textfield.internalSetCaretPosition( |
|
516 TextWrapper.eswtGetCaretPosition((Control) keyEvent.widget)); |
|
517 } |
|
518 |
|
519 } |
|
520 |
|
521 class AllMouseListener implements MouseListener, MouseMoveListener |
|
522 { |
|
523 |
|
524 private TextField textfield; |
|
525 private boolean isEnabled; |
|
526 |
|
527 AllMouseListener(TextField tf) |
|
528 { |
|
529 textfield = tf; |
|
530 } |
|
531 |
|
532 public void enable(boolean enabled) |
|
533 { |
|
534 isEnabled = enabled; |
|
535 } |
|
536 |
|
537 public void mouseUp(MouseEvent me) |
|
538 { |
|
539 if(isEnabled) |
|
540 { |
|
541 // |
|
542 } |
|
543 } |
|
544 |
|
545 public void mouseDown(MouseEvent me) |
|
546 { |
|
547 textfield.internalSetCaretPosition( |
|
548 TextWrapper.eswtGetCaretPosition((Control) me.widget)); |
|
549 } |
|
550 |
|
551 public void mouseMove(MouseEvent me) |
|
552 { |
|
553 } |
|
554 |
|
555 public void mouseDoubleClick(MouseEvent me) |
|
556 { |
|
557 } |
|
558 |
|
559 } |
|
560 |
|
561 } |