From 3d73e9d9eb5fbdc01f695272db38af8654b79203 Mon Sep 17 00:00:00 2001 From: Doug Pedrick Date: Sat, 5 May 2012 02:23:14 +0000 Subject: [PATCH] Initial GUI for editing and saving component presets. See ComponentPresetPanel.main() for standalone execution. --- core/resources/l10n/messages.properties | 45 +- .../gui/preset/ComponentPresetPanel.java | 229 +++ .../gui/preset/ImagePreviewPanel.java | 105 ++ .../openrocket/gui/preset/MaterialModel.java | 120 ++ .../gui/preset/PresetEditorDialog.java | 1636 +++++++++++++++++ .../gui/preset/PresetResultListener.java | 11 + .../sf/openrocket/gui/print/PrintUnit.java | 11 +- .../sf/openrocket/gui/util/FileHelper.java | 89 +- .../sf/openrocket/preset/ComponentPreset.java | 7 +- .../preset/xml/BaseComponentDTO.java | 9 +- .../preset/xml/OpenRocketComponentSaver.java | 55 +- .../rocketcomponent/Transition.java | 286 +-- 12 files changed, 2376 insertions(+), 227 deletions(-) create mode 100644 core/src/net/sf/openrocket/gui/preset/ComponentPresetPanel.java create mode 100644 core/src/net/sf/openrocket/gui/preset/ImagePreviewPanel.java create mode 100644 core/src/net/sf/openrocket/gui/preset/MaterialModel.java create mode 100644 core/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java create mode 100644 core/src/net/sf/openrocket/gui/preset/PresetResultListener.java diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 4978126cb..79f7a90a1 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -25,7 +25,7 @@ RocketActions.DelCompAct.Delete = Delete RocketActions.DelCompAct.ttip.Delete = Delete the selected component. RocketActions.DelSimuAct.Delete = Delete RocketActions.DelSimuAct.ttip.Delete = Delete the selected simulation. -RocketActions.DelAct.Delete = Delete +RocketActions.DelAct.Delete = Delete RocketActions.DelAct.ttip.Delete = Delete the selected component or simulation. RocketActions.CutAction.Cut = Cut RocketActions.CutAction.ttip.Cut = Cut this component or simulation to the clipboard and remove from this design @@ -47,7 +47,7 @@ RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards. RocketPanel.FigTypeAct.Sideview = Side view RocketPanel.FigTypeAct.ttip.Sideview = Side view RocketPanel.FigTypeAct.Backview = Back view -RocketPanel.FigTypeAct.ttip.Backview = Rear view +RocketPanel.FigTypeAct.ttip.Backview = Rear view RocketPanel.FigViewAct.2D = 2D View RocketPanel.FigViewAct.ttip.2D = 2D View RocketPanel.FigViewAct.3D = 3D View @@ -95,6 +95,7 @@ filetypes.pdf = PDF files (*.pdf) BasicFrame.SimpleFileFilter1 = All rocket designs (*.ork; *.rkt) BasicFrame.SimpleFileFilter2 = OpenRocket designs (*.ork) BasicFrame.SimpleFileFilter3 = RockSim designs (*.rkt) +BasicFrame.SimpleFileFilter4 = OpenRocket presets (*.orc) filetypes.images = Image files @@ -106,9 +107,9 @@ AboutDialog.lbl.version = Version ! - AboutDialog.lbl.translatorWebsite is a URL to the translator / group (may be empty) ! - AboutDialog.lbl.translatorIcon is the file name of an icon under pix/translators/ (may be empty) AboutDialog.lbl.translation = English translation by: -AboutDialog.lbl.translator = -AboutDialog.lbl.translatorWebsite = -AboutDialog.lbl.translatorIcon = +AboutDialog.lbl.translator = +AboutDialog.lbl.translatorWebsite = +AboutDialog.lbl.translatorIcon = ! Print dialog @@ -219,7 +220,7 @@ pref.dlg.but.reset = Reset pref.dlg.but.checknow = Check now pref.dlg.but.defaultmetric = Default metric pref.dlg.but.defaultimperial = Default imperial -pref.dlg.title.Preferences = Preferences +pref.dlg.title.Preferences = Preferences pref.dlg.tab.Units = Units pref.dlg.tab.Defaultunits = Default units pref.dlg.tab.Materials = Materials @@ -338,7 +339,7 @@ simedtdlg.lbl.ttip.Timestep1 = The time between simulation steps.
A sma simedtdlg.lbl.ttip.Timestep2 = The 4th order simulation method is quite accurate with a time step of simedtdlg.but.ttip.resettodefault = Reset the time step to its default value ( simedtdlg.border.Simlist = Simulator listeners -simedtdlg.txt.longA1 = Simulation listeners is an advanced feature that allows user-written code to listen to and interact with the simulation. +simedtdlg.txt.longA1 = Simulation listeners is an advanced feature that allows user-written code to listen to and interact with the simulation. simedtdlg.txt.longA2 = For details on writing simulation listeners, see the OpenRocket technical documentation. simedtdlg.lbl.Curlist = Current listeners: simedtdlg.lbl.Addsimlist = Add simulation listener @@ -432,7 +433,7 @@ SimExpPan.Fileexists.desc1 = File \" SimExpPan.Fileexists.desc2 = \" exists. Overwrite? SimExpPan.Fileexists.title = File exists SimExpPan.ExportingVar.desc1 = Exporting 1 variable out of -SimExpPan.ExportingVar.desc2 = Exporting +SimExpPan.ExportingVar.desc2 = Exporting SimExpPan.ExportingVar.desc3 = variables out of SimExpPan.Col.Variable = Variable SimExpPan.Col.Unit = Unit @@ -470,7 +471,7 @@ simplotpanel.but.Plotflight = Plot flight simplotpanel.lbl.Axis = Axis: simplotpanel.but.ttip.Removethisplot = Remove this plot simplotpanel.Desc = The data will be plotted in time order even if the X axis type is not time. -simplotpanel.OptionPane.lbl1 = A maximum of 15 plots is allowed. +simplotpanel.OptionPane.lbl1 = A maximum of 15 plots is allowed. simplotpanel.OptionPane.lbl2 = Cannot add plot simplotpanel.AUTO_NAME = Auto simplotpanel.LEFT_NAME = Left @@ -536,14 +537,14 @@ componentanalysisdlg.rollTableModel = Roll dynamics componentanalysisdlg.rollTableModel.ttip = Roll dynamics componentanalysisdlg.println.closingmethod = Closing method called: componentanalysisdlg.println.settingnam = SETTING NAN VALUES -componentanalysisdlg.lbl.reflenght = Reference length: -componentanalysisdlg.lbl.refarea = Reference area: +componentanalysisdlg.lbl.reflenght = Reference length: +componentanalysisdlg.lbl.refarea = Reference area: !componentanalysisdlg.But.close =Close componentanalysisdlg.TabStability.Col.Component = Component ! Custom Material dialog custmatdlg.title.Custommaterial = Custom material -custmatdlg.lbl.Materialname = Material name: +custmatdlg.lbl.Materialname = Material name: custmatdlg.lbl.Materialtype = Material type: custmatdlg.lbl.Materialdensity = Material density: custmatdlg.checkbox.Addmaterial = Add material to database @@ -656,7 +657,7 @@ RocketCompCfg.checkbox.Endcapped = End capped RocketCompCfg.ttip.Endcapped = Whether the end of the shoulder is capped. RocketCompCfg.title.Noseconeshoulder = Nose cone shoulder RocketCompCfg.title.Aftshoulder = Aft shoulder -RocketCompCfg.border.Foreshoulder = Fore shoulder +RocketCompCfg.border.Foreshoulder = Fore shoulder !RocketCompCfg.lbl.Length = Length: ! BulkheadConfig @@ -800,7 +801,7 @@ ParachuteCfg.lbl.Material = Material: ParachuteCfg.combo.MaterialModel = The component material affects the weight of the component. ParachuteCfg.lbl.longA1 = Drag coefficient CD: ParachuteCfg.lbl.longB1 = The drag coefficient relative to the total area of the parachute.
-ParachuteCfg.lbl.longB2 = A larger drag coefficient yields a slowed descent rate. +ParachuteCfg.lbl.longB2 = A larger drag coefficient yields a slowed descent rate. ParachuteCfg.lbl.longB3 = A typical value for parachutes is 0.8. ParachuteCfg.but.Reset = Reset ParachuteCfg.lbl.Shroudlines = Shroud lines: @@ -823,7 +824,7 @@ ParachuteCfg.lbl.Radialdirection = Radial direction: ParachuteCfg.but.Reset = Reset ParachuteCfg.lbl.plusdelay = plus -! ShockCordConfig +! ShockCordConfig ShockCordCfg.lbl.Shockcordlength = Shock cord length: ShockCordCfg.lbl.Shockcordmaterial = Shock cord material: ShockCordCfg.lbl.Posrelativeto = Position relative to: @@ -967,7 +968,7 @@ PlotDialog.lbl.Chart = Click and drag down+right to zoom in, up+left to zoom out ! "main" prefix is used for the main application dialog -# FIXME: Rename the description keys +# FIXME: Rename the description keys main.menu.file = File main.menu.file.desc = File-handling related tasks @@ -1104,7 +1105,7 @@ Shape.Ogive.desc1 = An ogive nose cone has a profile that is a segment of a circ Shape.Ogive.desc2 = An ogive transition has a profile that is a segment of a circle. The shape parameter value 1 produces a tangent ogive, which has a smooth transition to the body tube at the aft end, values less than 1 produce secant ogives. Shape.Ellipsoid = Ellipsoid Shape.Ellipsoid.desc1 = An ellipsoidal nose cone has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. -Shape.Ellipsoid.desc2 = An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. If the transition is not clipped, then the profile is extended at the center by the corresponding radius. +Shape.Ellipsoid.desc2 = An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. If the transition is not clipped, then the profile is extended at the center by the corresponding radius. Shape.Powerseries = Power series Shape.Powerseries.desc1 = A power series nose cone has a profile of Radius × (x / Length)k where k is the shape parameter. For k=0.5 this is a \u00BD-power or parabolic nose cone, for k=0.75 a \u00BE-power, and for k=1 a conical nose cone. Shape.Powerseries.desc2 = A power series transition has a profile of Radius × (x / Length)k where k is the shape parameter. For k=0.5 the transition is \u00BD-power or parabolic, for k=0.75 a \u00BE-power, and for k=1 conical. @@ -1113,7 +1114,7 @@ Shape.Parabolicseries.desc1 = A parabolic series nose cone has a profile of a pa Shape.Parabolicseries.desc2 = A parabolic series transition has a profile of a parabola. The shape parameter defines the segment of the parabola to utilize. The shape parameter 1.0 produces a full parabola which is tangent to the body tube at the aft end, 0.75 produces a 3/4 parabola, 0.5 procudes a 1/2 parabola and 0 produces a conical transition. Shape.Haackseries = Haack series Shape.Haackseries.desc1 = The Haack series nose cones are designed to minimize drag. The shape parameter 0 produces an LD-Haack or Von Karman nose cone, which minimizes drag for fixed length and diameter, while a value of 0.333 produces an LV-Haack nose cone, which minimizes drag for fixed length and volume. -Shape.Haackseries.desc2 = The Haack series nose cones are designed to minimize drag. These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions. The shape parameter 0 produces an LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack shape. +Shape.Haackseries.desc2 = The Haack series nose cones are designed to minimize drag. These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions. The shape parameter 0 produces an LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack shape. ! RocketComponent @@ -1169,7 +1170,7 @@ MotorMount.IgnitionEvent.EJECTION_CHARGE = First ejection charge of previous sta MotorMount.IgnitionEvent.BURNOUT = First burnout of previous stage MotorMount.IgnitionEvent.NEVER = Never -!ComponentIcons +!ComponentIcons ComponentIcons.Nosecone = Nose cone ComponentIcons.Bodytube = Body tube ComponentIcons.Transition = Transition @@ -1224,7 +1225,7 @@ TCurveMotorCol.LENGTH = Length ! RocketInfo RocketInfo.lengthLine.Length = Length RocketInfo.lengthLine.maxdiameter = , max. diameter -RocketInfo.massText1 = Mass with motors +RocketInfo.massText1 = Mass with motors RocketInfo.massText2 = Mass with no motors RocketInfo.at = at M= RocketInfo.cgText = CG: @@ -1577,8 +1578,8 @@ GuidedTourSelectionDialog.btn.start = Start tour! ! Custom Fin BMP Importer CustomFinImport.button.label = Import from image CustomFinImport.badFinImage = Invalid fin image. Make sure the fin is a solid black or dark color and touching the bottom of the image. -CustomFinImport.errorLoadingFile = Error loading file: -CustomFinImport.errorParsingFile = Error parsing fin image: +CustomFinImport.errorLoadingFile = Error loading file: +CustomFinImport.errorParsingFile = Error parsing fin image: CustomFinImport.undo = Import freeform fin set CustomFinImport.error.title = Error loading fin profile CustomFinImport.error.badimage = Could not deduce fin shape from image. diff --git a/core/src/net/sf/openrocket/gui/preset/ComponentPresetPanel.java b/core/src/net/sf/openrocket/gui/preset/ComponentPresetPanel.java new file mode 100644 index 000000000..1a34b3d4d --- /dev/null +++ b/core/src/net/sf/openrocket/gui/preset/ComponentPresetPanel.java @@ -0,0 +1,229 @@ +package net.sf.openrocket.gui.preset; + + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.FileHelper; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.ResourceBundleTranslator; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.preset.xml.OpenRocketComponentSaver; +import net.sf.openrocket.startup.Application; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import javax.xml.bind.JAXBException; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * A UI for editing component presets. Currently this is a standalone application - run the main within this class. + * TODO: Full I18n + * TODO: Delete component + * TODO: Open .orc for editing + * TODO: Import .csv + * TODO: Export .csv + * TODO: proper mass unit conversion + */ +public class ComponentPresetPanel extends JPanel implements PresetResultListener { + + private static final LogHelper log = Application.getLogger(); + + private static ResourceBundleTranslator trans = null; + + static { + trans = new ResourceBundleTranslator("l10n.messages"); + net.sf.openrocket.startup.Application.setBaseTranslator(trans); + } + + private JTable table; + private DataTableModel model; + private boolean editingSelected = false; + + /** + * Create the panel. + */ + public ComponentPresetPanel() { + setLayout(new MigLayout("", "[82.00px][168.00px][84px][117.00px][][222px]", "[346.00px][29px]")); + + model = new DataTableModel(new String[]{"Manufacturer", "Type", "Part No", "Description"}); + + table = new JTable(model); + JScrollPane scrollPane = new JScrollPane(table); + table.setFillsViewportHeight(true); + table.setAutoCreateRowSorter(true); + add(scrollPane, "cell 0 0 6 1,grow"); + + table.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + JTable target = (JTable) e.getSource(); + int row = target.getSelectedRow(); + editingSelected = true; + new PresetEditorDialog(ComponentPresetPanel.this, (ComponentPreset) model.getAssociatedObject(row)).setVisible(true); + } + } + }); + JButton addBtn = new JButton("Add"); + addBtn.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent arg0) { + editingSelected = false; + new PresetEditorDialog(ComponentPresetPanel.this).setVisible(true); + } + }); + add(addBtn, "cell 0 1,alignx left,aligny top"); + + JButton saveBtn = new JButton("Save..."); + saveBtn.setHorizontalAlignment(SwingConstants.RIGHT); + add(saveBtn, "flowx,cell 5 1,alignx right,aligny center"); + saveBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + try { + saveAsORC(); + } + catch (JAXBException e1) { + //TODO + e1.printStackTrace(); + } + catch (IOException e1) { + //TODO + e1.printStackTrace(); + } + } + }); + + JButton cancelBtn = new JButton("Cancel"); + cancelBtn.setHorizontalAlignment(SwingConstants.RIGHT); + add(cancelBtn, "cell 5 1,alignx right,aligny top"); + cancelBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + System.exit(0); + } + }); + + } + + @Override + public void notifyResult(final ComponentPreset preset) { + if (preset != null) { + DataTableModel model = (DataTableModel) table.getModel(); + if (!editingSelected) { + model.addRow(new String[]{preset.getManufacturer().getDisplayName(), preset.getType().name(), preset.getPartNo(), preset.get(ComponentPreset.DESCRIPTION)}, + preset); + } + else { + int row = table.getSelectedRow(); + model.setValueAt(preset.getManufacturer().getDisplayName(), row, 0); + model.setValueAt(preset.getType().name(), row, 1); + model.setValueAt(preset.getPartNo(), row, 2); + model.setValueAt(preset.get(ComponentPreset.DESCRIPTION), row, 3); + model.associated.set(row, preset); + } + } + editingSelected = false; + } + + /** + * Launch the test main. + */ + public static void main(String[] args) { + try { + Application.setPreferences(new SwingPreferences()); + JFrame dialog = new JFrame(); + dialog.getContentPane().add(new ComponentPresetPanel()); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.pack(); + dialog.setVisible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + class DataTableModel extends DefaultTableModel { + + private List associated = new ArrayList(); + + /** + * Constructs a DefaultTableModel with as many columns as there are elements in + * columnNames and rowCount of null object values. Each column's name + * will be taken from the columnNames array. + * + * @param columnNames array containing the names of the new columns; if this is null + * then the model has no columns + * + * @see #setDataVector + * @see #setValueAt + */ + DataTableModel(final Object[] columnNames) { + super(columnNames, 0); + } + + public void addRow(Object[] data, Object associatedData) { + super.addRow(data); + associated.add(getRowCount() - 1, associatedData); + } + + public void removeRow(int row) { + super.removeRow(row); + associated.remove(row); + } + + public Object getAssociatedObject(int row) { + return associated.get(row); + } + + public boolean isCellEditable(int rowIndex, int mColIndex) { + return false; + } + } + + private boolean saveAsORC() throws JAXBException, IOException { + File file = null; + + final JFileChooser chooser = new JFileChooser(); + chooser.addChoosableFileFilter(FileHelper.OPEN_ROCKET_COMPONENT_FILTER); + + chooser.setFileFilter(FileHelper.OPEN_ROCKET_COMPONENT_FILTER); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + + int option = chooser.showSaveDialog(ComponentPresetPanel.this); + if (option != JFileChooser.APPROVE_OPTION) { + log.user("User decided not to save, option=" + option); + return false; + } + + file = chooser.getSelectedFile(); + if (file == null) { + log.user("User did not select a file"); + return false; + } + + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); + + file = FileHelper.forceExtension(file, "orc"); + + List materials = new ArrayList(); + List presets = new ArrayList(); + + for (int x = 0; x< model.getRowCount(); x++) { + ComponentPreset preset = (ComponentPreset)model.getAssociatedObject(x); + if (!materials.contains(preset.get(ComponentPreset.MATERIAL))) { + materials.add(preset.get(ComponentPreset.MATERIAL)); + } + presets.add(preset); + } + + return FileHelper.confirmWrite(file, this) && new OpenRocketComponentSaver().save(file, materials, presets); + } +} diff --git a/core/src/net/sf/openrocket/gui/preset/ImagePreviewPanel.java b/core/src/net/sf/openrocket/gui/preset/ImagePreviewPanel.java new file mode 100644 index 000000000..bab36ec56 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/preset/ImagePreviewPanel.java @@ -0,0 +1,105 @@ + +package net.sf.openrocket.gui.preset; + + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; + +/** + * From a JavaLobby article by Michael Urban: http://www.javalobby.org/java/forums/t49462.html + */ +public class ImagePreviewPanel extends JPanel + implements PropertyChangeListener { + + private int width, height; + private ImageIcon icon; + private Image image; + private static final int ACCSIZE = 155; + private Color bg; + + public ImagePreviewPanel() { + setPreferredSize(new Dimension(ACCSIZE, -1)); + bg = getBackground(); + } + + public void propertyChange(PropertyChangeEvent e) { + String propertyName = e.getPropertyName(); + + // Make sure we are responding to the right event. + if (propertyName.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { + File selection = (File)e.getNewValue(); + String name; + + if (selection == null) + return; + else + name = selection.getAbsolutePath(); + + /* + * Make reasonably sure we have an image format that AWT can + * handle so we don't try to draw something silly. + */ + if ((name != null) && + name.toLowerCase().endsWith(".jpg") || + name.toLowerCase().endsWith(".jpeg") || + name.toLowerCase().endsWith(".gif") || + name.toLowerCase().endsWith(".png")) { + icon = new ImageIcon(name); + image = icon.getImage(); + scaleImage(); + repaint(); + } + } + } + + private void scaleImage() { + width = image.getWidth(this); + height = image.getHeight(this); + double ratio = 1.0; + + /* + * Determine how to scale the image. Since the accessory can expand + * vertically make sure we don't go larger than 150 when scaling + * vertically. + */ + if (width >= height) { + ratio = (double)(ACCSIZE-5) / width; + width = ACCSIZE-5; + height = (int)(height * ratio); + } + else { + if (getHeight() > 150) { + ratio = (double)(ACCSIZE-5) / height; + height = ACCSIZE-5; + width = (int)(width * ratio); + } + else { + ratio = (double)getHeight() / height; + height = getHeight(); + width = (int)(width * ratio); + } + } + + image = image.getScaledInstance(width, height, Image.SCALE_DEFAULT); + } + + public void paintComponent(Graphics g) { + g.setColor(bg); + + /* + * If we don't do this, we will end up with garbage from previous + * images if they have larger sizes than the one we are currently + * drawing. Also, it seems that the file list can paint outside + * of its rectangle, and will cause odd behavior if we don't clear + * or fill the rectangle for the accessory before drawing. This might + * be a bug in JFileChooser. + */ + g.fillRect(0, 0, ACCSIZE, getHeight()); + g.drawImage(image, getWidth() / 2 - width / 2 + 5, + getHeight() / 2 - height / 2, this); + } + +} diff --git a/core/src/net/sf/openrocket/gui/preset/MaterialModel.java b/core/src/net/sf/openrocket/gui/preset/MaterialModel.java new file mode 100644 index 000000000..a80e6847e --- /dev/null +++ b/core/src/net/sf/openrocket/gui/preset/MaterialModel.java @@ -0,0 +1,120 @@ +package net.sf.openrocket.gui.preset; + +import net.sf.openrocket.database.Database; +import net.sf.openrocket.database.DatabaseListener; +import net.sf.openrocket.database.Databases; +import net.sf.openrocket.gui.dialogs.CustomMaterialDialog; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.startup.Application; + +import javax.swing.*; +import java.awt.*; + +/** + * A material model specifically for presets. + */ +public class MaterialModel extends DefaultComboBoxModel implements DatabaseListener { + + private static final String CUSTOM = "Custom"; + + private final Database database; + + private static final Translator trans = Application.getTranslator(); + + private Component parent; + public MaterialModel(Component theParent, Material.Type type) { + parent = theParent; + + switch (type) { + case LINE: + this.database = Databases.LINE_MATERIAL; + break; + + case BULK: + this.database = Databases.BULK_MATERIAL; + break; + + case SURFACE: + this.database = Databases.SURFACE_MATERIAL; + break; + + default: + throw new IllegalArgumentException("Unknown material type:" + type); + } + + database.addDatabaseListener(this); + } + + @Override + public void setSelectedItem(Object item) { + if (item == null) { + // Clear selection - huh? + return; + } + + if (item == CUSTOM) { + + // Open custom material dialog in the future, after combo box has closed + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + CustomMaterialDialog dialog = new CustomMaterialDialog( + SwingUtilities.getWindowAncestor(parent), + (Material) getSelectedItem(), true, + //// Define custom material + trans.get("MaterialModel.title.Defcustmat")); + + dialog.setVisible(true); + + if (!dialog.getOkClicked()) { + return; + } + + Material material = dialog.getMaterial(); + MaterialModel.super.setSelectedItem(material); + if (dialog.isAddSelected()) { + database.add(material); + } + } + }); + + } + else if (item instanceof Material) { + super.setSelectedItem(item); + } + else { + throw new IllegalArgumentException("Illegal item class " + item.getClass() + + " item=" + item); + } + } + + @Override + public Object getElementAt(int index) { + if (index == database.size()) { + return CUSTOM; + } + else if (index >= database.size() + 1) { + return null; + } + return database.get(index); + } + + @Override + public int getSize() { + return database.size() + 1; + } + + //////// Change listeners + + @Override + public void elementAdded(Material element, Database source) { + this.fireContentsChanged(this, 0, database.size()); + } + + @Override + public void elementRemoved(Material element, Database source) { + this.fireContentsChanged(this, 0, database.size()); + } + +} diff --git a/core/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java b/core/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java new file mode 100644 index 000000000..1608aeb11 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java @@ -0,0 +1,1636 @@ +package net.sf.openrocket.gui.preset; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.print.PrintUnit; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.preset.ComponentPresetFactory; +import net.sf.openrocket.preset.InvalidComponentPresetException; +import net.sf.openrocket.preset.TypedPropertyMap; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.startup.Application; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.filechooser.FileFilter; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PresetEditorDialog extends JDialog implements ItemListener { + + private static Translator trans = Application.getTranslator(); + + private static LogHelper log = Application.getLogger(); + + private static final String NON_NEGATIVE_DECIMAL_FIELD = "(\\d){1,10}\\.(\\d){1,10}"; + + /** + * Input of non-negative decimals. + */ + final PresetInputVerifier NON_NEGATIVE_DECIMAL = + new PresetInputVerifier(Pattern.compile(NON_NEGATIVE_DECIMAL_FIELD)); + + private final JPanel contentPanel = new JPanel(); + private JComboBox typeCombo; + private JTextField mfgTextField; + private JComboBox materialChooser; + private JComboBox massUnitCombo; + private JComboBox lenUnitCombo; + + private JTextField ncPartNoTextField; + private JTextField ncDescTextField; + private JTextField ncLengthTextField; + private JCheckBox ncFilledCB; + private JComboBox ncShapeCB; + private JTextField ncAftDiaTextField; + private JTextField ncAftShoulderDiaTextField; + private JTextField ncAftShoulderLenTextField; + private JTextField ncMassTextField; + private ImageIcon ncImage; + private JButton ncImageBtn; + + private JTextField trPartNoTextField; + private JTextField trDescTextField; + private JTextField trLengthTextField; + private JTextField trAftDiaTextField; + private JTextField trAftShoulderDiaTextField; + private JTextField trAftShoulderLenTextField; + private JTextField trForeDiaTextField; + private JTextField trForeShoulderDiaTextField; + private JTextField trForeShoulderLenTextField; + private JTextField trMassTextField; + private ImageIcon trImage; + private JCheckBox trFilledCB; + private JComboBox trShapeCB; + private JButton trImageBtn; + + private JTextField btPartNoTextField; + private JTextField btDescTextField; + private JTextField btMassTextField; + private JTextField btInnerDiaTextField; + private JTextField btOuterDiaTextField; + private JTextField btLengthTextField; + private ImageIcon btImage; + private JButton btImageBtn; + + private JTextField tcPartNoTextField; + private JTextField tcDescTextField; + private JTextField tcMassTextField; + private JTextField tcInnerDiaTextField; + private JTextField tcOuterDiaTextField; + private JTextField tcLengthTextField; + private ImageIcon tcImage; + private JButton tcImageBtn; + + private JTextField bhPartNoTextField; + private JTextField bhDescTextField; + private JTextField bhOuterDiaTextField; + private JTextField bhLengthTextField; + private JTextField bhMassTextField; + private ImageIcon bhImage; + private JButton bhImageBtn; + + private JTextField crPartNoTextField; + private JTextField crDescTextField; + private JTextField crOuterDiaTextField; + private JTextField crInnerDiaTextField; + private JTextField crThicknessTextField; + private JTextField crMassTextField; + private ImageIcon crImage; + private JButton crImageBtn; + + private JTextField ebPartNoTextField; + private JTextField ebDescTextField; + private JTextField ebOuterDiaTextField; + private JTextField ebInnerDiaTextField; + private JTextField ebThicknessTextField; + private JTextField ebMassTextField; + private ImageIcon ebImage; + private JButton ebImageBtn; + + private final JFileChooser imageChooser = createImageChooser(); + + private JPanel componentOverlayPanel; + + private PresetResultListener resultListener; + + private static Map componentMap = new HashMap(); + private static Map lengthMap = new HashMap(); + + private static final String NOSE_CONE_KEY = "NoseCone.NoseCone"; + private static final String BODY_TUBE_KEY = "BodyTube.BodyTube"; + private static final String TUBE_COUPLER_KEY = "TubeCoupler.TubeCoupler"; + private static final String TRANSITION_KEY = "Transition.Transition"; + private static final String CR_KEY = "ComponentIcons.Centeringring"; + private static final String BULKHEAD_KEY = "Bulkhead.Bulkhead"; + private static final String EB_KEY = "ComponentIcons.Engineblock"; + + + static { + componentMap.put(trans.get(NOSE_CONE_KEY), "NOSECONE"); + componentMap.put(trans.get(BODY_TUBE_KEY), "BODYTUBE"); + componentMap.put(trans.get(TUBE_COUPLER_KEY), "TUBECOUPLER"); + componentMap.put(trans.get(TRANSITION_KEY), "TRANSITION"); + componentMap.put(trans.get(CR_KEY), "CENTERINGRING"); + componentMap.put(trans.get(BULKHEAD_KEY), "BULKHEAD"); + componentMap.put(trans.get(EB_KEY), "ENGINEBLOCK"); + + lengthMap.put("m", PrintUnit.METERS); + lengthMap.put("cm", PrintUnit.CENTIMETERS); + lengthMap.put("mm", PrintUnit.MILLIMETERS); + lengthMap.put("in", PrintUnit.INCHES); + lengthMap.put("ft", PrintUnit.FOOT); + } + + /** + * Launch the application. + */ + public static void main(String[] args) { + try { + Application.setPreferences(new SwingPreferences()); + PresetEditorDialog dialog = new PresetEditorDialog(); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Create the dialog. + */ + public PresetEditorDialog() { + this(new PresetResultListener() { + @Override + public void notifyResult(final ComponentPreset preset) { + } + }); + } + + public PresetEditorDialog(PresetResultListener theCallback) { + this(theCallback, null); + typeCombo.setEditable(true); + } + + public PresetEditorDialog(PresetResultListener theCallback, ComponentPreset toEdit) { + resultListener = theCallback; + getContentPane().setMinimumSize(new Dimension(200, 200)); + setBounds(100, 100, 720, 610); + getContentPane().setLayout(new BorderLayout()); + contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + contentPanel.setLayout(new MigLayout("", "[][grow][94.00,grow][232.0,grow][130.00][grow]", "[][][20.00,grow][grow]")); + JLabel lblManufacturer = new JLabel("Manufacturer:"); + contentPanel.add(lblManufacturer, "cell 2 0,alignx left,aligny center"); + + mfgTextField = new JTextField(); + contentPanel.add(mfgTextField, "cell 3 0,growx"); + mfgTextField.setColumns(10); + + JLabel lenUnitLabel = new JLabel("Length Unit:"); + contentPanel.add(lenUnitLabel, "cell 4 0,alignx left,aligny center"); + + lenUnitCombo = new JComboBox(); + lenUnitCombo.setModel(new DefaultComboBoxModel(new String[]{"m", "cm", "mm", "in", "ft"})); + contentPanel.add(lenUnitCombo, "cell 5 0,growx"); + + JLabel typeLabel = new JLabel("Type:"); + contentPanel.add(typeLabel, "cell 2 1,alignx left,aligny center"); + + typeCombo = new JComboBox(); + typeCombo.addItemListener(this); + typeCombo.setModel(new DefaultComboBoxModel(new String[]{ + trans.get(NOSE_CONE_KEY), trans.get(BODY_TUBE_KEY), trans.get(TUBE_COUPLER_KEY), trans.get(TRANSITION_KEY), + trans.get(CR_KEY), trans.get(BULKHEAD_KEY), trans.get(EB_KEY)})); + contentPanel.add(typeCombo, "cell 3 1,growx"); + + JLabel massUnitLabel = new JLabel("Mass Unit:"); + contentPanel.add(massUnitLabel, "cell 4 1,alignx left,aligny center"); + + massUnitCombo = new JComboBox(); + massUnitCombo.setModel(new DefaultComboBoxModel(new String[]{"kg", "g", "oz", "lb"})); + contentPanel.add(massUnitCombo, "cell 5 1,growx"); + + JLabel bhMaterialLabel = new JLabel("Material:"); + contentPanel.add(bhMaterialLabel, "cell 2 2, alignx left"); + + materialChooser = new JComboBox(new MaterialModel(this, Material.Type.BULK)); + contentPanel.add(materialChooser, "cell 3 2,growx"); + + componentOverlayPanel = new JPanel(); + contentPanel.add(componentOverlayPanel, "cell 1 3 5 2,grow"); + componentOverlayPanel.setLayout(new CardLayout(0, 0)); + + { + JPanel ncPanel = new JPanel(); + componentOverlayPanel.add(ncPanel, "NOSECONE"); + ncPanel.setLayout(new MigLayout("", "[61px][159.00,grow][35.00][109.00,grow][189.00,grow][grow]", "[16px][][][][][]")); + JLabel ncPartNoLabel = new JLabel("Part No:"); + ncPanel.add(ncPartNoLabel, "cell 0 0,alignx left,aligny center"); + + ncPartNoTextField = new JTextField(); + ncPanel.add(ncPartNoTextField, "cell 1 0,growx"); + ncPartNoTextField.setColumns(10); + + JLabel ncDescLabel = new JLabel("Description:"); + ncPanel.add(ncDescLabel, "cell 3 0,alignx left,aligny center"); + + ncDescTextField = new JTextField(); + ncPanel.add(ncDescTextField, "cell 4 0,growx"); + ncDescTextField.setColumns(10); + + ncFilledCB = new JCheckBox("Filled"); + ncPanel.add(ncFilledCB, "cell 1 1"); + + JLabel ncMaterialLabel = new JLabel("Material:"); + ncPanel.add(ncMaterialLabel, "cell 0 1,alignx left"); + + JLabel ncMassLabel = new JLabel("Mass:"); + ncPanel.add(ncMassLabel, "cell 3 1,alignx left"); + + ncMassTextField = new JTextField(); + ncMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ncPanel.add(ncMassTextField, "cell 4 1,growx"); + ncMassTextField.setColumns(10); + + JLabel ncShapeLabel = new JLabel("Shape:"); + ncPanel.add(ncShapeLabel, "cell 0 2,alignx left"); + + ncShapeCB = new JComboBox(); + ncShapeCB.setModel(new DefaultComboBoxModel(new String[]{Transition.Shape.OGIVE.getName(), + Transition.Shape.CONICAL.getName(), Transition.Shape.PARABOLIC.getName(), + Transition.Shape.ELLIPSOID.getName(), Transition.Shape.HAACK.getName()})); + ncPanel.add(ncShapeCB, "cell 1 2,growx"); + + JLabel ncLengthLabel = new JLabel("Length:"); + ncPanel.add(ncLengthLabel, "cell 3 2,alignx left"); + + ncLengthTextField = new JTextField(); + ncLengthTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ncPanel.add(ncLengthTextField, "cell 4 2,growx"); + ncLengthTextField.setColumns(10); + + JLabel ncAftDiaLabel = new JLabel("Aft Dia.:"); + ncPanel.add(ncAftDiaLabel, "cell 0 3,alignx left, aligny top"); + + ncAftDiaTextField = new JTextField(); + ncAftDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ncPanel.add(ncAftDiaTextField, "cell 1 3,growx, aligny top"); + ncAftDiaTextField.setColumns(10); + + JLabel ncAftShoulderLenLabel = new JLabel("Aft Shoulder Len:"); + ncPanel.add(ncAftShoulderLenLabel, "cell 0 4,alignx left, aligny top"); + + ncAftShoulderLenTextField = new JTextField(); + ncAftShoulderLenTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ncPanel.add(ncAftShoulderLenTextField, "cell 1 4,growx,aligny top"); + ncAftShoulderLenTextField.setColumns(10); + + JLabel ncAftShoulderDiaLabel = new JLabel("Aft Shoulder Dia.:"); + ncPanel.add(ncAftShoulderDiaLabel, "cell 0 5,alignx left, aligny top"); + + ncAftShoulderDiaTextField = new JTextField(); + ncAftShoulderDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ncPanel.add(ncAftShoulderDiaTextField, "cell 1 5,growx, aligny top"); + ncAftShoulderDiaTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + ncPanel.add(panel, "cell 4 3, span 1 3"); + panel.setLayout(null); + ncImageBtn = new JButton("No Image"); + ncImageBtn.setMaximumSize(new Dimension(75, 75)); + ncImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(ncImageBtn); + ncImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + ncImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + ncImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + ncImageBtn.setIcon(ncImage); + } + } + }); + + } + { + JPanel trPanel = new JPanel(); + componentOverlayPanel.add(trPanel, "TRANSITION"); + trPanel.setLayout(new MigLayout("", "[][grow][][grow]", "[][][28.00][31.00][][]")); + + JLabel trPartNoLabel = new JLabel("Part No:"); + trPanel.add(trPartNoLabel, "cell 0 0,alignx left"); + + trPartNoTextField = new JTextField(); + trPanel.add(trPartNoTextField, "cell 1 0,growx"); + trPartNoTextField.setColumns(10); + + JLabel trDescLabel = new JLabel("Description:"); + trPanel.add(trDescLabel, "cell 2 0,alignx left"); + + trDescTextField = new JTextField(); + trPanel.add(trDescTextField, "cell 3 0,growx"); + trDescTextField.setColumns(10); + + trFilledCB = new JCheckBox("Filled"); + trPanel.add(trFilledCB, "cell 1 1"); + + JLabel trMassLabel = new JLabel("Mass:"); + trPanel.add(trMassLabel, "cell 2 1,alignx left"); + + trMassTextField = new JTextField(); + trMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trMassTextField, "cell 3 1,growx"); + trMassTextField.setColumns(10); + + JLabel trShapeLabel = new JLabel("Shape:"); + trPanel.add(trShapeLabel, "cell 0 2,alignx left"); + + trShapeCB = new JComboBox(); + trShapeCB.setModel(new DefaultComboBoxModel(new String[]{Transition.Shape.OGIVE.getName(), + Transition.Shape.CONICAL.getName(), Transition.Shape.PARABOLIC.getName(), + Transition.Shape.ELLIPSOID.getName(), Transition.Shape.HAACK.getName()})); + trPanel.add(trShapeCB, "cell 1 2,growx"); + + JLabel trLengthLabel = new JLabel("Length:"); + trPanel.add(trLengthLabel, "cell 2 2,alignx left"); + + trLengthTextField = new JTextField(); + trLengthTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trLengthTextField, "cell 3 2,growx"); + trLengthTextField.setColumns(10); + + JLabel trAftDiaLabel = new JLabel("Aft Dia.:"); + trPanel.add(trAftDiaLabel, "cell 0 3,alignx left"); + + trAftDiaTextField = new JTextField(); + trAftDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trAftDiaTextField, "cell 1 3,growx"); + trAftDiaTextField.setColumns(10); + + JLabel trForeDiaLabel = new JLabel("Fore Dia.:"); + trPanel.add(trForeDiaLabel, "cell 2 3,alignx left"); + + trForeDiaTextField = new JTextField(); + trForeDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trForeDiaTextField, "cell 3 3,growx"); + trForeDiaTextField.setColumns(10); + + JLabel trAftShouldDiaLabel = new JLabel("Aft Shoulder Dia.:"); + trPanel.add(trAftShouldDiaLabel, "cell 0 4,alignx left"); + + trAftShoulderDiaTextField = new JTextField(); + trAftShoulderDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trAftShoulderDiaTextField, "cell 1 4,growx"); + trAftShoulderDiaTextField.setColumns(10); + + JLabel trForeShouldDiaLabel = new JLabel("Fore Shoulder Dia.:"); + trPanel.add(trForeShouldDiaLabel, "cell 2 4,alignx left"); + + trForeShoulderDiaTextField = new JTextField(); + trForeShoulderDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trForeShoulderDiaTextField, "cell 3 4,growx"); + trForeShoulderDiaTextField.setColumns(10); + + JLabel trAftShoulderLenLabel = new JLabel("Aft Shoulder Len.:"); + trPanel.add(trAftShoulderLenLabel, "cell 0 5,alignx left"); + + trAftShoulderLenTextField = new JTextField(); + trAftShoulderLenTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trAftShoulderLenTextField, "cell 1 5,growx"); + trAftShoulderLenTextField.setColumns(10); + + JLabel lblForeShoulderLen = new JLabel("Fore Shoulder Len.:"); + trPanel.add(lblForeShoulderLen, "cell 2 5,alignx left"); + + trForeShoulderLenTextField = new JTextField(); + trForeShoulderLenTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + trPanel.add(trForeShoulderLenTextField, "cell 3 5,growx"); + trForeShoulderLenTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + trPanel.add(panel, "cell 3 6"); + panel.setLayout(null); + trImageBtn = new JButton("No Image"); + trImageBtn.setMaximumSize(new Dimension(75, 75)); + trImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(trImageBtn); + trImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + trImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + trImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + trImageBtn.setIcon(trImage); + } + } + }); + + } + { + JPanel btPanel = new JPanel(); + componentOverlayPanel.add(btPanel, "BODYTUBE"); + btPanel.setLayout(new MigLayout("", "[][grow][][grow]", "[][][][]")); + JLabel btPartNoLabel = new JLabel("Part No:"); + btPanel.add(btPartNoLabel, "cell 0 0,alignx left"); + + btPartNoTextField = new JTextField(); + btPanel.add(btPartNoTextField, "cell 1 0,growx"); + btPartNoTextField.setColumns(10); + + JLabel btDescLabel = new JLabel("Description:"); + btPanel.add(btDescLabel, "cell 2 0,alignx left"); + + btDescTextField = new JTextField(); + btPanel.add(btDescTextField, "cell 3 0,growx"); + btDescTextField.setColumns(10); + + JLabel btMassLabel = new JLabel("Mass:"); + btPanel.add(btMassLabel, "cell 2 1,alignx left"); + + btMassTextField = new JTextField(); + btMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + btPanel.add(btMassTextField, "cell 3 1,growx"); + btMassTextField.setColumns(10); + + JLabel btInnerDiaLabel = new JLabel("Inner Dia.:"); + btPanel.add(btInnerDiaLabel, "cell 0 2,alignx left"); + + btInnerDiaTextField = new JTextField(); + btInnerDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + btPanel.add(btInnerDiaTextField, "cell 1 2,growx"); + btInnerDiaTextField.setColumns(10); + + JLabel btOuterDiaLabel = new JLabel("Outer Dia.:"); + btPanel.add(btOuterDiaLabel, "cell 2 2,alignx left"); + + btOuterDiaTextField = new JTextField(); + btOuterDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + btPanel.add(btOuterDiaTextField, "cell 3 2,growx"); + btOuterDiaTextField.setColumns(10); + + JLabel btLengthLabel = new JLabel("Length:"); + btPanel.add(btLengthLabel, "cell 0 1,alignx left"); + + btLengthTextField = new JTextField(); + btLengthTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + btPanel.add(btLengthTextField, "cell 1 1,growx"); + btLengthTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + btPanel.add(panel, "cell 3 3"); + panel.setLayout(null); + btImageBtn = new JButton("No Image"); + btImageBtn.setMaximumSize(new Dimension(75, 75)); + btImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(btImageBtn); + btImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + btImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + btImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + btImageBtn.setIcon(btImage); + } + } + }); + + } + { + JPanel tcPanel = new JPanel(); + componentOverlayPanel.add(tcPanel, "TUBECOUPLER"); + tcPanel.setLayout(new MigLayout("", "[][grow][][grow]", "[][][][]")); + JLabel tcPartNoLabel = new JLabel("Part No:"); + tcPanel.add(tcPartNoLabel, "cell 0 0,alignx left"); + + tcPartNoTextField = new JTextField(); + tcPanel.add(tcPartNoTextField, "cell 1 0,growx"); + tcPartNoTextField.setColumns(10); + + JLabel tcDescLabel = new JLabel("Description:"); + tcPanel.add(tcDescLabel, "cell 2 0,alignx left"); + + tcDescTextField = new JTextField(); + tcPanel.add(tcDescTextField, "cell 3 0,growx"); + tcDescTextField.setColumns(10); + + JLabel tcMassLabel = new JLabel("Mass:"); + tcPanel.add(tcMassLabel, "cell 2 1,alignx left"); + + tcMassTextField = new JTextField(); + tcMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + tcPanel.add(tcMassTextField, "cell 3 1,growx"); + tcMassTextField.setColumns(10); + + JLabel tcInnerDiaLabel = new JLabel("Inner Dia.:"); + tcPanel.add(tcInnerDiaLabel, "cell 0 2,alignx left, aligny top"); + + tcInnerDiaTextField = new JTextField(); + tcInnerDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + tcPanel.add(tcInnerDiaTextField, "cell 1 2,growx, aligny top"); + tcInnerDiaTextField.setColumns(10); + + JLabel tcOuterDiaLabel = new JLabel("Outer Dia.:"); + tcPanel.add(tcOuterDiaLabel, "cell 2 2,alignx left, aligny top"); + + tcOuterDiaTextField = new JTextField(); + tcOuterDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + tcPanel.add(tcOuterDiaTextField, "cell 3 2,growx, aligny top"); + tcOuterDiaTextField.setColumns(10); + + JLabel tcLengthLabel = new JLabel("Length:"); + tcPanel.add(tcLengthLabel, "cell 0 1,alignx left"); + + tcLengthTextField = new JTextField(); + tcLengthTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + tcPanel.add(tcLengthTextField, "cell 1 1,growx"); + tcLengthTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + tcPanel.add(panel, "cell 3 3"); + panel.setLayout(null); + tcImageBtn = new JButton("No Image"); + tcImageBtn.setMaximumSize(new Dimension(75, 75)); + tcImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(tcImageBtn); + tcImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + tcImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + tcImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + tcImageBtn.setIcon(tcImage); + } + } + }); + + + } + { + JPanel bhPanel = new JPanel(); + componentOverlayPanel.add(bhPanel, "BULKHEAD"); + bhPanel.setLayout(new MigLayout("", "[][157.00,grow 79][65.00][grow]", "[][][][]")); + + JLabel bhPartNoLabel = new JLabel("Part No:"); + bhPanel.add(bhPartNoLabel, "cell 0 0,alignx left"); + + bhPartNoTextField = new JTextField(); + bhPanel.add(bhPartNoTextField, "cell 1 0,growx"); + bhPartNoTextField.setColumns(10); + + JLabel bhDescLabel = new JLabel("Description:"); + bhPanel.add(bhDescLabel, "cell 2 0,alignx left"); + + bhDescTextField = new JTextField(); + bhPanel.add(bhDescTextField, "cell 3 0,growx"); + bhDescTextField.setColumns(10); + + JLabel bhOuterDiaLabel = new JLabel("Outer Dia.:"); + bhPanel.add(bhOuterDiaLabel, "cell 0 2,alignx left, aligny top"); + + bhOuterDiaTextField = new JTextField(); + bhPanel.add(bhOuterDiaTextField, "cell 1 2,growx, aligny top"); + bhOuterDiaTextField.setColumns(10); + + JLabel bhMassLabel = new JLabel("Mass:"); + bhPanel.add(bhMassLabel, "cell 2 1,alignx left"); + + bhMassTextField = new JTextField(); + bhPanel.add(bhMassTextField, "cell 3 1,growx"); + bhMassTextField.setColumns(10); + + JLabel bhLengthLabel = new JLabel("Thickness:"); + bhPanel.add(bhLengthLabel, "cell 0 1,alignx left"); + + bhLengthTextField = new JTextField(); + bhPanel.add(bhLengthTextField, "cell 1 1,growx"); + bhLengthTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + bhPanel.add(panel, "cell 3 2"); + panel.setLayout(null); + bhImageBtn = new JButton("No Image"); + bhImageBtn.setMaximumSize(new Dimension(75, 75)); + bhImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(bhImageBtn); + bhImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + bhImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + bhImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + bhImageBtn.setIcon(bhImage); + } + } + }); + + } + { + JPanel crPanel = new JPanel(); + componentOverlayPanel.add(crPanel, "CENTERINGRING"); + crPanel.setLayout(new MigLayout("", "[][grow][][grow]", "[][][][]")); + + JLabel crPartNoLabel = new JLabel("Part No:"); + crPanel.add(crPartNoLabel, "cell 0 0,alignx left"); + + crPartNoTextField = new JTextField(); + crPanel.add(crPartNoTextField, "cell 1 0, growx"); + crPartNoTextField.setColumns(10); + + JLabel crDescLabel = new JLabel("Description:"); + crPanel.add(crDescLabel, "cell 2 0,alignx left"); + + crDescTextField = new JTextField(); + crPanel.add(crDescTextField, "cell 3 0, growx"); + crDescTextField.setColumns(10); + + JLabel crMassLabel = new JLabel("Mass:"); + crPanel.add(crMassLabel, "cell 2 1,alignx left"); + + crMassTextField = new JTextField(); + crMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + crPanel.add(crMassTextField, "cell 3 1, growx"); + crMassTextField.setColumns(10); + + JLabel crOuterDiaLabel = new JLabel("Outer Dia.:"); + crPanel.add(crOuterDiaLabel, "cell 0 2,alignx left"); + + crOuterDiaTextField = new JTextField(); + crOuterDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + crPanel.add(crOuterDiaTextField, "cell 1 2, growx"); + crOuterDiaTextField.setColumns(10); + + JLabel crInnerDiaLabel = new JLabel("Inner Dia.:"); + crPanel.add(crInnerDiaLabel, "cell 2 2,alignx left"); + + crInnerDiaTextField = new JTextField(); + crInnerDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + crPanel.add(crInnerDiaTextField, "cell 3 2, growx"); + crInnerDiaTextField.setColumns(10); + + JLabel crThicknessLabel = new JLabel("Thickness:"); + crPanel.add(crThicknessLabel, "cell 0 1,alignx left"); + + crThicknessTextField = new JTextField(); + crThicknessTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + crPanel.add(crThicknessTextField, "cell 1 1, growx"); + crThicknessTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + crPanel.add(panel, "cell 3 3"); + panel.setLayout(null); + crImageBtn = new JButton("No Image"); + crImageBtn.setMaximumSize(new Dimension(75, 75)); + crImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(crImageBtn); + crImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + crImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + crImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + crImageBtn.setIcon(crImage); + } + } + }); + + } + { + JPanel ebPanel = new JPanel(); + componentOverlayPanel.add(ebPanel, "ENGINEBLOCK"); + ebPanel.setLayout(new MigLayout("", "[][grow][][grow]", "[][][][]")); + JLabel ebPartNoLabel = new JLabel("Part No:"); + ebPanel.add(ebPartNoLabel, "cell 0 0,alignx left"); + + ebPartNoTextField = new JTextField(); + ebPanel.add(ebPartNoTextField, "cell 1 0,growx"); + ebPartNoTextField.setColumns(10); + + JLabel ebDescLabel = new JLabel("Description:"); + ebPanel.add(ebDescLabel, "cell 2 0,alignx left"); + + ebDescTextField = new JTextField(); + ebPanel.add(ebDescTextField, "cell 3 0,growx"); + ebDescTextField.setColumns(10); + + JLabel ebMassLabel = new JLabel("Mass:"); + ebPanel.add(ebMassLabel, "cell 2 1,alignx left"); + + ebMassTextField = new JTextField(); + ebMassTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ebPanel.add(ebMassTextField, "cell 3 1,growx"); + ebMassTextField.setColumns(10); + + JLabel ebOuterDiaLabel = new JLabel("Outer Dia.:"); + ebPanel.add(ebOuterDiaLabel, "cell 0 2,alignx left"); + + ebOuterDiaTextField = new JTextField(); + ebOuterDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ebPanel.add(ebOuterDiaTextField, "cell 1 2,growx"); + ebOuterDiaTextField.setColumns(10); + + JLabel ebInnerDiaLabel = new JLabel("Inner Dia.:"); + ebPanel.add(ebInnerDiaLabel, "cell 2 2,alignx left"); + + ebInnerDiaTextField = new JTextField(); + ebInnerDiaTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ebPanel.add(ebInnerDiaTextField, "cell 3 2,growx"); + ebInnerDiaTextField.setColumns(10); + + JLabel ebThicknessLabel = new JLabel("Thickness:"); + ebPanel.add(ebThicknessLabel, "cell 0 1,alignx left"); + + ebThicknessTextField = new JTextField(); + ebThicknessTextField.setInputVerifier(NON_NEGATIVE_DECIMAL); + ebPanel.add(ebThicknessTextField, "cell 1 1,growx"); + ebThicknessTextField.setColumns(10); + + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(200, 200)); + ebPanel.add(panel, "cell 3 3"); + panel.setLayout(null); + ebImageBtn = new JButton("No Image"); + ebImageBtn.setMaximumSize(new Dimension(75, 75)); + ebImageBtn.setMinimumSize(new Dimension(75, 75)); + panel.add(ebImageBtn); + ebImageBtn.setBounds(new Rectangle(6, 6, 132, 145)); + + ebImageBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + int returnVal = imageChooser.showOpenDialog(PresetEditorDialog.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = imageChooser.getSelectedFile(); + ebImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155); + ebImageBtn.setIcon(ebImage); + } + } + }); + } + + JPanel buttonPane = new JPanel(); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + buttonPane.setLayout(new MigLayout("", "[130px][176.00px][131.00px]", "[29px]")); + JButton btnSaveAndNew = new JButton("Save and New"); + btnSaveAndNew.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent arg0) { + saveResult(); + } + }); + buttonPane.add(btnSaveAndNew, "cell 0 0,alignx left,aligny top"); + + JButton okButton = new JButton("Save and Close"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + saveResult(); + dispose(); + } + }); + okButton.setActionCommand("OK"); + buttonPane.add(okButton, "cell 1 0,alignx left,aligny top"); + getRootPane().setDefaultButton(okButton); + + JButton cancelButton = new JButton("Close"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + dispose(); + } + }); + cancelButton.setActionCommand("Close"); + buttonPane.add(cancelButton, "cell 6 0,alignx right,aligny top"); + + if (toEdit != null) { + fillEditor(toEdit); + typeCombo.setEditable(false); + } + else { + typeCombo.setEditable(true); + } + } + + private JFileChooser createImageChooser() { + final JFileChooser chooser = new JFileChooser(); + ImagePreviewPanel preview = new ImagePreviewPanel(); + chooser.setAccessory(preview); + chooser.addPropertyChangeListener(preview); + chooser.setAcceptAllFileFilterUsed(false); + chooser.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(final File f) { + return f.getName().endsWith(".png") || f.getName().endsWith(".jpg"); + } + + @Override + public String getDescription() { + return "Image Files"; + } + }); + return chooser; + } + + private void fillEditor(ComponentPreset preset) { + ComponentPreset.Type t = preset.getType(); + + mfgTextField.setText(preset.get(ComponentPreset.MANUFACTURER).getDisplayName()); + materialChooser.getModel().setSelectedItem(preset.get(ComponentPreset.MATERIAL)); + switch (t) { + case BODY_TUBE: + typeCombo.setSelectedItem(trans.get(BODY_TUBE_KEY)); + btDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + + if (preset.has(ComponentPreset.INNER_DIAMETER)) { + btInnerDiaTextField.setText(preset.get(ComponentPreset.INNER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.LENGTH)) { + btLengthTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + btMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + btOuterDiaTextField.setText(preset.get(ComponentPreset.OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + btImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + btImageBtn.setIcon(btImage); + } + btPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case BULK_HEAD: + typeCombo.setSelectedItem(trans.get(BULKHEAD_KEY)); + bhDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.LENGTH)) { + bhLengthTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + bhMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + bhOuterDiaTextField.setText(preset.get(ComponentPreset.OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + bhImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + bhImageBtn.setIcon(bhImage); + } + bhPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case CENTERING_RING: + typeCombo.setSelectedItem(trans.get(CR_KEY)); + crDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.INNER_DIAMETER)) { + crInnerDiaTextField.setText(preset.get(ComponentPreset.INNER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.LENGTH)) { + crThicknessTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + crMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + crOuterDiaTextField.setText(preset.get(ComponentPreset.OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + crImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + crImageBtn.setIcon(crImage); + } + crPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case ENGINE_BLOCK: + typeCombo.setSelectedItem(trans.get(EB_KEY)); + ebDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.INNER_DIAMETER)) { + ebInnerDiaTextField.setText(preset.get(ComponentPreset.INNER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.LENGTH)) { + ebThicknessTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + ebMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + ebOuterDiaTextField.setText(preset.get(ComponentPreset.OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + ebImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + ebImageBtn.setIcon(ebImage); + } + ebPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case NOSE_CONE: + typeCombo.setSelectedItem(trans.get(NOSE_CONE_KEY)); + ncDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.AFT_OUTER_DIAMETER)) { + ncAftDiaTextField.setText(preset.get(ComponentPreset.AFT_OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.AFT_SHOULDER_DIAMETER)) { + ncAftShoulderDiaTextField.setText(preset.get(ComponentPreset.AFT_SHOULDER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.AFT_SHOULDER_LENGTH)) { + ncAftShoulderLenTextField.setText(preset.get(ComponentPreset.AFT_SHOULDER_LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + ncMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.SHAPE)) { + ncShapeCB.setSelectedItem(preset.get(ComponentPreset.SHAPE).toString()); + } + if (preset.has(ComponentPreset.FILLED)) { + ncFilledCB.setSelected((preset.get(ComponentPreset.FILLED))); + } + if (preset.has(ComponentPreset.LENGTH)) { + ncLengthTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + ncImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + ncImageBtn.setIcon(ncImage); + } + ncPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case TRANSITION: + typeCombo.setSelectedItem(trans.get(TRANSITION_KEY)); + trDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.AFT_OUTER_DIAMETER)) { + trAftDiaTextField.setText(preset.get(ComponentPreset.AFT_OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.AFT_SHOULDER_DIAMETER)) { + trAftShoulderDiaTextField.setText(preset.get(ComponentPreset.AFT_SHOULDER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.AFT_SHOULDER_LENGTH)) { + trAftShoulderLenTextField.setText(preset.get(ComponentPreset.AFT_SHOULDER_LENGTH).toString()); + } + if (preset.has(ComponentPreset.FORE_OUTER_DIAMETER)) { + trForeDiaTextField.setText(preset.get(ComponentPreset.FORE_OUTER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.FORE_SHOULDER_DIAMETER)) { + trForeShoulderDiaTextField.setText(preset.get(ComponentPreset.FORE_SHOULDER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.FORE_SHOULDER_LENGTH)) { + trForeShoulderLenTextField.setText(preset.get(ComponentPreset.FORE_SHOULDER_LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + trMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.SHAPE)) { + trShapeCB.setSelectedItem(preset.get(ComponentPreset.SHAPE).toString()); + } + if (preset.has(ComponentPreset.FILLED)) { + trFilledCB.setSelected((preset.get(ComponentPreset.FILLED))); + } + if (preset.has(ComponentPreset.LENGTH)) { + trLengthTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.IMAGE)) { + trImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + trImageBtn.setIcon(trImage); + } + trPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + break; + case TUBE_COUPLER: + typeCombo.setSelectedItem(trans.get(TUBE_COUPLER_KEY)); + tcDescTextField.setText(preset.get(ComponentPreset.DESCRIPTION)); + if (preset.has(ComponentPreset.INNER_DIAMETER)) { + tcInnerDiaTextField.setText(preset.get(ComponentPreset.INNER_DIAMETER).toString()); + } + if (preset.has(ComponentPreset.LENGTH)) { + tcLengthTextField.setText(preset.get(ComponentPreset.LENGTH).toString()); + } + if (preset.has(ComponentPreset.MASS)) { + tcMassTextField.setText(preset.get(ComponentPreset.MASS).toString()); + } + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + tcOuterDiaTextField.setText(preset.get(ComponentPreset.OUTER_DIAMETER).toString()); + } + tcPartNoTextField.setText(preset.get(ComponentPreset.PARTNO)); + if (preset.has(ComponentPreset.IMAGE)) { + tcImage = new ImageIcon(byteArrayToImage(preset.get(ComponentPreset.IMAGE))); + tcImageBtn.setIcon(tcImage); + } + break; + default: + } + } + + private void saveResult() { + String type = (String) typeCombo.getSelectedItem(); + + ComponentPreset result = null; + + if (type.equals(trans.get(NOSE_CONE_KEY))) { + result = extractNoseCone(); + if (result != null) { + clearNoseCone(); + } + } + else if (type.equals(trans.get(TRANSITION_KEY))) { + result = extractTransition(); + if (result != null) { + clearTransition(); + } + } + else if (type.equals(trans.get(BODY_TUBE_KEY))) { + result = extractBodyTube(); + if (result != null) { + clearBodyTube(); + } + } + else if (type.equals(trans.get(TUBE_COUPLER_KEY))) { + result = extractTubeCoupler(); + if (result != null) { + clearTubeCoupler(); + } + } + else if (type.equals(trans.get(EB_KEY))) { + result = extractEngineBlock(); + if (result != null) { + clearEngineBlock(); + } + } + else if (type.equals(trans.get(CR_KEY))) { + result = extractCenteringRing(); + if (result != null) { + clearCenteringRing(); + } + } + else if (type.equals(trans.get(BULKHEAD_KEY))) { + result = extractBulkhead(); + if (result != null) { + clearBulkhead(); + } + } + resultListener.notifyResult(result); + } + + private ComponentPreset extractNoseCone() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + + props.put(ComponentPreset.TYPE, ComponentPreset.Type.NOSE_CONE); + if (!ncAftDiaTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(ncAftDiaTextField.getText()))); + } + if (!ncAftShoulderDiaTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_SHOULDER_DIAMETER, lpu.toMeters(Double.parseDouble(ncAftShoulderDiaTextField.getText()))); + } + if (!ncAftShoulderLenTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_SHOULDER_LENGTH, lpu.toMeters(Double.parseDouble(ncAftShoulderLenTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, ncDescTextField.getText()); + props.put(ComponentPreset.PARTNO, ncPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!ncLengthTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(ncLengthTextField.getText()))); + } + props.put(ComponentPreset.SHAPE, Transition.Shape.toShape((String) ncShapeCB.getSelectedItem())); + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!ncMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(ncMassTextField.getText()))); + } + props.put(ComponentPreset.FILLED, ncFilledCB.isSelected()); + if (ncImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(ncImage.getImage())); + } + + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert nose cone attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory nose cone attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearNoseCone() { + ncAftDiaTextField.setText(""); + ncAftShoulderDiaTextField.setText(""); + ncAftShoulderLenTextField.setText(""); + ncDescTextField.setText(""); + ncPartNoTextField.setText(""); + ncLengthTextField.setText(""); + ncMassTextField.setText(""); + ncFilledCB.setSelected(false); + ncImage = null; + ncImageBtn.setIcon(null); + } + + private ComponentPreset extractTransition() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.TRANSITION); + if (!trAftDiaTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(trAftDiaTextField.getText()))); + } + if (!trAftShoulderDiaTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_SHOULDER_DIAMETER, lpu.toMeters(Double.parseDouble(trAftShoulderDiaTextField.getText()))); + } + if (!trAftShoulderLenTextField.getText().equals("")) { + props.put(ComponentPreset.AFT_SHOULDER_LENGTH, lpu.toMeters(Double.parseDouble(trAftShoulderLenTextField.getText()))); + } + if (!trForeDiaTextField.getText().equals("")) { + props.put(ComponentPreset.FORE_OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(trForeDiaTextField.getText()))); + } + if (!trForeShoulderDiaTextField.getText().equals("")) { + props.put(ComponentPreset.FORE_SHOULDER_DIAMETER, lpu.toMeters(Double.parseDouble(trForeShoulderDiaTextField.getText()))); + } + if (!trForeShoulderLenTextField.getText().equals("")) { + props.put(ComponentPreset.FORE_SHOULDER_LENGTH, lpu.toMeters(Double.parseDouble(trForeShoulderLenTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, trDescTextField.getText()); + props.put(ComponentPreset.PARTNO, trPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + + if (!trLengthTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(trLengthTextField.getText()))); + } + props.put(ComponentPreset.SHAPE, Transition.Shape.toShape((String) trShapeCB.getSelectedItem())); + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!trMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(trMassTextField.getText()))); + } + props.put(ComponentPreset.FILLED, trFilledCB.isSelected()); + if (trImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(trImage.getImage())); + } + + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert transition attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory transition attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearTransition() { + trAftDiaTextField.setText(""); + trAftShoulderDiaTextField.setText(""); + trAftShoulderLenTextField.setText(""); + trForeDiaTextField.setText(""); + trForeShoulderDiaTextField.setText(""); + trForeShoulderLenTextField.setText(""); + trDescTextField.setText(""); + trPartNoTextField.setText(""); + trLengthTextField.setText(""); + trMassTextField.setText(""); + trFilledCB.setSelected(false); + trImage = null; + trImageBtn.setIcon(null); + } + + private ComponentPreset extractBodyTube() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.BODY_TUBE); + if (!btOuterDiaTextField.getText().equals("")) { + props.put(ComponentPreset.OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(btOuterDiaTextField.getText()))); + } + if (!btInnerDiaTextField.getText().equals("")) { + props.put(ComponentPreset.INNER_DIAMETER, lpu.toMeters(Double.parseDouble(btInnerDiaTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, btDescTextField.getText()); + props.put(ComponentPreset.PARTNO, btPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!btLengthTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(btLengthTextField.getText()))); + } + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!btMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(btMassTextField.getText()))); + } + if (btImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(btImage.getImage())); + } + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + nfe.printStackTrace(); + JOptionPane.showMessageDialog(null, "Could not convert body tube attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory body tube attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearBodyTube() { + btOuterDiaTextField.setText(""); + btInnerDiaTextField.setText(""); + btDescTextField.setText(""); + btPartNoTextField.setText(""); + btLengthTextField.setText(""); + btMassTextField.setText(""); + btImage = null; + btImageBtn.setIcon(null); + } + + public ComponentPreset extractTubeCoupler() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.TUBE_COUPLER); + if (!tcOuterDiaTextField.getText().equals("")) { + props.put(ComponentPreset.OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(tcOuterDiaTextField.getText()))); + } + if (!tcInnerDiaTextField.getText().equals("")) { + props.put(ComponentPreset.INNER_DIAMETER, lpu.toMeters(Double.parseDouble(tcInnerDiaTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, tcDescTextField.getText()); + props.put(ComponentPreset.PARTNO, tcPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!tcLengthTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(tcLengthTextField.getText()))); + } + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!tcMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(tcMassTextField.getText()))); + } + if (tcImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(tcImage.getImage())); + } + + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert tube coupler attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory tube coupler attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearTubeCoupler() { + tcOuterDiaTextField.setText(""); + tcInnerDiaTextField.setText(""); + tcDescTextField.setText(""); + tcPartNoTextField.setText(""); + tcLengthTextField.setText(""); + tcMassTextField.setText(""); + tcImage = null; + tcImageBtn.setIcon(null); + } + + private ComponentPreset extractBulkhead() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.BULK_HEAD); + if (!bhOuterDiaTextField.getText().equals("")) { + props.put(ComponentPreset.OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(bhOuterDiaTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, bhDescTextField.getText()); + props.put(ComponentPreset.PARTNO, bhPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!bhLengthTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(bhLengthTextField.getText()))); + } + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!bhMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(bhMassTextField.getText()))); + } + if (bhImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(bhImage.getImage())); + } + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert bulkhead attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory bulkhead attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearBulkhead() { + bhOuterDiaTextField.setText(""); + bhDescTextField.setText(""); + bhPartNoTextField.setText(""); + bhLengthTextField.setText(""); + bhMassTextField.setText(""); + bhImage = null; + bhImageBtn.setIcon(null); + } + + private ComponentPreset extractCenteringRing() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.CENTERING_RING); + if (!crOuterDiaTextField.getText().equals("")) { + props.put(ComponentPreset.OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(crOuterDiaTextField.getText()))); + } + if (!crInnerDiaTextField.getText().equals("")) { + props.put(ComponentPreset.INNER_DIAMETER, lpu.toMeters(Double.parseDouble(crInnerDiaTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, crDescTextField.getText()); + props.put(ComponentPreset.PARTNO, crPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!crThicknessTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(crThicknessTextField.getText()))); + } + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!crMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(crMassTextField.getText()))); + } + if (crImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(crImage.getImage())); + } + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert centering ring attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory centering ring attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearCenteringRing() { + crOuterDiaTextField.setText(""); + crInnerDiaTextField.setText(""); + crDescTextField.setText(""); + crPartNoTextField.setText(""); + crThicknessTextField.setText(""); + crMassTextField.setText(""); + crImage = null; + crImageBtn.setIcon(null); + } + + public ComponentPreset extractEngineBlock() { + TypedPropertyMap props = new TypedPropertyMap(); + try { + PrintUnit lpu = lengthMap.get(lenUnitCombo.getSelectedItem()); + props.put(ComponentPreset.TYPE, ComponentPreset.Type.ENGINE_BLOCK); + if (!ebOuterDiaTextField.getText().equals("")) { + props.put(ComponentPreset.OUTER_DIAMETER, lpu.toMeters(Double.parseDouble(ebOuterDiaTextField.getText()))); + } + if (!ebInnerDiaTextField.getText().equals("")) { + props.put(ComponentPreset.INNER_DIAMETER, lpu.toMeters(Double.parseDouble(ebInnerDiaTextField.getText()))); + } + props.put(ComponentPreset.DESCRIPTION, ebDescTextField.getText()); + props.put(ComponentPreset.PARTNO, ebPartNoTextField.getText()); + props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); + if (!ebThicknessTextField.getText().equals("")) { + props.put(ComponentPreset.LENGTH, lpu.toMeters(Double.parseDouble(ebThicknessTextField.getText()))); + } + final Material material = (Material) materialChooser.getSelectedItem(); + if (material != null) { + props.put(ComponentPreset.MATERIAL, material); + } + else { + JOptionPane.showMessageDialog(null, "A material must be selected.", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + if (!ebMassTextField.getText().equals("")) { + props.put(ComponentPreset.MASS, lpu.toMeters(Double.parseDouble(ebMassTextField.getText()))); + } + if (ebImage != null) { + props.put(ComponentPreset.IMAGE, imageToByteArray(ebImage.getImage())); + } + return ComponentPresetFactory.create(props); + } + catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(null, "Could not convert engine block attribute.", "Error", JOptionPane.ERROR_MESSAGE); + } + catch (InvalidComponentPresetException e) { + JOptionPane.showMessageDialog(null, "Mandatory engine block attribute not set.", "Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + private void clearEngineBlock() { + ebOuterDiaTextField.setText(""); + ebInnerDiaTextField.setText(""); + ebDescTextField.setText(""); + ebPartNoTextField.setText(""); + ebThicknessTextField.setText(""); + ebMassTextField.setText(""); + ebImage = null; + ebImageBtn.setIcon(null); + } + + public void itemStateChanged(ItemEvent evt) { + CardLayout cl = (CardLayout) (componentOverlayPanel.getLayout()); + cl.show(componentOverlayPanel, componentMap.get((String) evt.getItem())); + + } + + /** + * Convert an image to a byte array in png format. + * + * @param originalImage + * @return + */ + private byte[] imageToByteArray(Image originalImage) { + byte[] imageInByte = null; + try { + BufferedImage bi = imageToBufferedImage(originalImage); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(bi, "png", baos); + baos.flush(); + imageInByte = baos.toByteArray(); + baos.close(); + } + catch (IOException e) { + log.error("Could not read image."); + } + return imageInByte; + } + + private BufferedImage imageToBufferedImage(final Image originalImage) { + BufferedImage bi = new BufferedImage( + originalImage.getWidth(null), + originalImage.getHeight(null), + BufferedImage.TYPE_INT_RGB); + + Graphics2D g2 = bi.createGraphics(); + g2.drawImage(originalImage, 0, 0, null); + return bi; + } + + private BufferedImage byteArrayToImage(byte[] src) { + // convert byte array back to BufferedImage + InputStream in = new ByteArrayInputStream(src); + try { + return ImageIO.read(in); + } + catch (IOException e) { + log.error("Could not convert image."); + } + return null; + } + + private ImageIcon scaleImage(Image image, int targetDimension) { + int width = image.getWidth(this); + int height = image.getHeight(this); + double ratio = 1.0; + + /* + * Determine how to scale the image. Since the accessory can expand + * vertically make sure we don't go larger than 150 when scaling + * vertically. + */ + if (width >= height) { + ratio = (double) (targetDimension - 5) / width; + width = targetDimension - 5; + height = (int) (height * ratio); + } + else { + if (getHeight() > 150) { + ratio = (double) (targetDimension - 5) / height; + height = targetDimension - 5; + width = (int) (width * ratio); + } + else { + ratio = (double) getHeight() / height; + height = getHeight(); + width = (int) (width * ratio); + } + } + + return new ImageIcon(image.getScaledInstance(width, height, Image.SCALE_DEFAULT)); + } + + static class PresetInputVerifier extends InputVerifier { + + /** + * Matches user input against a regular expression. + */ + private Matcher matcher; + + PresetInputVerifier(final Pattern thePattern) { + matcher = thePattern.matcher(""); + } + + /** + * Return true only if the untrimmed user input matches the regular expression provided to the constructor. + * + * @param aComponent must be an instance of JTextComponent. + */ + public boolean verify(JComponent aComponent) { + JTextComponent textComponent = (JTextComponent) aComponent; + matcher.reset(textComponent.getText()); + return matcher.matches(); + } + + /** + * Always returns true, in this implementation, such that focus can always transfer to another + * component whenever the validation fails. + *

+ *

If super.shouldYieldFocus returns false, then clear the text field. + * + * @param aComponent is a JTextComponent. + */ + @Override + public boolean shouldYieldFocus(JComponent aComponent) { + if (!super.shouldYieldFocus(aComponent)) { + ((JTextComponent) aComponent).setText(""); + } + return true; + } + } +} diff --git a/core/src/net/sf/openrocket/gui/preset/PresetResultListener.java b/core/src/net/sf/openrocket/gui/preset/PresetResultListener.java new file mode 100644 index 000000000..a5c2e1bec --- /dev/null +++ b/core/src/net/sf/openrocket/gui/preset/PresetResultListener.java @@ -0,0 +1,11 @@ + +package net.sf.openrocket.gui.preset; + +import net.sf.openrocket.preset.ComponentPreset; + +/** + */ +public interface PresetResultListener { + + void notifyResult(ComponentPreset preset); +} diff --git a/core/src/net/sf/openrocket/gui/print/PrintUnit.java b/core/src/net/sf/openrocket/gui/print/PrintUnit.java index c603f2f97..310fba320 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintUnit.java +++ b/core/src/net/sf/openrocket/gui/print/PrintUnit.java @@ -7,6 +7,14 @@ package net.sf.openrocket.gui.print; * Utilities for print units. */ public enum PrintUnit { + FOOT { + public double toInches(double d) { return d*12; } + public double toMillis(double d) { return d/FEET_PER_MM; } + public double toCentis(double d) { return d/(FEET_PER_MM*TEN); } + public double toMeters(double d) { return d/(FEET_PER_MM*TEN*TEN*TEN); } + public long toPoints(double d) { return (long)(d * POINTS_PER_INCH * 12); } + public double convert(double d, PrintUnit u) { return u.toInches(d)/12; } + }, INCHES { public double toInches(double d) { return d; } public double toMillis(double d) { return d/INCHES_PER_MM; } @@ -50,13 +58,14 @@ public enum PrintUnit { // Handy constants for conversion methods public static final double INCHES_PER_MM = 0.0393700787d; + public static final double FEET_PER_MM = INCHES_PER_MM /12; public static final double MM_PER_INCH = 1.0d/INCHES_PER_MM; public static final long TEN = 10; /** * PPI is Postscript Point and is a standard of 72. Java2D also uses this internally as a pixel-per-inch, so pixels * and points are for the most part interchangeable (unless the defaults are changed), which makes translating * between the screen and a print job easier. - * + * * Not to be confused with Dots-Per-Inch, which is printer and print mode dependent. */ public static final int POINTS_PER_INCH = 72; diff --git a/core/src/net/sf/openrocket/gui/util/FileHelper.java b/core/src/net/sf/openrocket/gui/util/FileHelper.java index 937be1528..7fcdc2a4c 100644 --- a/core/src/net/sf/openrocket/gui/util/FileHelper.java +++ b/core/src/net/sf/openrocket/gui/util/FileHelper.java @@ -1,70 +1,73 @@ package net.sf.openrocket.gui.util; -import java.awt.Component; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Locale; - -import javax.imageio.ImageIO; -import javax.swing.JOptionPane; -import javax.swing.filechooser.FileFilter; - import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; + /** * Helper methods related to user-initiated file manipulation. *

* These methods log the necessary information to the debug log. -* +* * @author Sampo Niskanen */ public final class FileHelper { private static final LogHelper log = Application.getLogger(); private static final Translator trans = Application.getTranslator(); - - + + // TODO: HIGH: Rename translation keys - + /** File filter for any rocket designs (*.ork, *.rkt) */ public static final FileFilter ALL_DESIGNS_FILTER = new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter1"), ".ork", ".ork.gz", ".rkt", ".rkt.gz"); - + /** File filter for OpenRocket designs (*.ork) */ public static final FileFilter OPENROCKET_DESIGN_FILTER = new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter2"), ".ork", ".ork.gz"); - + /** File filter for RockSim designs (*.rkt) */ public static final FileFilter ROCKSIM_DESIGN_FILTER = new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter3"), ".rkt", ".rkt.gz"); - + + /** File filter for OpenRocket components and presets (*.orc) */ + public static final FileFilter OPEN_ROCKET_COMPONENT_FILTER = + new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter4"), ".orc", ".orc.gz"); + /** File filter for PDF files (*.pdf) */ public static final FileFilter PDF_FILTER = new SimpleFileFilter(trans.get("filetypes.pdf"), ".pdf"); - + /** File filter for CSV files (*.csv) */ public static final FileFilter CSV_FILE_FILTER = new SimpleFileFilter(trans.get("SimExpPan.desc"), ".csv"); - - - - + + + + private FileHelper() { // Prevent instantiation } - - + + public static FileFilter getImageFileFilter() { String[] extensions = ImageIO.getReaderFileSuffixes(); for (int i = 0; i < extensions.length; i++) { extensions[i] = extensions[i].toLowerCase(Locale.ENGLISH); } Arrays.sort(extensions); - + StringBuilder sb = new StringBuilder(); sb.append(trans.get("filetypes.images")); sb.append(" ("); @@ -75,31 +78,31 @@ public final class FileHelper { } } sb.append(")"); - + return new SimpleFileFilter(sb.toString(), extensions); } - - + + /** * Ensure that the provided file has a file extension. If the file does not have * any extension, append the provided extension to it. - * + * * @param original the original file * @param extension the extension to append if none exists (without preceding dot) * @return the resulting file */ public static File ensureExtension(File original, String extension) { - + if (original.getName().indexOf('.') < 0) { log.debug(1, "File name does not contain extension, adding '" + extension + "'"); String name = original.getAbsolutePath(); name = name + "." + extension; return new File(name); } - + return original; } - + /** * Ensure that the provided file has the given file extension. This differs from ensureExtension in that this * method guarantees that the file will have the extension, whereas ensureExtension only treats the extension @@ -110,7 +113,7 @@ public final class FileHelper { * @return the resulting file */ public static File forceExtension(File original, String extension) { - + if (!original.getName().toLowerCase(Locale.ENGLISH).endsWith(extension.toLowerCase(Locale.ENGLISH))) { log.debug(1, "File name does not contain extension, adding '" + extension + "'"); String name = original.getAbsolutePath(); @@ -122,15 +125,15 @@ public final class FileHelper { } return new File(name); } - + return original; } - - + + /** * Confirm that it is allowed to write to a file. If the file exists, * a confirmation dialog will be presented to the user to ensure overwriting is ok. - * + * * @param file the file that is going to be written. * @param parent the parent component for the dialog. * @return true to write, false to abort. @@ -149,23 +152,23 @@ public final class FileHelper { } return true; } - - + + /** * Display an error message to the user that writing a file failed. - * + * * @param e the I/O exception that caused the error. * @param parent the parent component for the dialog. */ public static void errorWriting(IOException e, Component parent) { - + log.warn(1, "Error writing to file", e); JOptionPane.showMessageDialog(parent, new Object[] { trans.get("error.writing.desc"), e.getLocalizedMessage() }, trans.get("error.writing.title"), JOptionPane.ERROR_MESSAGE); - + } - + } diff --git a/core/src/net/sf/openrocket/preset/ComponentPreset.java b/core/src/net/sf/openrocket/preset/ComponentPreset.java index ca33af84a..12331495d 100644 --- a/core/src/net/sf/openrocket/preset/ComponentPreset.java +++ b/core/src/net/sf/openrocket/preset/ComponentPreset.java @@ -52,6 +52,7 @@ public class ComponentPreset implements Comparable { ComponentPreset.SHAPE, ComponentPreset.AFT_OUTER_DIAMETER, ComponentPreset.AFT_SHOULDER_DIAMETER, + ComponentPreset.AFT_SHOULDER_LENGTH, ComponentPreset.LENGTH} ), TRANSITION( new TypedKey[] { @@ -97,7 +98,7 @@ public class ComponentPreset implements Comparable { ComponentPreset.INNER_DIAMETER, ComponentPreset.OUTER_DIAMETER, ComponentPreset.LENGTH} ), - + LAUNCH_LUG( new TypedKey[] { ComponentPreset.MANUFACTURER, ComponentPreset.PARTNO, @@ -114,7 +115,7 @@ public class ComponentPreset implements Comparable { ComponentPreset.WIDTH, ComponentPreset.THICKNESS, ComponentPreset.MATERIAL} ), - + PARACHUTE( new TypedKey[] { ComponentPreset.MANUFACTURER, ComponentPreset.PARTNO, @@ -177,7 +178,7 @@ public class ComponentPreset implements Comparable { public final static TypedKey LINE_COUNT = new TypedKey("LineCount", Integer.class); public final static TypedKey LINE_LENGTH = new TypedKey("LineLength", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey LINE_MATERIAL = new TypedKey("LineMaterial", Material.class); - + public final static TypedKey IMAGE = new TypedKey("Image", byte[].class); public final static List> orderedKeyList = Arrays.>asList( MANUFACTURER, diff --git a/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java b/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java index c9278ecff..9f49d9482 100644 --- a/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java +++ b/core/src/net/sf/openrocket/preset/xml/BaseComponentDTO.java @@ -1,7 +1,6 @@ package net.sf.openrocket.preset.xml; -import net.sf.openrocket.database.Databases; import net.sf.openrocket.material.Material; import net.sf.openrocket.motor.Manufacturer; import net.sf.openrocket.preset.ComponentPreset; @@ -76,6 +75,9 @@ public abstract class BaseComponentDTO { if ( preset.has(ComponentPreset.FILLED) ) { setFilled( preset.get(ComponentPreset.FILLED)); } + if (preset.has(ComponentPreset.IMAGE) ) { + setImageData(preset.get(ComponentPreset.IMAGE)); + } } public String getManufacturer() { @@ -171,6 +173,9 @@ public abstract class BaseComponentDTO { if ( filled != null ) { props.put(ComponentPreset.FILLED, getFilled()); } + if (image != null) { + props.put(ComponentPreset.IMAGE, image); + } } protected Material find(List materialList, AnnotatedMaterialDTO dto) { @@ -195,7 +200,7 @@ public abstract class BaseComponentDTO { } else { return null; } - + } static class AnnotatedMaterialDTO { diff --git a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java index 708a1e093..6a70fe2c6 100644 --- a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java +++ b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentSaver.java @@ -1,6 +1,17 @@ package net.sf.openrocket.preset.xml; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.preset.InvalidComponentPresetException; +import net.sf.openrocket.startup.Application; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -10,16 +21,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; - -import net.sf.openrocket.material.Material; -import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.preset.InvalidComponentPresetException; -import net.sf.openrocket.startup.Application; - /** * The active manager class that is the entry point for reading and writing *.orc files. */ @@ -39,6 +40,16 @@ public class OpenRocketComponentSaver { } } + public boolean save(File file, List theMaterialList, List thePresetList) throws + JAXBException, + IOException { + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); + writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList)); + writer.flush(); + writer.close(); + return true; + } + /** * This method marshals a list of materials and ComponentPresets into an .orc formatted XML string. * @@ -57,31 +68,31 @@ public class OpenRocketComponentSaver { StringWriter sw = new StringWriter(); // We're going to sort the initial data since that makes the output much easier on the eyes. - + Collections.sort(theMaterialList, new Comparator() { @Override public int compare(Material o1, Material o2) { return o1.getName().compareTo( o2.getName() ); } - + }); - + Collections.sort(thePresetList, new Comparator() { @Override public int compare(ComponentPreset o1, ComponentPreset o2) { int manucmp = o1.getManufacturer().getSimpleName().compareTo( o2.getManufacturer().getSimpleName() ); - + if ( manucmp != 0 ) { return manucmp; } - + return o1.getPartNo().compareTo( o2.getPartNo()); } - + }); - + marshaller.marshal(toOpenRocketComponentDTO(theMaterialList, thePresetList), sw); return sw.toString(); @@ -90,8 +101,8 @@ public class OpenRocketComponentSaver { /** * This method unmarshals from a Reader that is presumed to be open on an XML file in .orc format. * - * @param is an open reader; StringBufferInputStream could not be used because it's deprecated and does not handle UTF - * characters correctly + * @param is an open reader; StringBufferInputStream could not be used because it's deprecated and does not handle + * UTF characters correctly * * @return a list of ComponentPresets * @@ -127,13 +138,13 @@ public class OpenRocketComponentSaver { * * @param is an open Reader; assumed to be opened on a file of XML in .orc format * - * @return the OpenRocketComponentDTO that is a POJO representation of the XML; null if the data could not be read or - * was in an invalid format + * @return the OpenRocketComponentDTO that is a POJO representation of the XML; null if the data could not be read + * or was in an invalid format */ private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException { /** The context is thread-safe, but unmarshallers are not. Create a local one. */ Unmarshaller unmarshaller = context.createUnmarshaller(); - return (OpenRocketComponentDTO) unmarshaller.unmarshal(is); + return (OpenRocketComponentDTO) unmarshaller.unmarshal(is); //new StreamSource(is)); } /** diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java index a0804b20c..6b12f156e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -1,10 +1,5 @@ package net.sf.openrocket.rocketcomponent; -import static java.lang.Math.sin; -import static net.sf.openrocket.util.MathUtil.*; - -import java.util.Collection; - import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; @@ -12,19 +7,25 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import java.util.Collection; + +import static java.lang.Math.sin; +import static net.sf.openrocket.util.MathUtil.pow2; +import static net.sf.openrocket.util.MathUtil.pow3; + public class Transition extends SymmetricComponent { private static final Translator trans = Application.getTranslator(); private static final double CLIP_PRECISION = 0.0001; - + private Shape type; private double shapeParameter; private boolean clipped; // Not to be read - use isClipped(), which may be overriden - + private double radius1, radius2; private boolean autoRadius1, autoRadius2; // Whether the start radius is automatic - + private double foreShoulderRadius; private double foreShoulderThickness; @@ -34,25 +35,25 @@ public class Transition extends SymmetricComponent { private double aftShoulderThickness; private double aftShoulderLength; private boolean aftShoulderCapped; - + // Used to cache the clip length private double clipLength = -1; - + public Transition() { super(); - + this.radius1 = DEFAULT_RADIUS; this.radius2 = DEFAULT_RADIUS; this.length = DEFAULT_RADIUS * 3; this.autoRadius1 = true; this.autoRadius2 = true; - + this.type = Shape.CONICAL; this.shapeParameter = 0; this.clipped = true; } - + //////// Length //////// @Override public void setLength( double length ) { @@ -66,7 +67,7 @@ public class Transition extends SymmetricComponent { //////// Fore radius //////// - + @Override public double getForeRadius() { @@ -83,39 +84,39 @@ public class Transition extends SymmetricComponent { } return radius1; } - + public void setForeRadius(double radius) { if ((this.radius1 == radius) && (autoRadius1 == false)) return; - + this.autoRadius1 = false; this.radius1 = Math.max(radius, 0); - + if (this.thickness > this.radius1 && this.thickness > this.radius2) this.thickness = Math.max(this.radius1, this.radius2); - + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isForeRadiusAutomatic() { return autoRadius1; } - + public void setForeRadiusAutomatic(boolean auto) { if (autoRadius1 == auto) return; - + autoRadius1 = auto; clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + //////// Aft radius ///////// - + @Override public double getAftRadius() { if (isAftRadiusAutomatic()) { @@ -131,66 +132,66 @@ public class Transition extends SymmetricComponent { } return radius2; } - - + + public void setAftRadius(double radius) { if ((this.radius2 == radius) && (autoRadius2 == false)) return; - + this.autoRadius2 = false; this.radius2 = Math.max(radius, 0); - + if (this.thickness > this.radius1 && this.thickness > this.radius2) this.thickness = Math.max(this.radius1, this.radius2); clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isAftRadiusAutomatic() { return autoRadius2; } - + public void setAftRadiusAutomatic(boolean auto) { if (autoRadius2 == auto) return; - + autoRadius2 = auto; clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + //// Radius automatics - + @Override protected double getFrontAutoRadius() { if (isAftRadiusAutomatic()) return -1; return getAftRadius(); } - - + + @Override protected double getRearAutoRadius() { if (isForeRadiusAutomatic()) return -1; return getForeRadius(); } - - + + //////// Type & shape ///////// - + public Shape getType() { return type; } - + public void setType(Shape type) { if (type == null) { throw new IllegalArgumentException("setType called with null argument"); @@ -202,50 +203,50 @@ public class Transition extends SymmetricComponent { this.shapeParameter = type.defaultParameter(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public double getShapeParameter() { return shapeParameter; } - + public void setShapeParameter(double n) { if (shapeParameter == n) return; this.shapeParameter = MathUtil.clamp(n, type.minParameter(), type.maxParameter()); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public boolean isClipped() { if (!type.isClippable()) return false; return clipped; } - + public void setClipped(boolean c) { if (clipped == c) return; clipped = c; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public boolean isClippedEnabled() { return type.isClippable(); } - + public double getShapeParameterMin() { return type.minParameter(); } - + public double getShapeParameterMax() { return type.maxParameter(); } - - + + //////// Shoulders //////// - + public double getForeShoulderRadius() { return foreShoulderRadius; } - + public void setForeShoulderRadius(double foreShoulderRadius) { if (MathUtil.equals(this.foreShoulderRadius, foreShoulderRadius)) return; @@ -253,47 +254,47 @@ public class Transition extends SymmetricComponent { clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getForeShoulderThickness() { return foreShoulderThickness; } - + public void setForeShoulderThickness(double foreShoulderThickness) { if (MathUtil.equals(this.foreShoulderThickness, foreShoulderThickness)) return; this.foreShoulderThickness = foreShoulderThickness; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getForeShoulderLength() { return foreShoulderLength; } - + public void setForeShoulderLength(double foreShoulderLength) { if (MathUtil.equals(this.foreShoulderLength, foreShoulderLength)) return; this.foreShoulderLength = foreShoulderLength; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public boolean isForeShoulderCapped() { return foreShoulderCapped; } - + public void setForeShoulderCapped(boolean capped) { if (this.foreShoulderCapped == capped) return; this.foreShoulderCapped = capped; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + public double getAftShoulderRadius() { return aftShoulderRadius; } - + public void setAftShoulderRadius(double aftShoulderRadius) { if (MathUtil.equals(this.aftShoulderRadius, aftShoulderRadius)) return; @@ -301,45 +302,45 @@ public class Transition extends SymmetricComponent { clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getAftShoulderThickness() { return aftShoulderThickness; } - + public void setAftShoulderThickness(double aftShoulderThickness) { if (MathUtil.equals(this.aftShoulderThickness, aftShoulderThickness)) return; this.aftShoulderThickness = aftShoulderThickness; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getAftShoulderLength() { return aftShoulderLength; } - + public void setAftShoulderLength(double aftShoulderLength) { if (MathUtil.equals(this.aftShoulderLength, aftShoulderLength)) return; this.aftShoulderLength = aftShoulderLength; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public boolean isAftShoulderCapped() { return aftShoulderCapped; } - + public void setAftShoulderCapped(boolean capped) { if (this.aftShoulderCapped == capped) return; this.aftShoulderCapped = capped; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /////////// Shape implementations //////////// - + /** @@ -349,20 +350,20 @@ public class Transition extends SymmetricComponent { public double getRadius(double x) { if (x < 0 || x > length) return 0; - + double r1 = getForeRadius(); double r2 = getAftRadius(); - + if (r1 == r2) return r1; - + if (r1 > r2) { x = length - x; double tmp = r1; r1 = r2; r2 = tmp; } - + if (isClipped()) { // Check clip calculation if (clipLength < 0) @@ -373,7 +374,7 @@ public class Transition extends SymmetricComponent { return r1 + type.getRadius(x, r2 - r1, length, shapeParameter); } } - + /** * Numerically solve clipLength from the equation * r1 == type.getRadius(clipLength,r2,clipLength+length) @@ -381,27 +382,27 @@ public class Transition extends SymmetricComponent { */ private void calculateClip(double r1, double r2) { double min = 0, max = length; - + if (r1 >= r2) { double tmp = r1; r1 = r2; r2 = tmp; } - + if (r1 == 0) { clipLength = 0; return; } - + if (length <= 0) { clipLength = 0; return; } - + // Required: // getR(min,min+length,r2) - r1 < 0 // getR(max,max+length,r2) - r1 > 0 - + int n = 0; while (type.getRadius(max, r2, max + length, shapeParameter) - r1 < 0) { min = max; @@ -410,7 +411,7 @@ public class Transition extends SymmetricComponent { if (n > 10) break; } - + while (true) { clipLength = (min + max) / 2; if ((max - min) < CLIP_PRECISION) @@ -423,14 +424,14 @@ public class Transition extends SymmetricComponent { } } } - - + + @Override public double getInnerRadius(double x) { return Math.max(getRadius(x) - thickness, 0); } - - + + @Override public Collection getComponentBounds() { @@ -454,7 +455,7 @@ public class Transition extends SymmetricComponent { final double ir = Math.max(getForeShoulderRadius() - getForeShoulderThickness(), 0); volume += ringVolume(ir, 0, getForeShoulderThickness() ); } - + if (getAftShoulderLength() > 0.001) { final double or = getAftShoulderRadius(); final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); @@ -464,7 +465,7 @@ public class Transition extends SymmetricComponent { final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); volume += ringVolume(ir, 0, getAftShoulderThickness() ); } - + return volume; } @@ -482,7 +483,7 @@ public class Transition extends SymmetricComponent { getForeShoulderThickness() - getForeShoulderLength(), getMaterial().getDensity())); } - + if (getAftShoulderLength() > 0.001) { final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); cg = cg.average(ringCG(getAftShoulderRadius(), ir, getLength(), @@ -496,8 +497,8 @@ public class Transition extends SymmetricComponent { } return cg; } - - + + /* * The moments of inertia are not explicitly corrected for the shoulders. * However, since the mass is corrected, the inertia is automatically corrected @@ -514,13 +515,13 @@ public class Transition extends SymmetricComponent { //// Transition return trans.get("Transition.Transition"); } - + @Override protected void componentChanged(ComponentChangeEvent e) { super.componentChanged(e); clipLength = -1; } - + /** * Check whether the given type can be added to this component. Transitions allow any * InternalComponents to be added. @@ -534,7 +535,7 @@ public class Transition extends SymmetricComponent { return true; return false; } - + @Override public Type getPresetType() { return ComponentPreset.Type.TRANSITION; @@ -548,7 +549,7 @@ public class Transition extends SymmetricComponent { if ( preset.has(ComponentPreset.FILLED ) ) { presetFilled = preset.get( ComponentPreset.FILLED); } - + if ( preset.has(ComponentPreset.SHAPE) ) { Shape s = preset.get(ComponentPreset.SHAPE); this.setType(s); @@ -598,7 +599,7 @@ public class Transition extends SymmetricComponent { * @author Sampo Niskanen */ public static enum Shape { - + /** * Conical shape. */ @@ -616,7 +617,7 @@ public class Transition extends SymmetricComponent { return radius * x / length; } }, - + /** * Ogive shape. The shape parameter is the portion of an extended tangent ogive * that will be used. That is, for param==1 a tangent ogive will be produced, and @@ -632,12 +633,12 @@ public class Transition extends SymmetricComponent { public boolean usesParameter() { return true; // Range 0...1 is default } - + @Override public double defaultParameter() { return 1.0; // Tangent ogive by default } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -645,17 +646,17 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 1; - + // Impossible to calculate ogive for length < radius, scale instead // TODO: LOW: secant ogive could be calculated lower if (length < radius) { x = x * radius / length; length = radius; } - + if (param < 0.001) return CONICAL.getRadius(x, radius, length, param); - + // Radius of circle is: double R = MathUtil.safeSqrt((pow2(length) + pow2(radius)) * (pow2((2 - param) * length) + pow2(param * radius)) / (4 * pow2(param * radius))); @@ -665,7 +666,7 @@ public class Transition extends SymmetricComponent { return MathUtil.safeSqrt(R * R - (L - x) * (L - x)) - y0; } }, - + /** * Ellipsoidal shape. */ @@ -673,7 +674,7 @@ public class Transition extends SymmetricComponent { ELLIPSOID(trans.get("Shape.Ellipsoid"), //// An ellipsoidal nose cone has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. trans.get("Shape.Ellipsoid.desc1"), - //// An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. If the transition is not clipped, then the profile is extended at the center by the corresponding radius. + //// An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. If the transition is not clipped, then the profile is extended at the center by the corresponding radius. trans.get("Shape.Ellipsoid.desc2"), true) { @Override public double getRadius(double x, double radius, double length, double param) { @@ -684,7 +685,7 @@ public class Transition extends SymmetricComponent { return MathUtil.safeSqrt(2 * radius * x - x * x); // radius/length * sphere } }, - + //// Power series POWER(trans.get("Shape.Powerseries"), trans.get("Shape.Powerseries.desc1"), @@ -693,12 +694,12 @@ public class Transition extends SymmetricComponent { public boolean usesParameter() { // Range 0...1 return true; } - + @Override public double defaultParameter() { return 0.5; } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -714,29 +715,29 @@ public class Transition extends SymmetricComponent { } return radius * Math.pow(x / length, param); } - + }, - + //// Parabolic series PARABOLIC(trans.get("Shape.Parabolicseries"), ////A parabolic series nose cone has a profile of a parabola. The shape parameter defines the segment of the parabola to utilize. The shape parameter 1.0 produces a full parabola which is tangent to the body tube, 0.75 produces a 3/4 parabola, 0.5 procudes a 1/2 parabola and 0 produces a conical nose cone. trans.get("Shape.Parabolicseries.desc1"), ////A parabolic series transition has a profile of a parabola. The shape parameter defines the segment of the parabola to utilize. The shape parameter 1.0 produces a full parabola which is tangent to the body tube at the aft end, 0.75 produces a 3/4 parabola, 0.5 procudes a 1/2 parabola and 0 produces a conical transition. trans.get("Shape.Parabolicseries.desc2")) { - + // In principle a parabolic transition is clippable, but the difference is // negligible. - + @Override public boolean usesParameter() { // Range 0...1 return true; } - + @Override public double defaultParameter() { return 1.0; } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -744,28 +745,28 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 1; - + return radius * ((2 * x / length - param * pow2(x / length)) / (2 - param)); } }, - + //// Haack series HAACK(trans.get("Shape.Haackseries"), //// The Haack series nose cones are designed to minimize drag. The shape parameter 0 produces an LD-Haack or Von Karman nose cone, which minimizes drag for fixed length and diameter, while a value of 0.333 produces an LV-Haack nose cone, which minimizes drag for fixed length and volume. trans.get("Shape.Haackseries.desc1"), - //// The Haack series nose cones are designed to minimize drag. These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions. The shape parameter 0 produces an LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack shape. + //// The Haack series nose cones are designed to minimize drag. These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions. The shape parameter 0 produces an LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack shape. trans.get("Shape.Haackseries.desc2"), true) { - + @Override public boolean usesParameter() { return true; } - + @Override public double maxParameter() { return 1.0 / 3.0; // Range 0...1/3 } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -773,7 +774,7 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 2; - + double theta = Math.acos(1 - 2 * x / length); if (MathUtil.equals(param, 0)) { return radius * MathUtil.safeSqrt((theta - sin(2 * theta) / 2) / Math.PI); @@ -781,7 +782,7 @@ public class Transition extends SymmetricComponent { return radius * MathUtil.safeSqrt((theta - sin(2 * theta) / 2 + param * pow3(sin(theta))) / Math.PI); } }, - + // POLYNOMIAL("Smooth polynomial", // "A polynomial is fitted such that the nose cone profile is horizontal "+ // "at the aft end of the transition. The angle at the tip is defined by "+ @@ -813,18 +814,18 @@ public class Transition extends SymmetricComponent { // } // } ; - + // Privete fields of the shapes private final String name; private final String transitionDesc; private final String noseconeDesc; private final boolean canClip; - + // Non-clippable constructor Shape(String name, String noseconeDesc, String transitionDesc) { this(name, noseconeDesc, transitionDesc, false); } - + // Clippable constructor Shape(String name, String noseconeDesc, String transitionDesc, boolean canClip) { this.name = name; @@ -832,29 +833,29 @@ public class Transition extends SymmetricComponent { this.noseconeDesc = noseconeDesc; this.transitionDesc = transitionDesc; } - - + + /** * Return the name of the transition shape name. */ public String getName() { return name; } - + /** * Get a description of the Transition shape. */ public String getTransitionDescription() { return transitionDesc; } - + /** * Get a description of the NoseCone shape. */ public String getNoseConeDescription() { return noseconeDesc; } - + /** * Check whether the shape differs in clipped mode. The clipping should be * enabled by default if possible. @@ -862,35 +863,35 @@ public class Transition extends SymmetricComponent { public boolean isClippable() { return canClip; } - + /** * Return whether the shape uses the shape parameter. (Default false.) */ public boolean usesParameter() { return false; } - + /** * Return the minimum value of the shape parameter. (Default 0.) */ public double minParameter() { return 0.0; } - + /** * Return the maximum value of the shape parameter. (Default 1.) */ public double maxParameter() { return 1.0; } - + /** * Return the default value of the shape parameter. (Default 0.) */ public double defaultParameter() { return 0.0; } - + /** * Calculate the basic radius of a transition with the given radius, length and * shape parameter at the point x from the tip of the component. It is assumed @@ -904,8 +905,8 @@ public class Transition extends SymmetricComponent { * @return The basic radius at the given position. */ public abstract double getRadius(double x, double radius, double length, double param); - - + + /** * Returns the name of the shape (same as getName()). */ @@ -913,5 +914,22 @@ public class Transition extends SymmetricComponent { public String toString() { return name; } + + /** + * Lookup the Shape given the localized name. This differs from the standard valueOf as that looks up + * based on the canonical name, not the localized name which is an instance var. + * + * @param localizedName + * @return + */ + public static Shape toShape(String localizedName) { + Shape[] values = Shape.values(); + for (Shape value : values) { + if (value.getName().equals(localizedName)) { + return value; + } + } + return null; + } } }