/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.form.binding.jide;
import com.jidesoft.swing.DefaultOverlayable;
import org.springframework.util.Assert;
import org.valkyriercp.binding.form.FormModel;
import org.valkyriercp.form.binding.Binding;
import org.valkyriercp.form.binding.swing.SwingBindingFactory;
import javax.swing.*;
import java.awt.*;
import java.util.Map;
/**
* A binding factory implementation with <code>Overlayable</code> support. This implementation ensures any binding
* created by this factory has overlay support based on Jide OSS {@link DefaultOverlayable}.
* <p>
* A first approach was to intercept methods returning <code>Binding</code> objects using Spring AOP, this has a pitfall
* consisting on exists user level code used that invokes <code>SwingBindingFactory</code> extension methods. When using
* AOP interception the returned binding factory is a <code>$Proxy</code> class that cannot be assigned to
* <code>SwingBindingFactory</code>. That's the reason why rewriting interface methods is the more suitable way.
*
* @author <a href = "mailto:julio.arguello@gmail.com" >Julio Arg??ello (JAF)</a>
*
* @see JideRepaintManager, since according to <a href="http://forums.sun.com/thread.jspa?threadID=725127">this
* thread</a> the "unique" way to listen for repaint changes is overriding <code>RepaintManager</code>. This is not
* a recommended practice but works anyway...
*/
public class JideBindingFactory extends SwingBindingFactory {
/**
* The default insets to be applied to components.
*/
public static final Insets DEFAULT_INSETS = new Insets(5, 0, 5, 0);
/**
* Creates a binding factory given the target form model.
*
* @param formModel
* the form model.
*/
public JideBindingFactory(FormModel formModel) {
super(formModel);
}
/**
* {@inheritDoc}
*/
@Override
public Binding bindControl(JComponent control, String formPropertyPath) {
return new OverlayableBinding(super.bindControl(control, formPropertyPath));
}
/**
* {@inheritDoc}
*/
@Override
public Binding bindControl(JComponent control, String formPropertyPath, @SuppressWarnings("rawtypes") Map context) {
return new OverlayableBinding(super.bindControl(control, formPropertyPath, context));
}
/**
* {@inheritDoc}
*/
@Override
public Binding createBinding(String formPropertyPath) {
return new OverlayableBinding(super.createBinding(formPropertyPath));
}
/**
* {@inheritDoc}
*/
@Override
public Binding createBinding(String formPropertyPath, @SuppressWarnings("rawtypes") Map context) {
return new OverlayableBinding(super.createBinding(formPropertyPath, context));
}
/**
* {@inheritDoc}
*/
@Override
public Binding createBinding(@SuppressWarnings("rawtypes") Class controlType, String formPropertyPath) {
return new OverlayableBinding(super.createBinding(controlType, formPropertyPath));
}
/**
* {@inheritDoc}
*/
@Override
public Binding createBinding(@SuppressWarnings("rawtypes") Class controlType, String formPropertyPath,
@SuppressWarnings("rawtypes") Map context) {
return new OverlayableBinding(super.createBinding(controlType, formPropertyPath, context));
}
/**
* A binding implementation that wraps the original control into an overlay ready component.
*
* @author <a href = "mailto:julio.arguello@gmail.com" >Julio Arg??ello (JAF)</a>
*
* @see DefaultOverlayable
*/
private static class OverlayableBinding implements Binding {
/**
* The control.
*/
private JComponent control;
/**
* The form model.
*/
private FormModel formModel;
/**
* The property name.
*/
private String property;
/**
* Constructs the binding given its source.
*
* @param source
* the source binding.
*/
public OverlayableBinding(Binding source) {
this(source.getControl(), source.getFormModel(), source.getProperty());
}
/**
* Constructs the binding given its control, form model and property name.
*
* @param control
* the control.
* @param formModel
* the form model.
* @param property
* the property name.
*/
private OverlayableBinding(JComponent control, FormModel formModel, String property) {
super();
this.setControl(this.addOverlaySupportIfNeeded(control));
this.setFormModel(formModel);
this.setProperty(property);
// Install the JideRepaintManagerWrapper if not already done.
// Note it is installed in a lazy way to avoid unnecessary installations when the binding factory is
// instantiated (but not employed)
JideRepaintManager.installJideRepaintManagerIfNeeded();
}
/**
* {@inheritDoc}
*/
@Override
public JComponent getControl() {
return this.control;
}
/**
* {@inheritDoc}
*/
@Override
public FormModel getFormModel() {
return this.formModel;
}
/**
* {@inheritDoc}
*/
@Override
public String getProperty() {
return this.property;
}
/**
* Returns an overlable component given the target one.
*
* @param control
* the target control.
*
* @return an overlayable component over the original one.
*/
protected final JComponent addOverlaySupportIfNeeded(JComponent control) {
if (!(control instanceof DefaultOverlayable)) {
// Set a default overlay location insets to avoid overlayable bounds be changed after adding overlays
final DefaultOverlayable overlayable = new DefaultOverlayable(control);
overlayable.setOverlayLocationInsets(JideBindingFactory.DEFAULT_INSETS);
return overlayable;
}
return control;
}
/**
* Sets the control.
*
* @param control
* the control to set.
*/
private void setControl(JComponent control) {
Assert.notNull(control, "control");
this.control = control;
}
/**
* Sets the form model.
*
* @param formModel
* model the form model to set.
*/
private void setFormModel(FormModel formModel) {
Assert.notNull(formModel, "formModel");
this.formModel = formModel;
}
/**
* Sets the property name.
*
* @param property
* the property name to set.
*/
private void setProperty(String property) {
Assert.notNull(property, "property");
this.property = property;
}
}
}