/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP 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.
*
* OpenPnP 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 OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.gui.support;
import java.awt.Color;
import javax.swing.JComponent;
import org.jdesktop.beansbinding.AbstractBindingListener;
import org.jdesktop.beansbinding.AutoBinding;
import org.jdesktop.beansbinding.AutoBinding.UpdateStrategy;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.Binding;
import org.jdesktop.beansbinding.Binding.SyncFailure;
import org.jdesktop.beansbinding.BindingListener;
import org.jdesktop.beansbinding.Bindings;
import org.jdesktop.beansbinding.Converter;
import org.openpnp.model.AbstractModelObject;
/**
* Provides convenience bindings for JComponents that add value buffering and visual feedback on
* conversion failure. Buffered values have a read-write binding with the JComponent and a read
* binding with the source value. The returned WrappedBinding allows you to save the buffered value
* to the source or to reset it from the source.
*
* @author Jason von Nieda <jason@vonnieda.org>
*/
public class JBindings {
public static <SS, SV, TS extends JComponent, TV> WrappedBinding<SS, SV, TS, TV> bind(SS source,
String sourcePropertyName, TS component, String targetPropertyName) {
return new WrappedBinding<>(source, sourcePropertyName, component, targetPropertyName, null,
(BindingListener[]) null);
}
public static <SS, SV, TS extends JComponent, TV> WrappedBinding<SS, SV, TS, TV> bind(SS source,
String sourcePropertyName, TS component, String targetPropertyName,
Converter<SV, TV> converter) {
return new WrappedBinding<>(source, sourcePropertyName, component, targetPropertyName,
converter, (BindingListener[]) null);
}
public static <SS, SV, TS extends JComponent, TV> WrappedBinding<SS, SV, TS, TV> bind(SS source,
String sourcePropertyName, TS component, String targetPropertyName,
Converter<SV, TV> converter, BindingListener... listeners) {
return new WrappedBinding<>(source, sourcePropertyName, component, targetPropertyName,
converter, listeners);
}
public static <SS, SV, TS extends JComponent, TV> WrappedBinding<SS, SV, TS, TV> bind(SS source,
String sourcePropertyName, TS component, String targetPropertyName,
BindingListener... listeners) {
return new WrappedBinding<>(source, sourcePropertyName, component, targetPropertyName, null,
listeners);
}
public static class WrappedBinding<SS, SV, TS extends JComponent, TV> {
private SS source;
private BeanProperty<SS, SV> sourceProperty;
private Wrapper<SV> wrapper;
private AutoBinding wrappedBinding;
public final void addBindingListener(BindingListener listener) {
wrappedBinding.addBindingListener(listener);
}
public WrappedBinding(SS source, String sourcePropertyName, TS component,
String targetPropertyName, Converter<SV, TV> converter,
BindingListener... listeners) {
this.source = source;
this.sourceProperty = BeanProperty.create(sourcePropertyName);
this.wrapper = new Wrapper<>(sourceProperty.getValue(source));
BeanProperty<Wrapper<SV>, SV> wrapperProperty = BeanProperty.create("value");
BeanProperty<TS, TV> targetProperty = BeanProperty.create(targetPropertyName);
wrappedBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, wrapper,
wrapperProperty, component, targetProperty);
if (converter != null) {
wrappedBinding.setConverter(converter);
}
wrappedBinding.addBindingListener(new JComponentBackgroundUpdater(component));
if (listeners != null) {
for (BindingListener listener : listeners) {
wrappedBinding.addBindingListener(listener);
}
}
wrappedBinding.bind();
AutoBinding<SS, SV, Wrapper<SV>, SV> binding = Bindings.createAutoBinding(
UpdateStrategy.READ, source, sourceProperty, wrapper, wrapperProperty);
binding.bind();
}
public void save() {
sourceProperty.setValue(source, wrapper.getValue());
}
public void reset() {
wrapper.setValue(sourceProperty.getValue(source));
}
public Wrapper<SV> getWrapper() {
return wrapper;
}
}
public static class Wrapper<T> extends AbstractModelObject {
private T value;
public Wrapper(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
T oldValue = this.value;
this.value = value;
firePropertyChange("value", oldValue, this.value);
}
}
private static class JComponentBackgroundUpdater extends AbstractBindingListener {
private static Color errorColor = new Color(0xff, 0xdd, 0xdd);
private JComponent component;
private Color oldBackground;
public JComponentBackgroundUpdater(JComponent component) {
this.component = component;
oldBackground = component.getBackground();
}
@Override
public void syncFailed(Binding binding, SyncFailure failure) {
component.setBackground(errorColor);
}
@Override
public void synced(Binding binding) {
component.setBackground(oldBackground);
}
}
}