Merge pull request #151 from kruland2607/feature-motorfilter
Improve MotorSelectionDialog box by having more sophisticated filtering.
This commit is contained in:
commit
7687568f81
@ -1087,10 +1087,13 @@ StorageOptChooser.lbl.Saveopt = Save options
|
|||||||
! ThrustCurveMotorSelectionPanel
|
! ThrustCurveMotorSelectionPanel
|
||||||
TCMotorSelPan.lbl.Selrocketmotor = Select rocket motor:
|
TCMotorSelPan.lbl.Selrocketmotor = Select rocket motor:
|
||||||
TCMotorSelPan.checkbox.hideSimilar = Hide very similar thrust curves
|
TCMotorSelPan.checkbox.hideSimilar = Hide very similar thrust curves
|
||||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc1 = Show all motors
|
TCMotorSelPan.checkbox.hideUsed = Hide motors already used in the mount
|
||||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc2 = Show motors with diameter less than that of the motor mount
|
TCMotorSelPan.btn.details = Show Details
|
||||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc3 = Show motors with diameter equal to that of the motor mount
|
TCMotorSelPan.btn.filter = Filter Motors
|
||||||
TCMotorSelPan.lbl.Motormountdia = Motor mount diameter:
|
TCMotorSelPan.MotorSize = Motor Dimensions
|
||||||
|
TCMotorSelPan.Diameter = Daimeter
|
||||||
|
TCMotorSelPan.Length = Length
|
||||||
|
TCMotorSelPan.MotorMountDimensions = Motor mount dimensions:
|
||||||
TCMotorSelPan.lbl.Search = Search:
|
TCMotorSelPan.lbl.Search = Search:
|
||||||
TCMotorSelPan.lbl.Selectthrustcurve = Select thrust curve:
|
TCMotorSelPan.lbl.Selectthrustcurve = Select thrust curve:
|
||||||
TCMotorSelPan.lbl.Ejectionchargedelay = Ejection charge delay:
|
TCMotorSelPan.lbl.Ejectionchargedelay = Ejection charge delay:
|
||||||
@ -1108,6 +1111,10 @@ TCMotorSelPan.title.Thrustcurve = Thrust curve:
|
|||||||
TCMotorSelPan.title.Thrust = Thrust
|
TCMotorSelPan.title.Thrust = Thrust
|
||||||
TCMotorSelPan.delayBox.None = None
|
TCMotorSelPan.delayBox.None = None
|
||||||
TCMotorSelPan.noDescription = No description available.
|
TCMotorSelPan.noDescription = No description available.
|
||||||
|
TCMotorSelPan.btn.checkAll = Select All
|
||||||
|
TCMotorSelPan.btn.checkNone = Clear All
|
||||||
|
TCMotorSelPan.btn.close = Close
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
! PlotDialog
|
! PlotDialog
|
||||||
|
@ -11,7 +11,7 @@ import net.sf.openrocket.util.ChangeSource;
|
|||||||
*
|
*
|
||||||
* @param <E> the parameter type
|
* @param <E> the parameter type
|
||||||
*/
|
*/
|
||||||
public interface FlightConfiguration<E extends ChangeSource> extends FlightConfigurableComponent {
|
public interface FlightConfiguration<E extends ChangeSource> extends FlightConfigurableComponent, Iterable<E> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default parameter value for this FlightConfiguration.
|
* Return the default parameter value for this FlightConfiguration.
|
||||||
|
@ -2,6 +2,7 @@ package net.sf.openrocket.rocketcomponent;
|
|||||||
|
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import net.sf.openrocket.util.StateChangeListener;
|
import net.sf.openrocket.util.StateChangeListener;
|
||||||
import net.sf.openrocket.util.Utils;
|
import net.sf.openrocket.util.Utils;
|
||||||
@ -79,6 +80,10 @@ class FlightConfigurationImpl<E extends FlightConfigurableParameter<E>> implemen
|
|||||||
fireEvent();
|
fireEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return map.values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -103,6 +103,10 @@ public abstract class Unit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val = roundForDecimalFormat(val);
|
val = roundForDecimalFormat(val);
|
||||||
|
// Check for approximate integer
|
||||||
|
if (Math.abs(val - Math.floor(val)) < 0.001) {
|
||||||
|
return intFormat.format(val);
|
||||||
|
}
|
||||||
return decFormat.format(val);
|
return decFormat.format(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,8 +184,7 @@ public class MotorConfig extends JPanel {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
String id = configuration.getFlightConfigurationID();
|
String id = configuration.getFlightConfigurationID();
|
||||||
|
|
||||||
MotorChooserDialog dialog = new MotorChooserDialog(mount.getMotor(id),
|
MotorChooserDialog dialog = new MotorChooserDialog(mount, id,
|
||||||
mount.getMotorDelay(id), mount.getMotorMountDiameter(),
|
|
||||||
SwingUtilities.getWindowAncestor(MotorConfig.this));
|
SwingUtilities.getWindowAncestor(MotorConfig.this));
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
Motor m = dialog.getSelectedMotor();
|
Motor m = dialog.getSelectedMotor();
|
||||||
|
@ -43,9 +43,11 @@ public class MotorConfigurationPanel extends JPanel {
|
|||||||
private final MotorConfigurationTableModel configurationTableModel;
|
private final MotorConfigurationTableModel configurationTableModel;
|
||||||
private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton;
|
private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton;
|
||||||
|
|
||||||
|
private final MotorChooserDialog dialog;
|
||||||
|
|
||||||
MotorConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) {
|
MotorConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) {
|
||||||
super(new MigLayout("fill"));
|
super(new MigLayout("fill"));
|
||||||
|
dialog = new MotorChooserDialog(flightConfigurationDialog);
|
||||||
this.flightConfigurationDialog = flightConfigurationDialog;
|
this.flightConfigurationDialog = flightConfigurationDialog;
|
||||||
this.rocket = rocket;
|
this.rocket = rocket;
|
||||||
|
|
||||||
@ -207,11 +209,7 @@ public class MotorConfigurationPanel extends JPanel {
|
|||||||
|
|
||||||
MotorConfiguration config = mount.getMotorConfiguration().get(id);
|
MotorConfiguration config = mount.getMotorConfiguration().get(id);
|
||||||
|
|
||||||
MotorChooserDialog dialog = new MotorChooserDialog(
|
dialog.setMotorMountAndConfig(mount, id);
|
||||||
config.getMotor(),
|
|
||||||
config.getEjectionDelay(),
|
|
||||||
mount.getMotorMountDiameter(),
|
|
||||||
flightConfigurationDialog);
|
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
Motor m = dialog.getSelectedMotor();
|
Motor m = dialog.getSelectedMotor();
|
||||||
double d = dialog.getSelectedDelay();
|
double d = dialog.getSelectedDelay();
|
||||||
|
@ -16,7 +16,7 @@ import net.sf.openrocket.gui.dialogs.motor.thrustcurve.ThrustCurveMotorSelection
|
|||||||
import net.sf.openrocket.gui.util.GUIUtil;
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
import net.sf.openrocket.l10n.Translator;
|
import net.sf.openrocket.l10n.Translator;
|
||||||
import net.sf.openrocket.motor.Motor;
|
import net.sf.openrocket.motor.Motor;
|
||||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
|
|
||||||
public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
||||||
@ -26,16 +26,20 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
|||||||
private boolean okClicked = false;
|
private boolean okClicked = false;
|
||||||
private static final Translator trans = Application.getTranslator();
|
private static final Translator trans = Application.getTranslator();
|
||||||
|
|
||||||
|
public MotorChooserDialog(MotorMount mount, String currentConfig, Window owner) {
|
||||||
|
this(owner);
|
||||||
|
setMotorMountAndConfig(mount, currentConfig);
|
||||||
|
}
|
||||||
|
|
||||||
public MotorChooserDialog(Motor current, double delay, double diameter, Window owner) {
|
public MotorChooserDialog(Window owner) {
|
||||||
super(owner, trans.get("MotorChooserDialog.title"), Dialog.ModalityType.APPLICATION_MODAL);
|
super(owner, trans.get("MotorChooserDialog.title"), Dialog.ModalityType.APPLICATION_MODAL);
|
||||||
|
|
||||||
|
|
||||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||||
|
|
||||||
selectionPanel = new ThrustCurveMotorSelectionPanel((ThrustCurveMotor) current, delay, diameter);
|
selectionPanel = new ThrustCurveMotorSelectionPanel();
|
||||||
|
|
||||||
panel.add(selectionPanel, "grow, wrap para");
|
panel.add(selectionPanel, "grow, wrap");
|
||||||
|
|
||||||
|
|
||||||
// OK / Cancel buttons
|
// OK / Cancel buttons
|
||||||
@ -74,6 +78,9 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
|||||||
selectionPanel.setCloseableDialog(this);
|
selectionPanel.setCloseableDialog(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMotorMountAndConfig( MotorMount mount, String currentConfig ) {
|
||||||
|
selectionPanel.setMotorMountAndConfig(mount, currentConfig);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted.
|
* Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted.
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||||
|
|
||||||
|
|
||||||
|
public enum ImpulseClass {
|
||||||
|
|
||||||
|
A("A",0.0, 2.5 ),
|
||||||
|
B("B",2.5, 5.0 ),
|
||||||
|
C("C",5.0, 10.0),
|
||||||
|
D("D",10.0, 20.0),
|
||||||
|
E("E",20.0, 40.0),
|
||||||
|
F("F", 40.0, 80.0),
|
||||||
|
G("G", 80.0, 160.0),
|
||||||
|
H("H", 160.0, 320.0),
|
||||||
|
I("I", 320.0, 640.0),
|
||||||
|
J("J", 640.0, 1280.0),
|
||||||
|
K("K", 1280.0, 2560.0),
|
||||||
|
L("L", 2560.0, 5120.0),
|
||||||
|
M("M", 5120.0, 10240.0),
|
||||||
|
N("N", 10240.0, 20480.0),
|
||||||
|
O("O", 20480.0, Double.MAX_VALUE);
|
||||||
|
|
||||||
|
private ImpulseClass( String name, double low, double high ) {
|
||||||
|
this.name = name;
|
||||||
|
this.low = low;
|
||||||
|
this.high = high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLow() {
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHigh() {
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double low;
|
||||||
|
private double high;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,317 @@
|
|||||||
|
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
|
import javax.swing.border.TitledBorder;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.event.ListDataEvent;
|
||||||
|
import javax.swing.event.ListDataListener;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import net.sf.openrocket.gui.SpinnerEditor;
|
||||||
|
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||||
|
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;
|
||||||
|
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
|
|
||||||
|
import com.itextpdf.text.Font;
|
||||||
|
|
||||||
|
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 MotorRowFilter filter;
|
||||||
|
|
||||||
|
// Things we change the label on based on the MotorMount.
|
||||||
|
private final JLabel motorMountDimension;
|
||||||
|
private final MultiSlider lengthSlider;
|
||||||
|
private final MultiSlider diameterSlider;
|
||||||
|
|
||||||
|
public MotorFilterPanel(Collection<Manufacturer> allManufacturers, MotorRowFilter filter ) {
|
||||||
|
super(new MigLayout("fill", "[grow]"));
|
||||||
|
this.filter = filter;
|
||||||
|
|
||||||
|
List<Manufacturer> unselectedManusFromPreferences = ((SwingPreferences) Application.getPreferences()).getExcludedMotorManufacturers();
|
||||||
|
filter.setExcludedManufacturers(unselectedManusFromPreferences);
|
||||||
|
|
||||||
|
//// Hide used motor files
|
||||||
|
{
|
||||||
|
final JCheckBox hideUsedBox = new JCheckBox(trans.get("TCMotorSelPan.checkbox.hideUsed"));
|
||||||
|
GUIUtil.changeFontSize(hideUsedBox, -1);
|
||||||
|
hideUsedBox.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
MotorFilterPanel.this.filter.setHideUsedMotors(hideUsedBox.isSelected());
|
||||||
|
onSelectionChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.add(hideUsedBox, "gapleft para, spanx, growx, wrap");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Manufacturer selection
|
||||||
|
JPanel sub = new JPanel(new MigLayout("fill"));
|
||||||
|
TitledBorder border = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.MANUFACTURER"));
|
||||||
|
GUIUtil.changeFontStyle(border, Font.BOLD);
|
||||||
|
sub.setBorder(border);
|
||||||
|
|
||||||
|
this.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
|
|
||||||
|
List<Manufacturer> manufacturers = new ArrayList<Manufacturer>();
|
||||||
|
for (Manufacturer m : allManufacturers) {
|
||||||
|
manufacturers.add(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(manufacturers, new Comparator<Manufacturer>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Manufacturer o1, Manufacturer o2) {
|
||||||
|
return o1.getSimpleName().compareTo( o2.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
manufacturerCheckList = new CheckList.Builder().<Manufacturer>build();
|
||||||
|
manufacturerCheckList.setData(manufacturers);
|
||||||
|
|
||||||
|
manufacturerCheckList.setUncheckedItems(unselectedManusFromPreferences);
|
||||||
|
manufacturerCheckList.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.setExcludedManufacturers( manufacturerCheckList.getUncheckedItems() );
|
||||||
|
onSelectionChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.add(new JScrollPane(manufacturerCheckList.getList()), "grow,wrap");
|
||||||
|
|
||||||
|
JButton clearMotors = new JButton(trans.get("TCMotorSelPan.btn.checkNone"));
|
||||||
|
clearMotors.addActionListener( new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
MotorFilterPanel.this.manufacturerCheckList.clearAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.add(clearMotors,"split 2");
|
||||||
|
|
||||||
|
JButton selectMotors = new JButton(trans.get("TCMotorSelPan.btn.checkAll"));
|
||||||
|
selectMotors.addActionListener( new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
MotorFilterPanel.this.manufacturerCheckList.checkAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.add(selectMotors,"wrap");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
motorMountDimension = new JLabel();
|
||||||
|
GUIUtil.changeFontSize(motorMountDimension, -1);
|
||||||
|
sub.add(motorMountDimension,"growx,wrap");
|
||||||
|
{
|
||||||
|
sub.add( new JLabel("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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sub.add( diameterSlider, "growx, wrap");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
sub.add( new JLabel(trans.get("TCMotorSelPan.Length")), "split 2, wrap");
|
||||||
|
|
||||||
|
final DoubleModel minimumLength = new DoubleModel(filter, "MinimumLength", UnitGroup.UNITS_MOTOR_DIMENSIONS, 0);
|
||||||
|
final DoubleModel maximumLength = new DoubleModel(filter, "MaximumLength", UnitGroup.UNITS_MOTOR_DIMENSIONS, 0);
|
||||||
|
|
||||||
|
JSpinner spin = new JSpinner(minimumLength.getSpinnerModel());
|
||||||
|
spin.setEditor(new SpinnerEditor(spin));
|
||||||
|
sub.add(spin, "split 5, growx");
|
||||||
|
|
||||||
|
sub.add(new UnitSelector(minimumLength), "");
|
||||||
|
|
||||||
|
lengthSlider = new MultiSlider(MultiSlider.HORIZONTAL,0, 1000, 0, 1000);
|
||||||
|
lengthSlider.setBounded(true); // thumbs cannot cross
|
||||||
|
lengthSlider.setMajorTickSpacing(100);
|
||||||
|
lengthSlider.setPaintTicks(true);
|
||||||
|
lengthSlider.setLabelTable(diameterLabels);
|
||||||
|
lengthSlider.addChangeListener( new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
int minLength = lengthSlider.getValueAt(0);
|
||||||
|
minimumLength.setValue(minLength/1000.0);
|
||||||
|
int maxLength = lengthSlider.getValueAt(1);
|
||||||
|
maximumLength.setValue(maxLength/1000.0);
|
||||||
|
onSelectionChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
minimumLength.addChangeListener( new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
lengthSlider.setValueAt(0, (int)(1000* minimumLength.getValue()));
|
||||||
|
lengthSlider.setValueAt(1, (int) (1000* maximumLength.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.add( lengthSlider, "growx");
|
||||||
|
|
||||||
|
spin = new JSpinner(maximumLength.getSpinnerModel());
|
||||||
|
spin.setEditor(new SpinnerEditor(spin));
|
||||||
|
sub.add(spin, "growx");
|
||||||
|
|
||||||
|
sub.add(new UnitSelector(maximumLength), "wrap");
|
||||||
|
}
|
||||||
|
this.add(sub, "grow,wrap");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMotorMount( MotorMount mount ) {
|
||||||
|
filter.setMotorMount(mount);
|
||||||
|
onSelectionChanged();
|
||||||
|
if ( mount == null ) {
|
||||||
|
// Disable diameter controls?
|
||||||
|
lengthSlider.setValueAt(1, 1000);
|
||||||
|
motorMountDimension.setText("");
|
||||||
|
} else {
|
||||||
|
double mountLength = ((RocketComponent)mount).getLength();
|
||||||
|
lengthSlider.setValueAt(1, (int) Math.min(1000,Math.round(1000*mountLength)));
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
diameterSlider.setValueAt(1, i-1);
|
||||||
|
|
||||||
|
motorMountDimension.setText( trans.get("TCMotorSelPan.MotorMountDimensions") + " " +
|
||||||
|
UnitGroup.UNITS_MOTOR_DIMENSIONS.toStringUnit(mountDiameter)+ " x " + UnitGroup.UNITS_MOTOR_DIMENSIONS.toStringUnit(mountLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onSelectionChanged();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,315 @@
|
|||||||
|
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
|
import net.sf.openrocket.gui.util.Icons;
|
||||||
|
import net.sf.openrocket.l10n.Translator;
|
||||||
|
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
|
|
||||||
|
import org.jfree.chart.ChartFactory;
|
||||||
|
import org.jfree.chart.ChartPanel;
|
||||||
|
import org.jfree.chart.JFreeChart;
|
||||||
|
import org.jfree.chart.axis.ValueAxis;
|
||||||
|
import org.jfree.chart.plot.PlotOrientation;
|
||||||
|
import org.jfree.chart.plot.XYPlot;
|
||||||
|
import org.jfree.chart.title.TextTitle;
|
||||||
|
import org.jfree.data.xy.XYSeries;
|
||||||
|
import org.jfree.data.xy.XYSeriesCollection;
|
||||||
|
|
||||||
|
class MotorInformationPanel extends JPanel {
|
||||||
|
|
||||||
|
private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50;
|
||||||
|
private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12;
|
||||||
|
|
||||||
|
private static final Color NO_COMMENT_COLOR = Color.GRAY;
|
||||||
|
private static final Color WITH_COMMENT_COLOR = Color.BLACK;
|
||||||
|
|
||||||
|
private static final Translator trans = Application.getTranslator();
|
||||||
|
|
||||||
|
// Motors in set
|
||||||
|
private List<ThrustCurveMotor> selectedMotorSet;
|
||||||
|
// Selected motor
|
||||||
|
private ThrustCurveMotor selectedMotor;
|
||||||
|
|
||||||
|
private final JLabel totalImpulseLabel;
|
||||||
|
private final JLabel classificationLabel;
|
||||||
|
private final JLabel avgThrustLabel;
|
||||||
|
private final JLabel maxThrustLabel;
|
||||||
|
private final JLabel burnTimeLabel;
|
||||||
|
private final JLabel launchMassLabel;
|
||||||
|
private final JLabel emptyMassLabel;
|
||||||
|
private final JLabel dataPointsLabel;
|
||||||
|
private final JLabel digestLabel;
|
||||||
|
|
||||||
|
private final JTextArea comment;
|
||||||
|
private final Font noCommentFont;
|
||||||
|
private final Font withCommentFont;
|
||||||
|
|
||||||
|
private final JFreeChart chart;
|
||||||
|
private final ChartPanel chartPanel;
|
||||||
|
private final JLabel zoomIcon;
|
||||||
|
|
||||||
|
public MotorInformationPanel() {
|
||||||
|
super(new MigLayout("fill"));
|
||||||
|
|
||||||
|
// Thrust curve info
|
||||||
|
//// Total impulse:
|
||||||
|
{
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Totalimpulse")));
|
||||||
|
totalImpulseLabel = new JLabel();
|
||||||
|
this.add(totalImpulseLabel, "split");
|
||||||
|
|
||||||
|
classificationLabel = new JLabel();
|
||||||
|
classificationLabel.setEnabled(false); // Gray out
|
||||||
|
this.add(classificationLabel, "gapleft unrel, wrap");
|
||||||
|
|
||||||
|
//// Avg. thrust:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Avgthrust")));
|
||||||
|
avgThrustLabel = new JLabel();
|
||||||
|
this.add(avgThrustLabel, "wrap");
|
||||||
|
|
||||||
|
//// Max. thrust:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Maxthrust")));
|
||||||
|
maxThrustLabel = new JLabel();
|
||||||
|
this.add(maxThrustLabel, "wrap");
|
||||||
|
|
||||||
|
//// Burn time:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Burntime")));
|
||||||
|
burnTimeLabel = new JLabel();
|
||||||
|
this.add(burnTimeLabel, "wrap");
|
||||||
|
|
||||||
|
//// Launch mass:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Launchmass")));
|
||||||
|
launchMassLabel = new JLabel();
|
||||||
|
this.add(launchMassLabel, "wrap");
|
||||||
|
|
||||||
|
//// Empty mass:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Emptymass")));
|
||||||
|
emptyMassLabel = new JLabel();
|
||||||
|
this.add(emptyMassLabel, "wrap");
|
||||||
|
|
||||||
|
//// Data points:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Datapoints")));
|
||||||
|
dataPointsLabel = new JLabel();
|
||||||
|
this.add(dataPointsLabel, "wrap para");
|
||||||
|
|
||||||
|
if (System.getProperty("openrocket.debug.motordigest") != null) {
|
||||||
|
//// Digest:
|
||||||
|
this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Digest")));
|
||||||
|
digestLabel = new JLabel();
|
||||||
|
this.add(digestLabel, "w :300:, wrap para");
|
||||||
|
} else {
|
||||||
|
digestLabel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
comment = new JTextArea(5, 5);
|
||||||
|
GUIUtil.changeFontSize(comment, -2);
|
||||||
|
withCommentFont = comment.getFont();
|
||||||
|
noCommentFont = withCommentFont.deriveFont(Font.ITALIC);
|
||||||
|
comment.setLineWrap(true);
|
||||||
|
comment.setWrapStyleWord(true);
|
||||||
|
comment.setEditable(false);
|
||||||
|
JScrollPane scrollpane = new JScrollPane(comment);
|
||||||
|
this.add(scrollpane, "spanx, growx, wrap para");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thrust curve plot
|
||||||
|
{
|
||||||
|
chart = ChartFactory.createXYLineChart(
|
||||||
|
null, // title
|
||||||
|
null, // xAxisLabel
|
||||||
|
null, // yAxisLabel
|
||||||
|
null, // dataset
|
||||||
|
PlotOrientation.VERTICAL,
|
||||||
|
false, // legend
|
||||||
|
false, // tooltips
|
||||||
|
false // urls
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the data and formatting to the plot
|
||||||
|
XYPlot plot = chart.getXYPlot();
|
||||||
|
|
||||||
|
changeLabelFont(plot.getRangeAxis(), -2);
|
||||||
|
changeLabelFont(plot.getDomainAxis(), -2);
|
||||||
|
|
||||||
|
//// Thrust curve:
|
||||||
|
chart.setTitle(new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont()));
|
||||||
|
chart.setBackgroundPaint(this.getBackground());
|
||||||
|
plot.setBackgroundPaint(Color.WHITE);
|
||||||
|
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
|
||||||
|
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
|
||||||
|
|
||||||
|
chartPanel = new ChartPanel(chart,
|
||||||
|
false, // properties
|
||||||
|
false, // save
|
||||||
|
false, // print
|
||||||
|
false, // zoom
|
||||||
|
false); // tooltips
|
||||||
|
chartPanel.setMouseZoomable(false);
|
||||||
|
chartPanel.setPopupMenu(null);
|
||||||
|
chartPanel.setMouseWheelEnabled(false);
|
||||||
|
chartPanel.setRangeZoomable(false);
|
||||||
|
chartPanel.setDomainZoomable(false);
|
||||||
|
|
||||||
|
chartPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
|
chartPanel.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (selectedMotor == null || selectedMotorSet == null)
|
||||||
|
return;
|
||||||
|
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||||
|
// Open plot dialog
|
||||||
|
ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(selectedMotorSet,
|
||||||
|
selectedMotorSet.indexOf(selectedMotor),
|
||||||
|
SwingUtilities.getWindowAncestor(MotorInformationPanel.this));
|
||||||
|
plotDialog.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JLayeredPane layer = new CustomLayeredPane();
|
||||||
|
|
||||||
|
layer.setBorder(BorderFactory.createLineBorder(Color.BLUE));
|
||||||
|
|
||||||
|
layer.add(chartPanel, (Integer) 0);
|
||||||
|
|
||||||
|
zoomIcon = new JLabel(Icons.ZOOM_IN);
|
||||||
|
zoomIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
|
layer.add(zoomIcon, (Integer) 1);
|
||||||
|
|
||||||
|
this.add(layer, "width 300:300:, height 180:180:, grow, spanx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearData() {
|
||||||
|
totalImpulseLabel.setText("");
|
||||||
|
totalImpulseLabel.setToolTipText(null);
|
||||||
|
classificationLabel.setText("");
|
||||||
|
classificationLabel.setToolTipText(null);
|
||||||
|
avgThrustLabel.setText("");
|
||||||
|
maxThrustLabel.setText("");
|
||||||
|
burnTimeLabel.setText("");
|
||||||
|
launchMassLabel.setText("");
|
||||||
|
emptyMassLabel.setText("");
|
||||||
|
dataPointsLabel.setText("");
|
||||||
|
if (digestLabel != null) {
|
||||||
|
digestLabel.setText("");
|
||||||
|
}
|
||||||
|
setComment("");
|
||||||
|
chart.getXYPlot().setDataset(new XYSeriesCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateData( List<ThrustCurveMotor> motors, ThrustCurveMotor selectedMotor ) {
|
||||||
|
|
||||||
|
this.selectedMotorSet = motors;
|
||||||
|
this.selectedMotor = selectedMotor;
|
||||||
|
|
||||||
|
// Update thrust curve data
|
||||||
|
double impulse = selectedMotor.getTotalImpulseEstimate();
|
||||||
|
MotorClass mc = MotorClass.getMotorClass(impulse);
|
||||||
|
totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(impulse));
|
||||||
|
classificationLabel.setText("(" + mc.getDescription(impulse) + ")");
|
||||||
|
totalImpulseLabel.setToolTipText(mc.getClassDescription());
|
||||||
|
classificationLabel.setToolTipText(mc.getClassDescription());
|
||||||
|
|
||||||
|
avgThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(
|
||||||
|
selectedMotor.getAverageThrustEstimate()));
|
||||||
|
maxThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(
|
||||||
|
selectedMotor.getMaxThrustEstimate()));
|
||||||
|
burnTimeLabel.setText(UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().toStringUnit(
|
||||||
|
selectedMotor.getBurnTimeEstimate()));
|
||||||
|
launchMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
|
||||||
|
selectedMotor.getLaunchCG().weight));
|
||||||
|
emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
|
||||||
|
selectedMotor.getEmptyCG().weight));
|
||||||
|
dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1));
|
||||||
|
if (digestLabel != null) {
|
||||||
|
digestLabel.setText(selectedMotor.getDigest());
|
||||||
|
}
|
||||||
|
|
||||||
|
setComment(selectedMotor.getDescription());
|
||||||
|
|
||||||
|
// Update the plot
|
||||||
|
XYPlot plot = chart.getXYPlot();
|
||||||
|
final int index = motors.indexOf(selectedMotor);
|
||||||
|
|
||||||
|
XYSeriesCollection dataset = new XYSeriesCollection();
|
||||||
|
for (int i = 0; i < motors.size(); i++) {
|
||||||
|
ThrustCurveMotor m = motors.get(i);
|
||||||
|
|
||||||
|
//// Thrust
|
||||||
|
XYSeries series = new XYSeries(trans.get("TCMotorSelPan.title.Thrust") + " (" + i + ")");
|
||||||
|
double[] time = m.getTimePoints();
|
||||||
|
double[] thrust = m.getThrustPoints();
|
||||||
|
|
||||||
|
for (int j = 0; j < time.length; j++) {
|
||||||
|
series.add(time[j], thrust[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataset.addSeries(series);
|
||||||
|
|
||||||
|
boolean selected = (i == index);
|
||||||
|
plot.getRenderer().setSeriesStroke(i, new BasicStroke(selected ? 3 : 1));
|
||||||
|
plot.getRenderer().setSeriesPaint(i, ThrustCurveMotorSelectionPanel.getColor(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
plot.setDataset(dataset);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setComment(String s) {
|
||||||
|
s = s.trim();
|
||||||
|
if (s.length() == 0) {
|
||||||
|
//// No description available.
|
||||||
|
comment.setText(trans.get("TCMotorSelPan.noDescription"));
|
||||||
|
comment.setFont(noCommentFont);
|
||||||
|
comment.setForeground(NO_COMMENT_COLOR);
|
||||||
|
} else {
|
||||||
|
comment.setText(s);
|
||||||
|
comment.setFont(withCommentFont);
|
||||||
|
comment.setForeground(WITH_COMMENT_COLOR);
|
||||||
|
}
|
||||||
|
comment.setCaretPosition(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeLabelFont(ValueAxis axis, float size) {
|
||||||
|
Font font = axis.getTickLabelFont();
|
||||||
|
font = font.deriveFont(font.getSize2D() + size);
|
||||||
|
axis.setTickLabelFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom layered pane that sets the bounds of the components on every layout.
|
||||||
|
*/
|
||||||
|
public class CustomLayeredPane extends JLayeredPane {
|
||||||
|
@Override
|
||||||
|
public void doLayout() {
|
||||||
|
synchronized (getTreeLock()) {
|
||||||
|
int w = getWidth();
|
||||||
|
int h = getHeight();
|
||||||
|
chartPanel.setBounds(0, 0, w, h);
|
||||||
|
zoomIcon.setBounds(w - ZOOM_ICON_POSITION_NEGATIVE_X, ZOOM_ICON_POSITION_POSITIVE_Y, 50, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,242 @@
|
|||||||
|
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.swing.RowFilter;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
|
import net.sf.openrocket.database.motor.ThrustCurveMotorSet;
|
||||||
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
|
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||||
|
import net.sf.openrocket.rocketcomponent.MotorConfiguration;
|
||||||
|
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||||
|
import net.sf.openrocket.util.AbstractChangeSource;
|
||||||
|
import net.sf.openrocket.util.ChangeSource;
|
||||||
|
import net.sf.openrocket.util.StateChangeListener;
|
||||||
|
|
||||||
|
//////// Row filters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract adapter class.
|
||||||
|
*/
|
||||||
|
public class MotorRowFilter extends RowFilter<TableModel, Integer> implements ChangeSource {
|
||||||
|
|
||||||
|
// configuration data used in the filter process
|
||||||
|
private final ThrustCurveMotorDatabaseModel model;
|
||||||
|
private List<ThrustCurveMotor> usedMotors = new ArrayList<ThrustCurveMotor>();
|
||||||
|
|
||||||
|
private final AbstractChangeSource changeSourceDelegate = new AbstractChangeSource();
|
||||||
|
private final Object change = new Object();
|
||||||
|
|
||||||
|
// things which can be changed to modify filter behavior
|
||||||
|
|
||||||
|
// Limit motors based on length
|
||||||
|
private double minimumLength = 0;
|
||||||
|
private double maximumLength = Double.MAX_VALUE;
|
||||||
|
|
||||||
|
// Limit motors based on diameter
|
||||||
|
private Double minimumDiameter;
|
||||||
|
private Double maximumDiameter;
|
||||||
|
|
||||||
|
// Collection of strings which match text in the motor
|
||||||
|
private List<String> searchTerms = Collections.<String> emptyList();
|
||||||
|
|
||||||
|
// Boolean which hides motors in the usedMotors list
|
||||||
|
private boolean hideUsedMotors = false;
|
||||||
|
|
||||||
|
// List of manufacturers to exclude.
|
||||||
|
private List<Manufacturer> excludedManufacturers = new ArrayList<Manufacturer>();
|
||||||
|
|
||||||
|
// Impulse class filtering
|
||||||
|
private ImpulseClass minimumImpulse;
|
||||||
|
private ImpulseClass maximumImpulse;
|
||||||
|
|
||||||
|
|
||||||
|
public MotorRowFilter(ThrustCurveMotorDatabaseModel model) {
|
||||||
|
super();
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMotorMount( MotorMount mount ) {
|
||||||
|
if (mount != null) {
|
||||||
|
for (MotorConfiguration m : mount.getMotorConfiguration()) {
|
||||||
|
this.usedMotors.add((ThrustCurveMotor) m.getMotor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchTerms(final List<String> searchTerms) {
|
||||||
|
this.searchTerms = new ArrayList<String>();
|
||||||
|
for (String s : searchTerms) {
|
||||||
|
s = s.trim().toLowerCase(Locale.getDefault());
|
||||||
|
if (s.length() > 0) {
|
||||||
|
this.searchTerms.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMinimumLength() {
|
||||||
|
return minimumLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinimumLength(double minimumLength) {
|
||||||
|
if ( this.minimumLength != minimumLength ) {
|
||||||
|
this.minimumLength = minimumLength;
|
||||||
|
fireChangeEvent(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaximumLength() {
|
||||||
|
return maximumLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaximumLength(double maximumLength) {
|
||||||
|
if ( this.maximumLength != maximumLength ) {
|
||||||
|
this.maximumLength = maximumLength;
|
||||||
|
fireChangeEvent(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Double getMinimumDiameter() {
|
||||||
|
return minimumDiameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinimumDiameter(Double minimumDiameter) {
|
||||||
|
this.minimumDiameter = minimumDiameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Double getMaximumDiameter() {
|
||||||
|
return maximumDiameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaximumDiameter(Double maximumDiameter) {
|
||||||
|
this.maximumDiameter = maximumDiameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHideUsedMotors(boolean hideUsedMotors) {
|
||||||
|
this.hideUsedMotors = hideUsedMotors;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Manufacturer> getExcludedManufacturers() {
|
||||||
|
return excludedManufacturers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExcludedManufacturers(Collection<Manufacturer> excludedManufacturers) {
|
||||||
|
this.excludedManufacturers.clear();
|
||||||
|
this.excludedManufacturers.addAll(excludedManufacturers);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImpulseClass getMinimumImpulse() {
|
||||||
|
return minimumImpulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinimumImpulse(ImpulseClass minimumImpulse) {
|
||||||
|
this.minimumImpulse = minimumImpulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImpulseClass getMaximumImpulse() {
|
||||||
|
return maximumImpulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaximumImpulse(ImpulseClass maximumImpulse) {
|
||||||
|
this.maximumImpulse = maximumImpulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
|
||||||
|
int index = entry.getIdentifier();
|
||||||
|
ThrustCurveMotorSet m = model.getMotorSet(index);
|
||||||
|
return filterManufacturers(m) && filterUsed(m) && filterBySize(m) && filterByString(m) && filterByImpulseClass(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filterManufacturers(ThrustCurveMotorSet m) {
|
||||||
|
if (excludedManufacturers.contains(m.getManufacturer())) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filterUsed(ThrustCurveMotorSet m) {
|
||||||
|
if (!hideUsedMotors) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (ThrustCurveMotor motor : usedMotors) {
|
||||||
|
if (m.matches(motor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filterBySize(ThrustCurveMotorSet m) {
|
||||||
|
|
||||||
|
if ( minimumDiameter != null ) {
|
||||||
|
if ( m.getDiameter() <= minimumDiameter - 0.0015 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( maximumDiameter != null ) {
|
||||||
|
if ( m.getDiameter() >= maximumDiameter + 0.0004 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m.getLength() > maximumLength ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m.getLength() < minimumLength ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean filterByString(ThrustCurveMotorSet m) {
|
||||||
|
main: for (String s : searchTerms) {
|
||||||
|
for (ThrustCurveMotorColumns col : ThrustCurveMotorColumns.values()) {
|
||||||
|
String str = col.getValue(m).toString().toLowerCase(Locale.getDefault());
|
||||||
|
if (str.indexOf(s) >= 0)
|
||||||
|
continue main;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filterByImpulseClass(ThrustCurveMotorSet m) {
|
||||||
|
if ( minimumImpulse != null ) {
|
||||||
|
if( m.getTotalImpuse() < minimumImpulse.getLow() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( maximumImpulse != null ) {
|
||||||
|
if( m.getTotalImpuse() > maximumImpulse.getHigh() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void addChangeListener(StateChangeListener listener) {
|
||||||
|
changeSourceDelegate.addChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void removeChangeListener(StateChangeListener listener) {
|
||||||
|
changeSourceDelegate.removeChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireChangeEvent(Object source) {
|
||||||
|
changeSourceDelegate.fireChangeEvent(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
189
swing/src/net/sf/openrocket/gui/util/CheckList.java
Normal file
189
swing/src/net/sf/openrocket/gui/util/CheckList.java
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, EzWare
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.Redistributions
|
||||||
|
* in binary form must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.Neither the name of the
|
||||||
|
* EzWare nor the names of its contributors may be used to endorse or
|
||||||
|
* promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.openrocket.gui.util;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The decorator for JList which makes it work like check list
|
||||||
|
* UI can be designed using JList and which can be later decorated to become a check list
|
||||||
|
* @author Eugene Ryzhikov
|
||||||
|
*
|
||||||
|
* @param <T> list item type
|
||||||
|
*/
|
||||||
|
public class CheckList<T> {
|
||||||
|
|
||||||
|
private final JList list;
|
||||||
|
private static final MouseAdapter checkBoxEditor = new CheckListEditor();
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private JList list;
|
||||||
|
|
||||||
|
public Builder(JList list) {
|
||||||
|
this.list = list == null ? new JList() : list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> CheckList<T> build() {
|
||||||
|
return new CheckList<T>(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the standard JList and makes it work like check list
|
||||||
|
* @param list
|
||||||
|
*/
|
||||||
|
private CheckList(final JList list) {
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
this.list = list;
|
||||||
|
this.list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
|
if (!isEditorAttached())
|
||||||
|
list.addMouseListener(checkBoxEditor);
|
||||||
|
this.list.setCellRenderer(new CheckListRenderer());
|
||||||
|
|
||||||
|
setupKeyboardActions(list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private void setupKeyboardActions(final JList list) {
|
||||||
|
String actionKey = "toggle-check";
|
||||||
|
list.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), actionKey);
|
||||||
|
list.getActionMap().put(actionKey, new AbstractAction() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
toggleIndex(list.getSelectedIndex());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEditorAttached() {
|
||||||
|
|
||||||
|
for (MouseListener ml : list.getMouseListeners()) {
|
||||||
|
if (ml instanceof CheckListEditor)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public JList getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets data to a check list. Simplification for setting new the model
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
public void setData(Collection<T> data) {
|
||||||
|
setModel(new DefaultCheckListModel<T>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the model for check list.
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public void setModel(DefaultCheckListModel<T> model) {
|
||||||
|
list.setModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public DefaultCheckListModel<T> getModel() {
|
||||||
|
return (DefaultCheckListModel<T>) list.getModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of checked items.
|
||||||
|
* @return collection of checked items. Empty collection if nothing is selected
|
||||||
|
*/
|
||||||
|
public Collection<T> getCheckedItems() {
|
||||||
|
return getModel().getCheckedItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<T> getUncheckedItems() {
|
||||||
|
List<T> unchecked = new ArrayList<T>();
|
||||||
|
for (int i = getModel().getSize() - 1; i >= 0; i--) {
|
||||||
|
unchecked.add((T) getModel().getElementAt(i));
|
||||||
|
}
|
||||||
|
unchecked.removeAll(getCheckedItems());
|
||||||
|
return unchecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAll() {
|
||||||
|
getModel().checkAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAll() {
|
||||||
|
getModel().clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets checked elements
|
||||||
|
* @param elements
|
||||||
|
*/
|
||||||
|
public void setCheckedItems(Collection<T> elements) {
|
||||||
|
getModel().setCheckedItems(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUncheckedItems( Collection<T> elements ) {
|
||||||
|
getModel().setUncheckedItems(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleIndex(int index) {
|
||||||
|
if (index >= 0 && index < list.getModel().getSize()) {
|
||||||
|
DefaultCheckListModel<T> model = getModel();
|
||||||
|
model.setCheckedIndex(index, !model.isCheckedIndex(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
87
swing/src/net/sf/openrocket/gui/util/CheckListEditor.java
Normal file
87
swing/src/net/sf/openrocket/gui/util/CheckListEditor.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, EzWare
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.Redistributions
|
||||||
|
* in binary form must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.Neither the name of the
|
||||||
|
* EzWare nor the names of its contributors may be used to endorse or
|
||||||
|
* promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.openrocket.gui.util;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines mouse click and
|
||||||
|
* 1. Toggles the check on selected item if clicked once
|
||||||
|
* 2. Clears checks and checks selected item if clicked more then once
|
||||||
|
*
|
||||||
|
* Created on Feb 4, 2011
|
||||||
|
* @author Eugene Ryzhikov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class CheckListEditor extends MouseAdapter {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
|
||||||
|
if (!SwingUtilities.isLeftMouseButton(e))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JList list = (JList) e.getSource();
|
||||||
|
if (!list.isEnabled() || (!(list.getModel() instanceof DefaultCheckListModel<?>)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = list.locationToIndex(e.getPoint());
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle bounds = list.getCellBounds(index, index);
|
||||||
|
|
||||||
|
if (bounds.contains(e.getPoint())) {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
DefaultCheckListModel<Object> model = (DefaultCheckListModel<Object>) list.getModel();
|
||||||
|
|
||||||
|
if (e.getClickCount() > 1) {
|
||||||
|
// clear all and check selected for more then 1 clicks
|
||||||
|
// Original implementation had this implementation. I didn't like that behavior.
|
||||||
|
// model.setCheckedItems(Arrays.asList(model.getElementAt(index)));
|
||||||
|
} else {
|
||||||
|
// simple toggle for 1 click
|
||||||
|
model.setCheckedIndex(index, !model.isCheckedIndex(index));
|
||||||
|
}
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
228
swing/src/net/sf/openrocket/gui/util/CheckListRenderer.java
Normal file
228
swing/src/net/sf/openrocket/gui/util/CheckListRenderer.java
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, EzWare
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.Redistributions
|
||||||
|
* in binary form must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.Neither the name of the
|
||||||
|
* EzWare nor the names of its contributors may be used to endorse or
|
||||||
|
* promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.openrocket.gui.util;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.swing.DefaultListCellRenderer;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.ListCellRenderer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
|
public class CheckListRenderer extends JCheckBox implements ListCellRenderer, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final Border NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
|
||||||
|
private static final Border SAFE_NO_FOCUS_BORDER = NO_FOCUS_BORDER; // may change in the feature
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a default renderer object for an item in a list.
|
||||||
|
*/
|
||||||
|
public CheckListRenderer() {
|
||||||
|
super();
|
||||||
|
setOpaque(true);
|
||||||
|
setBorder(getNoFocusBorder());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Border getNoFocusBorder() {
|
||||||
|
if (System.getSecurityManager() != null) {
|
||||||
|
return SAFE_NO_FOCUS_BORDER;
|
||||||
|
} else {
|
||||||
|
return NO_FOCUS_BORDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
|
||||||
|
boolean cellHasFocus) {
|
||||||
|
|
||||||
|
setComponentOrientation(list.getComponentOrientation());
|
||||||
|
|
||||||
|
Color bg = null;
|
||||||
|
Color fg = null;
|
||||||
|
|
||||||
|
JList.DropLocation dropLocation = list.getDropLocation();
|
||||||
|
if (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == index) {
|
||||||
|
|
||||||
|
bg = UIManager.getColor("List.dropCellBackground");
|
||||||
|
fg = UIManager.getColor("List.dropCellForeground");
|
||||||
|
|
||||||
|
isSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
setBackground(bg == null ? list.getSelectionBackground() : bg);
|
||||||
|
setForeground(fg == null ? list.getSelectionForeground() : fg);
|
||||||
|
} else {
|
||||||
|
setBackground(list.getBackground());
|
||||||
|
setForeground(list.getForeground());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Icon) {
|
||||||
|
setIcon((Icon) value);
|
||||||
|
setText("");
|
||||||
|
} else {
|
||||||
|
setIcon(null);
|
||||||
|
setText(getObjectAsText(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(isChecked(list, index));
|
||||||
|
|
||||||
|
setEnabled(list.isEnabled());
|
||||||
|
setFont(list.getFont());
|
||||||
|
|
||||||
|
Border border = null;
|
||||||
|
if (cellHasFocus) {
|
||||||
|
if (isSelected) {
|
||||||
|
border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
|
||||||
|
}
|
||||||
|
if (border == null) {
|
||||||
|
border = UIManager.getBorder("List.focusCellHighlightBorder");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
border = getNoFocusBorder();
|
||||||
|
}
|
||||||
|
setBorder(border);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getObjectAsText(Object obj) {
|
||||||
|
return (obj == null) ? "" : obj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isChecked(JList list, int index) {
|
||||||
|
|
||||||
|
if (list.getModel() instanceof DefaultCheckListModel<?>) {
|
||||||
|
return ((DefaultCheckListModel<?>) list.getModel()).isCheckedIndex(index);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the background is opaque and differs from the JList's background; false otherwise
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isOpaque() {
|
||||||
|
Color back = getBackground();
|
||||||
|
Component p = getParent();
|
||||||
|
if (p != null) {
|
||||||
|
p = p.getParent();
|
||||||
|
}
|
||||||
|
// p should now be the JList.
|
||||||
|
boolean colorMatch = (back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque();
|
||||||
|
return !colorMatch && super.isOpaque();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
|
||||||
|
|
||||||
|
if ("text".equals(propertyName) ||
|
||||||
|
(("font".equals(propertyName) || "foreground".equals(propertyName)) &&
|
||||||
|
oldValue != newValue && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {
|
||||||
|
|
||||||
|
super.firePropertyChange(propertyName, oldValue, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods below are overridden for performance reasons.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void repaint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void revalidate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void repaint(long tm, int x, int y, int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void repaint(Rectangle r) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, char oldValue, char newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, short oldValue, short newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, int oldValue, int newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, long oldValue, long newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, float oldValue, float newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, double oldValue, double newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class UIResource extends DefaultListCellRenderer implements javax.swing.plaf.UIResource {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
144
swing/src/net/sf/openrocket/gui/util/DefaultCheckListModel.java
Normal file
144
swing/src/net/sf/openrocket/gui/util/DefaultCheckListModel.java
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, EzWare
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.Redistributions
|
||||||
|
* in binary form must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.Neither the name of the
|
||||||
|
* EzWare nor the names of its contributors may be used to endorse or
|
||||||
|
* promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.openrocket.gui.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.swing.AbstractListModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default model for check list. It is based on the list of items
|
||||||
|
* Implementation of checks is based on HashSet of checked items
|
||||||
|
*
|
||||||
|
* @author Eugene Ryzhikov
|
||||||
|
*
|
||||||
|
* @param <T> list element type
|
||||||
|
*/
|
||||||
|
public class DefaultCheckListModel<T> extends AbstractListModel {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final List<T> data = new ArrayList<T>();
|
||||||
|
private final Set<T> checks = new HashSet<T>();
|
||||||
|
|
||||||
|
public DefaultCheckListModel(Collection<? extends T> data) {
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
for (T object : data) {
|
||||||
|
this.data.add(object);
|
||||||
|
checks.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultCheckListModel(T... data) {
|
||||||
|
this(Arrays.asList(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.oxbow.swingbits.list.ICheckListModel#getSize()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return data().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<T> data() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getElementAt(int index) {
|
||||||
|
return data().get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCheckedIndex(int index) {
|
||||||
|
return checks.contains(data().get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckedIndex(int index, boolean value) {
|
||||||
|
T o = data().get(index);
|
||||||
|
if (value)
|
||||||
|
checks.add(o);
|
||||||
|
else
|
||||||
|
checks.remove(o);
|
||||||
|
fireContentsChanged(this, index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<T> getCheckedItems() {
|
||||||
|
List<T> items = new ArrayList<T>(checks);
|
||||||
|
items.retainAll(data);
|
||||||
|
return Collections.unmodifiableList(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAll() {
|
||||||
|
checks.clear();
|
||||||
|
fireContentsChanged(this, 0, checks.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAll() {
|
||||||
|
checks.addAll(data);
|
||||||
|
fireContentsChanged(this, 0, checks.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckedItems(Collection<T> items) {
|
||||||
|
|
||||||
|
// if ( CollectionUtils.isEmpty(items)) return;
|
||||||
|
|
||||||
|
List<T> correctedItems = new ArrayList<T>(items);
|
||||||
|
correctedItems.retainAll(data);
|
||||||
|
|
||||||
|
checks.clear();
|
||||||
|
checks.addAll(correctedItems);
|
||||||
|
fireContentsChanged(this, 0, checks.size() - 1);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUncheckedItems( Collection<T> items ) {
|
||||||
|
|
||||||
|
List<T> correctedItems = new ArrayList<T>(data);
|
||||||
|
correctedItems.removeAll(items);
|
||||||
|
|
||||||
|
checks.clear();
|
||||||
|
checks.addAll(correctedItems);
|
||||||
|
fireContentsChanged(this, 0, checks.size() - 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import java.awt.Point;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -14,12 +15,10 @@ import java.util.Set;
|
|||||||
import java.util.prefs.BackingStoreException;
|
import java.util.prefs.BackingStoreException;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import net.sf.openrocket.arch.SystemInfo;
|
import net.sf.openrocket.arch.SystemInfo;
|
||||||
import net.sf.openrocket.document.Simulation;
|
import net.sf.openrocket.document.Simulation;
|
||||||
import net.sf.openrocket.material.Material;
|
import net.sf.openrocket.material.Material;
|
||||||
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
import net.sf.openrocket.preset.ComponentPreset;
|
import net.sf.openrocket.preset.ComponentPreset;
|
||||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||||
import net.sf.openrocket.simulation.FlightDataType;
|
import net.sf.openrocket.simulation.FlightDataType;
|
||||||
@ -30,6 +29,9 @@ import net.sf.openrocket.unit.UnitGroup;
|
|||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
import net.sf.openrocket.util.BuildProperties;
|
import net.sf.openrocket.util.BuildProperties;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SwingPreferences.class);
|
private static final Logger log = LoggerFactory.getLogger(SwingPreferences.class);
|
||||||
@ -575,9 +577,9 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
|
|
||||||
return materials;
|
return materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Preset Component Favorites
|
//// Preset Component Favorites
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite) {
|
public void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite) {
|
||||||
Preferences prefs = PREFNODE.node("favoritePresets").node(type.name());
|
Preferences prefs = PREFNODE.node("favoritePresets").node(type.name());
|
||||||
@ -599,35 +601,67 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
}
|
}
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Decal Editor Setting
|
//// Decal Editor Setting
|
||||||
private final static String DECAL_EDITOR_PREFERNCE_NODE = "decalEditorPreference";
|
private final static String DECAL_EDITOR_PREFERNCE_NODE = "decalEditorPreference";
|
||||||
private final static String DECAL_EDITOR_USE_SYSTEM_DEFAULT = "<SYSTEM>";
|
private final static String DECAL_EDITOR_USE_SYSTEM_DEFAULT = "<SYSTEM>";
|
||||||
|
|
||||||
public void clearDecalEditorPreference( ) {
|
public void clearDecalEditorPreference() {
|
||||||
putString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
putString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecalEditorPreference(boolean useSystem, String commandLine) {
|
public void setDecalEditorPreference(boolean useSystem, String commandLine) {
|
||||||
if ( useSystem ) {
|
if (useSystem) {
|
||||||
putString(DECAL_EDITOR_PREFERNCE_NODE,DECAL_EDITOR_USE_SYSTEM_DEFAULT);
|
putString(DECAL_EDITOR_PREFERNCE_NODE, DECAL_EDITOR_USE_SYSTEM_DEFAULT);
|
||||||
} else if ( commandLine != null ) {
|
} else if (commandLine != null) {
|
||||||
putString(DECAL_EDITOR_PREFERNCE_NODE, commandLine);
|
putString(DECAL_EDITOR_PREFERNCE_NODE, commandLine);
|
||||||
} else {
|
} else {
|
||||||
clearDecalEditorPreference();
|
clearDecalEditorPreference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDecalEditorPreferenceSet() {
|
public boolean isDecalEditorPreferenceSet() {
|
||||||
String s = getString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
String s = getString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||||
return s != null;
|
return s != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDecalEditorPreferenceSystem() {
|
public boolean isDecalEditorPreferenceSystem() {
|
||||||
String s = getString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
String s = getString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||||
return DECAL_EDITOR_USE_SYSTEM_DEFAULT.equals(s);
|
return DECAL_EDITOR_USE_SYSTEM_DEFAULT.equals(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDecalEditorCommandLine() {
|
public String getDecalEditorCommandLine() {
|
||||||
return getString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
return getString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Manufacturer> getExcludedMotorManufacturers() {
|
||||||
|
Preferences prefs = PREFNODE.node("excludedMotorManufacturers");
|
||||||
|
List<Manufacturer> collection = new ArrayList<Manufacturer>();
|
||||||
|
try {
|
||||||
|
String[] manuShortNames = prefs.keys();
|
||||||
|
for (String s : manuShortNames) {
|
||||||
|
Manufacturer m = Manufacturer.getManufacturer(s);
|
||||||
|
if (m != null) {
|
||||||
|
collection.add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (BackingStoreException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExcludedMotorManufacturers(Collection<Manufacturer> manus) {
|
||||||
|
Preferences prefs = PREFNODE.node("excludedMotorManufacturers");
|
||||||
|
try {
|
||||||
|
for (String s : prefs.keys()) {
|
||||||
|
prefs.remove(s);
|
||||||
|
}
|
||||||
|
} catch (BackingStoreException e) {
|
||||||
|
}
|
||||||
|
for (Manufacturer m : manus) {
|
||||||
|
prefs.putBoolean(m.getSimpleName(), 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