package android.widget; import java.awt.Component; import java.awt.TextComponent; import javax.swing.JComponent; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.DocumentFilter; import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import static com.applang.Util.*; import static com.applang.SwingUtil.*; public class EditText extends TextView { public EditText(Context context) { super(context, null); } public EditText(Context context, AttributeSet attrs) { super(context, attrs); } public EditText(Component component) { super(component); } @Override protected void create() { if (attributeSet == null) inputType = "textMultiLine"; if (isMultiLine()) { JTextComponent textArea = new JTextArea(); setComponent(textArea); } else { JTextField textField = new JTextField(); if (inputType != null && inputType.startsWith("number")) { PlainDocument doc = (PlainDocument) textField.getDocument(); doc.setDocumentFilter(new NumericFilter(inputType)); } setMaximumDimension(textField, 100); setComponent(textField); } } public JTextComponent getTextComponent() { return (JTextComponent) getTaggedComponent(); } @Override public void setText(String text) { getTextComponent().setText(text); } @Override public String getText() { return getTextComponent().getText(); } public void setSelection(int start, int stop) { JTextComponent textArea = getTextComponent(); textArea.setSelectionStart(start); textArea.setSelectionEnd(stop); } /** * Appends the given text to the end of the document. Does nothing if * the model is null or the string is null or empty. * * @param str the text to insert * @see #insert */ public void append(String str) { Document doc = getTextComponent().getDocument(); if (doc != null) { try { doc.insertString(doc.getLength(), str, null); } catch (BadLocationException e) { } } } /** * Replaces text from the indicated start to end position with the * new text specified. Does nothing if the model is null. Simply * does a delete if the new string is null or empty. * * @param str the text to use as the replacement * @param start the start position >= 0 * @param end the end position >= start * @exception IllegalArgumentException if part of the range is an * invalid position in the model * @see #insert * @see #replaceRange */ public void replaceRange(String str, int start, int end) { if (end < start) { throw new IllegalArgumentException("end before start"); } Document doc = getTextComponent().getDocument(); if (doc != null) { try { if (doc instanceof AbstractDocument) { ((AbstractDocument)doc).replace(start, end - start, str, null); } else { doc.remove(start, end - start); doc.insertString(start, str, null); } } catch (BadLocationException e) { throw new IllegalArgumentException(e.getMessage()); } } } /** * Inserts the specified text at the specified position. Does nothing * if the model is null or if the text is null or empty. * * @param str the text to insert * @param pos the position at which to insert >= 0 * @exception IllegalArgumentException if pos is an * invalid position in the model * @see TextComponent#setText * @see #replaceRange */ public void insert(String str, int pos) { Document doc = getTextComponent().getDocument(); if (doc != null) { try { doc.insertString(pos, str, null); } catch (BadLocationException e) { throw new IllegalArgumentException(e.getMessage()); } } } private Job<JComponent> onTextChanged = null; private void textChanged() { try { if (onTextChanged != null) onTextChanged.perform(getTextComponent(), objects()); } catch (Exception e) { Log.e(TAG, "textChanged", e); } } public void setOnTextChanged(final Job<JComponent> onTextChanged) { this.onTextChanged = onTextChanged; JTextComponent textComponent = getTextComponent(); if (textComponent != null) { textComponent.getDocument().addDocumentListener( new DocumentListener() { public void removeUpdate(DocumentEvent e) { textChanged(); } public void insertUpdate(DocumentEvent e) { textChanged(); } public void changedUpdate(DocumentEvent e) { textChanged(); } }); } else Log.w(TAG, "setOnTextChanged not possible"); } @Override public void applyAttributes() { if (attributeSet != null) { String value = attributeSet.getAttributeResourceItem("readOnly"); if (notNullOrEmpty(value)) getTextComponent().setEditable(!"true".equals(value)); } super.applyAttributes(); } class NumericFilter extends DocumentFilter { private String inputType; public NumericFilter(String inputType) { this.inputType = inputType; } public boolean test(String text, String inputType) { try { if (nullOrEmpty(text) || "-".equals(text) || "+".equals(text)) return true; else if (inputType.equals("numberDecimal")) { Double.parseDouble(text); } else { Long.parseLong(text); } return true; } catch (NumberFormatException e) { EditText.this.message("edittext.numeric-test.message"); return false; } } @Override public void insertString(FilterBypass fb, int offset, String string, javax.swing.text.AttributeSet attrs) throws BadLocationException { if (testInContext(fb, offset, null, string, new Function<StringBuilder>() { public StringBuilder apply(Object... params) { StringBuilder sb = (StringBuilder) params[0]; Integer offset = (Integer) params[1]; sb.insert(offset, (String) params[3]); return sb; } })) { super.insertString(fb, offset, string, attrs); } } @Override public void replace(FilterBypass fb, int offset, int length, String text, javax.swing.text.AttributeSet attrs) throws BadLocationException { if (testInContext(fb, offset, length, text, new Function<StringBuilder>() { public StringBuilder apply(Object... params) { StringBuilder sb = (StringBuilder) params[0]; Integer offset = (Integer) params[1]; sb.replace(offset, offset + (Integer) params[2], (String) params[3]); return sb; } })) { super.replace(fb, offset, length, text, attrs); } } @Override public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { if (testInContext(fb, offset, length, null, new Function<StringBuilder>() { public StringBuilder apply(Object... params) { StringBuilder sb = (StringBuilder) params[0]; Integer offset = (Integer) params[1]; sb.delete(offset, offset + (Integer) params[2]); return sb; } })) { super.remove(fb, offset, length); } } private boolean testInContext(FilterBypass fb, Integer offset, Integer length, String text, Function<StringBuilder> oper) throws BadLocationException { Document doc = fb.getDocument(); StringBuilder sb = new StringBuilder(); sb.append(doc.getText(0, doc.getLength())); sb = oper.apply(sb, offset, length, text); return test(sb.toString(), inputType); } } }