/*
* Copyright(c) 2002-2004, StarLight Systems
* All rights reserved.
*/
package com.starlight.ui;
import com.starlight.listeners.ListenerSupport;
import com.starlight.listeners.ListenerSupportFactory;
import org.jdesktop.swingx.JXDatePicker;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.JTextComponent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Enumeration;
/**
* Provides and easy way to listen for changes in value for any of a bunch of components.
* It will fire an event when a values are different.
*/
@SuppressWarnings( "UnusedDeclaration" )
public class ValueChangeRelay<K> {
private final ListenerSupport<ValueChangeListener,?> vcr_listeners;
private final InnerListener<K> inner_listener;
public ValueChangeRelay( ValueChangeListener<K> listener ) {
vcr_listeners =
ListenerSupportFactory.create( ValueChangeListener.class, false );
vcr_listeners.add( listener );
this.inner_listener = new InnerListener<K>( vcr_listeners.dispatch() );
}
public void addListener( ValueChangeListener<K> listener ) {
vcr_listeners.add( listener );
}
public void removeListener( ValueChangeListener<K> listener ) {
vcr_listeners.remove( listener );
}
/**
* Temporarily disable event notifications. Example:
* <pre>
* ValueChangeRelay vcr = ...
*
* vcr.disable();
* try {
* // Make a bunch of modifications to fields that you
* // don't want to be notified about.
* }
* finally {
* vcr.enable();
* }
* </pre>
*/
public void disable() {
inner_listener.enabled = false;
}
/**
* Enable event notifications.
* <pre>
* ValueChangeRelay vcr = ...
*
* vcr.disable();
* try {
* // Make a bunch of modifications to fields that you
* // don't want to be notified about.
* }
* finally {
* vcr.enable();
* }
* </pre>
*/
public void enable() {
inner_listener.enabled = true;
}
public boolean isEnabled() {
return inner_listener.enabled;
}
/**
* Add a new component to be monitored for changes.
*
* @param new_component The component to be watched.
* @param key A key to help identify the component.
*/
public void add( final JComponent new_component, final K key ) {
if ( new_component == null )
throw new IllegalArgumentException( "Component cannot be null." );
if ( new_component instanceof ValueChangeSource ) {
final ValueChangeSource source = ( ValueChangeSource ) new_component;
source.addValueChangeListener( new ValueChangeListener() {
@Override
public void valueChanged( JComponent component, Object inner_key,
Object new_value ) {
inner_listener.valueChanged( new_component, key, new_value );
}
} );
}
else if ( new_component instanceof AddRemovePanel ) {
final AddRemovePanel panel = ( AddRemovePanel ) new_component;
panel.addListDataListener( new ListDataListener() {
public void contentsChanged( ListDataEvent e ) {
inner_listener.valueChanged( new_component, key,
panel.getItems() );
}
public void intervalAdded( ListDataEvent e ) {
inner_listener.valueChanged( new_component, key,
panel.getItems() );
}
public void intervalRemoved( ListDataEvent e ) {
inner_listener.valueChanged( new_component, key,
panel.getItems() );
}
} );
}
else if ( new_component instanceof JFormattedTextField ) {
final JFormattedTextField text = ( JFormattedTextField ) new_component;
new_component.addPropertyChangeListener( "value",
new FormattedTextFieldPCListener( text.getValue(), new_component,
key ) );
// new_component.addPropertyChangeListener( "editValid",
// new FormattedTextFieldPCListener( text.getValue(), new_component,
// key ) );
// text.getDocument().addDocumentListener( new DocumentListener() {
// public void insertUpdate( DocumentEvent e ) {
// checkUpdate();
// }
//
// public void removeUpdate( DocumentEvent e ) {
// checkUpdate();
// }
//
// public void changedUpdate( DocumentEvent e ) {
// checkUpdate();
// }
//
// private void checkUpdate() {
// if ( !text.isEditValid() ) return;
//
// // See if it's the mask
// if ( text.getFormatter() instanceof MaskFormatter ) {
// MaskFormatter mformatter = ( MaskFormatter ) text.getFormatter();
// String text_text = text.getText();
//
// try {
// if ( MiscKit.equal( text_text, mformatter.getMask() ) ||
// MiscKit.equal( text_text, mformatter.getPlaceholder() ) ||
// MiscKit.equal( text_text, mformatter.valueToString( null ) ) ) {
//
// return;
// }
// }
// catch( ParseException ex ) {}
// }
//
// try {
// text.commitEdit();
// }
// catch( ParseException ex ) {
// System.err.println( "Failed to commit: \"" + text.getText() +
// "\" (" + text.getFormatter().getClass().getName() + ")" );
// ex.printStackTrace();
// return;
// }
//
// System.out.println( "FTF edit valid: " + text.getValue() );
// inner_listener.valueChanged( text, key, text.getValue() );
// }
// } );
}
else if ( new_component instanceof JTextComponent ) {
final JTextComponent text_component = ( JTextComponent ) new_component;
( ( JTextComponent ) new_component ).getDocument().addDocumentListener(
new DocumentListener() {
public void insertUpdate( DocumentEvent e ) {
inner_listener.valueChanged( new_component, key,
text_component.getText() );
}
public void removeUpdate( DocumentEvent e ) {
inner_listener.valueChanged( new_component, key,
text_component.getText() );
}
public void changedUpdate( DocumentEvent e ) {
inner_listener.valueChanged( new_component, key,
text_component.getText() );
}
} );
}
else if ( new_component instanceof JComboBox ) {
( ( JComboBox ) new_component ).addItemListener( new ItemListener() {
public void itemStateChanged( ItemEvent e ) {
JComboBox box = ( JComboBox ) e.getItemSelectable();
// Fire in two cases:
// - If it's a selection event
// - It's a deselection event and the new selection is null.
// We do this because no selection event is fired if null is selected.
if ( e.getStateChange() == ItemEvent.SELECTED ||
box.getSelectedItem() == null ) {
inner_listener.valueChanged( new_component, key,
( ( JComboBox ) e.getItemSelectable() ).getSelectedItem() );
}
}
} );
}
else if ( new_component instanceof JList ) {
final JList list = ( JList ) new_component;
( ( JList ) new_component ).addListSelectionListener( new ListSelectionListener() {
public void valueChanged( ListSelectionEvent e ) {
if ( e.getValueIsAdjusting() ) return;
inner_listener.valueChanged( new_component, key,
list.getSelectedValuesList() );
}
} );
}
else if ( new_component instanceof JSpinner ) {
final JSpinner spinner = ( JSpinner ) new_component;
( ( JSpinner ) new_component ).addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e ) {
inner_listener.valueChanged( new_component, key, spinner.getValue() );
}
} );
}
else if ( new_component instanceof JToggleButton ) {
( ( JToggleButton ) new_component ).addItemListener( new ItemListener() {
public void itemStateChanged( ItemEvent e ) {
inner_listener.valueChanged( new_component, key,
Boolean.valueOf( e.getStateChange() == ItemEvent.SELECTED ) );
}
} );
}
else if ( new_component instanceof JSlider ) {
final JSlider slider = ( JSlider ) new_component;
slider.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e ) {
inner_listener.valueChanged( slider, key,
Integer.valueOf( slider.getValue() ) );
}
} );
}
else if ( new_component instanceof JXDatePicker ) {
final JXDatePicker picker = ( JXDatePicker ) new_component;
picker.addPropertyChangeListener( "date", new PropertyChangeListener() {
public void propertyChange( PropertyChangeEvent evt ) {
inner_listener.valueChanged( picker, key, picker.getDate() );
}
} );
}
else {
throw new IllegalArgumentException( "Unsupported component type: " +
new_component.getClass().getName() );
}
}
/**
* Convenience method equivalent to:
* <pre>
* add( component, component );
* </pre>
* Note that this assume a key type of JComponent or some compatible type
* (can be untyped).
*/
public void add( JComponent component ) {
add( component, ( K ) component );
}
/**
* Add a button group whose buttons will be monitored for changes. Note that changes
* to the buttons in the group will not be seen after the group is added. (Sorry,
* the ButtonGroup class is kind of stupid.)
*
* @param group The ButtonGroup to be watched.
* @param key A key to help identify the component.
*/
public void add( final ButtonGroup group, final K key ) {
Enumeration enoom = group.getElements();
while( enoom.hasMoreElements() ) {
AbstractButton button = ( AbstractButton ) enoom.nextElement();
add( button, key );
}
}
public static interface ValueChangeListener<K> {
public void valueChanged( JComponent component, K key, Object new_value );
}
public static interface ValueChangeSource<K> {
public void addValueChangeListener( ValueChangeListener<K> listener );
}
private class InnerListener<K> implements ValueChangeListener<K> {
boolean enabled = true;
private ValueChangeListener<K> listener;
InnerListener( ValueChangeListener<K> listener ) {
this.listener = listener;
}
public void valueChanged( JComponent component, K key, Object new_value ) {
if ( !enabled ) return;
listener.valueChanged( component, key, new_value );
}
}
private class FormattedTextFieldPCListener implements PropertyChangeListener {
private Object initial_value;
private JComponent component;
private K key;
// private boolean fire_mode = false;
FormattedTextFieldPCListener( Object inital_value, JComponent component,
K key ) {
this.initial_value = inital_value;
this.component = component;
this.key = key;
}
public void propertyChange( PropertyChangeEvent evt ) {
// if ( !fire_mode ) {
// if ( MiscKit.equal( initial_value, evt.getNewValue() ) ) return;
//
// fire_mode = false;
// }
inner_listener.valueChanged( component, key, evt.getNewValue() );
}
}
}