/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.gui.component;
import icy.gui.component.ui.RangeSliderUI;
import javax.swing.BoundedRangeModel;
import javax.swing.JSlider;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
/**
* An extension of JSlider to select a range of values using two thumb controls.
* The thumb controls are used to select the lower and upper value of a range
* with predetermined minimum and maximum values.
* <p>
* Note that RangeSlider makes use of the default BoundedRangeModel, which supports an inner range
* defined by a value and an extent. The upper value returned by RangeSlider is simply the lower
* value plus the extent.
* </p>
*/
public class RangeSlider extends JSlider
{
/**
*
*/
private static final long serialVersionUID = 2079286476964629269L;
/**
* Creates a range slider with the specified orientation and the
* specified minimum, maximum, initial values and extend.
* The orientation can be
* either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>.
* <p>
* The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may
* arise from improperly setting the minimum, initial, and maximum values on the slider. See the
* {@code BoundedRangeModel} documentation for details.
*
* @param orientation
* the orientation of the slider
* @param min
* the minimum value of the slider
* @param max
* the maximum value of the slider
* @param low
* the lower range value of the slider
* @param high
* the higher range value of the slider
* @throws IllegalArgumentException
* if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
* @see BoundedRangeModel
* @see #setOrientation
* @see #setMinimum
* @see #setMaximum
* @see #setLowerValue
* @see #setUpperValue
*/
public RangeSlider(int orientation, int min, int max, int low, int high)
{
super(orientation, min, max, low);
// remove focus as we cannot choose which bound to move
super.setFocusable(false);
setExtent(high);
}
/**
* Creates a horizontal range slider using the specified min, max and value.
* <p>
* The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may
* arise from improperly setting the minimum, initial, and maximum values on the slider. See the
* {@code BoundedRangeModel} documentation for details.
*
* @param min
* the minimum value of the slider
* @param max
* the maximum value of the slider
* @param low
* the lower range value of the slider
* @param high
* the higher range value of the slider
* @see BoundedRangeModel
* @see #setMinimum
* @see #setMaximum
* @see #setLowerValue
* @see #setUpperValue
*/
public RangeSlider(int min, int max, int low, int high)
{
this(HORIZONTAL, min, max, low, high);
}
/**
* Creates a horizontal range slider using the specified min and max
* with an initial value equal to the average of the min plus max.
* <p>
* The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may
* arise from improperly setting the minimum and maximum values on the slider. See the
* {@code BoundedRangeModel} documentation for details.
*
* @param min
* the minimum value of the slider
* @param max
* the maximum value of the slider
* @see BoundedRangeModel
* @see #setMinimum
* @see #setMaximum
*/
public RangeSlider(int min, int max)
{
this(HORIZONTAL, min, max, (min + max) / 2, 0);
}
/**
* Creates a range slider using the specified orientation with the
* range {@code 0} to {@code 100} and an initial value of {@code 50}.
* The orientation can be
* either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>.
*
* @param orientation
* the orientation of the slider
* @throws IllegalArgumentException
* if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
* @see #setOrientation
*/
public RangeSlider(int orientation)
{
this(orientation, 0, 100, 40, 20);
}
/**
* Creates a horizontal range slider with the range 0 to 100 and
* an initial value of 50.
*/
public RangeSlider()
{
this(HORIZONTAL, 0, 100, 40, 20);
}
@Override
public void setFocusable(boolean focusable)
{
// not focusable
super.setFocusable(false);
}
/**
* Overrides the superclass method to install the UI delegate to draw two
* thumbs.
*/
@Override
public void updateUI()
{
if (SubstanceLookAndFeel.isCurrentLookAndFeel())
{
setUI(new RangeSliderUI(this));
// Update UI for slider labels. This must be called after updating the
// UI of the slider. Refer to JSlider.updateUI().
updateLabelUIs();
}
else
super.updateUI();
}
/**
* Returns the lower value in the range.
*/
@Override
public int getValue()
{
return super.getValue();
}
/**
* Sets the lower value in the range.
*/
@Override
public void setValue(int value)
{
int oldValue = getValue();
if (oldValue == value)
return;
// Compute new value and extent to maintain upper value.
int oldExtent = getExtent();
int newValue = Math.min(Math.max(getMinimum(), value), oldValue + oldExtent);
int newExtent = oldExtent + oldValue - newValue;
// Set new value and extent, and fire a single change event.
getModel().setRangeProperties(newValue, newExtent, getMinimum(), getMaximum(), getValueIsAdjusting());
}
/**
* Returns the lower value in the range.
*/
public int getLowerValue()
{
return getValue();
}
/**
* Sets the lower value in the range.
*/
public void setLowerValue(int value)
{
setValue(value);
}
/**
* Returns the upper value in the range.
*/
public int getUpperValue()
{
return getValue() + getExtent();
}
/**
* Sets the upper value in the range.
*/
public void setUpperValue(int value)
{
// Compute new extent.
int lowerValue = getValue();
int newExtent = Math.min(Math.max(0, value - lowerValue), getMaximum() - lowerValue);
// Set extent to set upper value.
setExtent(newExtent);
}
}