X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fnet%2Fsf%2Fopenrocket%2Fgui%2Fdialogs%2FMotorChooserDialog.java;fp=src%2Fnet%2Fsf%2Fopenrocket%2Fgui%2Fdialogs%2FMotorChooserDialog.java;h=3a5a154cac5aa45f501d99433a8305505e2f3442;hb=0d0afe488300aca47d09ac7651f8185190afb21f;hp=0000000000000000000000000000000000000000;hpb=6afc62224f6f7e581b1d321e125ed97a6ec77dc1;p=debian%2Fopenrocket diff --git a/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java b/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java new file mode 100644 index 00000000..3a5a154c --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java @@ -0,0 +1,699 @@ +package net.sf.openrocket.gui.dialogs; + + +import java.awt.Dialog; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.RowFilter; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.database.Databases; +import net.sf.openrocket.gui.components.ResizeLabel; +import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.GUIUtil; +import net.sf.openrocket.util.Prefs; + +public class MotorChooserDialog extends JDialog { + + private static final int SHOW_ALL = 0; + private static final int SHOW_SMALLER = 1; + private static final int SHOW_EXACT = 2; + private static final String[] SHOW_DESCRIPTIONS = { + "Show all motors", + "Show motors with diameter less than that of the motor mount", + "Show motors with diameter equal to that of the motor mount" + }; + private static final int SHOW_MAX = 2; + + private final JTextField searchField; + private String[] searchTerms = new String[0]; + + private final double diameter; + + private Motor selectedMotor = null; + private double selectedDelay = 0; + + private JTable table; + private TableRowSorter sorter; + private JComboBox delayBox; + private MotorDatabaseModel model; + + private boolean okClicked = false; + + + public MotorChooserDialog(double diameter) { + this(null,5,diameter,null); + } + + public MotorChooserDialog(Motor current, double delay, double diameter) { + this(current,delay,diameter,null); + } + + public MotorChooserDialog(Motor current, double delay, double diameter, Window owner) { + super(owner, "Select a rocket motor", Dialog.ModalityType.APPLICATION_MODAL); + + JButton button; + + this.selectedMotor = current; + this.selectedDelay = delay; + this.diameter = diameter; + + JPanel panel = new JPanel(new MigLayout("fill", "[grow][]")); + + // Label + JLabel label = new JLabel("Select a rocket motor:"); + label.setFont(label.getFont().deriveFont(Font.BOLD)); + panel.add(label,"growx"); + + label = new JLabel("Motor mount diameter: " + + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter)); + panel.add(label,"gapleft para, wrap paragraph"); + + + // Diameter selection + JComboBox combo = new JComboBox(SHOW_DESCRIPTIONS); + combo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JComboBox cb = (JComboBox) e.getSource(); + int sel = cb.getSelectedIndex(); + if ((sel < 0) || (sel > SHOW_MAX)) + sel = SHOW_ALL; + switch (sel) { + case SHOW_ALL: + sorter.setRowFilter(new MotorRowFilterAll()); + break; + + case SHOW_SMALLER: + sorter.setRowFilter(new MotorRowFilterSmaller()); + break; + + case SHOW_EXACT: + sorter.setRowFilter(new MotorRowFilterExact()); + break; + + default: + assert(false) : "Should not occur."; + } + Prefs.putChoise("MotorDiameterMatch", sel); + setSelectionVisible(); + } + }); + panel.add(combo,"growx 1000"); + + + + label = new JLabel("Search:"); + panel.add(label, "gapleft para, split 2"); + + 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+"); + ArrayList list = new ArrayList(); + for (String s: split) { + s = s.trim().toLowerCase(); + if (s.length() > 0) { + list.add(s); + } + } + searchTerms = list.toArray(new String[0]); + sorter.sort(); + } + }); + panel.add(searchField, "growx 1, wrap"); + + + + // Table, overridden to show meaningful tooltip texts + model = new MotorDatabaseModel(current); + table = new JTable(model) { + @Override + public String getToolTipText(MouseEvent e) { + java.awt.Point p = e.getPoint(); + int colIndex = columnAtPoint(p); + int viewRow = rowAtPoint(p); + if (viewRow < 0) + return null; + int rowIndex = convertRowIndexToModel(viewRow); + Motor motor = model.getMotor(rowIndex); + + if (colIndex < 0 || colIndex >= MotorColumns.values().length) + return null; + + return MotorColumns.values()[colIndex].getToolTipText(motor); + } + }; + + // Set comparators and widths + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + sorter = new TableRowSorter(model); + for (int i=0; i < MotorColumns.values().length; i++) { + MotorColumns column = MotorColumns.values()[i]; + sorter.setComparator(i, column.getComparator()); + table.getColumnModel().getColumn(i).setPreferredWidth(column.getWidth()); + } + table.setRowSorter(sorter); + + // 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); + Motor m = model.getMotor(row); + if (!m.equals(selectedMotor)) { + selectedMotor = model.getMotor(row); + setDelays(true); // Reset delay times + } + } + } + }); + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { + okClicked = true; + MotorChooserDialog.this.setVisible(false); + } + } + }); + // (Current selection and scrolling performed later) + + JScrollPane scrollpane = new JScrollPane(); + scrollpane.setViewportView(table); + panel.add(scrollpane,"spanx, grow, width :700:, height :300:, wrap paragraph"); + + + // Ejection delay + panel.add(new JLabel("Select ejection charge delay:"), "spanx, split 3, gap rel"); + + 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(); + if (sel.equalsIgnoreCase("None")) { + selectedDelay = Motor.PLUGGED; + } else { + try { + selectedDelay = Double.parseDouble(sel); + } catch (NumberFormatException ignore) { } + } + setDelays(false); + } + }); + panel.add(delayBox,"gapright unrel"); + panel.add(new ResizeLabel("(Number of seconds or \"None\")", -1), "wrap para"); + setDelays(false); + + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + okClicked = true; + MotorChooserDialog.this.setVisible(false); + } + }); + panel.add(okButton,"spanx, split, tag ok"); + + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MotorChooserDialog.this.setVisible(false); + } + }); + panel.add(button,"tag cancel"); + + + // Sets the filter: + int showMode = Prefs.getChoise("MotorDiameterMatch", SHOW_MAX, SHOW_EXACT); + combo.setSelectedIndex(showMode); + + + this.add(panel); + this.pack(); +// this.setAlwaysOnTop(true); + + GUIUtil.setDefaultButton(okButton); + GUIUtil.installEscapeCloseOperation(this); + this.setLocationByPlatform(true); + + // Table can be scrolled only after pack() has been called + setSelectionVisible(); + + // Focus the search field + searchField.grabFocus(); + } + + private void setSelectionVisible() { + if (selectedMotor != null) { + int index = table.convertRowIndexToView(model.getIndex(selectedMotor)); + table.getSelectionModel().setSelectionInterval(index, index); + Rectangle rect = table.getCellRect(index, 0, true); + rect = new Rectangle(rect.x,rect.y-100,rect.width,rect.height+200); + table.scrollRectToVisible(rect); + } + } + + + /** + * Set the values in the delay combo box. If reset is true + * then sets the selected value as the value closest to selectedDelay, otherwise + * leaves selection alone. + */ + private void setDelays(boolean reset) { + if (selectedMotor == null) { + + delayBox.setModel(new DefaultComboBoxModel(new String[] { "None" })); + delayBox.setSelectedIndex(0); + + } else { + + double[] delays = selectedMotor.getStandardDelays(); + String[] delayStrings = new String[delays.length]; + double currentDelay = selectedDelay; // Store current setting locally + + for (int i=0; i < delays.length; i++) { + delayStrings[i] = Motor.getDelayString(delays[i], "None"); + } + delayBox.setModel(new DefaultComboBoxModel(delayStrings)); + + if (reset) { + + // Find and set the closest value + double closest = Double.NaN; + for (int i=0; i < delays.length; i++) { + // if-condition to always become true for NaN + if (!(Math.abs(delays[i] - currentDelay) > + Math.abs(closest - currentDelay))) { + closest = delays[i]; + } + } + if (!Double.isNaN(closest)) { + selectedDelay = closest; + delayBox.setSelectedItem(Motor.getDelayString(closest, "None")); + } else { + delayBox.setSelectedItem("None"); + } + + } else { + + selectedDelay = currentDelay; + delayBox.setSelectedItem(Motor.getDelayString(currentDelay, "None")); + + } + + } + } + + + + public Motor getSelectedMotor() { + if (!okClicked) + return null; + return selectedMotor; + } + + + public double getSelectedDelay() { + return selectedDelay; + } + + + + + //////////////// JTable elements //////////////// + + + /** + * Enum defining the table columns. + */ + private enum MotorColumns { + MANUFACTURER("Manufacturer",100) { + @Override + public String getValue(Motor m) { + return m.getManufacturer(); + } +// @Override +// public String getToolTipText(Motor m) { +// return "" + m.getDescription().replace((CharSequence)"\n", "
"); +// } + @Override + public Comparator getComparator() { + return Collator.getInstance(); + } + }, + DESIGNATION("Designation") { + @Override + public String getValue(Motor m) { + return m.getDesignation(); + } +// @Override +// public String getToolTipText(Motor m) { +// return "" + m.getDescription().replace((CharSequence)"\n", "
"); +// } + @Override + public Comparator getComparator() { + return Motor.getDesignationComparator(); + } + }, + TYPE("Type") { + @Override + public String getValue(Motor m) { + return m.getMotorType().getName(); + } +// @Override +// public String getToolTipText(Motor m) { +// return m.getMotorType().getDescription(); +// } + @Override + public Comparator getComparator() { + return Collator.getInstance(); + } + }, + DIAMETER("Diameter") { + @Override + public String getValue(Motor m) { + return UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit( + m.getDiameter()); + } + @Override + public Comparator getComparator() { + return getNumericalComparator(); + } + }, + LENGTH("Length") { + @Override + public String getValue(Motor m) { + return UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit( + m.getLength()); + } + @Override + public Comparator getComparator() { + return getNumericalComparator(); + } + }, + IMPULSE("Impulse") { + @Override + public String getValue(Motor m) { + return UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit( + m.getTotalImpulse()); + } + @Override + public Comparator getComparator() { + return getNumericalComparator(); + } + }, + TIME("Burn time") { + @Override + public String getValue(Motor m) { + return UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().toStringUnit( + m.getAverageTime()); + } + @Override + public Comparator getComparator() { + return getNumericalComparator(); + } + }; + + + private final String title; + private final int width; + + MotorColumns(String title) { + this(title, 50); + } + + MotorColumns(String title, int width) { + this.title = title; + this.width = width; + } + + + public abstract String getValue(Motor m); + public abstract Comparator getComparator(); + + public String getTitle() { + return title; + } + + public int getWidth() { + return width; + } + + public String getToolTipText(Motor m) { + String tip = ""; + tip += "" + m.toString() + ""; + tip += " (" + m.getMotorType().getDescription() + ")

"; + + String desc = m.getDescription().trim(); + if (desc.length() > 0) { + tip += "" + desc.replace("\n", "
") + "


"; + } + + tip += ("Diameter: " + + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getDiameter()) + + "
"); + tip += ("Length: " + + UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getLength()) + + "
"); + tip += ("Maximum thrust: " + + UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getMaxThrust()) + + "
"); + tip += ("Average thrust: " + + UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getAverageThrust()) + + "
"); + tip += ("Burn time: " + + UnitGroup.UNITS_SHORT_TIME.getDefaultUnit() + .toStringUnit(m.getAverageTime()) + "
"); + tip += ("Total impulse: " + + UnitGroup.UNITS_IMPULSE.getDefaultUnit() + .toStringUnit(m.getTotalImpulse()) + "
"); + tip += ("Launch mass: " + + UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getMass(0)) + + "
"); + tip += ("Empty mass: " + + UnitGroup.UNITS_MASS.getDefaultUnit() + .toStringUnit(m.getMass(Double.MAX_VALUE))); + return tip; + } + + } + + + /** + * The JTable model. Includes an extra motor, given in the constructor, + * if it is not already in the database. + */ + private class MotorDatabaseModel extends AbstractTableModel { + private final Motor extra; + + public MotorDatabaseModel(Motor current) { + if (Databases.MOTOR.contains(current)) + extra = null; + else + extra = current; + } + + @Override + public int getColumnCount() { + return MotorColumns.values().length; + } + + @Override + public int getRowCount() { + if (extra == null) + return Databases.MOTOR.size(); + else + return Databases.MOTOR.size()+1; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + MotorColumns column = getColumn(columnIndex); + if (extra == null) { + return column.getValue(Databases.MOTOR.get(rowIndex)); + } else { + if (rowIndex == 0) + return column.getValue(extra); + else + return column.getValue(Databases.MOTOR.get(rowIndex - 1)); + } + } + + @Override + public String getColumnName(int columnIndex) { + return getColumn(columnIndex).getTitle(); + } + + + public Motor getMotor(int rowIndex) { + if (extra == null) { + return Databases.MOTOR.get(rowIndex); + } else { + if (rowIndex == 0) + return extra; + else + return Databases.MOTOR.get(rowIndex-1); + } + } + + public int getIndex(Motor m) { + if (extra == null) { + return Databases.MOTOR.indexOf(m); + } else { + if (extra.equals(m)) + return 0; + else + return Databases.MOTOR.indexOf(m)+1; + } + } + + private MotorColumns getColumn(int index) { + return MotorColumns.values()[index]; + } + } + + + //////// Row filters + + /** + * Abstract adapter class. + */ + private abstract class MotorRowFilter extends RowFilter { + @Override + public boolean include(RowFilter.Entry entry) { + int index = entry.getIdentifier(); + Motor m = model.getMotor(index); + return filterByDiameter(m) && filterByString(m); + } + + public abstract boolean filterByDiameter(Motor m); + + + public boolean filterByString(Motor m) { + main: for (String s : searchTerms) { + for (MotorColumns col : MotorColumns.values()) { + String str = col.getValue(m).toLowerCase(); + if (str.indexOf(s) >= 0) + continue main; + } + return false; + } + return true; + } + } + + /** + * Show all motors. + */ + private class MotorRowFilterAll extends MotorRowFilter { + @Override + public boolean filterByDiameter(Motor m) { + return true; + } + } + + /** + * Show motors smaller than the mount. + */ + private class MotorRowFilterSmaller extends MotorRowFilter { + @Override + public boolean filterByDiameter(Motor m) { + return (m.getDiameter() <= diameter + 0.0004); + } + } + + /** + * Show motors that fit the mount. + */ + private class MotorRowFilterExact extends MotorRowFilter { + @Override + public boolean filterByDiameter(Motor m) { + return ((m.getDiameter() <= diameter + 0.0004) && + (m.getDiameter() >= diameter - 0.0015)); + } + } + + + private static Comparator numericalComparator = null; + private static Comparator getNumericalComparator() { + if (numericalComparator == null) + numericalComparator = new NumericalComparator(); + return numericalComparator; + } + + private static class NumericalComparator implements Comparator { + private Pattern pattern = + Pattern.compile("^\\s*([0-9]*[.,][0-9]+|[0-9]+[.,]?[0-9]*).*?$"); + private Collator collator = null; + @Override + public int compare(String s1, String s2) { + Matcher m1, m2; + + m1 = pattern.matcher(s1); + m2 = pattern.matcher(s2); + if (m1.find() && m2.find()) { + double d1 = Double.parseDouble(m1.group(1)); + double d2 = Double.parseDouble(m2.group(1)); + + return (int)((d1-d2)*1000); + } + + if (collator == null) + collator = Collator.getInstance(Locale.US); + return collator.compare(s1, s2); + } + } + +}