/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ // TODO javadoc - remove this comment only when the class and all non-public // methods and fields are documented package org.beanfabrics.swing.internal; import java.awt.Graphics; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.text.Document; import org.beanfabrics.View; import org.beanfabrics.event.WeakPropertyChangeListener; import org.beanfabrics.model.ITextPM; import org.beanfabrics.model.PresentationModel; import org.beanfabrics.swing.ErrorIconPainter; import org.beanfabrics.swing.KeyBindingProcessor; /** * The <code>TextPMTextField</code> is a {@link JTextField} that is a view on an * {@link ITextPM}. * * @author Michael Karneim */ @SuppressWarnings("serial") public class TextPMTextField extends JTextField implements KeyBindingProcessor, View<ITextPM> { private boolean selectAllOnFocusGainedEnabled = true; private boolean reformatOnFocusLostEnabled = true; private BnPlainDocument document; private final PropertyChangeListener listener = new MyWeakPropertyChangeListener(); private class MyWeakPropertyChangeListener implements WeakPropertyChangeListener, Serializable { public void propertyChange(PropertyChangeEvent evt) { refresh(); } } private ErrorIconPainter errorIconPainter = createDefaultErrorIconPainter(); public TextPMTextField() { this.init(); } public TextPMTextField(int columns) { super(columns); this.init(); } public TextPMTextField(ITextPM pModel) { this.init(); setPresentationModel(pModel); } private void init() { this.setEnabled(false); this.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { TextPMTextField.this.onFocusGained(); } public void focusLost(FocusEvent e) { TextPMTextField.this.onFocusLost(); } }); } public boolean isSelectAllOnFocusGainedEnabled() { return selectAllOnFocusGainedEnabled; } public void setSelectAllOnFocusGainedEnabled(boolean selectAllOnFocusGainedEnabled) { this.selectAllOnFocusGainedEnabled = selectAllOnFocusGainedEnabled; } public boolean isReformatOnFocusLostEnabled() { return reformatOnFocusLostEnabled; } public void setReformatOnFocusLostEnabled(boolean reformatOnFocusLostEnabled) { this.reformatOnFocusLostEnabled = reformatOnFocusLostEnabled; } protected void onFocusGained() { this.repaint(); if (this.isSelectAllOnFocusGainedEnabled()) { this.selectAll(); } } protected void onFocusLost() { this.repaint(); if (this.isReformatOnFocusLostEnabled() && this.isConnected()) { this.document.getPresentationModel().reformat(); } } @Override protected Document createDefaultModel() { if (this.document != null && this.document.isConnected()) { throw new IllegalStateException("The document was initialized already."); } return this.document = new BnPlainDocument(); } @Override public void setDocument(Document document) { if (this.isConnected()) { throw new IllegalStateException("The document was initialized already."); } if (document instanceof BnPlainDocument) { this.document = (BnPlainDocument)document; } super.setDocument(document); } /** {@inheritDoc} */ public ITextPM getPresentationModel() { if (this.document == null) { return null; } return this.document.getPresentationModel(); } /** {@inheritDoc} */ public void setPresentationModel(ITextPM newModel) { ITextPM oldModel = this.document.getPresentationModel(); if (oldModel != null) { oldModel.removePropertyChangeListener(this.listener); } this.document.setPresentationModel(newModel); if (newModel != null) { newModel.addPropertyChangeListener(listener); } this.refresh(); this.firePropertyChange("presentationModel", oldModel, newModel); } /** * Returns whether this component is connected to the target * {@link PresentationModel} to synchronize with. This is a convenience * method. * * @return <code>true</code> when this component is connected, else * <code>false</code> */ boolean isConnected() { return this.document != null && this.document.getPresentationModel() != null; } /** * Configures the text field depending on the properties attributes. */ protected void refresh() { final ITextPM pModel = this.getPresentationModel(); if (pModel != null) { this.setEnabled(true); this.setToolTipText(pModel.isValid() == false ? pModel.getValidationState().getMessage() : pModel.getDescription()); this.setEditable(pModel.isEditable()); } else { this.setToolTipText(null); this.setEnabled(false); } this.repaint(); } private ErrorIconPainter createDefaultErrorIconPainter() { ErrorIconPainter result = new ErrorIconPainter(); result.setHorizontalAlignment(invertHorizontalAlignment(getHorizontalAlignment())); return result; } public ErrorIconPainter getErrorIconPainter() { return errorIconPainter; } public void setErrorIconPainter(ErrorIconPainter aErrorIconPainter) { if (aErrorIconPainter == null) { throw new IllegalArgumentException("aErrorIconPainter == null"); } this.errorIconPainter = aErrorIconPainter; } @Override public void paintChildren(Graphics g) { super.paintChildren(g); if (shouldPaintErrorIcon()) { errorIconPainter.paint(g, this); } } private boolean shouldPaintErrorIcon() { ITextPM pModel = this.getPresentationModel(); if (pModel == null) { return false; } return (pModel.isValid() == false); } @Override public void setHorizontalAlignment(int alignment) { super.setHorizontalAlignment(alignment); if (errorIconPainter != null) { this.errorIconPainter.setHorizontalAlignment(invertHorizontalAlignment(alignment)); } } private int invertHorizontalAlignment(int alignment) { switch (alignment) { case SwingConstants.LEFT: return SwingConstants.RIGHT; case SwingConstants.RIGHT: return SwingConstants.LEFT; case SwingConstants.LEADING: return SwingConstants.TRAILING; case SwingConstants.TRAILING: return SwingConstants.LEADING; default: return alignment; } } public boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { return super.processKeyBinding(ks, e, condition, pressed); } }