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
|
||||
TCMotorSelPan.lbl.Selrocketmotor = Select rocket motor:
|
||||
TCMotorSelPan.checkbox.hideSimilar = Hide very similar thrust curves
|
||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc1 = Show all motors
|
||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc2 = Show motors with diameter less than that of the motor mount
|
||||
TCMotorSelPan.SHOW_DESCRIPTIONS.desc3 = Show motors with diameter equal to that of the motor mount
|
||||
TCMotorSelPan.lbl.Motormountdia = Motor mount diameter:
|
||||
TCMotorSelPan.checkbox.hideUsed = Hide motors already used in the mount
|
||||
TCMotorSelPan.btn.details = Show Details
|
||||
TCMotorSelPan.btn.filter = Filter Motors
|
||||
TCMotorSelPan.MotorSize = Motor Dimensions
|
||||
TCMotorSelPan.Diameter = Daimeter
|
||||
TCMotorSelPan.Length = Length
|
||||
TCMotorSelPan.MotorMountDimensions = Motor mount dimensions:
|
||||
TCMotorSelPan.lbl.Search = Search:
|
||||
TCMotorSelPan.lbl.Selectthrustcurve = Select thrust curve:
|
||||
TCMotorSelPan.lbl.Ejectionchargedelay = Ejection charge delay:
|
||||
@ -1108,6 +1111,10 @@ TCMotorSelPan.title.Thrustcurve = Thrust curve:
|
||||
TCMotorSelPan.title.Thrust = Thrust
|
||||
TCMotorSelPan.delayBox.None = None
|
||||
TCMotorSelPan.noDescription = No description available.
|
||||
TCMotorSelPan.btn.checkAll = Select All
|
||||
TCMotorSelPan.btn.checkNone = Clear All
|
||||
TCMotorSelPan.btn.close = Close
|
||||
|
||||
|
||||
|
||||
! PlotDialog
|
||||
|
@ -11,7 +11,7 @@ import net.sf.openrocket.util.ChangeSource;
|
||||
*
|
||||
* @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.
|
||||
|
@ -2,6 +2,7 @@ package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
import java.util.EventObject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sf.openrocket.util.StateChangeListener;
|
||||
import net.sf.openrocket.util.Utils;
|
||||
@ -79,6 +80,10 @@ class FlightConfigurationImpl<E extends FlightConfigurableParameter<E>> implemen
|
||||
fireEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return map.values().iterator();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -103,6 +103,10 @@ public abstract class Unit {
|
||||
}
|
||||
|
||||
val = roundForDecimalFormat(val);
|
||||
// Check for approximate integer
|
||||
if (Math.abs(val - Math.floor(val)) < 0.001) {
|
||||
return intFormat.format(val);
|
||||
}
|
||||
return decFormat.format(val);
|
||||
}
|
||||
|
||||
|
@ -184,8 +184,7 @@ public class MotorConfig extends JPanel {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String id = configuration.getFlightConfigurationID();
|
||||
|
||||
MotorChooserDialog dialog = new MotorChooserDialog(mount.getMotor(id),
|
||||
mount.getMotorDelay(id), mount.getMotorMountDiameter(),
|
||||
MotorChooserDialog dialog = new MotorChooserDialog(mount, id,
|
||||
SwingUtilities.getWindowAncestor(MotorConfig.this));
|
||||
dialog.setVisible(true);
|
||||
Motor m = dialog.getSelectedMotor();
|
||||
|
@ -43,9 +43,11 @@ public class MotorConfigurationPanel extends JPanel {
|
||||
private final MotorConfigurationTableModel configurationTableModel;
|
||||
private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton;
|
||||
|
||||
private final MotorChooserDialog dialog;
|
||||
|
||||
MotorConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) {
|
||||
super(new MigLayout("fill"));
|
||||
dialog = new MotorChooserDialog(flightConfigurationDialog);
|
||||
this.flightConfigurationDialog = flightConfigurationDialog;
|
||||
this.rocket = rocket;
|
||||
|
||||
@ -207,11 +209,7 @@ public class MotorConfigurationPanel extends JPanel {
|
||||
|
||||
MotorConfiguration config = mount.getMotorConfiguration().get(id);
|
||||
|
||||
MotorChooserDialog dialog = new MotorChooserDialog(
|
||||
config.getMotor(),
|
||||
config.getEjectionDelay(),
|
||||
mount.getMotorMountDiameter(),
|
||||
flightConfigurationDialog);
|
||||
dialog.setMotorMountAndConfig(mount, id);
|
||||
dialog.setVisible(true);
|
||||
Motor m = dialog.getSelectedMotor();
|
||||
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.l10n.Translator;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
||||
@ -26,16 +26,20 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
||||
private boolean okClicked = false;
|
||||
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);
|
||||
|
||||
|
||||
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
|
||||
@ -74,6 +78,9 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
||||
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.
|
||||
|
@ -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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -14,12 +15,10 @@ import java.util.Set;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.arch.SystemInfo;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.motor.Manufacturer;
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
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.BuildProperties;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
private static final Logger log = LoggerFactory.getLogger(SwingPreferences.class);
|
||||
@ -575,9 +577,9 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
|
||||
return materials;
|
||||
}
|
||||
|
||||
|
||||
//// Preset Component Favorites
|
||||
|
||||
|
||||
@Override
|
||||
public void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite) {
|
||||
Preferences prefs = PREFNODE.node("favoritePresets").node(type.name());
|
||||
@ -599,35 +601,67 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
//// Decal Editor Setting
|
||||
private final static String DECAL_EDITOR_PREFERNCE_NODE = "decalEditorPreference";
|
||||
private final static String DECAL_EDITOR_USE_SYSTEM_DEFAULT = "<SYSTEM>";
|
||||
|
||||
public void clearDecalEditorPreference( ) {
|
||||
putString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
||||
public void clearDecalEditorPreference() {
|
||||
putString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||
}
|
||||
|
||||
public void setDecalEditorPreference(boolean useSystem, String commandLine) {
|
||||
if ( useSystem ) {
|
||||
putString(DECAL_EDITOR_PREFERNCE_NODE,DECAL_EDITOR_USE_SYSTEM_DEFAULT);
|
||||
} else if ( commandLine != null ) {
|
||||
if (useSystem) {
|
||||
putString(DECAL_EDITOR_PREFERNCE_NODE, DECAL_EDITOR_USE_SYSTEM_DEFAULT);
|
||||
} else if (commandLine != null) {
|
||||
putString(DECAL_EDITOR_PREFERNCE_NODE, commandLine);
|
||||
} else {
|
||||
clearDecalEditorPreference();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isDecalEditorPreferenceSet() {
|
||||
String s = getString(DECAL_EDITOR_PREFERNCE_NODE,null);
|
||||
String s = getString(DECAL_EDITOR_PREFERNCE_NODE, null);
|
||||
return s != null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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