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 java.awt.BorderLayout; 022 023import javax.swing.DefaultBoundedRangeModel; 024import javax.swing.JLabel; 025import javax.swing.JPanel; 026import javax.swing.JSpinner; 027import javax.swing.SpinnerNumberModel; 028import javax.swing.SwingConstants; 029import javax.swing.border.EmptyBorder; 030import javax.swing.event.ChangeEvent; 031import javax.swing.event.ChangeListener; 032 033/** 034 * Component letting user to define a range (RangeSlider + 2 inputs fields) 035 * 036 * @author Stephane 037 */ 038public class RangeComponent extends JPanel implements ChangeListener 039{ 040 /** 041 * 042 */ 043 private static final long serialVersionUID = 7244476681262628392L; 044 045 final protected JSpinner lowSpinner; 046 final protected JSpinner highSpinner; 047 final protected RangeSlider slider; 048 049 public RangeComponent(int orientation, double min, double max, double step) 050 { 051 super(); 052 053 lowSpinner = new JSpinner(new SpinnerNumberModel(min, min, max, step)); 054 lowSpinner.setToolTipText("Set low bound"); 055 highSpinner = new JSpinner(new SpinnerNumberModel(max, min, max, step)); 056 highSpinner.setToolTipText("Set high bound"); 057 slider = new RangeSlider(orientation); 058 updateSliderModel(); 059 060 lowSpinner.addChangeListener(this); 061 highSpinner.addChangeListener(this); 062 slider.addChangeListener(this); 063 064 slider.setBorder(new EmptyBorder(0, 0, 0, 0)); 065 066 setLayout(new BorderLayout(0, 0)); 067 068 if (orientation == SwingConstants.VERTICAL) 069 { 070 add(lowSpinner, BorderLayout.SOUTH); 071 add(slider, BorderLayout.CENTER); 072 add(highSpinner, BorderLayout.NORTH); 073 } 074 else 075 { 076 add(lowSpinner, BorderLayout.WEST); 077 add(slider, BorderLayout.CENTER); 078 add(highSpinner, BorderLayout.EAST); 079 } 080 081 validate(); 082 } 083 084 public RangeComponent(double min, double max, double step) 085 { 086 this(SwingConstants.HORIZONTAL, min, max, step); 087 } 088 089 public RangeComponent(int orientation) 090 { 091 this(orientation, 0d, 100d, 1d); 092 } 093 094 public RangeComponent() 095 { 096 this(SwingConstants.HORIZONTAL, 0d, 100d, 1d); 097 } 098 099 public JSpinner getLowSpinner() 100 { 101 return lowSpinner; 102 } 103 104 public JSpinner getHighSpinner() 105 { 106 return highSpinner; 107 } 108 109 public RangeSlider getSlider() 110 { 111 return slider; 112 } 113 114 public SpinnerNumberModel getLowModel() 115 { 116 return (SpinnerNumberModel) lowSpinner.getModel(); 117 } 118 119 public SpinnerNumberModel getHighModel() 120 { 121 return (SpinnerNumberModel) highSpinner.getModel(); 122 } 123 124 private double getRange() 125 { 126 return getMax() - getMin(); 127 } 128 129 private int getSliderRange() 130 { 131 return slider.getMaximum() - slider.getMinimum(); 132 } 133 134 private int spinnerToSlider(double value) 135 { 136 final double spinnerRange = getRange(); 137 138 if (spinnerRange == 0) 139 return 0; 140 141 return (int) ((value - getMin()) * getSliderRange() / spinnerRange); 142 } 143 144 private double sliderToSpinner(int value) 145 { 146 final int sliderRange = getSliderRange(); 147 148 if (sliderRange == 0) 149 return 0d; 150 151 return (value * getRange() / sliderRange) + getMin(); 152 } 153 154 private void updateSliderModel() 155 { 156 final int sliderRange = (int) Math.round(getRange() / getStep()); 157 158 slider.setModel(new DefaultBoundedRangeModel(0, 0, 0, sliderRange)); 159 slider.setLowerValue(spinnerToSlider(getLow())); 160 slider.setUpperValue(spinnerToSlider(getHigh())); 161 } 162 163 /** 164 * Set the lower and higher range value. 165 * 166 * @see #setLow(double) 167 * @see #setHigh(double) 168 * @see #setMin(double) 169 * @see #setMax(double) 170 */ 171 public void setLowHigh(double low, double high) 172 { 173 getLowModel().setValue(Double.valueOf(low)); 174 getHighModel().setValue(Double.valueOf(high)); 175 } 176 177 /** 178 * Get the lower range value. 179 * 180 * @see #getHigh() 181 * @see #getMin() 182 */ 183 public double getLow() 184 { 185 return getLowModel().getNumber().doubleValue(); 186 } 187 188 /** 189 * Get the higher range value. 190 * 191 * @see #getLow() 192 * @see #getMax() 193 */ 194 public double getHigh() 195 { 196 return getHighModel().getNumber().doubleValue(); 197 } 198 199 /** 200 * Set the lower range value. 201 * 202 * @see #setHigh(double) 203 * @see #setMin(double) 204 */ 205 public void setLow(double value) 206 { 207 getLowModel().setValue(Double.valueOf(value)); 208 } 209 210 /** 211 * Set the higher range value. 212 * 213 * @see #setLow(double) 214 * @see #setMax(double) 215 */ 216 public void setHigh(double value) 217 { 218 getHighModel().setValue(Double.valueOf(value)); 219 } 220 221 /** 222 * Return true if the range use integer number 223 */ 224 public boolean isInteger() 225 { 226 final SpinnerNumberModel model = getLowModel(); 227 final Number value = model.getNumber(); 228 final Number step = model.getStepSize(); 229 230 return (value.doubleValue() == value.longValue()) && (step.doubleValue() == step.longValue()); 231 } 232 233 /** 234 * Get range minimum value. 235 * 236 * @see #getMax() 237 * @see #getLow() 238 */ 239 public double getMin() 240 { 241 return ((Double) getLowModel().getMinimum()).doubleValue(); 242 } 243 244 /** 245 * Get range minimum value. 246 * 247 * @see #getMin() 248 * @see #getHigh() 249 */ 250 public double getMax() 251 { 252 return ((Double) getHighModel().getMaximum()).doubleValue(); 253 } 254 255 /** 256 * Get range step value. 257 * 258 * @see #getMin() 259 * @see #getMax() 260 */ 261 public double getStep() 262 { 263 return getLowModel().getStepSize().doubleValue(); 264 } 265 266 /** 267 * Set range bounds and step value. 268 * 269 * @see #setMin(double) 270 * @see #setMax(double) 271 * @see #setStep(double) 272 */ 273 public void setMinMaxStep(double min, double max, double step) 274 { 275 final double low = Math.max(Math.min(getLow(), max), min); 276 final double high = Math.max(Math.min(getHigh(), max), min); 277 278 lowSpinner.setModel(new SpinnerNumberModel(low, min, max, step)); 279 highSpinner.setModel(new SpinnerNumberModel(high, min, max, step)); 280 281 updateSliderModel(); 282 } 283 284 /** 285 * Set range bounds value. 286 * 287 * @see #setMin(double) 288 * @see #setMax(double) 289 * @see #setLow(double) 290 * @see #setHigh(double) 291 */ 292 public void setMinMax(double min, double max) 293 { 294 setMinMaxStep(min, max, getStep()); 295 } 296 297 /** 298 * Set range minimum value. 299 * 300 * @see #setMax(double) 301 * @see #setLow(double) 302 */ 303 public void setMin(double value) 304 { 305 setMinMaxStep(value, getMax(), getStep()); 306 } 307 308 /** 309 * Set range maximum value. 310 * 311 * @see #setMin(double) 312 * @see #setHigh(double) 313 */ 314 public void setMax(double value) 315 { 316 setMinMaxStep(getMin(), value, getStep()); 317 } 318 319 /** 320 * Set range step value. 321 * 322 * @see #setMin(double) 323 * @see #setMax(double) 324 */ 325 public void setStep(double value) 326 { 327 setMinMaxStep(getMin(), getMax(), value); 328 } 329 330 /** 331 * Set slider visible or not. 332 */ 333 public void setSliderVisible(boolean value) 334 { 335 if (value) 336 add(slider, BorderLayout.CENTER); 337 else 338 add(new JLabel(" - "), BorderLayout.CENTER); 339 } 340 341 @Override 342 public void setToolTipText(String text) 343 { 344 slider.setToolTipText(text); 345 346 super.setToolTipText(text); 347 } 348 349 @Override 350 public void setEnabled(boolean enabled) 351 { 352 lowSpinner.setEnabled(enabled); 353 highSpinner.setEnabled(enabled); 354 slider.setEnabled(enabled); 355 356 super.setEnabled(enabled); 357 } 358 359 protected void fireChangedEvent(ChangeEvent event) 360 { 361 for (ChangeListener listener : getListeners(ChangeListener.class)) 362 listener.stateChanged(event); 363 } 364 365 public void addChangeListener(ChangeListener listener) 366 { 367 listenerList.add(ChangeListener.class, listener); 368 } 369 370 public void removeChangeListener(ChangeListener listener) 371 { 372 listenerList.remove(ChangeListener.class, listener); 373 } 374 375 @Override 376 public void stateChanged(ChangeEvent e) 377 { 378 final Object source = e.getSource(); 379 final double low = getLow(); 380 final double high = getHigh(); 381 382 if (source == lowSpinner) 383 { 384 slider.setLowerValue(spinnerToSlider(low)); 385 386 if (high < low) 387 setHigh(low); 388 389 getHighModel().setMinimum(Double.valueOf(low)); 390 } 391 else if (source == highSpinner) 392 { 393 slider.setUpperValue(spinnerToSlider(high)); 394 395 if (low > high) 396 setLow(high); 397 398 getLowModel().setMaximum(Double.valueOf(high)); 399 } 400 else if (source == slider) 401 { 402 setLow(sliderToSpinner(slider.getLowerValue())); 403 setHigh(sliderToSpinner(slider.getUpperValue())); 404 } 405 406 fireChangedEvent(e); 407 } 408}