Rework the impulse and diameter to use a two thumbed slider.
This commit is contained in:
parent
afcedf90f1
commit
e5679259fa
@ -1,9 +1,5 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import net.sf.openrocket.database.motor.ThrustCurveMotorSet;
|
||||
|
||||
public enum ImpulseClass {
|
||||
|
||||
@ -32,10 +28,13 @@ public enum ImpulseClass {
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public double getLow() {
|
||||
return low;
|
||||
}
|
||||
|
||||
public boolean isIn( ThrustCurveMotorSet m ) {
|
||||
long motorImpulse = m.getTotalImpuse();
|
||||
return motorImpulse >= low && motorImpulse <= high;
|
||||
public double getHigh() {
|
||||
return high;
|
||||
}
|
||||
|
||||
private double low;
|
||||
|
@ -7,6 +7,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
@ -32,6 +33,7 @@ import net.sf.openrocket.gui.components.UnitSelector;
|
||||
import net.sf.openrocket.gui.util.CheckList;
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||
import net.sf.openrocket.gui.widgets.MultiSlider;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.motor.Manufacturer;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
@ -45,43 +47,52 @@ public abstract class MotorFilterPanel extends JPanel {
|
||||
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
private static Hashtable diameterLabels = new Hashtable();
|
||||
private static double[] diameterValues = new double[] {
|
||||
0,
|
||||
.013,
|
||||
.018,
|
||||
.024,
|
||||
.029,
|
||||
.038,
|
||||
.054,
|
||||
.075,
|
||||
.098,
|
||||
1.000
|
||||
};
|
||||
static {
|
||||
for( int i = 0; i< diameterValues.length; i++ ) {
|
||||
if( i == diameterValues.length-1) {
|
||||
diameterLabels.put( i, new JLabel("+"));
|
||||
} else {
|
||||
diameterLabels.put( i, new JLabel(UnitGroup.UNITS_MOTOR_DIMENSIONS.toString(diameterValues[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable impulseLabels = new Hashtable();
|
||||
static {
|
||||
int i =0;
|
||||
for( ImpulseClass impulseClass : ImpulseClass.values() ) {
|
||||
impulseLabels.put(i, new JLabel( impulseClass.name() ));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private final CheckList<Manufacturer> manufacturerCheckList;
|
||||
|
||||
private final CheckList<ImpulseClass> impulseCheckList;
|
||||
|
||||
private final MotorRowFilter filter;
|
||||
|
||||
|
||||
// Things we change the label on based on the MotorMount.
|
||||
private final JCheckBox maximumLengthCheckBox;
|
||||
private final JRadioButton showSmallerDiametersButton;
|
||||
private final JRadioButton showExactDiametersButton;
|
||||
|
||||
private Double mountLength;
|
||||
private final DoubleModel mountDiameter = new DoubleModel(1);
|
||||
|
||||
private int showMode = SHOW_ALL;
|
||||
private final MultiSlider diameterSlider;
|
||||
|
||||
private static final int SHOW_ALL = 0;
|
||||
private static final int SHOW_SMALLER = 1;
|
||||
private static final int SHOW_EXACT = 2;
|
||||
private static final int SHOW_MAX = 2;
|
||||
private Double mountLength;
|
||||
|
||||
public MotorFilterPanel(Collection<Manufacturer> allManufacturers, MotorRowFilter filter ) {
|
||||
super(new MigLayout("fill", "[grow]"));
|
||||
this.filter = filter;
|
||||
|
||||
showMode = Application.getPreferences().getChoice(net.sf.openrocket.startup.Preferences.MOTOR_DIAMETER_FILTER, MotorFilterPanel.SHOW_MAX, MotorFilterPanel.SHOW_EXACT);
|
||||
switch( showMode ) {
|
||||
case SHOW_ALL:
|
||||
filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.ALL);
|
||||
break;
|
||||
case SHOW_EXACT:
|
||||
filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.EXACT);
|
||||
break;
|
||||
case SHOW_SMALLER:
|
||||
filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.SMALLER);
|
||||
break;
|
||||
}
|
||||
List<Manufacturer> unselectedManusFromPreferences = ((SwingPreferences) Application.getPreferences()).getExcludedMotorManufacturers();
|
||||
filter.setExcludedManufacturers(unselectedManusFromPreferences);
|
||||
|
||||
@ -152,121 +163,64 @@ public abstract class MotorFilterPanel extends JPanel {
|
||||
this.add(sub,"grow, wrap");
|
||||
|
||||
// Impulse selection
|
||||
sub = new JPanel(new MigLayout("fill"));
|
||||
border = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.TOTAL_IMPULSE"));
|
||||
GUIUtil.changeFontStyle(border, Font.BOLD);
|
||||
sub.setBorder(border);
|
||||
|
||||
impulseCheckList = new CheckList.Builder().<ImpulseClass>build();
|
||||
impulseCheckList.setData(Arrays.asList(ImpulseClass.values()));
|
||||
impulseCheckList.checkAll();
|
||||
impulseCheckList.getModel().addListDataListener( new ListDataListener() {
|
||||
@Override
|
||||
public void intervalAdded(ListDataEvent e) {
|
||||
}
|
||||
@Override
|
||||
public void intervalRemoved(ListDataEvent e) {
|
||||
}
|
||||
@Override
|
||||
public void contentsChanged(ListDataEvent e) {
|
||||
MotorFilterPanel.this.filter.setExcludedImpulseClasses( impulseCheckList.getUncheckedItems() );
|
||||
onSelectionChanged();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
sub.add(new JScrollPane(impulseCheckList.getList()), "grow,wrap");
|
||||
|
||||
JButton clearImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkNone"));
|
||||
clearImpulse.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MotorFilterPanel.this.impulseCheckList.clearAll();
|
||||
|
||||
}
|
||||
});
|
||||
sub.add(clearImpulse,"split 2");
|
||||
|
||||
JButton selectImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkAll"));
|
||||
selectImpulse.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MotorFilterPanel.this.impulseCheckList.checkAll();
|
||||
|
||||
}
|
||||
});
|
||||
sub.add(selectImpulse,"wrap");
|
||||
{
|
||||
sub = new JPanel(new MigLayout("fill"));
|
||||
border = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.TOTAL_IMPULSE"));
|
||||
GUIUtil.changeFontStyle(border, Font.BOLD);
|
||||
sub.setBorder(border);
|
||||
|
||||
final MultiSlider impulseSlider = new MultiSlider(MultiSlider.HORIZONTAL,0, ImpulseClass.values().length-1,0, ImpulseClass.values().length-1);
|
||||
impulseSlider.setBounded(true); // thumbs cannot cross
|
||||
impulseSlider.setMajorTickSpacing(1);
|
||||
impulseSlider.setPaintTicks(true);
|
||||
impulseSlider.setLabelTable(impulseLabels);
|
||||
impulseSlider.setPaintLabels(true);
|
||||
impulseSlider.addChangeListener( new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
int minimpulse = impulseSlider.getValueAt(0);
|
||||
MotorFilterPanel.this.filter.setMinimumImpulse(ImpulseClass.values()[minimpulse]);
|
||||
int maximpulse = impulseSlider.getValueAt(1);
|
||||
MotorFilterPanel.this.filter.setMaximumImpulse(ImpulseClass.values()[maximpulse]);
|
||||
onSelectionChanged();
|
||||
}
|
||||
});
|
||||
sub.add( impulseSlider, "growx, wrap");
|
||||
}
|
||||
this.add(sub,"grow, wrap");
|
||||
|
||||
|
||||
|
||||
// Diameter selection
|
||||
|
||||
sub = new JPanel(new MigLayout("fill"));
|
||||
TitledBorder diameterTitleBorder = BorderFactory.createTitledBorder(trans.get("TCMotorSelPan.MotorSize"));
|
||||
GUIUtil.changeFontStyle(diameterTitleBorder, Font.BOLD);
|
||||
sub.setBorder(diameterTitleBorder);
|
||||
|
||||
JRadioButton showAllDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc1") );
|
||||
showAllDiametersButton.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showMode = SHOW_ALL;
|
||||
MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.ALL);
|
||||
saveMotorDiameterMatchPrefence();
|
||||
onSelectionChanged();
|
||||
}
|
||||
});
|
||||
showAllDiametersButton.setSelected( showMode == SHOW_ALL);
|
||||
sub.add(showAllDiametersButton, "growx,wrap");
|
||||
|
||||
showSmallerDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc2") );
|
||||
showSmallerDiametersButton.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showMode = SHOW_SMALLER;
|
||||
MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.SMALLER);
|
||||
saveMotorDiameterMatchPrefence();
|
||||
onSelectionChanged();
|
||||
}
|
||||
});
|
||||
showSmallerDiametersButton.setSelected( showMode == SHOW_SMALLER);
|
||||
sub.add(showSmallerDiametersButton, "growx,wrap");
|
||||
|
||||
showExactDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc3") );
|
||||
showExactDiametersButton.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showMode = SHOW_EXACT;
|
||||
MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.EXACT);
|
||||
saveMotorDiameterMatchPrefence();
|
||||
onSelectionChanged();
|
||||
}
|
||||
});
|
||||
showExactDiametersButton.setSelected( showMode == SHOW_EXACT );
|
||||
sub.add(showExactDiametersButton, "growx,wrap");
|
||||
ButtonGroup comboGroup = new ButtonGroup();
|
||||
comboGroup.add( showAllDiametersButton );
|
||||
comboGroup.add( showSmallerDiametersButton );
|
||||
comboGroup.add( showExactDiametersButton );
|
||||
|
||||
{
|
||||
sub.add( new JLabel("Minimum diameter"), "split 4");
|
||||
final DoubleModel minDiameter = new DoubleModel(0, UnitGroup.UNITS_MOTOR_DIMENSIONS, 0, .2);
|
||||
minDiameter.addChangeListener( new ChangeListener() {
|
||||
sub.add( new JLabel("Minimum diameter"), "split 2, wrap");
|
||||
diameterSlider = new MultiSlider(MultiSlider.HORIZONTAL,0, diameterValues.length-1, 0, diameterValues.length-1);
|
||||
diameterSlider.setBounded(true); // thumbs cannot cross
|
||||
diameterSlider.setMajorTickSpacing(1);
|
||||
diameterSlider.setPaintTicks(true);
|
||||
diameterSlider.setLabelTable(diameterLabels);
|
||||
diameterSlider.setPaintLabels(true);
|
||||
diameterSlider.addChangeListener( new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
MotorFilterPanel.this.filter.setMinimumDiameter(minDiameter.getValue());
|
||||
int minDiameter = diameterSlider.getValueAt(0);
|
||||
MotorFilterPanel.this.filter.setMinimumDiameter(diameterValues[minDiameter]);
|
||||
int maxDiameter = diameterSlider.getValueAt(1);
|
||||
if( maxDiameter == diameterValues.length-1 ) {
|
||||
MotorFilterPanel.this.filter.setMaximumDiameter(null);
|
||||
} else {
|
||||
MotorFilterPanel.this.filter.setMaximumDiameter(diameterValues[maxDiameter]);
|
||||
}
|
||||
onSelectionChanged();
|
||||
}
|
||||
});
|
||||
JSpinner spin = new JSpinner(minDiameter.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
sub.add(spin, "growx");
|
||||
|
||||
sub.add(new UnitSelector(minDiameter));
|
||||
sub.add(new BasicSlider(minDiameter.getSliderModel(0,0.5, mountDiameter)), "w 100lp, wrap");
|
||||
sub.add( diameterSlider, "growx, wrap");
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
maximumLengthCheckBox = new JCheckBox(trans.get("TCMotorSelPan.limitByLength"));
|
||||
maximumLengthCheckBox.addChangeListener( new ChangeListener() {
|
||||
@ -279,7 +233,7 @@ public abstract class MotorFilterPanel extends JPanel {
|
||||
}
|
||||
onSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
sub.add(maximumLengthCheckBox);
|
||||
}
|
||||
@ -292,28 +246,30 @@ public abstract class MotorFilterPanel extends JPanel {
|
||||
onSelectionChanged();
|
||||
if ( mount == null ) {
|
||||
// Disable diameter controls?
|
||||
showSmallerDiametersButton.setText(trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc2"));
|
||||
showExactDiametersButton.setText(trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc3"));
|
||||
maximumLengthCheckBox.setText("Limit by length");
|
||||
mountDiameter.setValue(1.0);
|
||||
mountLength = null;
|
||||
} else {
|
||||
mountDiameter.setValue(mount.getMotorMountDiameter());
|
||||
mountLength = ((RocketComponent)mount).getLength();
|
||||
showSmallerDiametersButton.setText(trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc2")
|
||||
+ " " + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(mount.getMotorMountDiameter())+ ")");
|
||||
showExactDiametersButton.setText(trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc3")
|
||||
+ " (" + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(mount.getMotorMountDiameter())+ ")");
|
||||
double mountDiameter = mount.getMotorMountDiameter();
|
||||
// find the next largest diameter
|
||||
int i;
|
||||
for( i =0; i< diameterValues.length; i++ ) {
|
||||
if ( mountDiameter<= diameterValues[i] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i >= diameterValues.length-1 ) {
|
||||
diameterSlider.setValueAt(1, diameterValues.length-1);
|
||||
} else {
|
||||
diameterSlider.setValueAt(1, i) ;
|
||||
}
|
||||
diameterSlider.setValueAt(1, i);
|
||||
maximumLengthCheckBox.setText("Limit by length"
|
||||
+ " (" + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(((RocketComponent)mount).getLength()) +")");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMotorDiameterMatchPrefence() {
|
||||
Application.getPreferences().putChoice("MotorDiameterMatch", showMode );
|
||||
}
|
||||
|
||||
public abstract void onSelectionChanged();
|
||||
|
||||
}
|
||||
|
@ -22,15 +22,8 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
*/
|
||||
class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
|
||||
public enum DiameterFilterControl {
|
||||
ALL,
|
||||
EXACT,
|
||||
SMALLER
|
||||
};
|
||||
|
||||
// configuration data used in the filter process
|
||||
private final ThrustCurveMotorDatabaseModel model;
|
||||
private Double diameter;
|
||||
private List<ThrustCurveMotor> usedMotors = new ArrayList<ThrustCurveMotor>();
|
||||
|
||||
// things which can be changed to modify filter behavior
|
||||
@ -39,21 +32,22 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
|
||||
// Limit motors based on minimum diameter
|
||||
private Double minimumDiameter;
|
||||
|
||||
private Double maximumDiameter;
|
||||
|
||||
// Collection of strings which match text in the motor
|
||||
private List<String> searchTerms = Collections.<String> emptyList();
|
||||
|
||||
// Limit motors based on diameter of the motor mount
|
||||
private DiameterFilterControl diameterControl = DiameterFilterControl.ALL;
|
||||
|
||||
// Boolean which hides motors in the usedMotors list
|
||||
private boolean hideUsedMotors = false;
|
||||
|
||||
// List of manufacturers to exclude.
|
||||
private List<Manufacturer> excludedManufacturers = new ArrayList<Manufacturer>();
|
||||
|
||||
// List of ImpulseClasses to exclude.
|
||||
private List<ImpulseClass> excludedImpulseClass = new ArrayList<ImpulseClass>();
|
||||
// Impulse class filtering
|
||||
private ImpulseClass minimumImpulse;
|
||||
private ImpulseClass maximumImpulse;
|
||||
|
||||
|
||||
public MotorRowFilter(ThrustCurveMotorDatabaseModel model) {
|
||||
super();
|
||||
@ -62,12 +56,9 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
|
||||
public void setMotorMount( MotorMount mount ) {
|
||||
if (mount != null) {
|
||||
this.diameter = mount.getMotorMountDiameter();
|
||||
for (MotorConfiguration m : mount.getMotorConfiguration()) {
|
||||
this.usedMotors.add((ThrustCurveMotor) m.getMotor());
|
||||
}
|
||||
} else {
|
||||
this.diameter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,12 +88,12 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
this.minimumDiameter = minimumDiameter;
|
||||
}
|
||||
|
||||
DiameterFilterControl getDiameterControl() {
|
||||
return diameterControl;
|
||||
Double getMaximumDiameter() {
|
||||
return maximumDiameter;
|
||||
}
|
||||
|
||||
void setDiameterControl(DiameterFilterControl diameterControl) {
|
||||
this.diameterControl = diameterControl;
|
||||
void setMaximumDiameter(Double maximumDiameter) {
|
||||
this.maximumDiameter = maximumDiameter;
|
||||
}
|
||||
|
||||
void setHideUsedMotors(boolean hideUsedMotors) {
|
||||
@ -118,9 +109,20 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
this.excludedManufacturers.addAll(excludedManufacturers);
|
||||
}
|
||||
|
||||
void setExcludedImpulseClasses(Collection<ImpulseClass> excludedImpulseClasses ) {
|
||||
this.excludedImpulseClass.clear();
|
||||
this.excludedImpulseClass.addAll(excludedImpulseClasses);
|
||||
ImpulseClass getMinimumImpulse() {
|
||||
return minimumImpulse;
|
||||
}
|
||||
|
||||
void setMinimumImpulse(ImpulseClass minimumImpulse) {
|
||||
this.minimumImpulse = minimumImpulse;
|
||||
}
|
||||
|
||||
ImpulseClass getMaximumImpulse() {
|
||||
return maximumImpulse;
|
||||
}
|
||||
|
||||
void setMaximumImpulse(ImpulseClass maximumImpulse) {
|
||||
this.maximumImpulse = maximumImpulse;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -158,20 +160,8 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
if (diameter != null) {
|
||||
switch (diameterControl) {
|
||||
default:
|
||||
case ALL:
|
||||
break;
|
||||
case EXACT:
|
||||
if ((m.getDiameter() <= diameter + 0.0004) && (m.getDiameter() >= diameter - 0.0015)) {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
case SMALLER:
|
||||
if (m.getDiameter() <= diameter + 0.0004) {
|
||||
break;
|
||||
}
|
||||
if ( maximumDiameter != null ) {
|
||||
if ( m.getDiameter() >= maximumDiameter + 0.0004 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -199,11 +189,18 @@ class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
}
|
||||
|
||||
private boolean filterByImpulseClass(ThrustCurveMotorSet m) {
|
||||
for( ImpulseClass c : excludedImpulseClass ) {
|
||||
if (c.isIn(m) ) {
|
||||
if ( minimumImpulse != null ) {
|
||||
if( m.getTotalImpuse() < minimumImpulse.getLow() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( maximumImpulse != null ) {
|
||||
if( m.getTotalImpuse() > maximumImpulse.getHigh() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
553
swing/src/net/sf/openrocket/gui/widgets/MultiSlider.java
Normal file
553
swing/src/net/sf/openrocket/gui/widgets/MultiSlider.java
Normal file
@ -0,0 +1,553 @@
|
||||
package net.sf.openrocket.gui.widgets;
|
||||
/* -------------------------------------------------------------------
|
||||
* GeoVISTA Center (Penn State, Dept. of Geography)
|
||||
*
|
||||
* Java source file for the class MultiSlider
|
||||
*
|
||||
* Copyright (c), 1999 - 2002, Masahiro Takatsuka and GeoVISTA Center
|
||||
* All Rights Researved.
|
||||
*
|
||||
* Original Author: Masahiro Takatsuka
|
||||
* $Author: eytanadar $
|
||||
*
|
||||
* $Date: 2005/10/05 20:19:52 $
|
||||
*
|
||||
*
|
||||
* Reference: Document no:
|
||||
* ___ ___
|
||||
*
|
||||
* To Do:
|
||||
* ___
|
||||
*
|
||||
------------------------------------------------------------------- */
|
||||
|
||||
/* --------------------------- Package ---------------------------- */
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import javax.accessibility.AccessibleContext;
|
||||
import javax.accessibility.AccessibleState;
|
||||
import javax.swing.BoundedRangeModel;
|
||||
import javax.swing.DefaultBoundedRangeModel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.plaf.SliderUI;
|
||||
|
||||
/*====================================================================
|
||||
Implementation of class MultiSlider
|
||||
====================================================================*/
|
||||
/***
|
||||
* A component that lets the user graphically select values by slding
|
||||
* multiple thumbs within a bounded interval. MultiSlider inherits all
|
||||
* fields and methods from javax.swing.JSlider.
|
||||
* <p>
|
||||
*
|
||||
* @version $Revision: 1.1 $
|
||||
* @author Masahiro Takatsuka (masa@jbeans.net)
|
||||
* @see JSlider
|
||||
*/
|
||||
|
||||
public class MultiSlider extends JSlider {
|
||||
/***
|
||||
* @see #getUIClassID
|
||||
* @see #readObject
|
||||
*/
|
||||
private static final String uiClassID = "MultiSliderUI";
|
||||
|
||||
/***
|
||||
* An array of data models that handle the numeric maximum values,
|
||||
* minimum values, and current-position values for the multi slider.
|
||||
*/
|
||||
private BoundedRangeModel[] sliderModels;
|
||||
|
||||
/***
|
||||
* If it is true, a thumb is bounded by adjacent thumbs.
|
||||
*/
|
||||
private boolean bounded = false;
|
||||
|
||||
/***
|
||||
* This is a color to paint the current thumb
|
||||
*/
|
||||
private Color currentThumbColor = Color.red;
|
||||
|
||||
transient private int valueBeforeStateChange;
|
||||
|
||||
/***
|
||||
* Creates a slider with the specified orientation and the
|
||||
* specified mimimum, maximum, and initial values.
|
||||
*
|
||||
* @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
|
||||
*
|
||||
* @see #setOrientation
|
||||
* @see #setMinimum
|
||||
* @see #setMaximum
|
||||
* @see #setValue
|
||||
*/
|
||||
public MultiSlider(int orientation, int min, int max,
|
||||
int val1, int val2) {
|
||||
checkOrientation(orientation);
|
||||
this.orientation = orientation;
|
||||
setNumberOfThumbs(min,max,new int[]{val1,val2});
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates a slider with the specified orientation and the
|
||||
* specified mimimum, maximum, and the number of thumbs.
|
||||
*
|
||||
* @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
|
||||
*
|
||||
* @see #setOrientation
|
||||
* @see #setMinimum
|
||||
* @see #setMaximum
|
||||
* @see #setValue
|
||||
*/
|
||||
public MultiSlider(int orientation, int min, int max) {
|
||||
checkOrientation(orientation);
|
||||
this.orientation = orientation;
|
||||
setNumberOfThumbs(min, max, 2);
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates a horizontal slider with the range 0 to 100 and
|
||||
* an intitial value of 50.
|
||||
*/
|
||||
public MultiSlider() {
|
||||
this(HORIZONTAL, 0, 100);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Creates a slider using the specified orientation with the
|
||||
* range 0 to 100 and an intitial value of 50.
|
||||
*/
|
||||
public MultiSlider(int orientation) {
|
||||
this(orientation, 0, 100);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Creates a horizontal slider using the specified min and max
|
||||
* with an intitial value of 50.
|
||||
*/
|
||||
public MultiSlider(int min, int max) {
|
||||
this(HORIZONTAL, min, max);
|
||||
}
|
||||
|
||||
public void setCurrentThumbColor(Color c) {
|
||||
this.currentThumbColor = c;
|
||||
}
|
||||
|
||||
public Color getCurrentThumbColor() {
|
||||
return this.currentThumbColor;
|
||||
}
|
||||
|
||||
public int getTrackBuffer() {
|
||||
return ((MultiSliderUI) this.ui).getTrackBuffer();
|
||||
}
|
||||
|
||||
/***
|
||||
* Validates the orientation parameter.
|
||||
*/
|
||||
private void checkOrientation(int orientation) {
|
||||
switch (orientation) {
|
||||
case VERTICAL:
|
||||
case HORIZONTAL:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Notification from the UIFactory that the L&F has changed.
|
||||
* Called to replace the UI with the latest version from the
|
||||
* default UIFactory.
|
||||
*
|
||||
* @see JComponent#updateUI
|
||||
*/
|
||||
public void updateUI() {
|
||||
updateLabelUIs();
|
||||
MultiSliderUI ui = new MultiSliderUI();
|
||||
if (this.sliderModels != null) {
|
||||
ui.setThumbCount(this.sliderModels.length);
|
||||
}
|
||||
setUI((SliderUI) ui);
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns the number of thumbs in the slider.
|
||||
*/
|
||||
public int getNumberOfThumbs() {
|
||||
return this.sliderModels.length;
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs with the specified parameters.
|
||||
*/
|
||||
private void setNumberOfThumbs(int min, int max, int num, boolean useEndPoints) {
|
||||
int [] values = createDefaultValues(min, max, num, useEndPoints);
|
||||
setNumberOfThumbs(min, max, values);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs with the specified parameters.
|
||||
*/
|
||||
private void setNumberOfThumbs(int min, int max, int num) {
|
||||
setNumberOfThumbs(min, max, num, false);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs with the specified parameters.
|
||||
*/
|
||||
private void setNumberOfThumbs(int min, int max, int[] values) {
|
||||
if (values == null || values.length < 1) {
|
||||
values = new int[] {50};
|
||||
}
|
||||
int num = values.length;
|
||||
this.sliderModels = new BoundedRangeModel[num];
|
||||
for (int i = 0; i < num; i++) {
|
||||
this.sliderModels[i] = new DefaultBoundedRangeModel(values[i], 0, min, max);
|
||||
this.sliderModels[i].addChangeListener(changeListener);
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs.
|
||||
*/
|
||||
private void setNumberOfThumbs(int num) {
|
||||
setNumberOfThumbs(num, false);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs.
|
||||
*/
|
||||
private void setNumberOfThumbs(int num, boolean useEndPoints) {
|
||||
if (getNumberOfThumbs() != num) {
|
||||
setNumberOfThumbs(getMinimum(), getMaximum(), num, useEndPoints);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of thumbs by specifying the initial values.
|
||||
*/
|
||||
private void setNumberOfThumbs(int[] values) {
|
||||
setNumberOfThumbs(getMinimum(), getMaximum(), values);
|
||||
}
|
||||
|
||||
/***
|
||||
* creates evenly spaced values for thumbs.
|
||||
*/
|
||||
private int[] createDefaultValues(int min, int max, int num_of_values, boolean useEndPoints) {
|
||||
int[] values = new int[num_of_values];
|
||||
int range = max - min;
|
||||
|
||||
if (!useEndPoints) {
|
||||
int step = range / (num_of_values + 1);
|
||||
for (int i = 0; i < num_of_values; i++) {
|
||||
values[i] = min + (i + 1) * step;
|
||||
}
|
||||
} else {
|
||||
if (num_of_values < 1) {
|
||||
return new int[0];
|
||||
}
|
||||
values[0] = getMinimum();
|
||||
values[num_of_values - 1] = getMaximum();
|
||||
int[] def = createDefaultValues(getMinimum(), getMaximum(), num_of_values - 2, false);
|
||||
for (int i = 0; i < def.length; i++) {
|
||||
values[i + 1] = def[i];
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns the index number of currently operated thumb.
|
||||
*/
|
||||
public int getCurrentThumbIndex() {
|
||||
return ((MultiSliderUI)ui).getCurrentIndex();
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns data model that handles the sliders three
|
||||
* fundamental properties: minimum, maximum, value.
|
||||
*
|
||||
* @see #setModel
|
||||
*/
|
||||
public BoundedRangeModel getModel() {
|
||||
return getModelAt(getCurrentThumbIndex());
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns data model that handles the sliders three
|
||||
* fundamental properties: minimum, maximum, value.
|
||||
*
|
||||
* @see #setModel
|
||||
*/
|
||||
public BoundedRangeModel getModelAt(int index) {
|
||||
if (this.sliderModels == null || index >= this.sliderModels.length) {
|
||||
return null;
|
||||
}
|
||||
return this.sliderModels[index];
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns data model that handles the sliders three
|
||||
* fundamental properties: minimum, maximum, value.
|
||||
*
|
||||
* @see #setModel
|
||||
*/
|
||||
public BoundedRangeModel[] getModels() {
|
||||
return this.sliderModels;
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the model that handles the sliders three
|
||||
* fundamental properties: minimum, maximum, value.
|
||||
*
|
||||
* @see #getModel
|
||||
* @beaninfo
|
||||
* bound: true
|
||||
* description: The sliders BoundedRangeModel.
|
||||
*/
|
||||
public void setModel(BoundedRangeModel newModel) {
|
||||
setModelAt(getCurrentThumbIndex(), newModel);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the model that handles the sliders three
|
||||
* fundamental properties: minimum, maximum, value.
|
||||
*
|
||||
* @see #getModel
|
||||
* @beaninfo
|
||||
* bound: true
|
||||
* description: The sliders BoundedRangeModel.
|
||||
*/
|
||||
public void setModelAt(int index, BoundedRangeModel newModel) {
|
||||
BoundedRangeModel oldModel = getModelAt(index);
|
||||
|
||||
if (oldModel != null) {
|
||||
oldModel.removeChangeListener(changeListener);
|
||||
}
|
||||
|
||||
this.sliderModels[index] = newModel;
|
||||
|
||||
if (newModel != null) {
|
||||
newModel.addChangeListener(changeListener);
|
||||
|
||||
if (accessibleContext != null) {
|
||||
accessibleContext.firePropertyChange(
|
||||
AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
|
||||
(oldModel == null
|
||||
? null : new Integer(oldModel.getValue())),
|
||||
(newModel == null
|
||||
? null : new Integer(newModel.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
firePropertyChange("model", oldModel, this.sliderModels[index]);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the models minimum property.
|
||||
*
|
||||
* @see #getMinimum
|
||||
* @see BoundedRangeModel#setMinimum
|
||||
* @beaninfo
|
||||
* bound: true
|
||||
* preferred: true
|
||||
* description: The sliders minimum value.
|
||||
*/
|
||||
public void setMinimum(int minimum) {
|
||||
int count = getNumberOfThumbs();
|
||||
int oldMin = getModel().getMinimum();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getModelAt(i).setMinimum(minimum);
|
||||
}
|
||||
firePropertyChange( "minimum", new Integer( oldMin ), new Integer( minimum ) );
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the models maximum property.
|
||||
*
|
||||
* @see #getMaximum
|
||||
* @see BoundedRangeModel#setMaximum
|
||||
* @beaninfo
|
||||
* bound: true
|
||||
* preferred: true
|
||||
* description: The sliders maximum value.
|
||||
*/
|
||||
public void setMaximum(int maximum) {
|
||||
int count = getNumberOfThumbs();
|
||||
int oldMax = getModel().getMaximum();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getModelAt(i).setMaximum(maximum);
|
||||
}
|
||||
firePropertyChange( "maximum", new Integer( oldMax ), new Integer( maximum ) );
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns the sliders value.
|
||||
* @return the models value property
|
||||
* @see #setValue
|
||||
*/
|
||||
public int getValue() {
|
||||
return getValueAt(getCurrentThumbIndex());
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns the sliders value.
|
||||
* @return the models value property
|
||||
* @see #setValue
|
||||
*/
|
||||
public int getValueAt(int index) {
|
||||
return getModelAt(index).getValue();
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the sliders current value. This method just forwards
|
||||
* the value to the model.
|
||||
*
|
||||
* @see #getValue
|
||||
* @beaninfo
|
||||
* preferred: true
|
||||
* description: The sliders current value.
|
||||
*/
|
||||
public void setValue(int n) {
|
||||
setValueAt(getCurrentThumbIndex(), n);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the sliders current value. This method just forwards
|
||||
* the value to the model.
|
||||
*
|
||||
* @see #getValue
|
||||
* @beaninfo
|
||||
* preferred: true
|
||||
* description: The sliders current value.
|
||||
*/
|
||||
public void setValueAt(int index, int n) {
|
||||
BoundedRangeModel m = getModelAt(index);
|
||||
int oldValue = m.getValue();
|
||||
m.setValue(n);
|
||||
|
||||
if (accessibleContext != null) {
|
||||
accessibleContext.firePropertyChange(
|
||||
AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
|
||||
new Integer(oldValue),
|
||||
new Integer(m.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* True if the slider knob is being dragged.
|
||||
*
|
||||
* @return the value of the models valueIsAdjusting property
|
||||
* @see #setValueIsAdjusting
|
||||
*/
|
||||
public boolean getValueIsAdjusting() {
|
||||
boolean result = false;
|
||||
int count = getNumberOfThumbs();
|
||||
for (int i = 0; i < count; i++) {
|
||||
result = (result || getValueIsAdjustingAt(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/***
|
||||
* True if the slider knob is being dragged.
|
||||
*/
|
||||
public boolean getValueIsAdjustingAt(int index) {
|
||||
return getModelAt(index).getValueIsAdjusting();
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the models valueIsAdjusting property. Slider look and
|
||||
* feel implementations should set this property to true when
|
||||
* a knob drag begins, and to false when the drag ends. The
|
||||
* slider model will not generate ChangeEvents while
|
||||
* valueIsAdjusting is true.
|
||||
*
|
||||
* @see #getValueIsAdjusting
|
||||
* @see BoundedRangeModel#setValueIsAdjusting
|
||||
* @beaninfo
|
||||
* expert: true
|
||||
* description: True if the slider knob is being dragged.
|
||||
*/
|
||||
public void setValueIsAdjusting(boolean b) {
|
||||
setValueIsAdjustingAt(getCurrentThumbIndex(), b);
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the models valueIsAdjusting property. Slider look and
|
||||
* feel implementations should set this property to true when
|
||||
* a knob drag begins, and to false when the drag ends. The
|
||||
* slider model will not generate ChangeEvents while
|
||||
* valueIsAdjusting is true.
|
||||
*/
|
||||
public void setValueIsAdjustingAt(int index, boolean b) {
|
||||
BoundedRangeModel m = getModelAt(index);
|
||||
boolean oldValue = m.getValueIsAdjusting();
|
||||
m.setValueIsAdjusting(b);
|
||||
|
||||
if ((oldValue != b) && (accessibleContext != null)) {
|
||||
accessibleContext.firePropertyChange(
|
||||
AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
|
||||
((oldValue) ? AccessibleState.BUSY : null),
|
||||
((b) ? AccessibleState.BUSY : null));
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the size of the range "covered" by the knob. Most look
|
||||
* and feel implementations will change the value by this amount
|
||||
* if the user clicks on either side of the knob.
|
||||
*
|
||||
* @see #getExtent
|
||||
* @see BoundedRangeModel#setExtent
|
||||
* @beaninfo
|
||||
* expert: true
|
||||
* description: Size of the range covered by the knob.
|
||||
*/
|
||||
public void setExtent(int extent) {
|
||||
int count = getNumberOfThumbs();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getModelAt(i).setExtent(extent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Sets a bounded attribute of a slider thumb.
|
||||
* <PRE>
|
||||
* </PRE>
|
||||
*
|
||||
* @param b
|
||||
* @return void
|
||||
*/
|
||||
public void setBounded(boolean b) {
|
||||
this.bounded = b;
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns a bounded attribute of a slider thumb.
|
||||
* <PRE>
|
||||
* </PRE>
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isBounded() {
|
||||
return this.bounded;
|
||||
}
|
||||
|
||||
public int getValueBeforeStateChange() {
|
||||
return this.valueBeforeStateChange;
|
||||
}
|
||||
|
||||
void setValueBeforeStateChange(int v) {
|
||||
this.valueBeforeStateChange = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
612
swing/src/net/sf/openrocket/gui/widgets/MultiSliderUI.java
Normal file
612
swing/src/net/sf/openrocket/gui/widgets/MultiSliderUI.java
Normal file
@ -0,0 +1,612 @@
|
||||
package net.sf.openrocket.gui.widgets;
|
||||
/* -------------------------------------------------------------------
|
||||
* GeoVISTA Center (Penn State, Dept. of Geography)
|
||||
*
|
||||
* Java source file for the class MultiSliderUI
|
||||
*
|
||||
* Copyright (c), 1999 - 2002, Masahiro Takatsuka and GeoVISTA Center
|
||||
* All Rights Researved.
|
||||
*
|
||||
* Original Author: Masahiro Takatsuka
|
||||
* $Author: eytanadar $
|
||||
*
|
||||
* $Date: 2005/10/05 20:19:52 $
|
||||
*
|
||||
*
|
||||
* Reference: Document no:
|
||||
* ___ ___
|
||||
*
|
||||
* To Do:
|
||||
* ___
|
||||
*
|
||||
------------------------------------------------------------------- */
|
||||
|
||||
/* --------------------------- Package ---------------------------- */
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BoundedRangeModel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicSliderUI;
|
||||
import javax.swing.plaf.metal.MetalSliderUI;
|
||||
|
||||
/*====================================================================
|
||||
Implementation of class MultiSliderUI
|
||||
====================================================================*/
|
||||
/***
|
||||
* A Basic L&F implementation of SliderUI.
|
||||
*
|
||||
* @version $Revision: 1.1 $
|
||||
* @author Masahiro Takatsuka (masa@jbeans.net)
|
||||
* @see MetalSliderUI
|
||||
*/
|
||||
|
||||
class MultiSliderUI extends BasicSliderUI {
|
||||
private Rectangle[] thumbRects = null;
|
||||
|
||||
private int thumbCount;
|
||||
transient private int currentIndex = 0;
|
||||
|
||||
transient private boolean isDragging;
|
||||
transient private int[] minmaxIndices = new int[2];
|
||||
|
||||
/***
|
||||
* ComponentUI Interface Implementation methods
|
||||
*/
|
||||
public static ComponentUI createUI(JComponent b) {
|
||||
return new MultiSliderUI();
|
||||
}
|
||||
|
||||
/***
|
||||
* Construct a new MultiSliderUI object.
|
||||
*/
|
||||
public MultiSliderUI() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
int getTrackBuffer() {
|
||||
return this.trackBuffer;
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets the number of Thumbs.
|
||||
*/
|
||||
public void setThumbCount(int count) {
|
||||
this.thumbCount = count;
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns the index number of the thumb currently operated.
|
||||
*/
|
||||
protected int getCurrentIndex() {
|
||||
return this.currentIndex;
|
||||
}
|
||||
|
||||
public void installUI(JComponent c) {
|
||||
this.thumbRects = new Rectangle[this.thumbCount];
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
this.thumbRects[i] = new Rectangle();
|
||||
}
|
||||
this.currentIndex = 0;
|
||||
if (this.thumbCount > 0) {
|
||||
thumbRect = this.thumbRects[this.currentIndex];
|
||||
}
|
||||
super.installUI(c);
|
||||
}
|
||||
|
||||
public void uninstallUI(JComponent c) {
|
||||
super.uninstallUI(c);
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
this.thumbRects[i] = null;
|
||||
}
|
||||
this.thumbRects = null;
|
||||
}
|
||||
|
||||
protected void installListeners( JSlider slider ) {
|
||||
slider.addMouseListener(trackListener);
|
||||
slider.addMouseMotionListener(trackListener);
|
||||
slider.addFocusListener(focusListener);
|
||||
slider.addComponentListener(componentListener);
|
||||
slider.addPropertyChangeListener( propertyChangeListener );
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
((MultiSlider)slider).getModelAt(i).addChangeListener(changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
protected void uninstallListeners( JSlider slider ) {
|
||||
slider.removeMouseListener(trackListener);
|
||||
slider.removeMouseMotionListener(trackListener);
|
||||
slider.removeFocusListener(focusListener);
|
||||
slider.removeComponentListener(componentListener);
|
||||
slider.removePropertyChangeListener( propertyChangeListener );
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
BoundedRangeModel model = ((MultiSlider)slider).getModelAt(i);
|
||||
if (model != null) {
|
||||
model.removeChangeListener(changeListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void calculateThumbSize() {
|
||||
Dimension size = getThumbSize();
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
this.thumbRects[i].setSize(size.width, size.height);
|
||||
}
|
||||
thumbRect.setSize(size.width, size.height);
|
||||
}
|
||||
|
||||
protected void calculateThumbLocation() {
|
||||
MultiSlider slider = (MultiSlider) this.slider;
|
||||
int majorTickSpacing = slider.getMajorTickSpacing();
|
||||
int minorTickSpacing = slider.getMinorTickSpacing();
|
||||
int tickSpacing = 0;
|
||||
|
||||
if (minorTickSpacing > 0) {
|
||||
tickSpacing = minorTickSpacing;
|
||||
} else if (majorTickSpacing > 0) {
|
||||
tickSpacing = majorTickSpacing;
|
||||
}
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
if (slider.getSnapToTicks()) {
|
||||
int sliderValue = slider.getValueAt(i);
|
||||
int snappedValue = sliderValue;
|
||||
if (tickSpacing != 0) {
|
||||
// If it's not on a tick, change the value
|
||||
if ((sliderValue - slider.getMinimum()) % tickSpacing != 0 ) {
|
||||
float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing;
|
||||
int whichTick = Math.round(temp);
|
||||
snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
|
||||
}
|
||||
|
||||
if( snappedValue != sliderValue ) {
|
||||
slider.setValueAt(i, snappedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slider.getOrientation() == JSlider.HORIZONTAL) {
|
||||
int valuePosition = xPositionForValue(slider.getValueAt(i));
|
||||
this.thumbRects[i].x = valuePosition - (this.thumbRects[i].width / 2);
|
||||
this.thumbRects[i].y = trackRect.y;
|
||||
} else {
|
||||
int valuePosition = yPositionForValue(slider.getValueAt(i));
|
||||
this.thumbRects[i].x = trackRect.x;
|
||||
this.thumbRects[i].y = valuePosition - (this.thumbRects[i].height / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
recalculateIfInsetsChanged();
|
||||
recalculateIfOrientationChanged();
|
||||
Rectangle clip = g.getClipBounds();
|
||||
|
||||
if (slider.getPaintTrack() && clip.intersects(trackRect)) {
|
||||
paintTrack( g );
|
||||
}
|
||||
if (slider.getPaintTicks() && clip.intersects(tickRect)) {
|
||||
paintTicks( g );
|
||||
}
|
||||
if (slider.getPaintLabels() && clip.intersects(labelRect)) {
|
||||
paintLabels( g );
|
||||
}
|
||||
if (slider.hasFocus() && clip.intersects(focusRect)) {
|
||||
paintFocus( g );
|
||||
}
|
||||
|
||||
// first paint unfocused thumbs.
|
||||
for (int i = 0; i < this.thumbCount; i++) {
|
||||
if (i != this.currentIndex) {
|
||||
if (clip.intersects(this.thumbRects[i])) {
|
||||
thumbRect = this.thumbRects[i];
|
||||
paintThumb(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
// then paint currently focused thumb.
|
||||
if (clip.intersects(this.thumbRects[this.currentIndex])) {
|
||||
thumbRect = this.thumbRects[this.currentIndex];
|
||||
paintThumb(g);
|
||||
}
|
||||
}
|
||||
|
||||
public void paintThumb(Graphics g) {
|
||||
super.paintThumb(g);
|
||||
}
|
||||
|
||||
public void paintTrack(Graphics g) {
|
||||
super.paintTrack(g);
|
||||
}
|
||||
|
||||
public void scrollByBlock(int direction) {
|
||||
synchronized(slider) {
|
||||
int oldValue = ((MultiSlider)slider).getValueAt(this.currentIndex);
|
||||
int blockIncrement = slider.getMaximum() / 10;
|
||||
int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
|
||||
((MultiSlider)slider).setValueAt(this.currentIndex, oldValue + delta);
|
||||
}
|
||||
}
|
||||
|
||||
public void scrollByUnit(int direction) {
|
||||
synchronized(slider) {
|
||||
int oldValue = ((MultiSlider)slider).getValueAt(this.currentIndex);
|
||||
int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
|
||||
((MultiSlider)slider).setValueAt(this.currentIndex, oldValue + delta);
|
||||
}
|
||||
}
|
||||
|
||||
protected TrackListener createTrackListener( JSlider slider ) {
|
||||
return new MultiTrackListener();
|
||||
}
|
||||
|
||||
/***
|
||||
* Track Listener Class tracks mouse movements.
|
||||
*/
|
||||
class MultiTrackListener extends BasicSliderUI.TrackListener {
|
||||
int _trackTop;
|
||||
int _trackBottom;
|
||||
int _trackLeft;
|
||||
int _trackRight;
|
||||
transient private int[] firstXY = new int[2];
|
||||
|
||||
/***
|
||||
* If the mouse is pressed above the "thumb" component
|
||||
* then reduce the scrollbars value by one page ("page up"),
|
||||
* otherwise increase it by one page. If there is no
|
||||
* thumb then page up if the mouse is in the upper half
|
||||
* of the track.
|
||||
*/
|
||||
public void mousePressed(MouseEvent e) {
|
||||
int[] neighbours = new int[2];
|
||||
boolean bounded = ((MultiSlider)slider).isBounded();
|
||||
if (!slider.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentMouseX = e.getX();
|
||||
currentMouseY = e.getY();
|
||||
firstXY[0] = currentMouseX;
|
||||
firstXY[1] = currentMouseY;
|
||||
|
||||
slider.requestFocus();
|
||||
// Clicked in the Thumb area?
|
||||
minmaxIndices[0] = -1;
|
||||
minmaxIndices[1] = -1;
|
||||
for (int i = 0; i < MultiSliderUI.this.thumbCount; i++) {
|
||||
if (MultiSliderUI.this.thumbRects[i].contains(currentMouseX, currentMouseY)) {
|
||||
if (minmaxIndices[0] == -1) {
|
||||
minmaxIndices[0] = i;
|
||||
MultiSliderUI.this.currentIndex = i;
|
||||
}
|
||||
if (minmaxIndices[1] < i) {
|
||||
minmaxIndices[1] = i;
|
||||
}
|
||||
switch (slider.getOrientation()) {
|
||||
case JSlider.VERTICAL:
|
||||
offset = currentMouseY - MultiSliderUI.this.thumbRects[i].y;
|
||||
break;
|
||||
case JSlider.HORIZONTAL:
|
||||
offset = currentMouseX - MultiSliderUI.this.thumbRects[i].x;
|
||||
break;
|
||||
}
|
||||
MultiSliderUI.this.isDragging = true;
|
||||
thumbRect = MultiSliderUI.this.thumbRects[i];
|
||||
if (bounded) {
|
||||
neighbours[0] = ((i - 1) < 0) ? -1 : (i - 1);
|
||||
neighbours[1] = ((i + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (i + 1);
|
||||
//findClosest(currentMouseX, currentMouseY, neighbours, i);
|
||||
} else {
|
||||
MultiSliderUI.this.currentIndex = i;
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(i, true);
|
||||
neighbours[0] = -1;
|
||||
neighbours[1] = -1;
|
||||
}
|
||||
setThumbBounds(neighbours);
|
||||
//return;
|
||||
}
|
||||
}
|
||||
if (minmaxIndices[0] > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultiSliderUI.this.currentIndex = findClosest(currentMouseX, currentMouseY, neighbours, -1);
|
||||
thumbRect = MultiSliderUI.this.thumbRects[MultiSliderUI.this.currentIndex];
|
||||
MultiSliderUI.this.isDragging = false;
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, true);
|
||||
|
||||
Dimension sbSize = slider.getSize();
|
||||
int direction = POSITIVE_SCROLL;
|
||||
|
||||
switch (slider.getOrientation()) {
|
||||
case JSlider.VERTICAL:
|
||||
if (thumbRect.isEmpty()) {
|
||||
int scrollbarCenter = sbSize.height / 2;
|
||||
if (!drawInverted()) {
|
||||
direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
|
||||
} else {
|
||||
direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
|
||||
}
|
||||
} else {
|
||||
int thumbY = thumbRect.y;
|
||||
if (!drawInverted()) {
|
||||
direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
|
||||
} else {
|
||||
direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JSlider.HORIZONTAL:
|
||||
if (thumbRect.isEmpty() ) {
|
||||
int scrollbarCenter = sbSize.width / 2;
|
||||
if (!drawInverted()) {
|
||||
direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
|
||||
} else {
|
||||
direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
|
||||
}
|
||||
} else {
|
||||
int thumbX = thumbRect.x;
|
||||
if (!drawInverted()) {
|
||||
direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
|
||||
} else {
|
||||
direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scrollDueToClickInTrack(direction);
|
||||
Rectangle r = thumbRect;
|
||||
if ( !r.contains(currentMouseX, currentMouseY) ) {
|
||||
if (shouldScroll(direction) ) {
|
||||
scrollTimer.stop();
|
||||
scrollListener.setDirection(direction);
|
||||
scrollTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Sets a track bound for th thumb currently operated.
|
||||
*/
|
||||
private void setThumbBounds(int[] neighbours) {
|
||||
int halfThumbWidth = thumbRect.width / 2;
|
||||
int halfThumbHeight = thumbRect.height / 2;
|
||||
|
||||
switch (slider.getOrientation()) {
|
||||
case JSlider.VERTICAL:
|
||||
_trackTop = (neighbours[1] == -1) ? trackRect.y : MultiSliderUI.this.thumbRects[neighbours[1]].y + halfThumbHeight;
|
||||
_trackBottom = (neighbours[0] == -1) ? trackRect.y + (trackRect.height - 1) : MultiSliderUI.this.thumbRects[neighbours[0]].y + halfThumbHeight;
|
||||
break;
|
||||
case JSlider.HORIZONTAL:
|
||||
_trackLeft = (neighbours[0] == -1) ? trackRect.x : MultiSliderUI.this.thumbRects[neighbours[0]].x + halfThumbWidth;
|
||||
_trackRight = (neighbours[1] == -1) ? trackRect.x + (trackRect.width - 1) : MultiSliderUI.this.thumbRects[neighbours[1]].x + halfThumbWidth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this is a very lazy way to find the closest. One might want to
|
||||
* implement a much faster algorithm.
|
||||
*/
|
||||
private int findClosest(int x, int y, int[] neighbours, int excluded) {
|
||||
int orientation = slider.getOrientation();
|
||||
int rightmin = Integer.MAX_VALUE; // for dxw, dy
|
||||
int leftmin = -Integer.MAX_VALUE; // for dx, dyh
|
||||
int dx = 0;
|
||||
int dxw = 0;
|
||||
int dy = 0;
|
||||
int dyh = 0;
|
||||
neighbours[0] = -1; // left
|
||||
neighbours[1] = -1; // right
|
||||
for (int i = 0; i < MultiSliderUI.this.thumbCount; i++) {
|
||||
if (i == excluded) {
|
||||
continue;
|
||||
}
|
||||
switch (orientation) {
|
||||
case JSlider.VERTICAL:
|
||||
dy = MultiSliderUI.this.thumbRects[i].y - y;
|
||||
dyh = (MultiSliderUI.this.thumbRects[i].y + MultiSliderUI.this.thumbRects[i].height) - y;
|
||||
if (dyh <= 0) {
|
||||
if (dyh > leftmin) { // has to be > and not >=
|
||||
leftmin = dyh;
|
||||
neighbours[0] = i;
|
||||
}
|
||||
}
|
||||
if (dy >= 0) {
|
||||
if (dy <= rightmin) {
|
||||
rightmin = dy;
|
||||
neighbours[1] = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JSlider.HORIZONTAL:
|
||||
dx = MultiSliderUI.this.thumbRects[i].x - x;
|
||||
dxw = (MultiSliderUI.this.thumbRects[i].x + MultiSliderUI.this.thumbRects[i].width) - x;
|
||||
if (dxw <= 0) {
|
||||
if (dxw >= leftmin) {
|
||||
leftmin = dxw;
|
||||
neighbours[0] = i;
|
||||
}
|
||||
}
|
||||
if (dx >= 0) {
|
||||
if (dx < rightmin) { // has to be < and not <=
|
||||
rightmin = dx;
|
||||
neighbours[1] = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
//System.out.println("neighbours = " + neighbours[0] + ", " + neighbours[1]);
|
||||
int closest = (Math.abs(leftmin) <= Math.abs(rightmin)) ? neighbours[0] : neighbours[1];
|
||||
return (closest == -1) ? 0 : closest;
|
||||
}
|
||||
|
||||
/***
|
||||
* Set the models value to the position of the top/left
|
||||
* of the thumb relative to the origin of the track.
|
||||
*/
|
||||
public void mouseDragged( MouseEvent e ) {
|
||||
((MultiSlider) MultiSliderUI.this.slider).setValueBeforeStateChange(((MultiSlider) MultiSliderUI.this.slider).getValueAt(MultiSliderUI.this.currentIndex));
|
||||
int thumbMiddle = 0;
|
||||
boolean bounded = ((MultiSlider)slider).isBounded();
|
||||
|
||||
if (!slider.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentMouseX = e.getX();
|
||||
currentMouseY = e.getY();
|
||||
|
||||
if (! MultiSliderUI.this.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (slider.getOrientation()) {
|
||||
case JSlider.VERTICAL:
|
||||
int halfThumbHeight = thumbRect.height / 2;
|
||||
int thumbTop = e.getY() - offset;
|
||||
if (bounded) {
|
||||
int[] neighbours = new int[2];
|
||||
int idx = -1;
|
||||
int diff = e.getY() - firstXY[1];
|
||||
//System.out.println("diff = " + diff);
|
||||
if (e.getY() - firstXY[1] > 0) {
|
||||
idx = minmaxIndices[0];
|
||||
} else {
|
||||
idx = minmaxIndices[1];
|
||||
}
|
||||
minmaxIndices[0] = minmaxIndices[1] = idx;
|
||||
//System.out.println("idx = " + idx);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
//System.out.println("thumbTop = " + thumbTop);
|
||||
neighbours[0] = ((idx - 1) < 0) ? -1 : (idx - 1);
|
||||
neighbours[1] = ((idx + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (idx + 1);
|
||||
thumbRect = MultiSliderUI.this.thumbRects[idx];
|
||||
MultiSliderUI.this.currentIndex = idx;
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(idx, true);
|
||||
setThumbBounds(neighbours);
|
||||
}
|
||||
|
||||
thumbTop = Math.max(thumbTop, _trackTop - halfThumbHeight);
|
||||
thumbTop = Math.min(thumbTop, _trackBottom - halfThumbHeight);
|
||||
|
||||
setThumbLocation(thumbRect.x, thumbTop);
|
||||
|
||||
thumbMiddle = thumbTop + halfThumbHeight;
|
||||
((MultiSlider)slider).setValueAt(MultiSliderUI.this.currentIndex, valueForYPosition(thumbMiddle) );
|
||||
break;
|
||||
case JSlider.HORIZONTAL:
|
||||
int halfThumbWidth = thumbRect.width / 2;
|
||||
int thumbLeft = e.getX() - offset;
|
||||
if (bounded) {
|
||||
int[] neighbours = new int[2];
|
||||
int idx = -1;
|
||||
if (e.getX() - firstXY[0] <= 0) {
|
||||
idx = minmaxIndices[0];
|
||||
} else {
|
||||
idx = minmaxIndices[1];
|
||||
}
|
||||
minmaxIndices[0] = minmaxIndices[1] = idx;
|
||||
//System.out.println("idx = " + idx);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
//System.out.println("thumbLeft = " + thumbLeft);
|
||||
neighbours[0] = ((idx - 1) < 0) ? -1 : (idx - 1);
|
||||
neighbours[1] = ((idx + 1) >= MultiSliderUI.this.thumbCount) ? -1 : (idx + 1);
|
||||
thumbRect = MultiSliderUI.this.thumbRects[idx];
|
||||
MultiSliderUI.this.currentIndex = idx;
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(idx, true);
|
||||
setThumbBounds(neighbours);
|
||||
}
|
||||
|
||||
thumbLeft = Math.max(thumbLeft, _trackLeft - halfThumbWidth);
|
||||
thumbLeft = Math.min(thumbLeft, _trackRight - halfThumbWidth);
|
||||
|
||||
setThumbLocation(thumbLeft, thumbRect.y);
|
||||
|
||||
thumbMiddle = thumbLeft + halfThumbWidth;
|
||||
|
||||
((MultiSlider)slider).setValueAt(MultiSliderUI.this.currentIndex, valueForXPosition(thumbMiddle));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (!slider.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
scrollTimer.stop();
|
||||
|
||||
if (slider.getSnapToTicks()) {
|
||||
MultiSliderUI.this.isDragging = false;
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, false);
|
||||
} else {
|
||||
((MultiSlider)slider).setValueIsAdjustingAt(MultiSliderUI.this.currentIndex, false);
|
||||
MultiSliderUI.this.isDragging = false;
|
||||
}
|
||||
|
||||
slider.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* A static version of the above.
|
||||
*/
|
||||
static class SharedActionScroller extends AbstractAction {
|
||||
int _dir;
|
||||
boolean _block;
|
||||
|
||||
public SharedActionScroller(int dir, boolean block) {
|
||||
_dir = dir;
|
||||
_block = block;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JSlider slider = (JSlider)e.getSource();
|
||||
MultiSliderUI ui = (MultiSliderUI)slider.getUI();
|
||||
if ( _dir == NEGATIVE_SCROLL || _dir == POSITIVE_SCROLL ) {
|
||||
int realDir = _dir;
|
||||
if (slider.getInverted()) {
|
||||
realDir = _dir == NEGATIVE_SCROLL ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
|
||||
}
|
||||
if (_block) {
|
||||
ui.scrollByBlock(realDir);
|
||||
} else {
|
||||
ui.scrollByUnit(realDir);
|
||||
}
|
||||
} else {
|
||||
if (slider.getInverted()) {
|
||||
if (_dir == MIN_SCROLL) {
|
||||
((MultiSlider)slider).setValueAt(ui.currentIndex,
|
||||
slider.getMaximum());
|
||||
} else if (_dir == MAX_SCROLL) {
|
||||
((MultiSlider)slider).setValueAt(ui.currentIndex,
|
||||
slider.getMinimum());
|
||||
}
|
||||
} else {
|
||||
if (_dir == MIN_SCROLL) {
|
||||
((MultiSlider)slider).setValueAt(ui.currentIndex,
|
||||
slider.getMinimum());
|
||||
} else if (_dir == MAX_SCROLL) {
|
||||
((MultiSlider)slider).setValueAt(ui.currentIndex,
|
||||
slider.getMaximum());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user