001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.component; 020 021import icy.gui.component.ui.RangeSliderUI; 022 023import javax.swing.BoundedRangeModel; 024import javax.swing.JSlider; 025 026import org.pushingpixels.substance.api.SubstanceLookAndFeel; 027 028/** 029 * An extension of JSlider to select a range of values using two thumb controls. 030 * The thumb controls are used to select the lower and upper value of a range 031 * with predetermined minimum and maximum values. 032 * <p> 033 * Note that RangeSlider makes use of the default BoundedRangeModel, which supports an inner range 034 * defined by a value and an extent. The upper value returned by RangeSlider is simply the lower 035 * value plus the extent. 036 * </p> 037 */ 038public class RangeSlider extends JSlider 039{ 040 /** 041 * 042 */ 043 private static final long serialVersionUID = 2079286476964629269L; 044 045 /** 046 * Creates a range slider with the specified orientation and the 047 * specified minimum, maximum, initial values and extend. 048 * The orientation can be 049 * either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>. 050 * <p> 051 * The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may 052 * arise from improperly setting the minimum, initial, and maximum values on the slider. See the 053 * {@code BoundedRangeModel} documentation for details. 054 * 055 * @param orientation 056 * the orientation of the slider 057 * @param min 058 * the minimum value of the slider 059 * @param max 060 * the maximum value of the slider 061 * @param low 062 * the lower range value of the slider 063 * @param high 064 * the higher range value of the slider 065 * @throws IllegalArgumentException 066 * if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL} 067 * @see BoundedRangeModel 068 * @see #setOrientation 069 * @see #setMinimum 070 * @see #setMaximum 071 * @see #setLowerValue 072 * @see #setUpperValue 073 */ 074 public RangeSlider(int orientation, int min, int max, int low, int high) 075 { 076 super(orientation, min, max, low); 077 // remove focus as we cannot choose which bound to move 078 super.setFocusable(false); 079 setExtent(high); 080 } 081 082 /** 083 * Creates a horizontal range slider using the specified min, max and value. 084 * <p> 085 * The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may 086 * arise from improperly setting the minimum, initial, and maximum values on the slider. See the 087 * {@code BoundedRangeModel} documentation for details. 088 * 089 * @param min 090 * the minimum value of the slider 091 * @param max 092 * the maximum value of the slider 093 * @param low 094 * the lower range value of the slider 095 * @param high 096 * the higher range value of the slider 097 * @see BoundedRangeModel 098 * @see #setMinimum 099 * @see #setMaximum 100 * @see #setLowerValue 101 * @see #setUpperValue 102 */ 103 public RangeSlider(int min, int max, int low, int high) 104 { 105 this(HORIZONTAL, min, max, low, high); 106 } 107 108 /** 109 * Creates a horizontal range slider using the specified min and max 110 * with an initial value equal to the average of the min plus max. 111 * <p> 112 * The <code>BoundedRangeModel</code> that holds the slider's data handles any issues that may 113 * arise from improperly setting the minimum and maximum values on the slider. See the 114 * {@code BoundedRangeModel} documentation for details. 115 * 116 * @param min 117 * the minimum value of the slider 118 * @param max 119 * the maximum value of the slider 120 * @see BoundedRangeModel 121 * @see #setMinimum 122 * @see #setMaximum 123 */ 124 public RangeSlider(int min, int max) 125 { 126 this(HORIZONTAL, min, max, (min + max) / 2, 0); 127 } 128 129 /** 130 * Creates a range slider using the specified orientation with the 131 * range {@code 0} to {@code 100} and an initial value of {@code 50}. 132 * The orientation can be 133 * either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>. 134 * 135 * @param orientation 136 * the orientation of the slider 137 * @throws IllegalArgumentException 138 * if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL} 139 * @see #setOrientation 140 */ 141 public RangeSlider(int orientation) 142 { 143 this(orientation, 0, 100, 40, 20); 144 } 145 146 /** 147 * Creates a horizontal range slider with the range 0 to 100 and 148 * an initial value of 50. 149 */ 150 public RangeSlider() 151 { 152 this(HORIZONTAL, 0, 100, 40, 20); 153 } 154 155 @Override 156 public void setFocusable(boolean focusable) 157 { 158 // not focusable 159 super.setFocusable(false); 160 } 161 162 /** 163 * Overrides the superclass method to install the UI delegate to draw two 164 * thumbs. 165 */ 166 @Override 167 public void updateUI() 168 { 169 if (SubstanceLookAndFeel.isCurrentLookAndFeel()) 170 { 171 setUI(new RangeSliderUI(this)); 172 // Update UI for slider labels. This must be called after updating the 173 // UI of the slider. Refer to JSlider.updateUI(). 174 updateLabelUIs(); 175 } 176 else 177 super.updateUI(); 178 } 179 180 /** 181 * Returns the lower value in the range. 182 */ 183 @Override 184 public int getValue() 185 { 186 return super.getValue(); 187 } 188 189 /** 190 * Sets the lower value in the range. 191 */ 192 @Override 193 public void setValue(int value) 194 { 195 int oldValue = getValue(); 196 if (oldValue == value) 197 return; 198 199 // Compute new value and extent to maintain upper value. 200 int oldExtent = getExtent(); 201 int newValue = Math.min(Math.max(getMinimum(), value), oldValue + oldExtent); 202 int newExtent = oldExtent + oldValue - newValue; 203 204 // Set new value and extent, and fire a single change event. 205 getModel().setRangeProperties(newValue, newExtent, getMinimum(), getMaximum(), getValueIsAdjusting()); 206 } 207 208 /** 209 * Returns the lower value in the range. 210 */ 211 public int getLowerValue() 212 { 213 return getValue(); 214 } 215 216 /** 217 * Sets the lower value in the range. 218 */ 219 public void setLowerValue(int value) 220 { 221 setValue(value); 222 } 223 224 /** 225 * Returns the upper value in the range. 226 */ 227 public int getUpperValue() 228 { 229 return getValue() + getExtent(); 230 } 231 232 /** 233 * Sets the upper value in the range. 234 */ 235 public void setUpperValue(int value) 236 { 237 // Compute new extent. 238 int lowerValue = getValue(); 239 int newExtent = Math.min(Math.max(0, value - lowerValue), getMaximum() - lowerValue); 240 241 // Set extent to set upper value. 242 setExtent(newExtent); 243 } 244}