/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.gui.component;
import icy.util.StringUtil;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.text.Format;
import java.util.EventListener;
import javax.swing.JFormattedTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* IcyTextField extends JFormattedTextField and provide easier text change handling.
*
* @author Stephane
*/
public class IcyTextField extends JFormattedTextField implements DocumentListener, ActionListener, FocusListener
{
/**
*
*/
private static final long serialVersionUID = 4294607311366304781L;
public interface TextChangeListener extends EventListener
{
public void textChanged(IcyTextField source, boolean validate);
}
// internals
/**
* @deprecated Don't use this property.
*/
@Deprecated
protected boolean consumeCharKeyPressEvent;
protected boolean changed;
/**
* Creates a <code>IcyTextField</code> with no <code>AbstractFormatterFactory</code>. Use
* <code>setMask</code> or <code>setFormatterFactory</code> to configure the
* <code>JFormattedTextField</code> to edit a particular type of
* value.
*/
public IcyTextField()
{
super();
init();
}
/**
* Creates a <code>IcyTextField</code> with the specified <code>AbstractFormatter</code>. The
* <code>AbstractFormatter</code> is placed in an <code>AbstractFormatterFactory</code>.
*
* @param formatter
* AbstractFormatter to use for formatting.
*/
public IcyTextField(AbstractFormatter formatter)
{
super(formatter);
init();
}
/**
* Creates a <code>IcyTextField</code>. <code>format</code> is
* wrapped in an appropriate <code>AbstractFormatter</code> which is
* then wrapped in an <code>AbstractFormatterFactory</code>.
*
* @param format
* Format used to look up an AbstractFormatter
*/
public IcyTextField(Format format)
{
super(format);
init();
}
/**
* Creates a IcyTextField with the specified value. This will
* create an <code>AbstractFormatterFactory</code> based on the
* type of <code>value</code>.
*
* @param value
* Initial value for the IcyTextField
*/
public IcyTextField(Object value)
{
super(value);
init();
}
protected void init()
{
changed = false;
consumeCharKeyPressEvent = false;
getDocument().addDocumentListener(this);
addActionListener(this);
addFocusListener(this);
}
protected void internalTextChanged(boolean validate)
{
// simple text change
if (!validate)
{
// keep mark of text change
changed = true;
textChanged(false);
}
else
{
// previous text change
if (changed)
{
textChanged(true);
changed = false;
}
}
}
protected void textChanged(boolean validate)
{
fireTextChanged(validate);
}
protected void fireTextChanged(boolean validate)
{
for (TextChangeListener listener : listenerList.getListeners(TextChangeListener.class))
listener.textChanged(this, validate);
}
/**
* Force the field to pass to unchanged state (after a {@link #setText(String)} for instance)<br>
* so it won't generate further <code>textChanged</code> event.
*/
public void setUnchanged()
{
changed = false;
}
public void addTextChangeListener(TextChangeListener listener)
{
listenerList.add(TextChangeListener.class, listener);
}
public void removeTextChangeListener(TextChangeListener listener)
{
listenerList.remove(TextChangeListener.class, listener);
}
/**
* @deprecated Should not be used (keep it to false)
*/
@Deprecated
public void setConsumeCharKeyPressEvent(boolean consumeCharKeyPressEvent)
{
this.consumeCharKeyPressEvent = consumeCharKeyPressEvent;
}
/**
* @deprecated Should not be used.
*/
@Deprecated
public boolean getConsumeCharKeyPressEvent()
{
return consumeCharKeyPressEvent;
}
@Override
protected void processComponentKeyEvent(KeyEvent e)
{
super.processComponentKeyEvent(e);
if (consumeCharKeyPressEvent)
{
final char c = e.getKeyChar();
// consume KEY_PRESSED character event
if ((e.getID() == KeyEvent.KEY_PRESSED) && Character.isDefined(c) && !Character.isISOControl(c))
e.consume();
}
}
@Override
public void setText(String t)
{
if (StringUtil.equals(t, getText(), false))
return;
super.setText(t);
// validate change
internalTextChanged(true);
}
@Override
public void changedUpdate(DocumentEvent e)
{
internalTextChanged(false);
}
@Override
public void insertUpdate(DocumentEvent e)
{
internalTextChanged(false);
}
@Override
public void removeUpdate(DocumentEvent e)
{
internalTextChanged(false);
}
@Override
public void actionPerformed(ActionEvent e)
{
internalTextChanged(true);
}
@Override
public void focusGained(FocusEvent e)
{
// nothing to do here
}
@Override
public void focusLost(FocusEvent e)
{
internalTextChanged(true);
}
}