All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xdev.ui.XdevFormattedTextField Maven / Gradle / Ivy

/*
 * XDEV Application Framework - XDEV Application Framework
 * Copyright © 2003 XDEV Software (https://xdev.software)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package xdev.ui;


import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.Format;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

import javax.swing.JFormattedTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;

import xdev.db.Operator;
import xdev.lang.NotNull;
import xdev.ui.text.TextFormat;
import xdev.util.DateFormatException;
import xdev.util.ObjectUtils;
import xdev.util.XdevDate;
import xdev.util.logging.LoggerFactory;
import xdev.util.logging.XdevLogger;
import xdev.vt.VirtualTable;


/**
 * The standard formatted textfield in XDEV. Based on
 * {@link JFormattedTextField}.
 * 
 * @see JFormattedTextField
 * @see ClientProperties
 * @see FormattedFormularComponent
 * @see XdevFocusCycleComponent
 * 
 * @author XDEV Software
 * 
 * @since 2.0
 */
@BeanSettings(useXdevCustomizer = true)
public class XdevFormattedTextField extends JFormattedTextField implements ClientProperties, TextComponent,
		FormattedFormularComponent,
		NumberFormularComponent, XdevFocusCycleComponent
{
	/**
	 * Logger instance for this class.
	 */
	private static final XdevLogger									log					= LoggerFactory
																								.getLogger(XdevFormattedTextField.class);
	
	/**
	 * 
	 */
	private static final long										serialVersionUID	= 2643519059601388480L;
	
	private TextFormat												textFormat;
	
	private Object													savedValue			= null;
	
	/**
	 * tabIndex is used to store the index for {@link XdevFocusCycleComponent}
	 * functionality.
	 */
	private int														tabIndex			= -1;
	
	/**
	 * @since 3.1
	 */
	private FocusGainedBehavior										focusGainedBehavior	= FocusGainedBehavior.SYSTEM_DEFAULT;
	
	/**
	 * @since 4.0
	 */
	private TextChangedBehavior										textChangedBehavior	= TextChangedBehavior.SYSTEM_DEFAULT;
	
	private final FormularComponentSupport	support				= new FormularComponentSupport(
																								this);
	
	
	/**
	 * Constructor for creating a new instance of a
	 * {@link XdevFormattedTextField}.
	 * 
	 */
	public XdevFormattedTextField()
	{
		this(TextFormat.getNumberInstance(Locale.getDefault(),null,2,2,true,false));
	}
	
	
	/**
	 * Constructor for creating a new instance of a
	 * {@link XdevFormattedTextField}.
	 * 
	 * @param textFormat
	 *            the {@link TextFormat} to format the display text,
	 *            null if none
	 */
	public XdevFormattedTextField(TextFormat textFormat)
	{
		this("",textFormat);
	}
	
	
	/**
	 * Constructor for creating a new instance of a
	 * {@link XdevFormattedTextField}.
	 * 
	 * @param text
	 *            the text to be displayed, null if none
	 * 
	 * @param textFormat
	 *            the {@link TextFormat} to format the display text,
	 *            null if none
	 */
	public XdevFormattedTextField(String text, TextFormat textFormat)
	{
		this(text,1000,textFormat);
	}
	
	
	/**
	 * Constructor for creating a new instance of a
	 * {@link XdevFormattedTextField}.
	 * 
	 * @param maxSigns
	 *            a int to determine the max signs of the
	 *            {@link MaxSignDocument}
	 * 
	 * @param textFormat
	 *            the {@link TextFormat} to format the display text,
	 *            null if none
	 * 
	 * @throws IllegalArgumentException
	 *             if the maxSigns is <= 0
	 */
	public XdevFormattedTextField(int maxSigns, TextFormat textFormat)
			throws IllegalArgumentException
	{
		this("",maxSigns,textFormat);
	}
	
	
	/**
	 * Constructor for creating a new instance of a
	 * {@link XdevFormattedTextField}.
	 * 
	 * @param text
	 *            the text to be displayed, null if none
	 * 
	 * @param maxSigns
	 *            a int to determine the max signs of the
	 *            {@link MaxSignDocument}
	 * 
	 * @param textFormat
	 *            the {@link TextFormat} to format the display text,
	 *            null if none
	 * 
	 * @throws IllegalArgumentException
	 *             if the maxSigns is <= 0
	 */
	public XdevFormattedTextField(String text, int maxSigns, final TextFormat textFormat)
			throws IllegalArgumentException
	{
		super();
		
		setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
		setDocument(new MaxSignDocument(maxSigns));
		setTextFormat(textFormat);
		
		if(text != null && text.length() > 0)
		{
			if(textFormat != null)
			{
				setText(textFormat.format(text));
			}
			else
			{
				setText(text);
			}
		}
		
		setColumns(20);
		
		addFocusListener(new FocusListener()
		{
			public void focusGained(FocusEvent e)
			{
				SwingUtilities.invokeLater(new Runnable()
				{
					@Override
					public void run()
					{
						focusGainedBehavior.focusGained(XdevFormattedTextField.this);
					}
				});
			}
			
			
			public void focusLost(FocusEvent e)
			{
				select(0,0);
			}
		});
	}
	
	
	/**
	 * Returns the {@link FocusGainedBehavior} of this TextComponent.
	 * 
	 * @return the {@link FocusGainedBehavior} of this TextComponent
	 * 
	 * @since 3.1
	 */
	public FocusGainedBehavior getFocusGainedBehavior()
	{
		return focusGainedBehavior;
	}
	
	
	/**
	 * Sets the new {@link FocusGainedBehavior} for this TextComponent.
	 * 
	 * @param focusGainedBehavior
	 *            the new {@link FocusGainedBehavior}
	 * @throws {@link IllegalArgumentException} if
	 *         focusGainedBehavior is null
	 * 
	 * @since 3.1
	 */
	public void setFocusGainedBehavior(@NotNull FocusGainedBehavior focusGainedBehavior)
	{
		if(focusGainedBehavior == null)
		{
			throw new IllegalArgumentException("focusGainedBehavior cannot be null");
		}
		else if(focusGainedBehavior != this.focusGainedBehavior)
		{
			FocusGainedBehavior old = this.focusGainedBehavior;
			this.focusGainedBehavior = focusGainedBehavior;
			firePropertyChange(FocusGainedBehavior.PROPERTY_NAME,old,focusGainedBehavior);
		}
	}
	
	
	/**
	 * Return the {@link TextChangedBehavior} of this TextComponent
	 * 
	 * @return the {@link TextChangedBehavior} of this TextComponent
	 * 
	 * @since 4.0
	 */
	public TextChangedBehavior getTextChangedBehavior()
	{
		return textChangedBehavior;
	}
	
	
	/**
	 * Sets the new {@link TextChangedBehavior} for this TextComponent.
	 * 
	 * @param textChangedBehavior
	 *            the new {@link TextChangedBehavior}
	 * @throws {@link IllegalArgumentException} if
	 *         textChangedBehavior is null
	 * 
	 * @since 4.0
	 */
	public void setTextChangedBehavior(@NotNull TextChangedBehavior textChangedBehavior)
	{
		if(textChangedBehavior == null)
		{
			throw new IllegalArgumentException("textChangedBehavior cannot be null");
		}
		else if(textChangedBehavior != this.textChangedBehavior)
		{
			TextChangedBehavior old = this.textChangedBehavior;
			this.textChangedBehavior = textChangedBehavior;
			firePropertyChange(TextChangedBehavior.PROPERTY_NAME,old,textChangedBehavior);
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @see UIUtils#scrollToStart(javax.swing.JComponent)
	 */
	@Override
	public void setText(String str)
	{
		try
		{
			if(textFormat != null)
			{
				Object o = textFormat.parse(str);
				if(o != null && textFormat.getSuppressZero() && o instanceof Number
						&& ((Number)o).doubleValue() == 0.0)
				{
					super.setText("");
				}
				else
				{
					super.setText(str);
				}
			}
			else
			{
				super.setText(str);
			}
		}
		catch(Exception e)
		{
			super.setText(str);
		}
		
		textChangedBehavior.textChanged(this);
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Dimension getPreferredSize()
	{
		// Hack because JTextField doesn't honor the set preferred size
		if(isPreferredSizeSet())
		{
			try
			{
				return UIUtils.getPrefSizeFieldValue(this);
			}
			catch(Exception e)
			{
				// Shouldn't happen
				log.error(e);
			}
		}
		
		return super.getPreferredSize();
	}
	
	
	/**
	 * Registers the given {@link DocumentListener} to begin receiving
	 * notifications when changes are made to the document.
	 * 
	 * @param listener
	 *            the {@link DocumentListener} to register
	 * 
	 * @see Document#addDocumentListener(DocumentListener)
	 */
	public void addDocumentListener(DocumentListener listener)
	{
		getDocument().addDocumentListener(listener);
	}
	
	
	/**
	 * Unregisters the given {@link DocumentListener} from the notification list
	 * so it will no longer receive change updates.
	 * 
	 * @param listener
	 *            the observer to register
	 * @see #addDocumentListener(DocumentListener)
	 */
	public void removeDocumentListener(DocumentListener listener)
	{
		getDocument().removeDocumentListener(listener);
	}
	
	
	/**
	 * Returns the max sign count of this {@link XdevFormattedTextField}.
	 * 
	 * 
	 * @return the max sign count of this {@link XdevFormattedTextField}, -1 if
	 *         the {@link Document} is not an instance of
	 *         {@link MaxSignDocument}.
	 * 
	 * @see MaxSignDocument
	 */
	public int getMaxSignCount()
	{
		Document doc = getDocument();
		if(doc instanceof MaxSignDocument)
		{
			return ((MaxSignDocument)doc).getMaxSignCount();
		}
		
		return -1;
	}
	
	
	/**
	 * Sets the max sign count of this {@link XdevFormattedTextField}.
	 * 
	 * @param maxSignCount
	 *            the max sign count of this {@link XdevFormattedTextField}
	 */
	public void setMaxSignCount(int maxSignCount)
	{
		Document doc = getDocument();
		if(doc instanceof MaxSignDocument)
		{
			MaxSignDocument msDoc = (MaxSignDocument)doc;
			int oldValue = msDoc.getMaxSignCount();
			if(oldValue != maxSignCount)
			{
				msDoc.setMaxSignCount(maxSignCount);
				firePropertyChange(MaxSignDocument.MAX_SIGNS_PROPERTY,oldValue,maxSignCount);
			}
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getFormularName()
	{
		return support.getFormularName();
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void setDataField(String dataField)
	{
		support.setDataField(dataField);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public String getDataField()
	{
		return support.getDataField();
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	@Deprecated
	public final void setFormularValue(VirtualTable vt, int col, Object value)
	{
		support.setFormularValue(vt,col,value);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.2
	 */
	@Override
	public void setFormularValue(VirtualTable vt, Map record)
	{
		if(!support.hasDataField())
		{
			return;
		}
		
		String dataField = getDataField();
		dataField = support.getFirstDataField(dataField);
		String columnName = support.getColumnName(dataField);
		Object value = record.get(columnName);
		
		try
		{
			setValue(value);
		}
		catch(Exception e)
		{
			String str = value != null ? value.toString() : "";
			
			if(textFormat != null && textFormat.getType() != TextFormat.PLAIN)
			{
				str = textFormat.format(value);
			}
			else if(vt != null && columnName != null)
			{
				str = vt.formatValue(value,columnName);
			}
			
			setText(str);
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object getFormularValue()
	{
		Object value = getValue();
		
		if("".equals(value) && textFormat != null && textFormat.getType() != TextFormat.PLAIN)
		{
			value = null;
		}
		
		return value;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public TextFormat getTextFormat()
	{
		return textFormat;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setTextFormat(TextFormat format)
	{
		this.textFormat = format;
		
		if(textFormat != null)
		{
			setFormatterFactory(new AbstractFormatterFactory()
			{
				@Override
				public AbstractFormatter getFormatter(JFormattedTextField tf)
				{
					return new AbstractFormatter()
					{
						@Override
						public Object stringToValue(String str) throws ParseException
						{
							return textFormat.parse(str);
						}
						
						
						@Override
						public String valueToString(Object value) throws ParseException
						{
							return textFormat.format(value);
						}
					};
				}
			});
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void commitEdit() throws ParseException
	{
		try
		{
			super.commitEdit();
		}
		catch(ParseException e)
		{
			if(textFormat == null)
			{
				throw e;
			}
			
			switch(textFormat.getType())
			{
				case TextFormat.CURRENCY:
				{
					Format numberFormat = textFormat.createFormat(TextFormat.NUMBER);
					setValue(numberFormat.parseObject(getText()));
				}
				break;
				
				case TextFormat.PERCENT:
				{
					Format numberFormat = textFormat.createFormat(TextFormat.NUMBER);
					Object value = numberFormat.parseObject(getText());
					if(value instanceof Number)
					{
						double d = ((Number)value).doubleValue();
						value = new Double(d / 100d);
					}
					setValue(value);
				}
				break;
				
				default:
				{
					throw e;
				}
			}
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void saveState()
	{
		savedValue = getValue();
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void restoreState()
	{
		setValue(savedValue);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public boolean hasStateChanged()
	{
		return !ObjectUtils.equals(savedValue,getFormularValue());
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void addValueChangeListener(final ValueChangeListener l)
	{
		addPropertyChangeListener("value",new PropertyChangeListener()
		{
			@Override
			public void propertyChange(PropertyChangeEvent evt)
			{
				l.valueChanged(evt);
			}
		});
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isMultiSelect()
	{
		return false;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean verify()
	{
		return support.verify();
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void addValidator(Validator validator)
	{
		support.addValidator(validator);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void removeValidator(Validator validator)
	{
		support.removeValidator(validator);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public Validator[] getValidators()
	{
		return support.getValidators();
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void validateState() throws ValidationException
	{
		support.validateState();
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void validateState(Validation validation) throws ValidationException
	{
		support.validateState(validation);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public void setFilterOperator(Operator filterOperator)
	{
		support.setFilterOperator(filterOperator);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.1
	 */
	@Override
	public Operator getFilterOperator()
	{
		return support.getFilterOperator();
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.2
	 */
	@Override
	public void setReadOnly(boolean readOnly)
	{
		support.setReadOnly(readOnly);
	}
	
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 3.2
	 */
	@Override
	public boolean isReadOnly()
	{
		return support.isReadOnly();
	}
	
	
	/**
	 * Sets the text back to an empty String.
* Has the same effect as setValue(null). */ public void reset() { setValue(null); } /** * Returns the selected text contained in this * {@link XdevFormattedTextField}. If the selection is null or * the document empty, returns a empty {@link String}. * * @return the selected text, or a empty {@link String} * * @throws IllegalArgumentException * if the selection doesn't have a valid mapping into the * document for some reason * * @see #setText * @see JTextComponent#getSelectedText() */ @Override public String getSelectedText() throws IllegalArgumentException { String st = super.getSelectedText(); if(st == null) { st = ""; } return st; } /** * Inserts a {@link String} into this {@link XdevFormattedTextField}. * * @param text * the {@link String} to insert * * @see Document#insertString(int, String, javax.swing.text.AttributeSet) */ public void insertText(String text) { try { getDocument().insertString(getCaretPosition(),text,null); } catch(Exception e) { log.error(e); } } /** * * Returns the value of this {@link XdevFormattedTextField} as * {@link XdevDate}. * * @return the value of this {@link XdevFormattedTextField} as * {@link XdevDate}. * * @throws DateFormatException * if no {@link TextFormat} is existing or the value of this * {@link XdevFormattedTextField} can not be converted. * * @see #getDate(XdevDate) * @see #getInt() * @see #getDouble() */ public XdevDate getDate() throws DateFormatException { Object val = getValue(); if(val instanceof Date) { return new XdevDate((Date)val); } else if(val instanceof Calendar) { return new XdevDate((Calendar)val); } if(textFormat != null && textFormat.getType() == TextFormat.DATETIME) { try { return new XdevDate(textFormat.parseDate(getText())); } catch(Exception e) { } } throw new DateFormatException(getText()); } /** * Returns the value of this {@link XdevFormattedTextField} as * {@link XdevDate}. If the internal value can not be converted into a * {@link XdevDate}, the defaultValue is returned. * * @param defaultValue * the default {@link XdevDate} * * @return the value of this {@link XdevFormattedTextField}. If the internal * value can not be converted into a {@link XdevDate}, the * defaultValue is returned. * * @see #getDate() */ public XdevDate getDate(XdevDate defaultValue) { try { return getDate(); } catch(DateFormatException e) { return defaultValue; } } /** * Returns the value of this {@link XdevFormattedTextField} as * int. * * @return the value of this {@link XdevFormattedTextField} as * int. * * @throws NumberFormatException * if no {@link TextFormat} is existing or the value of this * {@link XdevFormattedTextField} can not be converted. * * @see #getInt(int) * @see #getNumber() * @see #getDate() * @see #getDouble() */ public int getInt() throws NumberFormatException { return getNumber().intValue(); } /** * Returns the value of this {@link XdevFormattedTextField} as * int. If the internal value can not be converted into a * int, the defaultValue is returned. * * @param defaultValue * the default int * * @return the value of this {@link XdevFormattedTextField}. If the internal * value can not be converted into a int, the * defaultValue is returned. * * @see #getInt() */ public int getInt(int defaultValue) { try { return getInt(); } catch(NumberFormatException e) { return defaultValue; } } /** * Returns the value of this {@link XdevFormattedTextField} as * double. * * @return the value of this {@link XdevFormattedTextField} as * double * * @throws NumberFormatException * if no {@link TextFormat} is existing or the value of this * {@link XdevFormattedTextField} can not be converted. * * @see #getDouble(double) * @see #getNumber() * @see #getDate() * @see #getInt() */ public double getDouble() throws NumberFormatException { return getNumber().doubleValue(); } /** * Returns the value of this {@link XdevFormattedTextField} as * double. If the internal value can not be converted into a * double, the defaultValue is returned. * * @param defaultValue * the default double * * @return the value of this {@link XdevFormattedTextField}. If the internal * value can not be converted into a double, the * defaultValue is returned. * * @see #getDouble() */ public double getDouble(double defaultValue) { try { return getDouble(); } catch(NumberFormatException e) { return defaultValue; } } /** * Returns the value of this {@link XdevFormattedTextField} as * {@link Number}. * * @return the value of this {@link XdevFormattedTextField} as * {@link Number} * * @throws NumberFormatException * if no {@link TextFormat} is existing or the value of this * {@link XdevFormattedTextField} can not be converted. * * @see #getNumber(Number) * @see #getDouble() * @see #getDate() * @see #getInt() */ @NoBeanProperty @Override public Number getNumber() throws NumberFormatException { Object val = getValue(); if(val instanceof Number) { return (Number)val; } if(textFormat != null && textFormat.isNumeric()) { try { val = textFormat.parse(getText()); if(val instanceof Number) { return (Number)val; } } catch(Exception e) { } } throw new NumberFormatException(getText()); } /** * Returns the value of this {@link XdevFormattedTextField} as * {@link Number}. If the internal value can not be converted into a * {@link Number}, the defaultValue is returned. * * @param defaultValue * the default {@link Number} * * @return the value of this {@link XdevFormattedTextField}. If the internal * value can not be converted into a {@link Number}, the * defaultValue is returned. * * @see #getNumber() * @see #getDouble() * @see #getDate() * @see #getInt() */ @Override public Number getNumber(Number defaultValue) { try { return getNumber(); } catch(NumberFormatException e) { return defaultValue; } } /** * {@inheritDoc} */ @NoBeanProperty @Override public void setNumber(Number number) { setValue(number); } /** * {@inheritDoc} */ @Override public String toString() { if(textFormat != null) { return textFormat.format(getValue()); } else { return getText(); } } /** * {@inheritDoc} */ @Override public int getTabIndex() { return tabIndex; } /** * {@inheritDoc} */ @Override public void setTabIndex(int tabIndex) { if(this.tabIndex != tabIndex) { int oldValue = this.tabIndex; this.tabIndex = tabIndex; firePropertyChange(TAB_INDEX_PROPERTY,oldValue,tabIndex); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy