From e6755e0e8475b5a00b16d4cb11994ea1de809a8c Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Fri, 4 Oct 2013 20:36:30 -0500 Subject: [PATCH] Rework the thrustcurve motor filter and information. They now share tabs on the MotorSelectionPanel. Added Minimum Motor diameter to the motor filter. --- core/resources/l10n/messages.properties | 1 + ...erPopupMenu.java => MotorFilterPanel.java} | 538 ++++++------ .../thrustcurve/MotorInformationPanel.java | 315 +++++++ .../motor/thrustcurve/MotorRowFilter.java | 20 +- .../ThrustCurveMotorSelectionPanel.java | 824 ++++++------------ 5 files changed, 888 insertions(+), 810 deletions(-) rename swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/{MotorFilterPopupMenu.java => MotorFilterPanel.java} (61%) create mode 100644 swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 267f1e13d..55f9ece1a 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1088,6 +1088,7 @@ StorageOptChooser.lbl.Saveopt = Save options TCMotorSelPan.lbl.Selrocketmotor = Select rocket motor: TCMotorSelPan.checkbox.hideSimilar = Hide very similar thrust curves TCMotorSelPan.checkbox.hideUsed = Hide motors already used in the mount +TCMotorSelPan.btn.details = Show Details TCMotorSelPan.btn.filter = Filter Motors TCMotorSelPan.SHOW_DESCRIPTIONS.desc1 = Show all motors TCMotorSelPan.SHOW_DESCRIPTIONS.desc2 = Show motors with diameter less than that of the motor mount diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPopupMenu.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPanel.java similarity index 61% rename from swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPopupMenu.java rename to swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPanel.java index b124c6036..3c706c430 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPopupMenu.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorFilterPanel.java @@ -1,251 +1,287 @@ -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.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import javax.swing.BorderFactory; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.border.TitledBorder; -import javax.swing.event.ListDataEvent; -import javax.swing.event.ListDataListener; - -import net.miginfocom.swing.MigLayout; -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.l10n.Translator; -import net.sf.openrocket.motor.Manufacturer; -import net.sf.openrocket.startup.Application; - -import com.itextpdf.text.Font; - -public abstract class MotorFilterPopupMenu extends JPopupMenu { - - private static final Translator trans = Application.getTranslator(); - - private final CheckList manufacturerCheckList; - - private final CheckList impulseCheckList; - - private final MotorRowFilter filter; - - private int showMode = SHOW_ALL; - - private static final int SHOW_ALL = 0; - private static final int SHOW_SMALLER = 1; - private static final int SHOW_EXACT = 2; - private static final int SHOW_MAX = 2; - - - public MotorFilterPopupMenu(Collection allManufacturers, MotorRowFilter filter ) { - - this.filter = filter; - - showMode = Application.getPreferences().getChoice(net.sf.openrocket.startup.Preferences.MOTOR_DIAMETER_FILTER, MotorFilterPopupMenu.SHOW_MAX, MotorFilterPopupMenu.SHOW_EXACT); - List unselectedManusFromPreferences = ((SwingPreferences) Application.getPreferences()).getExcludedMotorManufacturers(); - - // 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); - - JPanel root = new JPanel(new MigLayout("fill", "[grow]")); - root.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); - - List manufacturers = new ArrayList(); - for (Manufacturer m : allManufacturers) { - manufacturers.add(m); - } - - Collections.sort(manufacturers, new Comparator() { - @Override - public int compare(Manufacturer o1, Manufacturer o2) { - return o1.getSimpleName().compareTo( o2.getSimpleName()); - } - - }); - - manufacturerCheckList = new CheckList.Builder().build(); - manufacturerCheckList.setData(manufacturers); - - manufacturerCheckList.setUncheckedItems(unselectedManusFromPreferences); - filter.setExcludedManufacturers(unselectedManusFromPreferences); - manufacturerCheckList.getModel().addListDataListener( new ListDataListener() { - @Override - public void intervalAdded(ListDataEvent e) { - } - @Override - public void intervalRemoved(ListDataEvent e) { - } - - @Override - public void contentsChanged(ListDataEvent e) { - MotorFilterPopupMenu.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) { - MotorFilterPopupMenu.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) { - MotorFilterPopupMenu.this.manufacturerCheckList.checkAll(); - - } - }); - - sub.add(selectMotors,"wrap"); - - root.add(sub,"grow, wrap"); - - // Impulse selection - sub = new JPanel(new MigLayout("fill")); - border = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.TOTAL_IMPULSE")); - GUIUtil.changeFontStyle(border, Font.BOLD); - sub.setBorder(border); - - impulseCheckList = new CheckList.Builder().build(); - impulseCheckList.setData(Arrays.asList(ImpulseClass.values())); - impulseCheckList.checkAll(); - impulseCheckList.getModel().addListDataListener( new ListDataListener() { - @Override - public void intervalAdded(ListDataEvent e) { - } - @Override - public void intervalRemoved(ListDataEvent e) { - } - @Override - public void contentsChanged(ListDataEvent e) { - MotorFilterPopupMenu.this.filter.setExcludedImpulseClasses( impulseCheckList.getUncheckedItems() ); - onSelectionChanged(); - } - - }); - - sub.add(new JScrollPane(impulseCheckList.getList()), "grow,wrap"); - - JButton clearImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkNone")); - clearImpulse.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - MotorFilterPopupMenu.this.impulseCheckList.clearAll(); - - } - }); - sub.add(clearImpulse,"split 2"); - - JButton selectImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkAll")); - selectImpulse.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - MotorFilterPopupMenu.this.impulseCheckList.checkAll(); - - } - }); - sub.add(selectImpulse,"wrap"); - - root.add(sub,"grow, wrap"); - - // Diameter selection - - sub = new JPanel(new MigLayout("fill")); - border = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.DIAMETER")); - GUIUtil.changeFontStyle(border, Font.BOLD); - sub.setBorder(border); - - JRadioButton showAllDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc1") ); - showAllDiametersButton.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showMode = SHOW_ALL; - MotorFilterPopupMenu.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.ALL); - onSelectionChanged(); - } - }); - showAllDiametersButton.setSelected( showMode == SHOW_ALL); - sub.add(showAllDiametersButton, "growx,wrap"); - - JRadioButton showSmallerDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc2") ); - showSmallerDiametersButton.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showMode = SHOW_SMALLER; - MotorFilterPopupMenu.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.SMALLER); - onSelectionChanged(); - } - }); - showSmallerDiametersButton.setSelected( showMode == SHOW_SMALLER); - sub.add(showSmallerDiametersButton, "growx,wrap"); - - JRadioButton showExactDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc3") ); - showExactDiametersButton.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showMode = SHOW_EXACT; - MotorFilterPopupMenu.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.EXACT); - onSelectionChanged(); - } - }); - showExactDiametersButton.setSelected( showMode == SHOW_EXACT ); - sub.add(showExactDiametersButton, "growx,wrap"); - - root.add(sub, "grow,wrap"); - ButtonGroup comboGroup = new ButtonGroup(); - comboGroup.add( showAllDiametersButton ); - comboGroup.add( showSmallerDiametersButton ); - comboGroup.add( showExactDiametersButton ); - - - // Close button - JButton closeButton = new JButton(trans.get("TCMotorSelPan.btn.close")); - closeButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - MotorFilterPopupMenu.this.onClose(); - } - - }); - root.add(closeButton, "split 2"); - - this.add(root); - - } - - public void onClose() { - - ((SwingPreferences) Application.getPreferences()).setExcludedMotorManufacturers(filter.getExcludedManufacturers()); - - Application.getPreferences().putChoice("MotorDiameterMatch", showMode ); - - setVisible(false); - } - - public abstract void onSelectionChanged(); - -} +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.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +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.BasicSlider; +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.l10n.Translator; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.rocketcomponent.MotorMount; +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 final CheckList manufacturerCheckList; + + private final CheckList impulseCheckList; + + private final MotorRowFilter filter; + private final TitledBorder diameterTitleBorder; + private final DoubleModel mountDiameter = new DoubleModel(1); + + private int showMode = SHOW_ALL; + + private static final int SHOW_ALL = 0; + private static final int SHOW_SMALLER = 1; + private static final int SHOW_EXACT = 2; + private static final int SHOW_MAX = 2; + + public MotorFilterPanel(Collection allManufacturers, MotorRowFilter filter ) { + super(new MigLayout("fill", "[grow]")); + this.filter = filter; + + showMode = Application.getPreferences().getChoice(net.sf.openrocket.startup.Preferences.MOTOR_DIAMETER_FILTER, MotorFilterPanel.SHOW_MAX, MotorFilterPanel.SHOW_EXACT); + switch( showMode ) { + case SHOW_ALL: + filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.ALL); + break; + case SHOW_EXACT: + filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.EXACT); + break; + case SHOW_SMALLER: + filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.SMALLER); + break; + } + List unselectedManusFromPreferences = ((SwingPreferences) Application.getPreferences()).getExcludedMotorManufacturers(); + filter.setExcludedManufacturers(unselectedManusFromPreferences); + + // 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 manufacturers = new ArrayList(); + for (Manufacturer m : allManufacturers) { + manufacturers.add(m); + } + + Collections.sort(manufacturers, new Comparator() { + @Override + public int compare(Manufacturer o1, Manufacturer o2) { + return o1.getSimpleName().compareTo( o2.getSimpleName()); + } + + }); + + manufacturerCheckList = new CheckList.Builder().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); + + impulseCheckList = new CheckList.Builder().build(); + impulseCheckList.setData(Arrays.asList(ImpulseClass.values())); + impulseCheckList.checkAll(); + impulseCheckList.getModel().addListDataListener( new ListDataListener() { + @Override + public void intervalAdded(ListDataEvent e) { + } + @Override + public void intervalRemoved(ListDataEvent e) { + } + @Override + public void contentsChanged(ListDataEvent e) { + MotorFilterPanel.this.filter.setExcludedImpulseClasses( impulseCheckList.getUncheckedItems() ); + onSelectionChanged(); + } + + }); + + sub.add(new JScrollPane(impulseCheckList.getList()), "grow,wrap"); + + JButton clearImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkNone")); + clearImpulse.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MotorFilterPanel.this.impulseCheckList.clearAll(); + + } + }); + sub.add(clearImpulse,"split 2"); + + JButton selectImpulse = new JButton(trans.get("TCMotorSelPan.btn.checkAll")); + selectImpulse.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MotorFilterPanel.this.impulseCheckList.checkAll(); + + } + }); + sub.add(selectImpulse,"wrap"); + + this.add(sub,"grow, wrap"); + + // Diameter selection + + sub = new JPanel(new MigLayout("fill")); + diameterTitleBorder = BorderFactory.createTitledBorder(trans.get("TCurveMotorCol.DIAMETER")); + GUIUtil.changeFontStyle(diameterTitleBorder, Font.BOLD); + sub.setBorder(diameterTitleBorder); + + JRadioButton showAllDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc1") ); + showAllDiametersButton.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showMode = SHOW_ALL; + MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.ALL); + saveMotorDiameterMatchPrefence(); + onSelectionChanged(); + } + }); + showAllDiametersButton.setSelected( showMode == SHOW_ALL); + sub.add(showAllDiametersButton, "growx,wrap"); + + JRadioButton showSmallerDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc2") ); + showSmallerDiametersButton.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showMode = SHOW_SMALLER; + MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.SMALLER); + saveMotorDiameterMatchPrefence(); + onSelectionChanged(); + } + }); + showSmallerDiametersButton.setSelected( showMode == SHOW_SMALLER); + sub.add(showSmallerDiametersButton, "growx,wrap"); + + JRadioButton showExactDiametersButton = new JRadioButton( trans.get("TCMotorSelPan.SHOW_DESCRIPTIONS.desc3") ); + showExactDiametersButton.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showMode = SHOW_EXACT; + MotorFilterPanel.this.filter.setDiameterControl(MotorRowFilter.DiameterFilterControl.EXACT); + saveMotorDiameterMatchPrefence(); + onSelectionChanged(); + } + }); + showExactDiametersButton.setSelected( showMode == SHOW_EXACT ); + sub.add(showExactDiametersButton, "growx,wrap"); + ButtonGroup comboGroup = new ButtonGroup(); + comboGroup.add( showAllDiametersButton ); + comboGroup.add( showSmallerDiametersButton ); + comboGroup.add( showExactDiametersButton ); + + { + sub.add( new JLabel("Minimum diameter"), "split 4"); + final DoubleModel minDiameter = new DoubleModel(0, UnitGroup.UNITS_MOTOR_DIMENSIONS, 0, .2); + minDiameter.addChangeListener( new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + MotorFilterPanel.this.filter.setMinimumDiameter(minDiameter.getValue()); + onSelectionChanged(); + } + }); + JSpinner spin = new JSpinner(minDiameter.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + sub.add(spin, "growx"); + + sub.add(new UnitSelector(minDiameter)); + sub.add(new BasicSlider(minDiameter.getSliderModel(0,0.5, mountDiameter)), "w 100lp, wrap"); + } + this.add(sub, "grow,wrap"); + + } + + public void setMotorMount( MotorMount mount ) { + filter.setMotorMount(mount); + onSelectionChanged(); + if ( mount == null ) { + // Disable diameter controls? + diameterTitleBorder.setTitle(trans.get("TCurveMotorCol.DIAMETER")); + mountDiameter.setValue(1.0); + } else { + mountDiameter.setValue(mount.getMotorMountDiameter()); + diameterTitleBorder.setTitle(trans.get("TCurveMotorCol.DIAMETER") + " " + + trans.get("TCMotorSelPan.lbl.Motormountdia") + " " + + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(mount.getMotorMountDiameter())); + + } + } + + private void saveMotorDiameterMatchPrefence() { + Application.getPreferences().putChoice("MotorDiameterMatch", showMode ); + } + + public abstract void onSelectionChanged(); + +} diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java new file mode 100644 index 000000000..8e5fb2ebb --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java @@ -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 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 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); + } + } + } + +} \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorRowFilter.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorRowFilter.java index a7961ec1d..568dd31e1 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorRowFilter.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorRowFilter.java @@ -35,7 +35,10 @@ class MotorRowFilter extends RowFilter { // things which can be changed to modify filter behavior - // Collection of strings which match text in the moto + // Limit motors based on minimum diameter + private Double minimumDiameter; + + // Collection of strings which match text in the motor private List searchTerms = Collections. emptyList(); // Limit motors based on diameter of the motor mount @@ -76,6 +79,14 @@ class MotorRowFilter extends RowFilter { } } + Double getMinimumDiameter() { + return minimumDiameter; + } + + void setMinimumDiameter(Double minimumDiameter) { + this.minimumDiameter = minimumDiameter; + } + DiameterFilterControl getDiameterControl() { return diameterControl; } @@ -130,6 +141,13 @@ class MotorRowFilter extends RowFilter { } private boolean filterByDiameter(ThrustCurveMotorSet m) { + + if ( minimumDiameter != null ) { + if ( m.getDiameter() <= minimumDiameter - 0.0015 ) { + return false; + } + } + if (diameter == null) { return true; } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java index 07d2ccc54..83b5e5b62 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java @@ -1,9 +1,7 @@ package net.sf.openrocket.gui.dialogs.motor.thrustcurve; -import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; -import java.awt.Cursor; import java.awt.Font; import java.awt.Paint; import java.awt.Rectangle; @@ -20,26 +18,24 @@ import java.util.List; import java.util.Set; import java.util.prefs.Preferences; -import javax.swing.BorderFactory; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; -import javax.swing.JLayeredPane; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSeparator; +import javax.swing.JTabbedPane; import javax.swing.JTable; -import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.ListSelectionModel; import javax.swing.RowSorter; import javax.swing.SortOrder; -import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; @@ -50,11 +46,9 @@ import javax.swing.table.TableRowSorter; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.database.motor.ThrustCurveMotorSet; import net.sf.openrocket.gui.components.StyledLabel; -import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.dialogs.motor.CloseableDialog; import net.sf.openrocket.gui.dialogs.motor.MotorSelector; import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; @@ -69,82 +63,50 @@ import net.sf.openrocket.util.BugException; import net.sf.openrocket.utils.MotorCorrelation; import org.jfree.chart.ChartColor; -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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelector { private static final Logger log = LoggerFactory.getLogger(ThrustCurveMotorSelectionPanel.class); - + private static final Translator trans = Application.getTranslator(); - + private static final double MOTOR_SIMILARITY_THRESHOLD = 0.95; - - private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50; - private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12; - + private static final Paint[] CURVE_COLORS = ChartColor.createDefaultPaintArray(); - - private static final Color NO_COMMENT_COLOR = Color.GRAY; - private static final Color WITH_COMMENT_COLOR = Color.BLACK; - + private static final ThrustCurveMotorComparator MOTOR_COMPARATOR = new ThrustCurveMotorComparator(); - + private List database; - + private CloseableDialog dialog = null; - - final ThrustCurveMotorDatabaseModel model; + + private final ThrustCurveMotorDatabaseModel model; private final JTable table; private final TableRowSorter sorter; private final MotorRowFilter rowFilter; - + private final JCheckBox hideSimilarBox; - + private final JTextField searchField; - String[] searchTerms = new String[0]; - - private final StyledLabel diameterLabel; + private final JLabel curveSelectionLabel; private final JComboBox curveSelectionBox; private final DefaultComboBoxModel curveSelectionModel; - - 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; - private final JComboBox delayBox; + private final MotorInformationPanel motorInformationPanel; + private final MotorFilterPanel motorFilterPanel; + private ThrustCurveMotor selectedMotor; private ThrustCurveMotorSet selectedMotorSet; private double selectedDelay; - + public ThrustCurveMotorSelectionPanel(MotorMount mount, String currentConfig) { this(); setMotorMountAndConfig( mount, currentConfig ); - + } /** * Sole constructor. @@ -155,169 +117,199 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec */ public ThrustCurveMotorSelectionPanel() { super(new MigLayout("fill", "[grow][]")); - + // Construct the database (adding the current motor if not in the db already) - List db; - db = Application.getThrustCurveMotorSetDatabase().getMotorSets(); - - database = db; - + database = Application.getThrustCurveMotorSetDatabase().getMotorSets(); + model = new ThrustCurveMotorDatabaseModel(database); rowFilter = new MotorRowFilter(model); - - //// GUI - - JPanel panel; - JLabel label; - - panel = new JPanel(new MigLayout("fill")); - this.add(panel, "grow"); - - - - // Selection label - //// Select rocket motor: - label = new StyledLabel(trans.get("TCMotorSelPan.lbl.Selrocketmotor"), Style.BOLD); - panel.add(label, "spanx, wrap para"); - - // Search field - //// Search: - label = new StyledLabel(trans.get("TCMotorSelPan.lbl.Search")); - panel.add(label, "split"); - - searchField = new JTextField(); - searchField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - update(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - update(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - update(); - } - - private void update() { - String text = searchField.getText().trim(); - String[] split = text.split("\\s+"); - rowFilter.setSearchTerms(Arrays.asList(split)); - sorter.sort(); - scrollSelectionVisible(); - } - }); - panel.add(searchField, "growx"); - + motorInformationPanel = new MotorInformationPanel(); + + //// MotorFilter { - // Find all the manufacturers: Set allManufacturers = new HashSet(); for (ThrustCurveMotorSet s : database) { allManufacturers.add(s.getManufacturer()); } - final MotorFilterPopupMenu popup = new MotorFilterPopupMenu(allManufacturers, rowFilter) { - + motorFilterPanel = new MotorFilterPanel(allManufacturers, rowFilter) { @Override public void onSelectionChanged() { sorter.sort(); scrollSelectionVisible(); } - }; - JButton manuFilter = new JButton(trans.get("TCMotorSelPan.btn.filter")); - manuFilter.addMouseListener(new MouseListener() { - + } + + //// GUI + JPanel panel = new JPanel(new MigLayout("fill","[][grow][]")); + this.add(panel, "grow"); + + //// Select thrust curve: + { + curveSelectionLabel = new JLabel(trans.get("TCMotorSelPan.lbl.Selectthrustcurve")); + panel.add(curveSelectionLabel); + + curveSelectionModel = new DefaultComboBoxModel(); + curveSelectionBox = new JComboBox(curveSelectionModel); + curveSelectionBox.setRenderer(new CurveSelectionRenderer(curveSelectionBox.getRenderer())); + curveSelectionBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Object value = curveSelectionBox.getSelectedItem(); + if (value != null) { + select(((MotorHolder) value).getMotor()); + } + } + }); + panel.add(curveSelectionBox, "growx"); + + final JPopupMenu popup = new JPopupMenu(); + popup.add( motorInformationPanel ); + JButton showDetailsButton = new JButton(trans.get("TCMotorSelPan.btn.details")); + showDetailsButton.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { } - @Override public void mousePressed(MouseEvent e) { } - @Override public void mouseReleased(MouseEvent e) { popup.show(e.getComponent(), e.getX(), e.getY()); } - @Override public void mouseEntered(MouseEvent e) { } - @Override public void mouseExited(MouseEvent e) { } - }); - panel.add(manuFilter, "gapleft para, wrap para"); - - + panel.add(showDetailsButton, "gapleft para, wrap"); + } - - // Motor selection table - table = new JTable(model); - - - // Set comparators and widths - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - sorter = new TableRowSorter(model); - for (int i = 0; i < ThrustCurveMotorColumns.values().length; i++) { - ThrustCurveMotorColumns column = ThrustCurveMotorColumns.values()[i]; - sorter.setComparator(i, column.getComparator()); - table.getColumnModel().getColumn(i).setPreferredWidth(column.getWidth()); - } - table.setRowSorter(sorter); - // force initial sort order to by diameter, total impulse, manufacturer + + // Ejection charge delay: { - RowSorter.SortKey[] sortKeys = { - new RowSorter.SortKey(ThrustCurveMotorColumns.DIAMETER.ordinal(), SortOrder.ASCENDING), - new RowSorter.SortKey(ThrustCurveMotorColumns.TOTAL_IMPULSE.ordinal(), SortOrder.ASCENDING), - new RowSorter.SortKey(ThrustCurveMotorColumns.MANUFACTURER.ordinal(), SortOrder.ASCENDING) - }; - sorter.setSortKeys(Arrays.asList(sortKeys)); + panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Ejectionchargedelay"))); + + delayBox = new JComboBox(); + delayBox.setEditable(true); + delayBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JComboBox cb = (JComboBox) e.getSource(); + String sel = (String) cb.getSelectedItem(); + //// None + if (sel.equalsIgnoreCase(trans.get("TCMotorSelPan.equalsIgnoreCase.None"))) { + selectedDelay = Motor.PLUGGED; + } else { + try { + selectedDelay = Double.parseDouble(sel); + } catch (NumberFormatException ignore) { + } + } + setDelays(false); + } + }); + panel.add(delayBox, "growx"); + //// (Number of seconds or \"None\") + panel.add(new StyledLabel(trans.get("TCMotorSelPan.lbl.NumberofsecondsorNone"), -3), "wrap para"); + setDelays(false); } - - sorter.setRowFilter(rowFilter); - - // Set selection and double-click listeners - table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - int row = table.getSelectedRow(); - if (row >= 0) { - row = table.convertRowIndexToModel(row); - ThrustCurveMotorSet motorSet = model.getMotorSet(row); - log.info(Markers.USER_MARKER, "Selected table row " + row + ": " + motorSet); - if (motorSet != selectedMotorSet) { - select(selectMotor(motorSet)); - } - } else { - log.info(Markers.USER_MARKER, "Selected table row " + row + ", nothing selected"); + + // Search field + { + //// Search: + StyledLabel label = new StyledLabel(trans.get("TCMotorSelPan.lbl.Search")); + panel.add(label); + searchField = new JTextField(); + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + update(); } + @Override + public void insertUpdate(DocumentEvent e) { + update(); + } + @Override + public void removeUpdate(DocumentEvent e) { + update(); + } + private void update() { + String text = searchField.getText().trim(); + String[] split = text.split("\\s+"); + rowFilter.setSearchTerms(Arrays.asList(split)); + sorter.sort(); + scrollSelectionVisible(); + } + }); + panel.add(searchField, "span, growx, wrap"); + } + + //// Motor selection table + { + table = new JTable(model); + + // Set comparators and widths + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + sorter = new TableRowSorter(model); + for (int i = 0; i < ThrustCurveMotorColumns.values().length; i++) { + ThrustCurveMotorColumns column = ThrustCurveMotorColumns.values()[i]; + sorter.setComparator(i, column.getComparator()); + table.getColumnModel().getColumn(i).setPreferredWidth(column.getWidth()); } - }); - table.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { - if (dialog != null) { - dialog.close(true); + table.setRowSorter(sorter); + // force initial sort order to by diameter, total impulse, manufacturer + { + RowSorter.SortKey[] sortKeys = { + new RowSorter.SortKey(ThrustCurveMotorColumns.DIAMETER.ordinal(), SortOrder.ASCENDING), + new RowSorter.SortKey(ThrustCurveMotorColumns.TOTAL_IMPULSE.ordinal(), SortOrder.ASCENDING), + new RowSorter.SortKey(ThrustCurveMotorColumns.MANUFACTURER.ordinal(), SortOrder.ASCENDING) + }; + sorter.setSortKeys(Arrays.asList(sortKeys)); + } + + sorter.setRowFilter(rowFilter); + + // Set selection and double-click listeners + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + int row = table.getSelectedRow(); + if (row >= 0) { + row = table.convertRowIndexToModel(row); + ThrustCurveMotorSet motorSet = model.getMotorSet(row); + log.info(Markers.USER_MARKER, "Selected table row " + row + ": " + motorSet); + if (motorSet != selectedMotorSet) { + select(selectMotor(motorSet)); + } + } else { + log.info(Markers.USER_MARKER, "Selected table row " + row + ", nothing selected"); } } - } - }); - - - JScrollPane scrollpane = new JScrollPane(); - scrollpane.setViewportView(table); - panel.add(scrollpane, "grow, width :500:, height :300:, spanx, wrap para"); - + }); + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { + if (dialog != null) { + dialog.close(true); + } + } + } + }); + + JScrollPane scrollpane = new JScrollPane(); + scrollpane.setViewportView(table); + panel.add(scrollpane, "grow, width :500:, height :300:, spanx, wrap para"); + + } + + //// Hide used motor files { final JCheckBox hideUsedBox = new JCheckBox(trans.get("TCMotorSelPan.checkbox.hideUsed")); GUIUtil.changeFontSize(hideUsedBox, -1); @@ -331,230 +323,53 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec }); panel.add(hideUsedBox, "gapleft para, spanx, growx, wrap para"); } - - scrollSelectionVisible(); - + //// Hide very similar thrust curves - hideSimilarBox = new JCheckBox(trans.get("TCMotorSelPan.checkbox.hideSimilar")); - GUIUtil.changeFontSize(hideSimilarBox, -1); - hideSimilarBox.setSelected(Application.getPreferences().getBoolean(net.sf.openrocket.startup.Preferences.MOTOR_HIDE_SIMILAR, true)); - hideSimilarBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Application.getPreferences().putBoolean(net.sf.openrocket.startup.Preferences.MOTOR_HIDE_SIMILAR, hideSimilarBox.isSelected()); - updateData(); - } - }); - panel.add(hideSimilarBox, "gapleft para, spanx, growx, wrap para"); - - // Motor mount diameter label - //// Motor mount diameter: - diameterLabel = new StyledLabel(); - panel.add(label, "gapright 30lp, spanx, split"); - + { + hideSimilarBox = new JCheckBox(trans.get("TCMotorSelPan.checkbox.hideSimilar")); + GUIUtil.changeFontSize(hideSimilarBox, -1); + hideSimilarBox.setSelected(Application.getPreferences().getBoolean(net.sf.openrocket.startup.Preferences.MOTOR_HIDE_SIMILAR, true)); + hideSimilarBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Application.getPreferences().putBoolean(net.sf.openrocket.startup.Preferences.MOTOR_HIDE_SIMILAR, hideSimilarBox.isSelected()); + updateData(); + } + }); + panel.add(hideSimilarBox, "gapleft para, spanx, growx, wrap para"); + } + // Vertical split this.add(panel, "grow"); this.add(new JSeparator(JSeparator.VERTICAL), "growy, gap para para"); - panel = new JPanel(new MigLayout("fill")); - - - - // Thrust curve selection - //// Select thrust curve: - curveSelectionLabel = new JLabel(trans.get("TCMotorSelPan.lbl.Selectthrustcurve")); - panel.add(curveSelectionLabel); - - curveSelectionModel = new DefaultComboBoxModel(); - curveSelectionBox = new JComboBox(curveSelectionModel); - curveSelectionBox.setRenderer(new CurveSelectionRenderer(curveSelectionBox.getRenderer())); - curveSelectionBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Object value = curveSelectionBox.getSelectedItem(); - if (value != null) { - select(((MotorHolder) value).getMotor()); - } - } - }); - panel.add(curveSelectionBox, "growx, wrap para"); - - // Ejection charge delay: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Ejectionchargedelay"))); - - delayBox = new JComboBox(); - delayBox.setEditable(true); - delayBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JComboBox cb = (JComboBox) e.getSource(); - String sel = (String) cb.getSelectedItem(); - //// None - if (sel.equalsIgnoreCase(trans.get("TCMotorSelPan.equalsIgnoreCase.None"))) { - selectedDelay = Motor.PLUGGED; - } else { - try { - selectedDelay = Double.parseDouble(sel); - } catch (NumberFormatException ignore) { - } - } - setDelays(false); - } - }); - panel.add(delayBox, "growx, wrap rel"); - //// (Number of seconds or \"None\") - panel.add(new StyledLabel(trans.get("TCMotorSelPan.lbl.NumberofsecondsorNone"), -3), "skip, wrap para"); - setDelays(false); - - - panel.add(new JSeparator(), "spanx, growx, wrap para"); - - - - // Thrust curve info - //// Total impulse: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Totalimpulse"))); - totalImpulseLabel = new JLabel(); - panel.add(totalImpulseLabel, "split"); - classificationLabel = new JLabel(); - classificationLabel.setEnabled(false); // Gray out - panel.add(classificationLabel, "gapleft unrel, wrap"); - - //// Avg. thrust: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Avgthrust"))); - avgThrustLabel = new JLabel(); - panel.add(avgThrustLabel, "wrap"); - - //// Max. thrust: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Maxthrust"))); - maxThrustLabel = new JLabel(); - panel.add(maxThrustLabel, "wrap"); - - //// Burn time: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Burntime"))); - burnTimeLabel = new JLabel(); - panel.add(burnTimeLabel, "wrap"); - - //// Launch mass: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Launchmass"))); - launchMassLabel = new JLabel(); - panel.add(launchMassLabel, "wrap"); - - //// Empty mass: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Emptymass"))); - emptyMassLabel = new JLabel(); - panel.add(emptyMassLabel, "wrap"); - - //// Data points: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Datapoints"))); - dataPointsLabel = new JLabel(); - panel.add(dataPointsLabel, "wrap para"); - - if (System.getProperty("openrocket.debug.motordigest") != null) { - //// Digest: - panel.add(new JLabel(trans.get("TCMotorSelPan.lbl.Digest"))); - digestLabel = new JLabel(); - panel.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); - scrollpane = new JScrollPane(comment); - panel.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 - List motors = getFilteredCurves(); - ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(motors, - motors.indexOf(selectedMotor), - SwingUtilities.getWindowAncestor(ThrustCurveMotorSelectionPanel.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); - - - panel.add(layer, "width 300:300:, height 180:180:, grow, spanx"); - - this.add(panel, "grow"); + JTabbedPane rightSide = new JTabbedPane(); + rightSide.add(trans.get("TCMotorSelPan.btn.filter"), motorFilterPanel); + rightSide.add(trans.get("TCMotorSelPan.btn.details"), motorInformationPanel); + + this.add(rightSide); + // Update the panel data + scrollSelectionVisible(); updateData(); setDelays(false); - + } - + public void setMotorMountAndConfig( MotorMount mount, String currentConfig ) { double diameter = 0; + if ( mount != null ) { + diameter = mount.getMotorMountDiameter(); + } + if (currentConfig != null && mount != null) { MotorConfiguration motorConf = mount.getMotorConfiguration().get(currentConfig); selectedMotor = (ThrustCurveMotor) motorConf.getMotor(); selectedDelay = motorConf.getEjectionDelay(); diameter = mount.getMotorMountDiameter(); } - + // If current motor is not found in db, add a new ThrustCurveMotorSet containing it if (selectedMotor != null) { for (ThrustCurveMotorSet motorSet : database) { @@ -575,46 +390,43 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec updateData(); setDelays(true); - - diameterLabel.setText(trans.get("TCMotorSelPan.lbl.Motormountdia") + " " + - UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(0)); - - rowFilter.setMotorMount(mount); + + motorFilterPanel.setMotorMount(mount); scrollSelectionVisible(); } - + @Override public Motor getSelectedMotor() { return selectedMotor; } - - + + @Override public double getSelectedDelay() { return selectedDelay; } - - + + @Override public JComponent getDefaultFocus() { return searchField; } - + @Override public void selectedMotor(Motor motorSelection) { if (!(motorSelection instanceof ThrustCurveMotor)) { log.error("Received argument that was not ThrustCurveMotor: " + motorSelection); return; } - + ThrustCurveMotor motor = (ThrustCurveMotor) motorSelection; ThrustCurveMotorSet set = findMotorSet(motor); if (set == null) { log.error("Could not find set for motor:" + motorSelection); return; } - + // Store selected motor in preferences node, set all others to false Preferences prefs = ((SwingPreferences) Application.getPreferences()).getNode(net.sf.openrocket.startup.Preferences.PREFERRED_THRUST_CURVE_MOTOR_NODE); for (ThrustCurveMotor m : set.getMotors()) { @@ -622,34 +434,27 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec prefs.putBoolean(digest, m == motor); } } - + public void setCloseableDialog(CloseableDialog dialog) { this.dialog = dialog; } - - - - private void changeLabelFont(ValueAxis axis, float size) { - Font font = axis.getTickLabelFont(); - font = font.deriveFont(font.getSize2D() + size); - axis.setTickLabelFont(font); - } - - + + + /** * Called when a different motor is selected from within the panel. */ private void select(ThrustCurveMotor motor) { if (selectedMotor == motor) return; - + ThrustCurveMotorSet set = findMotorSet(motor); if (set == null) { throw new BugException("Could not find motor from database, motor=" + motor); } - + boolean updateDelays = (selectedMotorSet != set); - + selectedMotor = motor; selectedMotorSet = set; updateData(); @@ -657,46 +462,30 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec setDelays(true); } } - - + + private void updateData() { - + if (selectedMotorSet == null) { // No motor selected curveSelectionModel.removeAllElements(); curveSelectionBox.setEnabled(false); curveSelectionLabel.setEnabled(false); - 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()); + motorInformationPanel.clearData(); return; } - - + // Check which thrust curves to display List motors = getFilteredCurves(); final int index = motors.indexOf(selectedMotor); - - + // Update the thrust curve selection box curveSelectionModel.removeAllElements(); for (int i = 0; i < motors.size(); i++) { curveSelectionModel.addElement(new MotorHolder(motors.get(i), i)); } curveSelectionBox.setSelectedIndex(index); - + if (motors.size() > 1) { curveSelectionBox.setEnabled(true); curveSelectionLabel.setEnabled(true); @@ -705,60 +494,11 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec curveSelectionLabel.setEnabled(false); } - - // 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(); - - 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, getColor(i)); - } - - plot.setDataset(dataset); + motorInformationPanel.updateData(motors, selectedMotor); + } - - private List getFilteredCurves() { + + List getFilteredCurves() { List motors = selectedMotorSet.getMotors(); if (hideSimilarBox.isSelected()) { List filtered = new ArrayList(motors.size()); @@ -768,7 +508,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec filtered.add(m); continue; } - + double similarity = MotorCorrelation.similarity(selectedMotor, m); log.debug("Motor similarity: " + similarity); if (similarity < MOTOR_SIMILARITY_THRESHOLD) { @@ -777,28 +517,13 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec } motors = filtered; } - + Collections.sort(motors, MOTOR_COMPARATOR); - + return motors; } - - - 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); - } - + + private void scrollSelectionVisible() { if (selectedMotorSet != null) { int index = table.convertRowIndexToView(model.getIndex(selectedMotorSet)); @@ -809,13 +534,13 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec table.scrollRectToVisible(rect); } } - - + + public static Color getColor(int index) { return (Color) CURVE_COLORS[index % CURVE_COLORS.length]; } - - + + /** * Find the ThrustCurveMotorSet that contains a motor. * @@ -828,12 +553,12 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec return set; } } - + return null; } - - - + + + /** * Select the default motor from this ThrustCurveMotorSet. This uses primarily motors * that the user has previously used, and secondarily a heuristic method of selecting which @@ -849,8 +574,8 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec if (set.getMotorCount() == 1) { return set.getMotors().get(0); } - - + + // Find which motor has been used the most recently List list = set.getMotors(); Preferences prefs = ((SwingPreferences) Application.getPreferences()).getNode(net.sf.openrocket.startup.Preferences.PREFERRED_THRUST_CURVE_MOTOR_NODE); @@ -860,13 +585,13 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec return m; } } - + // No motor has been used Collections.sort(list, MOTOR_COMPARATOR); return list.get(0); } - - + + /** * Set the values in the delay combo box. If reset is true * then sets the selected value as the value closest to selectedDelay, otherwise @@ -874,25 +599,25 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec */ private void setDelays(boolean reset) { if (selectedMotor == null) { - + //// None delayBox.setModel(new DefaultComboBoxModel(new String[] { trans.get("TCMotorSelPan.delayBox.None") })); delayBox.setSelectedIndex(0); - + } else { - + List delays = selectedMotorSet.getDelays(); String[] delayStrings = new String[delays.size()]; double currentDelay = selectedDelay; // Store current setting locally - + for (int i = 0; i < delays.size(); i++) { //// None delayStrings[i] = ThrustCurveMotor.getDelayString(delays.get(i), trans.get("TCMotorSelPan.delayBox.None")); } delayBox.setModel(new DefaultComboBoxModel(delayStrings)); - + if (reset) { - + // Find and set the closest value double closest = Double.NaN; for (int i = 0; i < delays.size(); i++) { @@ -908,60 +633,43 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec } else { delayBox.setSelectedItem("None"); } - + } else { - + selectedDelay = currentDelay; //// None delayBox.setSelectedItem(ThrustCurveMotor.getDelayString(currentDelay, trans.get("TCMotorSelPan.delayBox.None"))); - + } - + } } - - - - + ////////////////////// - - + + private class CurveSelectionRenderer implements ListCellRenderer { - + private final ListCellRenderer renderer; - + public CurveSelectionRenderer(ListCellRenderer renderer) { this.renderer = renderer; } - + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - + Component c = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof MotorHolder) { MotorHolder m = (MotorHolder) value; c.setForeground(getColor(m.getIndex())); } - + return c; } - - } - - - /** - * 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); - } - } + } + + }