You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractTextField.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.text.Format;
  6. import java.util.Map;
  7. import com.vaadin.event.FieldEvents.BlurEvent;
  8. import com.vaadin.event.FieldEvents.BlurListener;
  9. import com.vaadin.event.FieldEvents.BlurNotifier;
  10. import com.vaadin.event.FieldEvents.FocusEvent;
  11. import com.vaadin.event.FieldEvents.FocusListener;
  12. import com.vaadin.event.FieldEvents.FocusNotifier;
  13. import com.vaadin.event.FieldEvents.TextChangeEvent;
  14. import com.vaadin.event.FieldEvents.TextChangeListener;
  15. import com.vaadin.event.FieldEvents.TextChangeNotifier;
  16. import com.vaadin.terminal.PaintException;
  17. import com.vaadin.terminal.PaintTarget;
  18. import com.vaadin.terminal.gwt.client.ui.VTextField;
  19. public abstract class AbstractTextField extends AbstractField implements
  20. BlurNotifier, FocusNotifier, TextChangeNotifier {
  21. /**
  22. * Value formatter used to format the string contents.
  23. */
  24. @Deprecated
  25. private Format format;
  26. /**
  27. * Null representation.
  28. */
  29. private String nullRepresentation = "null";
  30. /**
  31. * Is setting to null from non-null value allowed by setting with null
  32. * representation .
  33. */
  34. private boolean nullSettingAllowed = false;
  35. /**
  36. * Maximum character count in text field.
  37. */
  38. private int maxLength = -1;
  39. /**
  40. * Number of visible columns in the TextField.
  41. */
  42. private int columns = 0;
  43. /**
  44. * The prompt to display in an empty field. Null when disabled.
  45. */
  46. private String inputPrompt = null;
  47. /**
  48. * The text content when the last messages to the server was sent.
  49. */
  50. private String lastKnownTextContent;
  51. /**
  52. * The position of the cursor when the last message to the server was sent.
  53. */
  54. private int lastKnownCursorPosition;
  55. /**
  56. * Flag indicating that a text change event is pending to be triggered.
  57. * Cleared by {@link #setInternalValue(Object)} and when the event is fired.
  58. */
  59. private boolean textChangeEventPending;
  60. private TextChangeEventMode textChangeEventMode = TextChangeEventMode.LAZY;
  61. private final int DEFAULT_TEXTCHANGE_TIMEOUT = 400;
  62. private int textChangeEventTimeout = DEFAULT_TEXTCHANGE_TIMEOUT;
  63. /**
  64. * Temporarily holds the new selection position. Cleared on paint.
  65. */
  66. private int selectionPosition = -1;
  67. /**
  68. * Temporarily holds the new selection length.
  69. */
  70. private int selectionLength;
  71. /**
  72. * Flag used to determine whether we are currently handling a state change
  73. * triggered by a user. Used to properly fire text change event before value
  74. * change event triggered by the client side.
  75. */
  76. private boolean changingVariables;
  77. protected AbstractTextField() {
  78. super();
  79. }
  80. @Override
  81. public void paintContent(PaintTarget target) throws PaintException {
  82. super.paintContent(target);
  83. if (getMaxLength() >= 0) {
  84. target.addAttribute("maxLength", getMaxLength());
  85. }
  86. // Adds the number of column and rows
  87. final int columns = getColumns();
  88. if (columns != 0) {
  89. target.addAttribute("cols", String.valueOf(columns));
  90. }
  91. if (getInputPrompt() != null) {
  92. target.addAttribute("prompt", getInputPrompt());
  93. }
  94. // Adds the content as variable
  95. String value = getFormattedValue();
  96. if (value == null) {
  97. value = getNullRepresentation();
  98. }
  99. if (value == null) {
  100. throw new IllegalStateException(
  101. "Null values are not allowed if the null-representation is null");
  102. }
  103. target.addVariable(this, "text", value);
  104. if (selectionPosition != -1) {
  105. target.addAttribute("selpos", selectionPosition);
  106. target.addAttribute("sellen", selectionLength);
  107. selectionPosition = -1;
  108. }
  109. if (hasListeners(TextChangeEvent.class)) {
  110. target.addAttribute(VTextField.ATTR_TEXTCHANGE_EVENTMODE,
  111. getTextChangeEventMode().toString());
  112. target.addAttribute(VTextField.ATTR_TEXTCHANGE_TIMEOUT,
  113. getTextChangeTimeout());
  114. }
  115. }
  116. /**
  117. * Gets the formatted string value. Sets the field value by using the
  118. * assigned Format.
  119. *
  120. * @return the Formatted value.
  121. * @see #setFormat(Format)
  122. * @see Format
  123. * @deprecated
  124. */
  125. @Deprecated
  126. protected String getFormattedValue() {
  127. Object v = getValue();
  128. if (v == null) {
  129. return null;
  130. }
  131. return v.toString();
  132. }
  133. @Override
  134. public Object getValue() {
  135. Object v = super.getValue();
  136. if (format == null || v == null) {
  137. return v;
  138. }
  139. try {
  140. return format.format(v);
  141. } catch (final IllegalArgumentException e) {
  142. return v;
  143. }
  144. }
  145. @Override
  146. public void changeVariables(Object source, Map<String, Object> variables) {
  147. changingVariables = true;
  148. try {
  149. super.changeVariables(source, variables);
  150. if (variables.containsKey(VTextField.VAR_CURSOR)) {
  151. Integer object = (Integer) variables.get(VTextField.VAR_CURSOR);
  152. lastKnownCursorPosition = object.intValue();
  153. }
  154. if (variables.containsKey(VTextField.VAR_CUR_TEXT)) {
  155. /*
  156. * NOTE, we might want to develop this further so that on a
  157. * value change event the whole text content don't need to be
  158. * sent from the client to server. Just "commit" the value from
  159. * currentText to the value.
  160. */
  161. handleInputEventTextChange(variables);
  162. }
  163. // Sets the text
  164. if (variables.containsKey("text") && !isReadOnly()) {
  165. // Only do the setting if the string representation of the value
  166. // has been updated
  167. String newValue = (String) variables.get("text");
  168. // server side check for max length
  169. if (getMaxLength() != -1 && newValue.length() > getMaxLength()) {
  170. newValue = newValue.substring(0, getMaxLength());
  171. }
  172. final String oldValue = getFormattedValue();
  173. if (newValue != null
  174. && (oldValue == null || isNullSettingAllowed())
  175. && newValue.equals(getNullRepresentation())) {
  176. newValue = null;
  177. }
  178. if (newValue != oldValue
  179. && (newValue == null || !newValue.equals(oldValue))) {
  180. boolean wasModified = isModified();
  181. setValue(newValue, true);
  182. // If the modified status changes, or if we have a
  183. // formatter, repaint is needed after all.
  184. if (format != null || wasModified != isModified()) {
  185. requestRepaint();
  186. }
  187. }
  188. }
  189. firePendingTextChangeEvent();
  190. if (variables.containsKey(FocusEvent.EVENT_ID)) {
  191. fireEvent(new FocusEvent(this));
  192. }
  193. if (variables.containsKey(BlurEvent.EVENT_ID)) {
  194. fireEvent(new BlurEvent(this));
  195. }
  196. } finally {
  197. changingVariables = false;
  198. }
  199. }
  200. @Override
  201. public Class getType() {
  202. return String.class;
  203. }
  204. /**
  205. * Gets the null-string representation.
  206. *
  207. * <p>
  208. * The null-valued strings are represented on the user interface by
  209. * replacing the null value with this string. If the null representation is
  210. * set null (not 'null' string), painting null value throws exception.
  211. * </p>
  212. *
  213. * <p>
  214. * The default value is string 'null'.
  215. * </p>
  216. *
  217. * @return the String Textual representation for null strings.
  218. * @see TextField#isNullSettingAllowed()
  219. */
  220. public String getNullRepresentation() {
  221. return nullRepresentation;
  222. }
  223. /**
  224. * Is setting nulls with null-string representation allowed.
  225. *
  226. * <p>
  227. * If this property is true, writing null-representation string to text
  228. * field always sets the field value to real null. If this property is
  229. * false, null setting is not made, but the null values are maintained.
  230. * Maintenance of null-values is made by only converting the textfield
  231. * contents to real null, if the text field matches the null-string
  232. * representation and the current value of the field is null.
  233. * </p>
  234. *
  235. * <p>
  236. * By default this setting is false
  237. * </p>
  238. *
  239. * @return boolean Should the null-string represenation be always converted
  240. * to null-values.
  241. * @see TextField#getNullRepresentation()
  242. */
  243. public boolean isNullSettingAllowed() {
  244. return nullSettingAllowed;
  245. }
  246. /**
  247. * Sets the null-string representation.
  248. *
  249. * <p>
  250. * The null-valued strings are represented on the user interface by
  251. * replacing the null value with this string. If the null representation is
  252. * set null (not 'null' string), painting null value throws exception.
  253. * </p>
  254. *
  255. * <p>
  256. * The default value is string 'null'
  257. * </p>
  258. *
  259. * @param nullRepresentation
  260. * Textual representation for null strings.
  261. * @see TextField#setNullSettingAllowed(boolean)
  262. */
  263. public void setNullRepresentation(String nullRepresentation) {
  264. this.nullRepresentation = nullRepresentation;
  265. requestRepaint();
  266. }
  267. /**
  268. * Sets the null conversion mode.
  269. *
  270. * <p>
  271. * If this property is true, writing null-representation string to text
  272. * field always sets the field value to real null. If this property is
  273. * false, null setting is not made, but the null values are maintained.
  274. * Maintenance of null-values is made by only converting the textfield
  275. * contents to real null, if the text field matches the null-string
  276. * representation and the current value of the field is null.
  277. * </p>
  278. *
  279. * <p>
  280. * By default this setting is false.
  281. * </p>
  282. *
  283. * @param nullSettingAllowed
  284. * Should the null-string representation always be converted to
  285. * null-values.
  286. * @see TextField#getNullRepresentation()
  287. */
  288. public void setNullSettingAllowed(boolean nullSettingAllowed) {
  289. this.nullSettingAllowed = nullSettingAllowed;
  290. requestRepaint();
  291. }
  292. /**
  293. * Gets the value formatter of TextField.
  294. *
  295. * @return the Format used to format the value.
  296. * @deprecated replaced by {@link com.vaadin.data.util.PropertyFormatter}
  297. */
  298. @Deprecated
  299. public Format getFormat() {
  300. return format;
  301. }
  302. /**
  303. * Gets the value formatter of TextField.
  304. *
  305. * @param format
  306. * the Format used to format the value. Null disables the
  307. * formatting.
  308. * @deprecated replaced by {@link com.vaadin.data.util.PropertyFormatter}
  309. */
  310. @Deprecated
  311. public void setFormat(Format format) {
  312. this.format = format;
  313. requestRepaint();
  314. }
  315. @Override
  316. protected boolean isEmpty() {
  317. return super.isEmpty() || toString().length() == 0;
  318. }
  319. /**
  320. * Returns the maximum number of characters in the field. Value -1 is
  321. * considered unlimited. Terminal may however have some technical limits.
  322. *
  323. * @return the maxLength
  324. */
  325. public int getMaxLength() {
  326. return maxLength;
  327. }
  328. /**
  329. * Sets the maximum number of characters in the field. Value -1 is
  330. * considered unlimited. Terminal may however have some technical limits.
  331. *
  332. * @param maxLength
  333. * the maxLength to set
  334. */
  335. public void setMaxLength(int maxLength) {
  336. this.maxLength = maxLength;
  337. requestRepaint();
  338. }
  339. /**
  340. * Gets the number of columns in the editor. If the number of columns is set
  341. * 0, the actual number of displayed columns is determined implicitly by the
  342. * adapter.
  343. *
  344. * @return the number of columns in the editor.
  345. */
  346. public int getColumns() {
  347. return columns;
  348. }
  349. /**
  350. * Sets the number of columns in the editor. If the number of columns is set
  351. * 0, the actual number of displayed columns is determined implicitly by the
  352. * adapter.
  353. *
  354. * @param columns
  355. * the number of columns to set.
  356. */
  357. public void setColumns(int columns) {
  358. if (columns < 0) {
  359. columns = 0;
  360. }
  361. this.columns = columns;
  362. requestRepaint();
  363. }
  364. /**
  365. * Gets the current input prompt.
  366. *
  367. * @see #setInputPrompt(String)
  368. * @return the current input prompt, or null if not enabled
  369. */
  370. public String getInputPrompt() {
  371. return inputPrompt;
  372. }
  373. /**
  374. * Sets the input prompt - a textual prompt that is displayed when the field
  375. * would otherwise be empty, to prompt the user for input.
  376. *
  377. * @param inputPrompt
  378. */
  379. public void setInputPrompt(String inputPrompt) {
  380. this.inputPrompt = inputPrompt;
  381. requestRepaint();
  382. }
  383. /* ** Text Change Events ** */
  384. private void firePendingTextChangeEvent() {
  385. if (textChangeEventPending) {
  386. textChangeEventPending = false;
  387. fireEvent(new TextChangeEventImpl(this));
  388. }
  389. }
  390. @Override
  391. protected void setInternalValue(Object newValue) {
  392. if (changingVariables && !textChangeEventPending) {
  393. /*
  394. * Fire a "simulated" text change event before value change event if
  395. * change is coming from the client side.
  396. *
  397. * Iff there is both value change and textChangeEvent in same
  398. * variable burst, it is a text field in non immediate mode and the
  399. * text change event "flushed" queued value change event. In this
  400. * case textChangeEventPending flag is already on and text change
  401. * event will be fired after the value change event.
  402. */
  403. if (newValue == null && lastKnownTextContent != null
  404. && !lastKnownTextContent.equals(getNullRepresentation())) {
  405. // Value was changed from something to null representation
  406. lastKnownTextContent = getNullRepresentation();
  407. textChangeEventPending = true;
  408. } else if (newValue != null
  409. && !newValue.toString().equals(lastKnownTextContent)) {
  410. // Value was changed to something else than null representation
  411. lastKnownTextContent = newValue.toString();
  412. textChangeEventPending = true;
  413. }
  414. firePendingTextChangeEvent();
  415. }
  416. super.setInternalValue(newValue);
  417. }
  418. private void handleInputEventTextChange(Map<String, Object> variables) {
  419. /*
  420. * TODO we could vastly optimize the communication of values by using
  421. * some sort of diffs instead of always sending the whole text content.
  422. * Also on value change events we could use the mechanism.
  423. */
  424. String object = (String) variables.get(VTextField.VAR_CUR_TEXT);
  425. lastKnownTextContent = object;
  426. textChangeEventPending = true;
  427. }
  428. /**
  429. * Sets the mode how the TextField triggers {@link TextChangeEvent}s.
  430. *
  431. * @param inputEventMode
  432. * the new mode
  433. *
  434. * @see TextChangeEventMode
  435. */
  436. public void setTextChangeEventMode(TextChangeEventMode inputEventMode) {
  437. textChangeEventMode = inputEventMode;
  438. requestRepaint();
  439. }
  440. /**
  441. * @return the mode used to trigger {@link TextChangeEvent}s.
  442. */
  443. public TextChangeEventMode getTextChangeEventMode() {
  444. return textChangeEventMode;
  445. }
  446. /**
  447. * Different modes how the TextField can trigger {@link TextChangeEvent}s.
  448. */
  449. public enum TextChangeEventMode {
  450. /**
  451. * An event is triggered on each text content change, most commonly key
  452. * press events.
  453. */
  454. EAGER,
  455. /**
  456. * Each text change event in the UI causes the event to be communicated
  457. * to the application after a timeout. The length of the timeout can be
  458. * controlled with {@link TextField#setInputEventTimeout(int)}. Only the
  459. * last input event is reported to the server side if several text
  460. * change events happen during the timeout.
  461. * <p>
  462. * In case of a {@link ValueChangeEvent} the schedule is not kept
  463. * strictly. Before a {@link ValueChangeEvent} a {@link TextChangeEvent}
  464. * is triggered if the text content has changed since the previous
  465. * TextChangeEvent regardless of the schedule.
  466. */
  467. TIMEOUT,
  468. /**
  469. * An event is triggered when there is a pause of text modifications.
  470. * The length of the pause can be modified with
  471. * {@link TextField#setInputEventTimeout(int)}. Like with the
  472. * {@link #TIMEOUT} mode, an event is forced before
  473. * {@link ValueChangeEvent}s, even if the user did not keep a pause
  474. * while entering the text.
  475. * <p>
  476. * This is the default mode.
  477. */
  478. LAZY
  479. }
  480. public void addListener(TextChangeListener listener) {
  481. addListener(TextChangeListener.EVENT_ID, TextChangeEvent.class,
  482. listener, TextChangeListener.EVENT_METHOD);
  483. }
  484. public void removeListener(TextChangeListener listener) {
  485. removeListener(TextChangeListener.EVENT_ID, TextChangeEvent.class,
  486. listener);
  487. }
  488. /**
  489. * The text change timeout modifies how often text change events are
  490. * communicated to the application when {@link #getTextChangeEventMode()} is
  491. * {@link TextChangeEventMode#LAZY} or {@link TextChangeEventMode#TIMEOUT}.
  492. *
  493. *
  494. * @see #getTextChangeEventMode()
  495. *
  496. * @param timeout
  497. * the timeout in milliseconds
  498. */
  499. public void setTextChangeTimeout(int timeout) {
  500. textChangeEventTimeout = timeout;
  501. requestRepaint();
  502. }
  503. /**
  504. * Gets the timeout used to fire {@link TextChangeEvent}s when the
  505. * {@link #getTextChangeEventMode()} is {@link TextChangeEventMode#LAZY} or
  506. * {@link TextChangeEventMode#TIMEOUT}.
  507. *
  508. * @return the timeout value in milliseconds
  509. */
  510. public int getTextChangeTimeout() {
  511. return textChangeEventTimeout;
  512. }
  513. public class TextChangeEventImpl extends TextChangeEvent {
  514. private String curText;
  515. private int cursorPosition;
  516. private TextChangeEventImpl(final AbstractTextField tf) {
  517. super(tf);
  518. curText = tf.getCurrentTextContent();
  519. cursorPosition = tf.getCursorPosition();
  520. }
  521. @Override
  522. public AbstractTextField getComponent() {
  523. return (AbstractTextField) super.getComponent();
  524. }
  525. @Override
  526. public String getText() {
  527. return curText;
  528. }
  529. @Override
  530. public int getCursorPosition() {
  531. return cursorPosition;
  532. }
  533. }
  534. /**
  535. * Gets the current (or the last known) text content in the field.
  536. * <p>
  537. * Note the text returned by this method is not necessary the same that is
  538. * returned by the {@link #getValue()} method. The value is updated when the
  539. * terminal fires a value change event via e.g. blurring the field or by
  540. * pressing enter. The value returned by this method is updated also on
  541. * {@link TextChangeEvent}s. Due to this high dependency to the terminal
  542. * implementation this method is (at least at this point) not published.
  543. *
  544. * @return the text which is currently displayed in the field.
  545. */
  546. private String getCurrentTextContent() {
  547. if (lastKnownTextContent != null) {
  548. return lastKnownTextContent;
  549. } else {
  550. Object text = getValue();
  551. if (text == null) {
  552. return getNullRepresentation();
  553. }
  554. return text.toString();
  555. }
  556. }
  557. /**
  558. * Selects all text in the field.
  559. *
  560. * @since 6.4
  561. */
  562. public void selectAll() {
  563. String text = getValue() == null ? "" : getValue().toString();
  564. setSelectionRange(0, text.length());
  565. }
  566. /**
  567. * Sets the range of text to be selected.
  568. *
  569. * As a side effect the field will become focused.
  570. *
  571. * @since 6.4
  572. *
  573. * @param pos
  574. * the position of the first character to be selected
  575. * @param length
  576. * the number of characters to be selected
  577. */
  578. public void setSelectionRange(int pos, int length) {
  579. selectionPosition = pos;
  580. selectionLength = length;
  581. focus();
  582. requestRepaint();
  583. }
  584. /**
  585. * Sets the cursor position in the field. As a side effect the field will
  586. * become focused.
  587. *
  588. * @since 6.4
  589. *
  590. * @param pos
  591. * the position for the cursor
  592. * */
  593. public void setCursorPosition(int pos) {
  594. setSelectionRange(pos, 0);
  595. lastKnownCursorPosition = pos;
  596. }
  597. /**
  598. * Returns the last known cursor position of the field.
  599. *
  600. * <p>
  601. * Note that due to the client server nature or the GWT terminal, Vaadin
  602. * cannot provide the exact value of the cursor position in most situations.
  603. * The value is updated only when the client side terminal communicates to
  604. * TextField, like on {@link ValueChangeEvent}s and {@link TextChangeEvent}
  605. * s. This may change later if a deep push integration is built to Vaadin.
  606. *
  607. * @return the cursor position
  608. */
  609. public int getCursorPosition() {
  610. return lastKnownCursorPosition;
  611. }
  612. public void addListener(FocusListener listener) {
  613. addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  614. FocusListener.focusMethod);
  615. }
  616. public void removeListener(FocusListener listener) {
  617. removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
  618. }
  619. public void addListener(BlurListener listener) {
  620. addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  621. BlurListener.blurMethod);
  622. }
  623. public void removeListener(BlurListener listener) {
  624. removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
  625. }
  626. }