// Copyright 2011 Palantir Technologies // // 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 com.palantir.ptoss.cinch.swing; import java.awt.Component; import java.beans.IntrospectionException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.palantir.ptoss.cinch.core.BindableModel; import com.palantir.ptoss.cinch.core.Binding; import com.palantir.ptoss.cinch.core.BindingContext; import com.palantir.ptoss.cinch.core.BindingWiring; import com.palantir.ptoss.cinch.core.Bindings; import com.palantir.ptoss.cinch.core.ModelUpdate; import com.palantir.ptoss.cinch.core.ObjectFieldMethod; import com.palantir.ptoss.util.Throwables; /** * A binding for an interface component that has an extent (setExtent/getExtent methods). */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface BoundExtent { /** * The model property to bind to. */ String to(); /** * When this binding should occur. */ String on() default ""; /** * Inner utility class that performs the runtime wiring of all {@link BoundExtent} bindings. * * @see Bindings#STANDARD_BINDINGS */ public static class Wiring implements BindingWiring { private static final Logger logger = LoggerFactory.getLogger(BoundExtent.class); public Collection<Binding> wire(BindingContext context) { List<Field> boundFields = context.getAnnotatedFields(BoundExtent.class); List<Binding> bindings = Lists.newArrayList(); for (Field field : boundFields) { BoundExtent bound = field.getAnnotation(BoundExtent.class); try { bindings.addAll(wire(bound, context, field)); } catch (Exception e) { throw Throwables.throwUncheckedException(e); } } return bindings; } private static Collection<Binding> wire(BoundExtent bound, BindingContext context, Field field) throws IntrospectionException { String target = bound.to(); final ObjectFieldMethod setter = context.findSetter(target); final ObjectFieldMethod getter = context.findGetter(target); if (setter == null || getter == null) { throw new IllegalArgumentException("could not find setter/getter for @BoundExtent: " + field); } final BindableModel model1 = context.getFieldObject(setter.getField(), BindableModel.class); final BindableModel model2 = context.getFieldObject(getter.getField(), BindableModel.class); assert model1 == model2; if (Component.class.isAssignableFrom(field.getType())) { final JSlider comp = context.getFieldObject(field, JSlider.class); return bindSlider(bound, context, comp, setter, getter, model1); } else { throw new IllegalArgumentException("don't know how to wire up @BoundExtent field: " + field.getName()); } } private static Collection<Binding> bindSlider(BoundExtent bound, BindingContext context, final JSlider slider, final ObjectFieldMethod setter, final ObjectFieldMethod getter, final BindableModel model1) { String on = bound.on(); final Object onObject = context.evalOnObject(on, model1); final ChangeListener changeListener = new ChangeListener() { public void stateChanged(ChangeEvent e) { try { setter.getMethod().invoke(model1, slider.getExtent()); } catch (Exception ex) { logger.error("could not invoke JSlider binding", ex); } } }; slider.addChangeListener(changeListener); Binding binding = new Binding() { public <T extends Enum<?> & ModelUpdate> void update(T... changed) { if (!BindingContext.isOn(onObject, changed)) { return; } try { int extent = (Integer) getter.getMethod().invoke(model1); slider.removeChangeListener(changeListener); slider.setExtent(extent); slider.addChangeListener(changeListener); } catch (Exception ex) { logger.error("could not invoke JSlider binding", ex); } } }; model1.bind(binding); return Collections.singleton(binding); } } }