1

I have wee bit of code that updates a JSlider; and at another point in time, the JSlider's maximum value needs to updated. The problem is when I call setMaximum() on the slider, it also dispatches a ChangeEvent. To avoid that I'm doing this:

slider.removeChangeListener(this);
slider.setMaximum(newMax);
slider.addChangeListener(this);

Is there a cleaner/more elegant way of doing this?

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • 1
    you can't and you shouldn't: in the general case, the listener might rely on being notified of _all_ changes of slider properties. Assuming that in your controlled contexd it doesn't, the question is still _why_ do you want it? Smells a bit like a suboptimal setup .. – kleopatra Apr 24 '13 at 14:33
  • @kleopatra yup, you're right, probably shouldn't but it's a small project that should be kept simple. It still needs to compile in [Processing](http://processing.org) editor so the [GUI](https://github.com/twsu/brighteyes/blob/master/BrightEyesEditor/GUI.pde) will be a nested class anyway. It's a bit dirty but does the job. – George Profenza Apr 24 '13 at 20:26

4 Answers4

3

A clean way might be (depending a bit on what you actually need) to implement a custom BoundedRangeModel which fires a custom ChangeEvent that can carry the actually changed properties:

/**
 * Extended model that passes the list of actually changed properties
 * in an extended changeEvent.
 */
public static class MyBoundedRangeModel extends DefaultBoundedRangeModel {

    public MyBoundedRangeModel() {
    }

    public MyBoundedRangeModel(int value, int extent, int min, int max) {
        super(value, extent, min, max);
    }

    @Override
    public void setRangeProperties(int newValue, int newExtent, int newMin,
            int newMax, boolean adjusting) {
        int oldMax = getMaximum();
        int oldMin = getMinimum();
        int oldValue = getValue();
        int oldExtent = getExtent();
        boolean oldAdjusting = getValueIsAdjusting();
        // todo: enforce constraints of new values for all
        List<String> changedProperties = new ArrayList<>();
        if (oldMax != newMax) {
           changedProperties.add("maximum"); 
        }
        if (oldValue != newValue) {
            changedProperties.add("value");
        }
        // todo: check and add other properties 
        changeEvent = changedProperties.size() > 0 ? 
                new MyChangeEvent(this, changedProperties) : null;
        super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
    }


}

/**
 * Extended ChangeEvent that provides a list of actually 
 * changed properties. 
 */
public static class MyChangeEvent extends ChangeEvent {

    private List<String> changedProperties;

    /**
     * @param source
     */
    public MyChangeEvent(Object source, List<String> changedProperties) {
        super(source);
        this.changedProperties = changedProperties;
    }

    public List<String> getChangedProperties() {
        return changedProperties;
    }

}

Its usage something like:

final JSlider slider = new JSlider();
slider.setModel(new MyBoundedRangeModel(0, 0, -100, 100));
ChangeListener l = new ChangeListener() {

    @Override
    public void stateChanged(ChangeEvent e) {
        if (e instanceof MyChangeEvent) {
            MyChangeEvent me = (MyChangeEvent) e;
            if (me.getChangedProperties().contains("value")) {
               System.out.println("new value: " + 
                    ((BoundedRangeModel) e.getSource()).getValue()); 
            }
            if (me.getChangedProperties().contains("maximum")) {
                System.out.println("new max: " + 
                    ((BoundedRangeModel) e.getSource()).getMaximum()); 
            }
        } else {
            // do something else or nothing
        }
    }
};
slider.getModel().addChangeListener(l);

Note that you have to register the listener with the model, not with the slider (reason being that the slider creates a new changeEvent of the plain type)

kleopatra
  • 51,061
  • 28
  • 99
  • 211
0

You could check who triggered the change in the listener. It's still pretty dirty but you won't have to remove the change listener.

DeadlyJesus
  • 1,503
  • 2
  • 12
  • 26
0

It looks like the same problem, in essence, as this question. In which case, I fear the answer is "no"

Community
  • 1
  • 1
MrB
  • 818
  • 8
  • 28
0

if you just need your slider to fire events when value is changing you can simplify kleopatra's answer in the following way:

// make sure slider only fires changEvents when value changes
slider.setModel(new DefaultBoundedRangeModel() {
        final ChangeEvent theOne=new ChangeEvent(this);         
        @Override
        public void setRangeProperties(int newValue, int newExtent, int newMin,int newMax, boolean adjusting) 
            changeEvent= (getValue() != newValue ? theOne:null);
            super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
        }
        @Override
        protected void fireStateChanged()
        {
            if(changeEvent==null) return;
            super.fireStateChanged();
        }
    });

Then all you need to do is register a "Standard" ChangeListener on the model and it will only get called when the value is changing not when maximum or minimum changes.

slider.getModel().addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) { 
        // do something here when value changes 
    });

});

ehubin
  • 63
  • 1
  • 4