/*
* Copyright 2002-2004 Greg Hinkle
*
* 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 org.signalml.app.model.components;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.swing.BoundedRangeModel;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicSliderUI;
import javax.swing.plaf.metal.MetalSliderUI;
/**
* This JSlider subclass uses a custom UI to allow a slider to work in
* logarithmic scale. Major and minor ticks are drawn for logarithmic scale as
* well.
*
* @author Greg Hinkle (ghinkle@users.sourceforge.net), Apr 10, 2004
* @author Piotr Różański (small adjustments)
*/
public class LogarithmicJSlider extends JSlider {
public LogarithmicJSlider(int orientation) {
super(orientation);
this.setLogSliderUI();
}
public LogarithmicJSlider(int min, int max) {
super(min, max);
this.setLogSliderUI();
}
public LogarithmicJSlider(int min, int max, int value) {
super(min, max, value);
this.setLogSliderUI();
}
public LogarithmicJSlider(int orientation, int min, int max, int value) {
super(orientation, min, max, value);
this.setLogSliderUI();
}
public LogarithmicJSlider(BoundedRangeModel brm) {
super(brm);
this.setLogSliderUI();
}
public LogarithmicJSlider() {
this.setLogSliderUI();
}
public static class LogSliderCore {
public static int xPositionForValue(int value, JSlider slider, boolean drawInverted, Rectangle trackRect) {
int min = slider.getMinimum();
int max = slider.getMaximum();
int trackLength = trackRect.width;
double valueRange = (double) Math.log(max) - (double) Math.log(min);
double pixelsPerValue = (double) trackLength / valueRange;
int trackLeft = trackRect.x;
int trackRight = trackRect.x + (trackRect.width - 1);
int xPosition;
if (!drawInverted) {
xPosition = trackLeft;
xPosition += Math.round(pixelsPerValue * ((double) Math.log(value) - Math.log(min)));
} else {
xPosition = trackRight;
xPosition -= Math.round(pixelsPerValue * ((double) Math.log(value) - Math.log(min)));
}
xPosition = Math.max(trackLeft, xPosition);
xPosition = Math.min(trackRight, xPosition);
return xPosition;
}
public static int valueForXPosition(int xPos, JSlider slider, boolean drawInverted, Rectangle trackRect) {
int value;
final int minValue = slider.getMinimum();
final int maxValue = slider.getMaximum();
final int trackLength = trackRect.width;
final int trackLeft = trackRect.x;
final int trackRight = trackRect.x + (trackRect.width - 1);
if (xPos <= trackLeft) {
value = drawInverted ? maxValue : minValue;
} else if (xPos >= trackRight) {
value = drawInverted ? minValue : maxValue;
} else {
int distanceFromTrackLeft = drawInverted ? trackRight - xPos : xPos - trackLeft;
double valueRange = Math.log((double) maxValue) - Math.log((double) minValue);
//double valuePerPixel = (double)valueRange / (double)trackLength;
//int valueFromTrackLeft =
// (int)Math.round( Math.pow(3.5,(double)distanceFromTrackLeft * (double)valuePerPixel));
int valueFromTrackLeft
= (int) Math.round(Math.pow(Math.E, Math.log(minValue) + ((((double) distanceFromTrackLeft) * valueRange) / (double) trackLength)));
value = (int) Math.log(minValue) + valueFromTrackLeft;
}
return value;
}
}
public static class LogMetalSliderUI extends MetalSliderUI {
public LogMetalSliderUI(JSlider slider) {
super();
this.slider = slider;
}
@Override
protected int xPositionForValue(int value) {
return LogSliderCore.xPositionForValue(value, slider, drawInverted(), trackRect);
}
@Override
public int valueForXPosition(int xPos) {
return LogSliderCore.valueForXPosition(xPos, slider, drawInverted(), trackRect);
}
@Override
public void paintTicks(Graphics g) {
// nothing here
}
}
public static class LogBasicSliderUI extends BasicSliderUI {
public LogBasicSliderUI(JSlider slider) {
super(slider);
}
@Override
protected int xPositionForValue(int value) {
return LogSliderCore.xPositionForValue(value, slider, drawInverted(), trackRect);
}
@Override
public int valueForXPosition(int xPos) {
return LogSliderCore.valueForXPosition(xPos, slider, drawInverted(), trackRect);
}
@Override
public void paintTicks(Graphics g) {
// nothing here
}
}
private void setLogSliderUI() {
if (this.getUI() instanceof MetalSliderUI) {
this.setUI(new LogMetalSliderUI(this));
} else {
this.setUI(new LogBasicSliderUI(this));
}
}
/**
* Creates a hashtable holding the text labels for this slider. This
* implementation uses the increment as a log-base.
*
* @return hash table
*/
@Override
public Hashtable createStandardLabels(int increment, int start) {
if (start > getMaximum() || start < getMinimum()) {
throw new IllegalArgumentException(
"Slider label start point out of range.");
}
if (increment <= 0) {
throw new IllegalArgumentException("Label incremement must be > 0");
}
class LabelHashtable extends Hashtable implements
PropertyChangeListener {
int increment = 0;
int start = 0;
boolean startAtMin = false;
public LabelHashtable(int increment, int start) {
super();
this.increment = increment;
this.start = start;
startAtMin = start == getMinimum();
createLabels(this, increment, start);
}
@Override
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals("minimum") && startAtMin) {
start = getMinimum();
}
if (e.getPropertyName().equals("minimum")
|| e.getPropertyName().equals("maximum")) {
Enumeration keys = getLabelTable().keys();
Object key = null;
Hashtable hashtable = new Hashtable();
// Save the labels that were added by the developer
while (keys.hasMoreElements()) {
key = keys.nextElement();
Object value = getLabelTable().get(key);
if (!(value instanceof LabelUIResource)) {
hashtable.put(key, value);
}
}
clear();
createLabels(this, increment, start);
// Add the saved labels
keys = hashtable.keys();
while (keys.hasMoreElements()) {
key = keys.nextElement();
put(key, hashtable.get(key));
}
((JSlider) e.getSource()).setLabelTable(this);
}
}
}
LabelHashtable table = new LabelHashtable(increment, start);
if (getLabelTable() != null
&& (getLabelTable() instanceof PropertyChangeListener)) {
removePropertyChangeListener((PropertyChangeListener) getLabelTable());
}
addPropertyChangeListener(table);
return table;
}
/**
* This method creates the table of labels that are used to label major
* ticks on the slider.
*
* @param table
* @param increment
* @param start
*/
protected void createLabels(Hashtable table, int increment, int start) {
for (int labelIndex = start; labelIndex <= getMaximum(); labelIndex *= increment) {
table.put(labelIndex, new LabelUIResource(""
+ labelIndex, JLabel.CENTER));
}
}
protected class LabelUIResource extends JLabel implements UIResource {
public LabelUIResource(String text, int alignment) {
super(text, alignment);
setName("Slider.label");
}
}
}