+2011-07-21 Sampo Niskanen
+
+ * [BUG] Converting triangular fin to freeform
+ * [BUG] Unit conversions in printout
+ * [BUG] Mass computations in printout
+ * [BUG] "Not a drop" exception in ComponentTreeTransferHandler
+ * [BUG] Plot annotation positioned wrong
+ * [BUG] Exception when writing PDF failed
+ * Language selector in preferences
+
2011-07-18 Sampo Niskanen
* Select motor type based on known manufacturers
#
# className.ComponentType.componentName
#
-
+#
+# Text tokens within braces should not be translated, e.g.
+# "The file '{filename}' exists."
+# They are pieces that are inserted dynamically.
+#
! Set to the name of the current translation file (used for debugging purposes)
BasicFrame.WarningDialog.txt2 = Some design features may not have been loaded correctly.
BasicFrame.WarningDialog.title = Warnings while opening file
+
+! General error messages used in multiple contexts
+error.fileExists.title = File exists
+error.fileExists.desc = File '{filename}' exists. Do you want to overwrite it?
+
+error.writing.title = Error writing file
+error.writing.desc = An error occurred while writing to the file:
+
+
! Labels used in buttons of dialog windows
button.ok = OK
button.cancel = Cancel
PrintDialog.error.preview.desc1 = Unable to open PDF preview.
PrintDialog.error.preview.desc2 = Please use the "Save as PDF" option instead.
+
!PrintSettingsDialog
PrintSettingsDialog.title = Print settings
PrintSettingsDialog.lbl.Templatefillcolor = Template fill color:
bugreport.dlg.failedmsg3 = Error sending report
bugreport.reportDialog.txt = <html><b>You can report a bug in OpenRocket by filling in and submitting the form below.</b><br>You can also report bugs and include attachments on the project web site.
bugreport.reportDialog.txt2 = <html><b>Please include a short description about what you were doing when the exception occurred.</b>
+bugreport.dlg.provideDescription = Please provide a description of the bug first.
+bugreport.dlg.provideDescription.title = Bug description missing
! Debug log dialog
matedtpan.but.ttip.revertall = Delete all user-defined materials
matedtpan.title.Deletealluser-defined = Delete all user-defined materials?
matedtpan.title.Revertall = Revert all?
-matedtpan.lbl.edtmaterials = <html><i>Editing materials will not affect existing rocket designs.</i>
+matedtpan.lbl.edtmaterials = Editing materials will not affect existing rocket designs.
!MaterialModel
MaterialModel.title.Material = Material
pref.dlg.lbl.Motordimensions = Motor dimensions:
pref.dlg.lbl.Surfacedensity = Surface density:
pref.dlg.lbl.Distance = Distance:
-pref.dlg.lbl.Bulkdensity = Bulk density::
+pref.dlg.lbl.Bulkdensity = Bulk density:
pref.dlg.lbl.Velocity = Velocity:
pref.dlg.lbl.Surfaceroughness = Surface roughness:
pref.dlg.lbl.Acceleration = Acceleration:
pref.dlg.lbl.Momentofinertia = Moment of inertia:
pref.dlg.lbl.Pressure = Pressure:
pref.dlg.lbl.Stability = Stability:
+pref.dlg.lbl.FlightTime = Flight time:
pref.dlg.lbl.effect1 = The effects will take place the next time you open a window.
pref.dlg.lbl.Checkingupdates = Checking for updates...
pref.dlg.lbl.msg1 = An error occurred while communicating with the server.
pref.dlg.Add = Add
pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket.
+PreferencesDialog.lbl.language = Interface language:
+PreferencesDialog.languages.default = System default
+PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket.
! Simulation edit dialog
simedtdlg.but.runsimulation = Run simulation
ScaleDialog.checkbox.scaleMass = Update explicit mass values
ScaleDialog.checkbox.scaleMass.ttip = Scale mass component and override mass values by the cube of the scaling factor
ScaleDialog.button.scale = Scale
-
+ScaleDialog.undo.scaleRocket = Scale rocket
+ScaleDialog.undo.scaleComponent = Scale component
+ScaleDialog.undo.scaleComponents = Scale components
!icons
Icons.Undo = Undo
matedtpan.but.ttip.revertall = Alle benutzerdefinierten Materialien löschen\r
matedtpan.title.Deletealluser-defined = Alle benutzerdefinierten Materialien löschen?\r
matedtpan.title.Revertall = Alle löschen?\r
-matedtpan.lbl.edtmaterials = <html><i>Das Bearbeiten der Materialien beeinflusst keine bereits existierenden Raketendesigns.</i>\r
+matedtpan.lbl.edtmaterials = Das Bearbeiten der Materialien beeinflusst keine bereits existierenden Raketendesigns.\r
\r
!MaterialModel\r
MaterialModel.title.Material = Material\r
matedtpan.but.ttip.revertall = Borrar todos los materiales predefinidos
matedtpan.title.Deletealluser-defined = ¿Borrar todos los materiales predefinidos?
matedtpan.title.Revertall = ¿Revertir todo?
-matedtpan.lbl.edtmaterials = <html><i>Editar materiales que no afectaran los diseños existentes.</i>
+matedtpan.lbl.edtmaterials = Editar materiales que no afectaran los diseños existentes.
!MaterialModel
MaterialModel.title.Material = Material
matedtpan.but.ttip.revertall = Supprimer tous les matériaux personnalisés\r
matedtpan.title.Deletealluser-defined = Effacer tous les matériaux personalisés?\r
matedtpan.title.Revertall = Revenir aux valeurs précédentes?\r
-matedtpan.lbl.edtmaterials = <html><i>Modifier les matériaux n'affectera pas les projets fusée existants.</i>\r
+matedtpan.lbl.edtmaterials = Modifier les matériaux n'affectera pas les projets fusée existants.\r
\r
!MaterialModel\r
MaterialModel.title.Material = Matériau\r
edit-paste.png
edit-redo.png
edit-undo.png
+edit-scale.png (modified from edit-copy.png)
From the "Crystal" project:
-
+ /**
+ * Simulate the flight.
+ *
+ * @param additionalListeners additional simulation listeners (those defined by the simulation are used in any case)
+ * @throws SimulationException if a problem occurs during simulation
+ */
public void simulate(SimulationListener... additionalListeners)
throws SimulationException {
mutex.lock("simulate");
import net.sf.openrocket.file.iterator.DirectoryIterator;
import net.sf.openrocket.file.iterator.FileIterator;
-import net.sf.openrocket.gui.main.SimpleFileFilter;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Pair;
+import net.sf.openrocket.util.SimpleFileFilter;
public final class MotorLoaderHelper {
public static final Charset CHARSET = Charset.forName(CHARSET_NAME);
- /** Any delay longed than this will be interpreted as a plugged motor. */
+ /** Any delay longer than this will be interpreted as a plugged motor. */
private static final int DELAY_LIMIT = 90;
- // FIXME: Obtain default motor type from manufacturer info
-
@Override
protected Charset getDefaultCharset() {
private static final String REPORT_EMAIL = "openrocket-bugs@lists.sourceforge.net";
private static final Translator trans = Application.getTranslator();
-
- public BugReportDialog(Window parent, String labelText, String message) {
+
+ public BugReportDialog(Window parent, String labelText, final String message, final boolean sendIfUnchanged) {
//// Bug report
super(parent, trans.get("bugreport.dlg.title"), Dialog.ModalityType.APPLICATION_MODAL);
panel.add(label, "gapleft para, wrap");
//// Otherwise, send the text below to the address:
- panel.add(new JLabel(trans.get("bugreport.dlg.otherwise") +" "),
+ panel.add(new JLabel(trans.get("bugreport.dlg.otherwise") + " "),
"gapleft para, split 2, gapright rel");
panel.add(new SelectableLabel(REPORT_EMAIL), "growx, wrap para");
@Override
public void actionPerformed(ActionEvent e) {
String text = textArea.getText();
+ if (text.equals(message) && !sendIfUnchanged) {
+ JOptionPane.showMessageDialog(BugReportDialog.this,
+ trans.get("bugreport.dlg.provideDescription"),
+ trans.get("bugreport.dlg.provideDescription.title"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
try {
BugReporter.sendBugReport(text);
"Bug report sent", JOptionPane.INFORMATION_MESSAGE);*/
JOptionPane.showMessageDialog(BugReportDialog.this,
new Object[] { trans.get("bugreport.dlg.successmsg1"),
- trans.get("bugreport.dlg.successmsg2") },
+ trans.get("bugreport.dlg.successmsg2") },
trans.get("bugreport.dlg.successmsg3"), JOptionPane.INFORMATION_MESSAGE);
} catch (Exception ex) {
new Object[] { trans.get("bugreport.dlg.failedmsg1"),
ex.getClass().getSimpleName() + ": " + ex.getMessage(), " ",
//// Please send the report manually to
- trans.get("bugreport.dlg.failedmsg2") +" " + REPORT_EMAIL },
+ trans.get("bugreport.dlg.failedmsg2") + " " + REPORT_EMAIL },
//// Error sending report
- trans.get("bugreport.dlg.failedmsg3"), JOptionPane.ERROR_MESSAGE);
+ trans.get("bugreport.dlg.failedmsg3"), JOptionPane.ERROR_MESSAGE);
}
}
});
sb.append("---------- End of bug report ----------\n");
sb.append('\n');
- BugReportDialog reportDialog =
- new BugReportDialog(parent,
- //// <html><b>You can report a bug in OpenRocket by filling in and submitting the form below.</b><br>You can also report bugs and include attachments on the project web site.
- trans.get("bugreport.reportDialog.txt"), sb.toString());
+ BugReportDialog reportDialog = new BugReportDialog(parent,
+ trans.get("bugreport.reportDialog.txt"), sb.toString(), false);
reportDialog.setVisible(true);
}
sb.append('\n');
BugReportDialog reportDialog =
- //// <html><b>Please include a short description about what you were doing when the exception occurred.</b>
- new BugReportDialog(parent, trans.get("bugreport.reportDialog.txt2"), sb.toString());
+ //// <html><b>Please include a short description about what you were doing when the exception occurred.</b>
+ new BugReportDialog(parent, trans.get("bugreport.reportDialog.txt2"), sb.toString(), true);
reportDialog.setVisible(true);
}
import javax.swing.JScrollPane;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
-import javax.swing.filechooser.FileFilter;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.main.ExceptionHandler;
import net.sf.openrocket.gui.print.PrintController;
import net.sf.openrocket.gui.print.PrintSettings;
import net.sf.openrocket.gui.print.PrintableContext;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.FileHelper;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Prefs;
*/
public class PrintDialog extends JDialog implements TreeSelectionListener {
- // FIXME: Printouts use SI units even when imperial are selected
- // FIXME: Array out of bounds exception when printing
-
private static final LogHelper log = Application.getLogger();
private static final Translator trans = Application.getTranslator();
private boolean onSavePDF() {
JFileChooser chooser = new JFileChooser();
- // Note: source for ExampleFileFilter can be found in FileChooserDemo,
- // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
- FileFilter filter = new FileFilter() {
+ chooser.setFileFilter(FileHelper.PDF_FILTER);
+
+ // Select initial directory
+ File dir = document.getFile();
+ if (dir != null) {
+ dir = dir.getParentFile();
+ }
+ if (dir == null) {
+ dir = Prefs.getDefaultDirectory();
+ }
+ chooser.setCurrentDirectory(dir);
+
+ int returnVal = chooser.showSaveDialog(this);
+ File file = chooser.getSelectedFile();
+ if (returnVal == JFileChooser.APPROVE_OPTION && file != null) {
- //Accept all directories and all pdf files.
- @Override
- public boolean accept(File f) {
- if (f.isDirectory())
- return true;
- return f.getName().toLowerCase().endsWith(".pdf");
+ file = FileHelper.ensureExtension(file, "pdf");
+ if (!FileHelper.confirmWrite(file, this)) {
+ return false;
}
- //The description of this filter
- @Override
- public String getDescription() {
- return trans.get("filetypes.pdf");
- }
- };
- chooser.setFileFilter(filter);
- int returnVal = chooser.showSaveDialog(this);
- if (returnVal == JFileChooser.APPROVE_OPTION) {
-
try {
- String fname = chooser.getSelectedFile().getCanonicalPath();
- if (!getExtension(fname).equals("pdf")) {
- fname = fname + ".pdf";
- }
- File f = new File(fname);
+
PrintSettings settings = Prefs.getPrintSettings();
// TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
TemplateProperties.setColors(settings);
- generateReport(f, settings);
+ generateReport(file, settings);
+
} catch (IOException e) {
- ExceptionHandler.handleErrorCondition(e);
+ FileHelper.errorWriting(e, this);
+ return false;
}
return true;
} else {
}
}
- /**
- * Get the extension of a file.
- */
- private static String getExtension(String s) {
- String ext = null;
- int i = s.lastIndexOf('.');
-
- if (i > 0 && i < s.length() - 1) {
- ext = s.substring(i + 1).toLowerCase();
- }
- return ext != null ? ext : "";
- }
}
private boolean changing = false;
- // FIXME: Localize
-
/**
* Sole constructor.
*
// Scale the entire rocket design
try {
- document.startUndo("Scale rocket");
+ document.startUndo(trans.get("undo.scaleRocket"));
for (RocketComponent c : document.getRocket()) {
scale(c, mul, scaleMass);
}
// Scale component and subcomponents
try {
- document.startUndo("Scale components");
+ document.startUndo(trans.get("undo.scaleComponents"));
for (RocketComponent c : selection) {
scale(c, mul, scaleMass);
}
// Scale only the selected component
try {
- document.startUndo("Scale component");
+ document.startUndo(trans.get("undo.scaleComponent"));
scale(selection, mul, scaleMass);
} finally {
document.stopUndo();
import net.sf.openrocket.database.Databases;
import net.sf.openrocket.gui.adaptors.Column;
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
+import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.dialogs.CustomMaterialDialog;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.unit.Value;
public class MaterialEditPanel extends JPanel {
-
+
private final JTable table;
private final JButton addButton;
private final JButton deleteButton;
private final JButton revertButton;
private static final Translator trans = Application.getTranslator();
-
+
public MaterialEditPanel() {
super(new MigLayout("fill"));
-
+
// TODO: LOW: Create sorter that keeps material types always in order
final ColumnTableModel model = new ColumnTableModel(
//// Material
public Object getValueAt(int row) {
return getMaterial(row).getType().toString();
}
+
@Override
public int getDefaultWidth() {
return 15;
throw new IllegalStateException("Material type " + m.getType());
}
}
+
@Override
public int getDefaultWidth() {
return 15;
}
+
@Override
public Class<?> getColumnClass() {
return Value.class;
}
}
- ) {
- @Override
- public int getRowCount() {
- return Databases.BULK_MATERIAL.size() + Databases.SURFACE_MATERIAL.size() +
- Databases.LINE_MATERIAL.size();
- }
- };
+ ) {
+ @Override
+ public int getRowCount() {
+ return Databases.BULK_MATERIAL.size() + Databases.SURFACE_MATERIAL.size() +
+ Databases.LINE_MATERIAL.size();
+ }
+ };
table = new JTable(model);
model.setColumnWidths(table.getColumnModel());
table.setDefaultRenderer(Object.class, new MaterialCellRenderer());
this.add(new JScrollPane(table), "w 200px, h 100px, grow 100");
-
+
//// New button
addButton = new JButton(trans.get("matedtpan.but.new"));
//// Add a new material
CustomMaterialDialog dialog = new CustomMaterialDialog(
SwingUtilities.getWindowAncestor(MaterialEditPanel.this),
//// Add a custom material
- null, false, trans.get("matedtpan.title.Addcustmaterial"));
+ null, false, trans.get("matedtpan.title.Addcustmaterial"));
dialog.setVisible(true);
if (dialog.getOkClicked()) {
Material mat = dialog.getMaterial();
dialog = new CustomMaterialDialog(
SwingUtilities.getWindowAncestor(MaterialEditPanel.this),
//// Add a custom material
- m, false, trans.get("matedtpan.title.Addcustmaterial"),
+ m, false, trans.get("matedtpan.title.Addcustmaterial"),
//// The built-in materials cannot be modified.
trans.get("matedtpan.title2.Editmaterial"));
}
});
this.add(deleteButton, "gap rel rel para para, growx 1, top");
-
+
this.add(new JPanel(), "grow 1");
//// Revert all button
revertButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- int sel = JOptionPane.showConfirmDialog(MaterialEditPanel.this,
+ int sel = JOptionPane.showConfirmDialog(MaterialEditPanel.this,
//// Delete all user-defined materials?
- trans.get("matedtpan.title.Deletealluser-defined"),
+ trans.get("matedtpan.title.Deletealluser-defined"),
//// Revert all?
- trans.get("matedtpan.title.Revertall"),
+ trans.get("matedtpan.title.Revertall"),
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (sel == JOptionPane.YES_OPTION) {
Iterator<Material> iterator;
-
+
iterator = Databases.LINE_MATERIAL.iterator();
while (iterator.hasNext()) {
if (iterator.next().isUserDefined())
iterator.remove();
}
-
+
iterator = Databases.SURFACE_MATERIAL.iterator();
while (iterator.hasNext()) {
if (iterator.next().isUserDefined())
iterator.remove();
}
-
+
iterator = Databases.BULK_MATERIAL.iterator();
while (iterator.hasNext()) {
if (iterator.next().isUserDefined())
}
}
});
- this.add(revertButton, "gap rel rel para para, growx 1, bottom, wrap");
+ this.add(revertButton, "gap rel rel para para, growx 1, bottom, wrap unrel");
setButtonStates();
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
//// <html><i>Editing materials will not affect existing
//// rocket designs.</i>
- this.add(new JLabel(trans.get("matedtpan.lbl.edtmaterials")), "span");
-
+ this.add(new StyledLabel(trans.get("matedtpan.lbl.edtmaterials"), -2, Style.ITALIC), "span");
+
}
case LINE:
return Databases.LINE_MATERIAL;
-
+
default:
- throw new IllegalArgumentException("Material type invalid, m="+m);
+ throw new IllegalArgumentException("Material type invalid, m=" + m);
}
}
} else {
deleteButton.setEnabled(false);
}
-
+
// Revert button enabled if any user-defined material exists
boolean found = false;
- for (Material m: Databases.BULK_MATERIAL) {
+ for (Material m : Databases.BULK_MATERIAL) {
if (m.isUserDefined()) {
found = true;
break;
}
}
if (!found) {
- for (Material m: Databases.SURFACE_MATERIAL) {
+ for (Material m : Databases.SURFACE_MATERIAL) {
if (m.isUserDefined()) {
found = true;
break;
}
}
if (!found) {
- for (Material m: Databases.LINE_MATERIAL) {
+ for (Material m : Databases.LINE_MATERIAL) {
if (m.isUserDefined()) {
found = true;
break;
if (row < n) {
return Databases.LINE_MATERIAL.get(row);
}
- throw new IndexOutOfBoundsException("row="+origRow+" while material count" +
- " bulk:" + Databases.BULK_MATERIAL.size() +
+ throw new IndexOutOfBoundsException("row=" + origRow + " while material count" +
+ " bulk:" + Databases.BULK_MATERIAL.size() +
" surface:" + Databases.SURFACE_MATERIAL.size() +
" line:" + Databases.LINE_MATERIAL.size());
}
private class MaterialCellRenderer extends DefaultTableCellRenderer {
-
+
/* (non-Javadoc)
* @see javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
*/
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
- Component c = super.getTableCellRendererComponent(table, value, isSelected,
+ Component c = super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
if (c instanceof JLabel) {
- JLabel label = (JLabel)c;
+ JLabel label = (JLabel) c;
Material m = getMaterial(row);
if (isSelected) {
}
}
-
+
}
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import net.sf.openrocket.communication.UpdateInfoRetriever;
import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
-import net.sf.openrocket.gui.main.SimpleFileFilter;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.Named;
import net.sf.openrocket.util.Prefs;
+import net.sf.openrocket.util.SimpleFileFilter;
+import net.sf.openrocket.util.Utils;
+
public class PreferencesDialog extends JDialog {
private static final LogHelper log = Application.getLogger();
private File defaultDirectory = null;
private static final Translator trans = Application.getTranslator();
-
- private PreferencesDialog() {
+
+ private PreferencesDialog(Window parent) {
//// Preferences
- super((Window) null, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL);
+ super(parent, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]", "[grow][]"));
panel.add(tabbedPane, "grow, wrap");
//// Units and Default units
- tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(),
+ tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(),
trans.get("pref.dlg.tab.Defaultunits"));
//// Materials and Custom materials
tabbedPane.addTab(trans.get("pref.dlg.tab.Materials"), null, new MaterialEditPanel(),
trans.get("pref.dlg.tab.Custommaterials"));
//// Options and Miscellaneous options
- tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(),
+ tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(),
trans.get("pref.dlg.tab.Miscellaneousoptions"));
//// Close button
private JPanel optionsPane() {
JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n"));
+
+ //// Language selector
+ Locale userLocale = Prefs.getUserLocale();
+ List<Named<Locale>> locales = new ArrayList<Named<Locale>>();
+ for (Locale l : Prefs.getSupportedLocales()) {
+ locales.add(new Named<Locale>(l, l.getDisplayLanguage()));
+ }
+ Collections.sort(locales);
+ locales.add(0, new Named<Locale>(null, trans.get("languages.default")));
+
+ final JComboBox languageCombo = new JComboBox(locales.toArray());
+ for (int i = 0; i < locales.size(); i++) {
+ if (Utils.equals(userLocale, locales.get(i).get())) {
+ languageCombo.setSelectedIndex(i);
+ }
+ }
+ languageCombo.addActionListener(new ActionListener() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public void actionPerformed(ActionEvent e) {
+ Named<Locale> selection = (Named<Locale>) languageCombo.getSelectedItem();
+ Prefs.setUserLocale(selection.get());
+ }
+ });
+ panel.add(new JLabel(trans.get("lbl.language")), "gapright para");
+ panel.add(languageCombo, "wrap rel, growx, sg combos");
+
+ panel.add(new StyledLabel(trans.get("PreferencesDialog.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2");
+
+
//// Position to insert new body components:
panel.add(new JLabel(trans.get("pref.dlg.lbl.Positiontoinsert")), "gapright para");
panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
//// Always ask
//// Insert in middle
//// Add to end
- trans.get("pref.dlg.PrefChoiseSelector1"),
- trans.get("pref.dlg.PrefChoiseSelector2"),
+ trans.get("pref.dlg.PrefChoiseSelector1"),
+ trans.get("pref.dlg.PrefChoiseSelector2"),
trans.get("pref.dlg.PrefChoiseSelector3"))), "wrap para, growx, sg combos");
//// Confirm deletion of simulations:
panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
//// Delete
//// Confirm
- trans.get("pref.dlg.PrefBooleanSelector1"),
+ trans.get("pref.dlg.PrefBooleanSelector1"),
trans.get("pref.dlg.PrefBooleanSelector2"), true)), "wrap 40lp, growx, sg combos");
//// User-defined thrust curves:
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
- SimpleFileFilter filter =
- new SimpleFileFilter(
- //// All thrust curve files (*.eng; *.rse; *.zip; directories)
- trans.get("pref.dlg.Allthrustcurvefiles"),
- true, "eng", "rse", "zip");
+ SimpleFileFilter filter =
+ new SimpleFileFilter(
+ //// All thrust curve files (*.eng; *.rse; *.zip; directories)
+ trans.get("pref.dlg.Allthrustcurvefiles"),
+ true, "eng", "rse", "zip");
chooser.addChoosableFileFilter(filter);
//// RASP motor files (*.eng)
chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RASPfiles"),
//// Check for software updates at startup
- final JCheckBox softwareUpdateBox =
- new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
+ final JCheckBox softwareUpdateBox =
+ new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
softwareUpdateBox.setSelected(Prefs.getCheckUpdates());
softwareUpdateBox.addActionListener(new ActionListener() {
@Override
combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
panel.add(combo, "sizegroup boxes, wrap");
+
//// Stability:
panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
//// The effects will take place the next time you open a window.
panel.add(new StyledLabel(
- trans.get("pref.dlg.lbl.effect1"), -2),
+ trans.get("pref.dlg.lbl.effect1"), -2, Style.ITALIC),
"spanx, wrap");
private static PreferencesDialog dialog = null;
- public static void showPreferences() {
+ public static void showPreferences(Window parent) {
if (dialog != null) {
dialog.dispose();
}
- dialog = new PreferencesDialog();
+ dialog = new PreferencesDialog(parent);
dialog.setVisible(true);
}
import javax.swing.border.TitledBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
-import javax.swing.filechooser.FileFilter;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.FileHelper;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.MemoryManagement;
private static final Translator trans = Application.getTranslator();
- // FileFilters for different types of rocket design files
- private static final FileFilter ALL_DESIGNS_FILTER =
- //// All rocket designs (*.ork; *.rkt)
- new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter1"),
- ".ork", ".ork.gz", ".rkt", ".rkt.gz");
-
- private static final FileFilter OPENROCKET_DESIGN_FILTER =
- //// OpenRocket designs (*.ork)
- new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter2"), ".ork", ".ork.gz");
-
- private static final FileFilter ROCKSIM_DESIGN_FILTER =
- //// RockSim designs (*.rkt)
- new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter3"), ".rkt", ".rkt.gz");
-
-
-
-
public static final int COMPONENT_TAB = 0;
public static final int SIMULATION_TAB = 1;
item = new JMenuItem(trans.get("main.menu.edit.resize"));
- // FIXME: Icon
- //item.setIcon(Icons.PREFERENCES);
- //// Setup the application preferences
+ item.setIcon(Icons.EDIT_SCALE);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc"));
item.addActionListener(new ActionListener() {
@Override
@Override
public void actionPerformed(ActionEvent e) {
log.user("Preferences selected");
- PreferencesDialog.showPreferences();
+ PreferencesDialog.showPreferences(BasicFrame.this);
}
});
menu.add(item);
private void openAction() {
JFileChooser chooser = new JFileChooser();
- chooser.addChoosableFileFilter(ALL_DESIGNS_FILTER);
- chooser.addChoosableFileFilter(OPENROCKET_DESIGN_FILTER);
- chooser.addChoosableFileFilter(ROCKSIM_DESIGN_FILTER);
- chooser.setFileFilter(ALL_DESIGNS_FILTER);
+ chooser.addChoosableFileFilter(FileHelper.ALL_DESIGNS_FILTER);
+ chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER);
+ chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER);
+ chooser.setFileFilter(FileHelper.ALL_DESIGNS_FILTER);
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(true);
log.info("Saving document to " + file);
// Saving RockSim designs is not supported
- if (ROCKSIM_DESIGN_FILTER.accept(file)) {
+ if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) {
file = new File(file.getAbsolutePath().replaceAll(".[rR][kK][tT](.[gG][zZ])?$",
".ork"));
StorageOptionChooser storageChooser =
new StorageOptionChooser(document, document.getDefaultStorageOptions());
JFileChooser chooser = new JFileChooser();
- chooser.setFileFilter(OPENROCKET_DESIGN_FILTER);
+ chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER);
chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
chooser.setAccessory(storageChooser);
if (document.getFile() != null)
Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
storageChooser.storeOptions(document.getDefaultStorageOptions());
- if (file.getName().indexOf('.') < 0) {
- log.debug("File name does not contain extension, adding .ork");
- String name = file.getAbsolutePath();
- name = name + ".ork";
- file = new File(name);
- }
-
- if (file.exists()) {
- log.info("File " + file + " exists, confirming overwrite from user");
- int result = JOptionPane.showConfirmDialog(this,
- "File '" + file.getName() + "' exists. Do you want to overwrite it?",
- "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
- if (result != JOptionPane.YES_OPTION) {
- log.user("User decided not to overwrite the file");
- return false;
- }
+ file = FileHelper.ensureExtension(file, "ork");
+ if (!FileHelper.confirmWrite(file, this)) {
+ return false;
}
return saveAs(file);
+++ /dev/null
-package net.sf.openrocket.gui.main;
-
-import java.io.File;
-
-import javax.swing.filechooser.FileFilter;
-
-/**
- * A FileFilter similar to FileNameExtensionFilter except that
- * it allows multipart extensions (.ork.gz), and also implements
- * the java.io.FileFilter interface.
- *
- * @author Sampo Niskanen <sampo.niskanen@iki.fi>
- */
-public class SimpleFileFilter extends FileFilter implements java.io.FileFilter {
-
- private final String description;
- private final boolean acceptDir;
- private final String[] extensions;
-
-
- /**
- * Create filter that accepts files with the provided extensions that
- * accepts directories as well.
- *
- * @param description the description of this file filter.
- * @param extensions an array of extensions that match this filter.
- */
- public SimpleFileFilter(String description, String ... extensions) {
- this(description, true, extensions);
- }
-
-
- /**
- * Create filter that accepts files with the provided extensions.
- *
- * @param description the description of this file filter.
- * @param acceptDir whether to accept directories
- * @param extensions an array of extensions that match this filter.
- */
- public SimpleFileFilter(String description, boolean acceptDir, String ... extensions) {
- this.description = description;
- this.acceptDir = acceptDir;
- this.extensions = new String[extensions.length];
- for (int i=0; i<extensions.length; i++) {
- String ext = extensions[i].toLowerCase();
- if (ext.charAt(0) == '.') {
- this.extensions[i] = ext;
- } else {
- this.extensions[i] = '.' + ext;
- }
- }
- }
-
-
- @Override
- public boolean accept(File file) {
- if (file == null)
- return false;
- if (file.isDirectory())
- return acceptDir;
-
- String filename = file.getName();
- filename = filename.toLowerCase();
- for (String ext: extensions) {
- if (filename.endsWith(ext))
- return true;
- }
-
- return false;
- }
-
- @Override
- public String getDescription() {
- return description;
- }
-
-}
@Override
public boolean importData(TransferHandler.TransferSupport support) {
+ // We currently only support drop, not paste
+ if (!support.isDrop()) {
+ log.warn("Import action is not a drop action");
+ return false;
+ }
+
// Sun JRE silently ignores any RuntimeExceptions in importData, yeech!
try {
private static final float PLOT_STROKE_WIDTH = 1.5f;
private static final Translator trans = Application.getTranslator();
-
+
private static final Color DEFAULT_EVENT_COLOR = new Color(0, 0, 0);
private static final Map<FlightEvent.Type, Color> EVENT_COLORS =
new HashMap<FlightEvent.Type, Color>();
}
}
- final double xcoord;
+ double xcoord;
if (a == 0) {
xcoord = domain.get(tindex);
} else {
FlightDataType type = config.getType(index);
List<Double> range = branch.get(type);
- final double ycoord;
+ // Image annotations are not supported on the right-side axis
+ // TODO: LOW: Can this be achieved by JFreeChart?
+ if (filled.getAxis(index) != SimulationPlotPanel.LEFT) {
+ continue;
+ }
+
+ double ycoord;
if (a == 0) {
ycoord = range.get(tindex);
} else {
ycoord = a * range.get(tindex) + (1 - a) * range.get(tindex + 1);
}
+ // Convert units
+ xcoord = config.getDomainAxisUnit().toUnit(xcoord);
+ ycoord = config.getUnit(index).toUnit(ycoord);
+
XYImageAnnotation annotation =
- new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
+ new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
annotation.setToolTipText(event);
plot.addAnnotation(annotation);
}
GUIUtil.setDisposableDialogOptions(this, button);
}
-
private String getLabel(FlightDataType type, Unit unit) {
String name = type.getName();
if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
*/
package net.sf.openrocket.gui.print;
-import com.itextpdf.text.*;
-import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.pdf.*;
+import java.awt.Graphics2D;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.figureelements.FigureElement;
import net.sf.openrocket.gui.figureelements.RocketInfo;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.masscalc.BasicMassCalculator;
+import net.sf.openrocket.masscalc.MassCalculator;
+import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.Motor;
-import net.sf.openrocket.rocketcomponent.*;
+import net.sf.openrocket.rocketcomponent.Configuration;
+import net.sf.openrocket.rocketcomponent.MotorMount;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.simulation.FlightData;
+import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.Chars;
+import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Prefs;
-import java.awt.*;
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.List;
+import com.itextpdf.text.Document;
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.Element;
+import com.itextpdf.text.Paragraph;
+import com.itextpdf.text.Rectangle;
+import com.itextpdf.text.pdf.BaseFont;
+import com.itextpdf.text.pdf.DefaultFontMapper;
+import com.itextpdf.text.pdf.PdfContentByte;
+import com.itextpdf.text.pdf.PdfPCell;
+import com.itextpdf.text.pdf.PdfPTable;
+import com.itextpdf.text.pdf.PdfWriter;
/**
* <pre>
* </pre>
*/
public class DesignReport {
-
- /**
- * The logger.
- */
- private static final LogHelper log = Application.getLogger();
-
- /**
- * The OR Document.
- */
- private OpenRocketDocument rocketDocument;
-
- /**
- * A panel used for rendering of the design diagram.
- */
- final RocketPanel panel;
-
- /**
- * The iText document.
- */
- protected Document document;
-
- /** The displayed strings. */
- private static final String STAGES = "Stages: ";
- private static final String MASS_WITH_MOTORS = "Mass (with motors): ";
- private static final String MASS_WITH_MOTOR = "Mass (with motor): ";
- private static final String MASS_EMPTY = "Mass (Empty): ";
- private static final String STABILITY = "Stability: ";
- private static final String CG = "Cg: ";
- private static final String CP = "Cp: ";
- private static final String MOTOR = "Motor";
- private static final String AVG_THRUST = "Avg Thrust";
- private static final String BURN_TIME = "Burn Time";
- private static final String MAX_THRUST = "Max Thrust";
- private static final String TOTAL_IMPULSE = "Total Impulse";
- private static final String THRUST_TO_WT = "Thrust to Wt";
- private static final String PROPELLANT_WT = "Propellant Wt";
- private static final String SIZE = "Size";
- private static final String ALTITUDE = "Altitude";
- private static final String FLIGHT_TIME = "Flight Time";
- private static final String TIME_TO_APOGEE = "Time to Apogee";
- private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
- private static final String MAX_VELOCITY = "Max Velocity";
- private static final String LANDING_VELOCITY = "Landing Velocity";
- private static final String ROCKET_DESIGN = "Rocket Design";
- private static final double GRAVITY_CONSTANT = 9.80665d;
-
- /**
- * Constructor.
- *
- * @param theRocDoc the OR document
- * @param theIDoc the iText document
- */
- public DesignReport (OpenRocketDocument theRocDoc, Document theIDoc) {
- document = theIDoc;
- rocketDocument = theRocDoc;
- panel = new RocketPanel(rocketDocument);
- }
-
- /**
- * Main entry point. Prints the rocket drawing and design data.
- *
- * @param writer a direct byte writer
- */
- public void writeToDocument (PdfWriter writer) {
- if (writer == null) {
- return;
- }
- com.itextpdf.text.Rectangle pageSize = document.getPageSize();
- int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2;
- int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop();
-
- PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN);
-
- Rocket rocket = rocketDocument.getRocket();
- final Configuration configuration = rocket.getDefaultConfiguration();
- configuration.setAllStages();
- PdfContentByte canvas = writer.getDirectContent();
-
- final PrintFigure figure = new PrintFigure(configuration);
-
- FigureElement cp = panel.getExtraCP();
- FigureElement cg = panel.getExtraCG();
- RocketInfo text = panel.getExtraText();
-
- double scale = paintRocketDiagram(pageImageableWidth, pageImageableHeight, canvas, figure, cp, cg);
-
- canvas.beginText();
- try {
- canvas.setFontAndSize(BaseFont.createFont(PrintUtilities.NORMAL.getFamilyname(), BaseFont.CP1252,
- BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE);
- }
- catch (DocumentException e) {
- log.error("Could not set font.", e);
- }
- catch (IOException e) {
- log.error("Could not create font.", e);
- }
- int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS
- .toPoints(1)));
- final int diagramHeight = pageImageableHeight * 2 - 70 - (int) (figHeightPts);
- canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight);
- canvas.moveTextWithLeading(0, -16);
-
- float initialY = canvas.getYTLM();
-
- canvas.showText(rocketDocument.getRocket().getName());
-
- canvas.newlineShowText(STAGES);
- canvas.showText("" + rocket.getStageCount());
-
-
- if (configuration.hasMotors()) {
- if (configuration.getStageCount() > 1) {
- canvas.newlineShowText(MASS_WITH_MOTORS);
- }
- else {
- canvas.newlineShowText(MASS_WITH_MOTOR);
- }
- }
- else {
- canvas.newlineShowText(MASS_EMPTY);
- }
- canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit()));
-
- canvas.newlineShowText(STABILITY);
- canvas.showText(text.getStability());
-
- canvas.newlineShowText(CG);
- canvas.showText(text.getCg());
-
- canvas.newlineShowText(CP);
- canvas.showText(text.getCp());
- canvas.endText();
-
- try {
- //Move the internal pointer of the document below that of what was just written using the direct byte buffer.
- Paragraph paragraph = new Paragraph();
- float finalY = canvas.getYTLM();
- int heightOfDiagramAndText = (int) (pageSize.getHeight() - (finalY - initialY + diagramHeight));
-
- paragraph.setSpacingAfter(heightOfDiagramAndText);
- document.add(paragraph);
-
- String[] mids = rocket.getMotorConfigurationIDs();
-
- List<Double> stages = getStageWeights(rocket);
-
- for (int j = 0; j < mids.length; j++) {
- String mid = mids[j];
- if (mid != null) {
- final List<Motor> motorList = getMotorList(rocket, mid);
-
- PdfPTable parent = new PdfPTable(2);
- parent.setWidthPercentage(100);
- parent.setHorizontalAlignment(Element.ALIGN_LEFT);
- parent.setSpacingBefore(0);
- parent.setWidths(new int[]{1, 3});
- int leading = 0;
- //The first motor config is always null. Skip it and the top-most motor, then set the leading.
- if (j > 1) {
- leading = 25;
- }
- addFlightData(rocket, mid, parent, leading);
- addMotorData(motorList, parent, stages);
- document.add(parent);
- }
- }
- }
- catch (DocumentException e) {
- log.error("Could not modify document.", e);
- }
- }
-
- /**
- * Get the motor list for all motor mounts.
- *
- * @param theRocket the rocket object
- * @param theMid the motor id
- *
- * @return a list of Motor
- */
- private List<Motor> getMotorList (final Rocket theRocket, final String theMid) {
- Iterator<RocketComponent> components = theRocket.deepIterator();
- final List<Motor> motorList = new ArrayList<Motor>();
- while (components.hasNext()) {
- RocketComponent rocketComponent = components.next();
- if (rocketComponent instanceof MotorMount) {
- MotorMount mm = (MotorMount) rocketComponent;
- final Motor motor = mm.getMotor(theMid);
- if (motor != null) {
- motorList.add(motor);
- }
- }
- }
- return motorList;
- }
-
- /**
- * Paint a diagram of the rocket into the PDF document.
- *
- * @param thePageImageableWidth the number of points in the width of the page available for drawing
- * @param thePageImageableHeight the number of points in the height of the page available for drawing
- * @param theCanvas the direct byte writer
- * @param theFigure the print figure
- * @param theCp the center of pressure figure element
- * @param theCg the center of gravity figure element
- *
- * @return the scale of the diagram
- */
- private double paintRocketDiagram (final int thePageImageableWidth, final int thePageImageableHeight,
- final PdfContentByte theCanvas, final PrintFigure theFigure,
- final FigureElement theCp, final FigureElement theCg) {
- theFigure.clearAbsoluteExtra();
- theFigure.clearRelativeExtra();
- theFigure.addRelativeExtra(theCp);
- theFigure.addRelativeExtra(theCg);
- theFigure.updateFigure();
-
- double scale =
- (thePageImageableWidth * 2.2) / theFigure.getFigureWidth();
-
- theFigure.setScale(scale);
- /*
- * page dimensions are in points-per-inch, which, in Java2D, are the same as pixels-per-inch; thus we don't need any conversion
- */
- theFigure.setSize(thePageImageableWidth, thePageImageableHeight);
- theFigure.updateFigure();
-
-
- final DefaultFontMapper mapper = new DefaultFontMapper();
- Graphics2D g2d = theCanvas.createGraphics(thePageImageableWidth, thePageImageableHeight * 2, mapper);
- g2d.translate(20, 120);
-
- g2d.scale(0.4d, 0.4d);
- theFigure.paint(g2d);
- g2d.dispose();
- return scale;
- }
-
- /**
- * Add the motor data for a motor configuration to the table.
- *
- * @param motors a motor configuration's list of motors
- * @param parent the parent to which the motor data will be added
- * @param stageWeights the stageWeights of each stage, in order
- */
- private void addMotorData (List<Motor> motors, final PdfPTable parent, List<Double> stageWeights) {
-
- PdfPTable motorTable = new PdfPTable(8);
- motorTable.setWidthPercentage(68);
- motorTable.setHorizontalAlignment(Element.ALIGN_LEFT);
-
- final PdfPCell motorCell = ITextHelper.createCell(MOTOR, PdfPCell.BOTTOM);
- final int mPad = 10;
- motorCell.setPaddingLeft(mPad);
- motorTable.addCell(motorCell);
- motorTable.addCell(ITextHelper.createCell(AVG_THRUST, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(BURN_TIME, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(MAX_THRUST, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(TOTAL_IMPULSE, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(THRUST_TO_WT, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(PROPELLANT_WT, PdfPCell.BOTTOM));
- motorTable.addCell(ITextHelper.createCell(SIZE, PdfPCell.BOTTOM));
-
- DecimalFormat df = new DecimalFormat("#,##0.0#");
- for (int i = 0; i < motors.size(); i++) {
- int border = Rectangle.BOTTOM;
- if (i == motors.size() - 1) {
- border = Rectangle.NO_BORDER;
- }
- Motor motor = motors.get(i);
- double motorWeight = (motor.getLaunchCG().weight - motor.getEmptyCG().weight) * 1000; //convert to grams
-
- final PdfPCell motorVCell = ITextHelper.createCell(motor.getDesignation(), border);
- motorVCell.setPaddingLeft(mPad);
- motorTable.addCell(motorVCell);
- motorTable.addCell(ITextHelper.createCell(df.format(motor.getAverageThrustEstimate()) + " " + UnitGroup
- .UNITS_FORCE
- .getDefaultUnit().toString(), border));
- motorTable.addCell(ITextHelper.createCell(df.format(motor.getBurnTimeEstimate()) + " " + UnitGroup
- .UNITS_FLIGHT_TIME
- .getDefaultUnit().toString(), border));
- motorTable.addCell(ITextHelper.createCell(df.format(motor.getMaxThrustEstimate()) + " " + UnitGroup
- .UNITS_FORCE.getDefaultUnit()
- .toString(), border));
- motorTable.addCell(ITextHelper.createCell(df.format(motor.getTotalImpulseEstimate()) + " " + UnitGroup
- .UNITS_IMPULSE
- .getDefaultUnit().toString(), border));
- double ttw = motor.getAverageThrustEstimate() / (getStageWeight(stageWeights, i) + (motor
- .getLaunchCG().weight * GRAVITY_CONSTANT));
- motorTable.addCell(ITextHelper.createCell(df.format(ttw) + ":1", border));
-
- motorTable.addCell(ITextHelper.createCell(df.format(motorWeight) + " " + UnitGroup.UNITS_MASS
- .getDefaultUnit().toString(), border));
-
- final Unit motorUnit = UnitGroup.UNITS_MOTOR_DIMENSIONS
- .getDefaultUnit();
- motorTable.addCell(ITextHelper.createCell(motorUnit.toString(motor.getDiameter()) +
- "/" +
- motorUnit.toString(motor.getLength()) + " " +
- motorUnit.toString(), border));
- }
- PdfPCell c = new PdfPCell(motorTable);
- c.setBorder(PdfPCell.LEFT);
- c.setBorderWidthTop(0f);
- parent.addCell(c);
- }
-
-
- /**
- * Add the motor data for a motor configuration to the table.
- *
- * @param theRocket the rocket
- * @param mid a motor configuration id
- * @param parent the parent to which the motor data will be added
- * @param leading the number of points for the leading
- */
- private void addFlightData (final Rocket theRocket, final String mid, final PdfPTable parent, int leading) {
- FlightData flight = null;
- if (theRocket.getMotorConfigurationIDs().length > 1) {
- Rocket duplicate = theRocket.copyWithOriginalID();
- Simulation simulation = Prefs.getBackgroundSimulation(duplicate);
- simulation.getConditions().setMotorConfigurationID(mid);
-
- flight = PrintSimulationWorker.doit(simulation);
-
- if (flight != null) {
- try {
- final Unit distanceUnit = UnitGroup.UNITS_DISTANCE.getDefaultUnit();
- final Unit velocityUnit = UnitGroup.UNITS_VELOCITY.getDefaultUnit();
- final Unit flightUnit = UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit();
-
- PdfPTable labelTable = new PdfPTable(2);
- labelTable.setWidths(new int[]{3, 2});
- final Paragraph chunk = ITextHelper.createParagraph(stripBrackets(
- theRocket.getMotorConfigurationNameOrDescription(mid)), PrintUtilities.BOLD);
- chunk.setLeading(leading);
- chunk.setSpacingAfter(3f);
-
- document.add(chunk);
-
- DecimalFormat df = new DecimalFormat("#,##0.0#");
-
- final PdfPCell cell = ITextHelper.createCell(ALTITUDE, 2, 2);
- cell.setUseBorderPadding(false);
- cell.setBorderWidthTop(0f);
- labelTable.addCell(cell);
- labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxAltitude()) + " " + distanceUnit,
- 2, 2));
-
- labelTable.addCell(ITextHelper.createCell(FLIGHT_TIME, 2, 2));
- labelTable.addCell(ITextHelper.createCell(df.format(flight.getFlightTime()) + " " + flightUnit, 2,
- 2));
-
- labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
- labelTable.addCell(ITextHelper.createCell(df.format(flight.getTimeToApogee()) + " " + flightUnit, 2,
- 2));
-
- labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
- labelTable.addCell(ITextHelper.createCell(df.format(
- flight.getLaunchRodVelocity()) + " " + velocityUnit, 2, 2));
-
- labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2));
- labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxVelocity()) + " " + velocityUnit,
- 2, 2));
-
- labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2));
- labelTable.addCell(ITextHelper.createCell(df.format(
- flight.getGroundHitVelocity()) + " " + velocityUnit, 2, 2));
-
- //Add the table to the parent; have to wrap it in a cell
- PdfPCell c = new PdfPCell(labelTable);
- c.setBorder(PdfPCell.RIGHT);
- c.setBorderWidthTop(0);
- c.setTop(0);
- parent.addCell(c);
- }
- catch (DocumentException e) {
- log.error("Could not add flight data to document.", e);
- }
- }
- }
- }
-
- /**
- * Strip [] brackets from a string.
- *
- * @param target the original string
- *
- * @return target with [] removed
- */
- private String stripBrackets (String target) {
- return stripLeftBracket(stripRightBracket(target));
- }
-
- /**
- * Strip [ from a string.
- *
- * @param target the original string
- *
- * @return target with [ removed
- */
- private String stripLeftBracket (String target) {
- return target.replace("[", "");
- }
-
- /**
- * Strip ] from a string.
- *
- * @param target the original string
- *
- * @return target with ] removed
- */
- private String stripRightBracket (String target) {
- return target.replace("]", "");
- }
-
- /**
- * From a list of Stages get the stage masses and convert to weight.
- *
- * @param rocket the rocket
- *
- * @return a sorted list of Stage weights (mass * gravity), in Newtons
- */
- private List<Double> getStageWeights (Rocket rocket) {
- List<Double> stages = getStageMasses(rocket);
-
- for (int i = 0; i < stages.size(); i++) {
- Double stage = stages.get(i);
- stages.set(i, stage * GRAVITY_CONSTANT);
- }
- return stages;
- }
-
- /**
- * From a list of Stages get the stage masses.
- *
- * @param rocket the rocket
- *
- * @return a sorted list of Stage masses
- */
- private List<Double> getStageMasses (final Rocket rocket) {
- Double mass = 0d;
-
- List<Double> stages = new ArrayList<Double>();
- Iterator<RocketComponent> iter = rocket.deepIterator();
- while (iter.hasNext()) {
- RocketComponent rocketComponent = iter.next();
- if (rocketComponent instanceof Stage) {
- if (mass > 0d) {
- stages.add(mass);
- mass = 0d;
- }
- }
- else {
- mass += rocketComponent.getMass();
- }
- }
- if (mass > 0d) {
- stages.add(mass);
- }
- return stages;
- }
-
- /**
- * Compute the total stage weight from a list of stage weights. This sums up the weight of the given stage plus all
- * stages that sit atop it (depend upon it for thrust).
- *
- * @param weights the list of stage weights, in Newtons
- * @param stage a stage number, 0 being topmost stage
- *
- * @return the total weight of the stage and all stages sitting atop the given stage, in Newtons
- */
- private double getStageWeight (List<Double> weights, int stage) {
-
- double result = 0d;
- for (int i = 0; i <= stage; i++) {
- result += weights.get(i);
- }
- return result;
- }
+
+ /**
+ * The logger.
+ */
+ private static final LogHelper log = Application.getLogger();
+
+ /**
+ * The OR Document.
+ */
+ private OpenRocketDocument rocketDocument;
+
+ /**
+ * A panel used for rendering of the design diagram.
+ */
+ final RocketPanel panel;
+
+ /**
+ * The iText document.
+ */
+ protected Document document;
+
+ /** The displayed strings. */
+ private static final String STAGES = "Stages: ";
+ private static final String MASS_WITH_MOTORS = "Mass (with motors): ";
+ private static final String MASS_WITH_MOTOR = "Mass (with motor): ";
+ private static final String MASS_EMPTY = "Mass (Empty): ";
+ private static final String STABILITY = "Stability: ";
+ private static final String CG = "CG: ";
+ private static final String CP = "CP: ";
+ private static final String MOTOR = "Motor";
+ private static final String AVG_THRUST = "Avg Thrust";
+ private static final String BURN_TIME = "Burn Time";
+ private static final String MAX_THRUST = "Max Thrust";
+ private static final String TOTAL_IMPULSE = "Total Impulse";
+ private static final String THRUST_TO_WT = "Thrust to Wt";
+ private static final String PROPELLANT_WT = "Propellant Wt";
+ private static final String SIZE = "Size";
+ private static final String ALTITUDE = "Altitude";
+ private static final String FLIGHT_TIME = "Flight Time";
+ private static final String TIME_TO_APOGEE = "Time to Apogee";
+ private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
+ private static final String MAX_VELOCITY = "Max Velocity";
+ private static final String LANDING_VELOCITY = "Landing Velocity";
+ private static final String ROCKET_DESIGN = "Rocket Design";
+ private static final double GRAVITY_CONSTANT = 9.80665d;
+
+ /**
+ * Constructor.
+ *
+ * @param theRocDoc the OR document
+ * @param theIDoc the iText document
+ */
+ public DesignReport(OpenRocketDocument theRocDoc, Document theIDoc) {
+ document = theIDoc;
+ rocketDocument = theRocDoc;
+ panel = new RocketPanel(rocketDocument);
+ }
+
+ /**
+ * Main entry point. Prints the rocket drawing and design data.
+ *
+ * @param writer a direct byte writer
+ */
+ public void writeToDocument(PdfWriter writer) {
+ if (writer == null) {
+ return;
+ }
+ com.itextpdf.text.Rectangle pageSize = document.getPageSize();
+ int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2;
+ int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop();
+
+ PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN);
+
+ Rocket rocket = rocketDocument.getRocket();
+ final Configuration configuration = rocket.getDefaultConfiguration();
+ configuration.setAllStages();
+ PdfContentByte canvas = writer.getDirectContent();
+
+ final PrintFigure figure = new PrintFigure(configuration);
+
+ FigureElement cp = panel.getExtraCP();
+ FigureElement cg = panel.getExtraCG();
+ RocketInfo text = panel.getExtraText();
+
+ double scale = paintRocketDiagram(pageImageableWidth, pageImageableHeight, canvas, figure, cp, cg);
+
+ canvas.beginText();
+ try {
+ canvas.setFontAndSize(BaseFont.createFont(PrintUtilities.NORMAL.getFamilyname(), BaseFont.CP1252,
+ BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE);
+ } catch (DocumentException e) {
+ log.error("Could not set font.", e);
+ } catch (IOException e) {
+ log.error("Could not create font.", e);
+ }
+ int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS
+ .toPoints(1)));
+ final int diagramHeight = pageImageableHeight * 2 - 70 - (figHeightPts);
+ canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight);
+ canvas.moveTextWithLeading(0, -16);
+
+ float initialY = canvas.getYTLM();
+
+ canvas.showText(rocketDocument.getRocket().getName());
+
+ canvas.newlineShowText(STAGES);
+ canvas.showText("" + rocket.getStageCount());
+
+
+ if (configuration.hasMotors()) {
+ if (configuration.getStageCount() > 1) {
+ canvas.newlineShowText(MASS_WITH_MOTORS);
+ } else {
+ canvas.newlineShowText(MASS_WITH_MOTOR);
+ }
+ } else {
+ canvas.newlineShowText(MASS_EMPTY);
+ }
+ canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit()));
+
+ canvas.newlineShowText(STABILITY);
+ canvas.showText(text.getStability());
+
+ canvas.newlineShowText(CG);
+ canvas.showText(text.getCg());
+
+ canvas.newlineShowText(CP);
+ canvas.showText(text.getCp());
+ canvas.endText();
+
+ try {
+ //Move the internal pointer of the document below that of what was just written using the direct byte buffer.
+ Paragraph paragraph = new Paragraph();
+ float finalY = canvas.getYTLM();
+ int heightOfDiagramAndText = (int) (pageSize.getHeight() - (finalY - initialY + diagramHeight));
+
+ paragraph.setSpacingAfter(heightOfDiagramAndText);
+ document.add(paragraph);
+
+ String[] motorIds = rocket.getMotorConfigurationIDs();
+
+ List<Double> stageMasses = getStageMasses(rocket);
+
+ for (int j = 0; j < motorIds.length; j++) {
+ String motorId = motorIds[j];
+ if (motorId != null) {
+ PdfPTable parent = new PdfPTable(2);
+ parent.setWidthPercentage(100);
+ parent.setHorizontalAlignment(Element.ALIGN_LEFT);
+ parent.setSpacingBefore(0);
+ parent.setWidths(new int[] { 1, 3 });
+ int leading = 0;
+ //The first motor config is always null. Skip it and the top-most motor, then set the leading.
+ if (j > 1) {
+ leading = 25;
+ }
+ addFlightData(rocket, motorId, parent, leading);
+ addMotorData(rocket, motorId, parent);
+ document.add(parent);
+ }
+ }
+ } catch (DocumentException e) {
+ log.error("Could not modify document.", e);
+ }
+ }
+
+ /**
+ * Get the motor list for all motor mounts.
+ *
+ * @param theRocket the rocket object
+ * @param theMid the motor id
+ *
+ * @return a list of Motor
+ */
+ private List<Motor> getMotorList(final Rocket theRocket, final String theMid) {
+ Iterator<RocketComponent> components = theRocket.iterator();
+ final List<Motor> motorList = new ArrayList<Motor>();
+ while (components.hasNext()) {
+ RocketComponent rocketComponent = components.next();
+ if (rocketComponent instanceof MotorMount) {
+ MotorMount mm = (MotorMount) rocketComponent;
+ final Motor motor = mm.getMotor(theMid);
+ if (motor != null) {
+ motorList.add(motor);
+ }
+ }
+ }
+ return motorList;
+ }
+
+ /**
+ * Paint a diagram of the rocket into the PDF document.
+ *
+ * @param thePageImageableWidth the number of points in the width of the page available for drawing
+ * @param thePageImageableHeight the number of points in the height of the page available for drawing
+ * @param theCanvas the direct byte writer
+ * @param theFigure the print figure
+ * @param theCp the center of pressure figure element
+ * @param theCg the center of gravity figure element
+ *
+ * @return the scale of the diagram
+ */
+ private double paintRocketDiagram(final int thePageImageableWidth, final int thePageImageableHeight,
+ final PdfContentByte theCanvas, final PrintFigure theFigure,
+ final FigureElement theCp, final FigureElement theCg) {
+ theFigure.clearAbsoluteExtra();
+ theFigure.clearRelativeExtra();
+ theFigure.addRelativeExtra(theCp);
+ theFigure.addRelativeExtra(theCg);
+ theFigure.updateFigure();
+
+ double scale =
+ (thePageImageableWidth * 2.2) / theFigure.getFigureWidth();
+
+ theFigure.setScale(scale);
+ /*
+ * page dimensions are in points-per-inch, which, in Java2D, are the same as pixels-per-inch; thus we don't need any conversion
+ */
+ theFigure.setSize(thePageImageableWidth, thePageImageableHeight);
+ theFigure.updateFigure();
+
+
+ final DefaultFontMapper mapper = new DefaultFontMapper();
+ Graphics2D g2d = theCanvas.createGraphics(thePageImageableWidth, thePageImageableHeight * 2, mapper);
+ g2d.translate(20, 120);
+
+ g2d.scale(0.4d, 0.4d);
+ theFigure.paint(g2d);
+ g2d.dispose();
+ return scale;
+ }
+
+ /**
+ * Add the motor data for a motor configuration to the table.
+ *
+ * @param rocket the rocket
+ * @param motorId the motor ID to output
+ * @param parent the parent to which the motor data will be added
+ */
+ private void addMotorData(Rocket rocket, String motorId, final PdfPTable parent) {
+
+ PdfPTable motorTable = new PdfPTable(8);
+ motorTable.setWidthPercentage(68);
+ motorTable.setHorizontalAlignment(Element.ALIGN_LEFT);
+
+ final PdfPCell motorCell = ITextHelper.createCell(MOTOR, PdfPCell.BOTTOM);
+ final int mPad = 10;
+ motorCell.setPaddingLeft(mPad);
+ motorTable.addCell(motorCell);
+ motorTable.addCell(ITextHelper.createCell(AVG_THRUST, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(BURN_TIME, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(MAX_THRUST, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(TOTAL_IMPULSE, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(THRUST_TO_WT, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(PROPELLANT_WT, PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell(SIZE, PdfPCell.BOTTOM));
+
+ DecimalFormat ttwFormat = new DecimalFormat("0.00");
+
+ MassCalculator massCalc = new BasicMassCalculator();
+
+ Configuration config = new Configuration(rocket);
+ config.setMotorConfigurationID(motorId);
+
+ int totalMotorCount = 0;
+ double totalPropMass = 0;
+ double totalImpulse = 0;
+ double totalTTW = 0;
+
+ int stage = 0;
+ double stageMass = 0;
+
+ boolean topBorder = false;
+ for (RocketComponent c : rocket) {
+
+ if (c instanceof Stage) {
+ config.setToStage(stage);
+ stage++;
+ stageMass = massCalc.getCG(config, MassCalcType.LAUNCH_MASS).weight;
+ // Calculate total thrust-to-weight from only lowest stage motors
+ totalTTW = 0;
+ topBorder = true;
+ }
+
+ if (c instanceof MotorMount && ((MotorMount) c).isMotorMount()) {
+ MotorMount mount = (MotorMount) c;
+
+ if (mount.isMotorMount() && mount.getMotor(motorId) != null) {
+ Motor motor = mount.getMotor(motorId);
+ int motorCount = c.toAbsolute(Coordinate.NUL).length;
+
+
+ int border = Rectangle.NO_BORDER;
+ if (topBorder) {
+ border = Rectangle.TOP;
+ topBorder = false;
+ }
+
+ String name = motor.getDesignation();
+ if (motorCount > 1) {
+ name += " (" + Chars.TIMES + motorCount + ")";
+ }
+
+ final PdfPCell motorVCell = ITextHelper.createCell(name, border);
+ motorVCell.setPaddingLeft(mPad);
+ motorTable.addCell(motorVCell);
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(motor.getAverageThrustEstimate()), border));
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit().toStringUnit(motor.getBurnTimeEstimate()), border));
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(motor.getMaxThrustEstimate()), border));
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(motor.getTotalImpulseEstimate()), border));
+
+ double ttw = motor.getAverageThrustEstimate() / (stageMass * GRAVITY_CONSTANT);
+ motorTable.addCell(ITextHelper.createCell(
+ ttwFormat.format(ttw) + ":1", border));
+
+ double propMass = (motor.getLaunchCG().weight - motor.getEmptyCG().weight);
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(propMass), border));
+
+ final Unit motorUnit = UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit();
+ motorTable.addCell(ITextHelper.createCell(motorUnit.toString(motor.getDiameter()) +
+ "/" +
+ motorUnit.toString(motor.getLength()) + " " +
+ motorUnit.toString(), border));
+
+ // Sum up total count
+ totalMotorCount += motorCount;
+ totalPropMass += propMass * motorCount;
+ totalImpulse += motor.getTotalImpulseEstimate() * motorCount;
+ totalTTW += ttw * motorCount;
+ }
+ }
+ }
+
+ if (totalMotorCount > 1) {
+ int border = Rectangle.TOP;
+ final PdfPCell motorVCell = ITextHelper.createCell("Total:", border);
+ motorVCell.setPaddingLeft(mPad);
+ motorTable.addCell(motorVCell);
+ motorTable.addCell(ITextHelper.createCell("", border));
+ motorTable.addCell(ITextHelper.createCell("", border));
+ motorTable.addCell(ITextHelper.createCell("", border));
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(totalImpulse), border));
+ motorTable.addCell(ITextHelper.createCell(
+ ttwFormat.format(totalTTW) + ":1", border));
+ motorTable.addCell(ITextHelper.createCell(
+ UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(totalPropMass), border));
+ motorTable.addCell(ITextHelper.createCell("", border));
+
+ }
+
+ PdfPCell c = new PdfPCell(motorTable);
+ c.setBorder(PdfPCell.LEFT);
+ c.setBorderWidthTop(0f);
+ parent.addCell(c);
+ }
+
+
+ /**
+ * Add the motor data for a motor configuration to the table.
+ *
+ * @param theRocket the rocket
+ * @param motorId a motor configuration id
+ * @param parent the parent to which the motor data will be added
+ * @param leading the number of points for the leading
+ */
+ private void addFlightData(final Rocket theRocket, final String motorId, final PdfPTable parent, int leading) {
+
+ // Perform flight simulation
+ Rocket duplicate = theRocket.copyWithOriginalID();
+ FlightData flight = null;
+ try {
+ Simulation simulation = Prefs.getBackgroundSimulation(duplicate);
+ simulation.getConditions().setMotorConfigurationID(motorId);
+ simulation.simulate();
+ flight = simulation.getSimulatedData();
+ } catch (SimulationException e1) {
+ // Ignore
+ }
+
+ // Output the flight data
+ if (flight != null) {
+ try {
+ final Unit distanceUnit = UnitGroup.UNITS_DISTANCE.getDefaultUnit();
+ final Unit velocityUnit = UnitGroup.UNITS_VELOCITY.getDefaultUnit();
+ final Unit flightTimeUnit = UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit();
+
+ PdfPTable labelTable = new PdfPTable(2);
+ labelTable.setWidths(new int[] { 3, 2 });
+ final Paragraph chunk = ITextHelper.createParagraph(stripBrackets(
+ theRocket.getMotorConfigurationNameOrDescription(motorId)), PrintUtilities.BOLD);
+ chunk.setLeading(leading);
+ chunk.setSpacingAfter(3f);
+
+ document.add(chunk);
+
+ final PdfPCell cell = ITextHelper.createCell(ALTITUDE, 2, 2);
+ cell.setUseBorderPadding(false);
+ cell.setBorderWidthTop(0f);
+ labelTable.addCell(cell);
+ labelTable.addCell(ITextHelper.createCell(distanceUnit.toStringUnit(flight.getMaxAltitude()), 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell(FLIGHT_TIME, 2, 2));
+ labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getFlightTime()), 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
+ labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
+ labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2));
+ labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getMaxVelocity()), 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2));
+ labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getGroundHitVelocity()), 2, 2));
+
+ //Add the table to the parent; have to wrap it in a cell
+ PdfPCell c = new PdfPCell(labelTable);
+ c.setBorder(PdfPCell.RIGHT);
+ c.setBorderWidthTop(0);
+ c.setTop(0);
+ parent.addCell(c);
+ } catch (DocumentException e) {
+ log.error("Could not add flight data to document.", e);
+ }
+ }
+ }
+
+ /**
+ * Strip [] brackets from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with [] removed
+ */
+ private String stripBrackets(String target) {
+ return stripLeftBracket(stripRightBracket(target));
+ }
+
+ /**
+ * Strip [ from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with [ removed
+ */
+ private String stripLeftBracket(String target) {
+ return target.replace("[", "");
+ }
+
+ /**
+ * Strip ] from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with ] removed
+ */
+ private String stripRightBracket(String target) {
+ return target.replace("]", "");
+ }
+
+
+ /**
+ * Return a list of cumulative stage masses. The latter masses include the mass
+ * of the upper stages as well.
+ *
+ * @param rocket the rocket
+ * @return a list containing the cumulative stage masses
+ */
+ private List<Double> getStageMasses(final Rocket rocket) {
+ List<Double> masses = new ArrayList<Double>();
+ int stages = rocket.getStageCount();
+
+ Configuration config = new Configuration(rocket);
+ MassCalculator calc = new BasicMassCalculator();
+
+ for (int i = 0; i < stages; i++) {
+ config.setToStage(i);
+ masses.add(calc.getCG(config, MassCalcType.NO_MOTORS).weight);
+ }
+
+ return masses;
+ }
+
}
* finished. The worker can be cancelled if necessary.
*/
public class PrintSimulationWorker {
-
- public static FlightData doit (Simulation sim) {
- return new InnerPrintSimulationWorker(sim).doit();
- }
-
- static class InnerPrintSimulationWorker extends SimulationWorker {
-
- public InnerPrintSimulationWorker (Simulation sim) {
- super(sim);
- }
-
- public FlightData doit() {
- return doInBackground();
- }
- @Override
- protected void simulationDone () {
- // Do nothing if cancelled
- if (isCancelled()) {
- return;
- }
-
- simulation.getSimulatedData();
- }
-
-
- /**
- * Called if the simulation is interrupted due to an exception.
- *
- * @param t the Throwable that caused the interruption
- */
- @Override
- protected void simulationInterrupted (final Throwable t) {
- }
- }
+
+ public static FlightData doit(Simulation sim) {
+ return new InnerPrintSimulationWorker(sim).doit();
+ }
+
+ static class InnerPrintSimulationWorker extends SimulationWorker {
+
+ public InnerPrintSimulationWorker(Simulation sim) {
+ super(sim);
+ }
+
+ public FlightData doit() {
+ return doInBackground();
+ }
+
+ @Override
+ protected void simulationDone() {
+ // Do nothing if cancelled
+ if (isCancelled()) {
+ return;
+ }
+
+ simulation.getSimulatedData();
+ }
+
+
+ /**
+ * Called if the simulation is interrupted due to an exception.
+ *
+ * @param t the Throwable that caused the interruption
+ */
+ @Override
+ protected void simulationInterrupted(final Throwable t) {
+ }
+ }
}
\ No newline at end of file
--- /dev/null
+package net.sf.openrocket.l10n;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * Helper methods for localization needs.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public final class L10N {
+
+ private L10N() {
+ // Prevent instantiation
+ }
+
+
+ /**
+ * Replace a text token by a replacement value.
+ * <p>
+ * A text token is a string portion that should be surrounded by
+ * braces, "{text}".
+ *
+ * @param original the original string.
+ * @param token the text token to replace.
+ * @param replacement the replacement text.
+ * @return the modified string.
+ */
+ public static String replace(String original, String token, String replacement) {
+ return Pattern.compile(token, Pattern.LITERAL).matcher(original).replaceAll(replacement);
+ }
+
+
+ /**
+ * Convert a language code into a Locale.
+ *
+ * @param langcode the language code (<code>null</code> ok).
+ * @return the corresponding locale (or <code>null</code> if the input was <code>null</code>)
+ */
+ public static Locale toLocale(String langcode) {
+ if (langcode == null) {
+ return null;
+ }
+
+ Locale l;
+ String[] split = langcode.split("[_-]", 3);
+ if (split.length == 1) {
+ l = new Locale(split[0]);
+ } else if (split.length == 2) {
+ l = new Locale(split[0], split[1]);
+ } else {
+ l = new Locale(split[0], split[1], split[2]);
+ }
+ return l;
+ }
+
+}
public class StabilityDomain implements SimulationDomain {
/*
- * FIXME: Should this rather inspect stability during flight
+ * TODO: HIGH: Should this rather inspect stability during flight
*/
private final double limit;
public class EllipticalFinSet extends FinSet {
private static final Translator trans = Application.getTranslator();
-
- public static final int POINTS = 21;
-
+
+ private static final int POINTS = 31;
+
+ // Static positioning for the fin points
private static final double[] POINT_X = new double[POINTS];
private static final double[] POINT_Y = new double[POINTS];
static {
- for (int i=0; i < POINTS; i++) {
- double a = Math.PI * (POINTS-1-i)/(POINTS-1);
- POINT_X[i] = (Math.cos(a)+1)/2;
+ for (int i = 0; i < POINTS; i++) {
+ double a = Math.PI * (POINTS - 1 - i) / (POINTS - 1);
+ POINT_X[i] = (Math.cos(a) + 1) / 2;
POINT_Y[i] = Math.sin(a);
}
POINT_X[0] = 0;
POINT_Y[0] = 0;
- POINT_X[POINTS-1] = 1;
- POINT_Y[POINTS-1] = 0;
+ POINT_X[POINTS - 1] = 1;
+ POINT_Y[POINTS - 1] = 0;
}
-
+
private double height = 0.05;
-
+
public EllipticalFinSet() {
this.length = 0.05;
}
-
-
+
+
@Override
public Coordinate[] getFinPoints() {
+ double len = MathUtil.max(length, 0.0001);
Coordinate[] points = new Coordinate[POINTS];
- for (int i=0; i < POINTS; i++) {
- points[i] = new Coordinate(POINT_X[i]*length, POINT_Y[i]*height);
+ for (int i = 0; i < POINTS; i++) {
+ points[i] = new Coordinate(POINT_X[i] * len, POINT_Y[i] * height);
}
return points;
}
-
+
@Override
public double getSpan() {
return height;
}
-
+
@Override
public String getComponentName() {
//// Elliptical fin set
return trans.get("EllipticalFinSet.Ellipticalfinset");
}
-
-
+
+
public double getHeight() {
return height;
}
-
+
public void setHeight(double height) {
if (MathUtil.equals(this.height, height))
return;
this.height = height;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
-
+
+
public void setLength(double length) {
if (MathUtil.equals(this.length, length))
return;
this.length = length;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
}
public abstract class FinSet extends ExternalComponent {
private static final Translator trans = Application.getTranslator();
- // FIXME: converting triangular fins to freeform fails
-
/**
* Maximum allowed cant of fins.
*/
package net.sf.openrocket.rocketcomponent;
+import java.util.ArrayList;
+import java.util.List;
+
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.MathUtil;
/**
* A set of trapezoidal fins. The root and tip chords are perpendicular to the rocket
public class TrapezoidFinSet extends FinSet {
private static final Translator trans = Application.getTranslator();
-
- public static final double MAX_SWEEP_ANGLE=(89*Math.PI/180.0);
-
+
+ public static final double MAX_SWEEP_ANGLE = (89 * Math.PI / 180.0);
+
/*
* sweep tipChord
* | |___________
private double tipChord = 0;
private double height = 0;
private double sweep = 0;
-
-
+
+
public TrapezoidFinSet() {
- this (3, 0.05, 0.05, 0.025, 0.05);
+ this(3, 0.05, 0.05, 0.025, 0.05);
}
-
+
// TODO: HIGH: height=0 -> CP = NaN
public TrapezoidFinSet(int fins, double rootChord, double tipChord, double sweep,
double height) {
super();
-
+
this.setFinCount(fins);
this.length = rootChord;
this.tipChord = tipChord;
this.sweep = sweep;
this.height = height;
}
-
-
+
+
public void setFinShape(double rootChord, double tipChord, double sweep, double height,
double thickness) {
- if (this.length==rootChord && this.tipChord==tipChord && this.sweep==sweep &&
- this.height==height && this.thickness==thickness)
+ if (this.length == rootChord && this.tipChord == tipChord && this.sweep == sweep &&
+ this.height == height && this.thickness == thickness)
return;
- this.length=rootChord;
- this.tipChord=tipChord;
- this.sweep=sweep;
- this.height=height;
- this.thickness=thickness;
+ this.length = rootChord;
+ this.tipChord = tipChord;
+ this.sweep = sweep;
+ this.height = height;
+ this.thickness = thickness;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
public double getRootChord() {
return length;
}
+
public void setRootChord(double r) {
if (length == r)
return;
- length = Math.max(r,0);
+ length = Math.max(r, 0);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
public double getTipChord() {
return tipChord;
}
+
public void setTipChord(double r) {
if (tipChord == r)
return;
- tipChord = Math.max(r,0);
+ tipChord = Math.max(r, 0);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
/**
* Get the sweep length.
*/
public double getSweep() {
return sweep;
}
+
/**
* Set the sweep length.
*/
sweep = r;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
/**
* Get the sweep angle. This is calculated from the true sweep and height, and is not
* stored separetely.
public double getSweepAngle() {
if (height == 0) {
if (sweep > 0)
- return Math.PI/2;
+ return Math.PI / 2;
if (sweep < 0)
- return -Math.PI/2;
+ return -Math.PI / 2;
return 0;
}
- return Math.atan(sweep/height);
+ return Math.atan(sweep / height);
}
+
/**
* Sets the sweep by the sweep angle. The sweep is calculated and set by this method,
* and the angle itself is not stored.
return;
setSweep(sweep);
}
-
+
public double getHeight() {
return height;
}
+
public void setHeight(double r) {
if (height == r)
return;
- height = Math.max(r,0);
+ height = Math.max(r, 0);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
-
+
+
/**
* Returns the geometry of a trapezoidal fin.
*/
@Override
public Coordinate[] getFinPoints() {
- Coordinate[] c = new Coordinate[4];
-
- c[0] = Coordinate.NUL;
- c[1] = new Coordinate(sweep,height);
- c[2] = new Coordinate(sweep+tipChord,height);
- c[3] = new Coordinate(length,0);
-
- return c;
+ List<Coordinate> list = new ArrayList<Coordinate>(4);
+
+ list.add(Coordinate.NUL);
+ list.add(new Coordinate(sweep, height));
+ if (tipChord > 0.0001) {
+ list.add(new Coordinate(sweep + tipChord, height));
+ }
+ list.add(new Coordinate(MathUtil.max(length, 0.0001), 0));
+
+ return list.toArray(new Coordinate[list.size()]);
}
-
+
/**
* Returns the span of a trapezoidal fin.
*/
public double getSpan() {
return height;
}
-
-
+
+
@Override
public String getComponentName() {
//// Trapezoidal fin set
return trans.get("TrapezoidFinSet.TrapezoidFinSet");
}
-
+
}
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
conditions.setLaunchLatitude(getLaunchLatitude());
PinkNoiseWindModel windModel = new PinkNoiseWindModel();
- // FIXME: Random seed
- windModel.setSeed(1);
+ // TODO: HIGH: Randomness source for simulation
+ windModel.setSeed(new Random().nextInt());
windModel.setAverage(getWindSpeedAverage());
windModel.setStandardDeviation(getWindSpeedDeviation());
conditions.setWindModel(windModel);
private static final double MIN_TIME_STEP = 0.001;
- // FIXME: Random seed
- private final Random random = new Random(10);
+ // TODO: HIGH: Randomness source from simulation
+ private final Random random = new Random();
* @return a translator.
*/
public static Translator getTranslator() {
+ if (baseTranslator instanceof DebugTranslator) {
+ return baseTranslator;
+ }
+
Translator t = baseTranslator;
t = new ClassBasedTranslator(t, 1);
t = new ExceptionSuppressingTranslator(t);
import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.main.ExceptionHandler;
-import net.sf.openrocket.gui.main.SimpleFileFilter;
import net.sf.openrocket.gui.main.Splash;
import net.sf.openrocket.l10n.DebugTranslator;
+import net.sf.openrocket.l10n.L10N;
import net.sf.openrocket.l10n.ResourceBundleTranslator;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.DelegatorLogger;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Prefs;
+import net.sf.openrocket.util.SimpleFileFilter;
/**
* Initializes the localization system.
*/
private static void initializeL10n() {
- String locale = System.getProperty("openrocket.locale");
- if (locale != null) {
- Locale l;
- String[] split = locale.split("[_-]", 3);
- if (split.length == 1) {
- l = new Locale(split[0]);
- } else if (split.length == 2) {
- l = new Locale(split[0], split[1]);
- } else {
- l = new Locale(split[0], split[1], split[2]);
- }
+
+ String langcode = System.getProperty("openrocket.locale");
+ if (langcode != null) {
+ Locale l = L10N.toLocale(langcode);
log.info("Setting custom locale " + l);
Locale.setDefault(l);
+ } else {
+ Locale l = Prefs.getUserLocale();
+ if (l != null) {
+ log.info("Setting user-selected locale " + l);
+ Locale.setDefault(l);
+ } else {
+ log.info("Using default locale " + Locale.getDefault());
+ }
}
Translator t;
}
-
private static void runMain(String[] args) {
// Initialize the splash screen with version info
public abstract class Unit {
/** No unit with 2 digit precision */
- public static final Unit NOUNIT2 = new GeneralUnit(1,""+Chars.ZWSP, 2);
-
- protected final double multiplier; // meters = units * multiplier
+ public static final Unit NOUNIT2 = new GeneralUnit(1, "" + Chars.ZWSP, 2);
+
+ protected final double multiplier; // meters = units * multiplier
protected final String unit;
-
+
/**
* Creates a new Unit with a given multiplier and unit name.
*
this.multiplier = multiplier;
this.unit = unit;
}
-
+
/**
* Converts from SI units to this unit. The default implementation simply divides by the
* multiplier.
* @return Value in these units
*/
public double toUnit(double value) {
- return value/multiplier;
+ return value / multiplier;
}
-
+
/**
* Convert from this type of units to SI units. The default implementation simply
* multiplies by the multiplier.
* @return Value in SI units
*/
public double fromUnit(double value) {
- return value*multiplier;
+ return value * multiplier;
}
-
+
/**
* Return the unit name.
return unit;
}
+ // TODO: Should this use grouping separator ("#,##0.##")?
+
private static final DecimalFormat intFormat = new DecimalFormat("#");
private static final DecimalFormat decFormat = new DecimalFormat("0.##");
private static final DecimalFormat expFormat = new DecimalFormat("0.00E0");
-
+
/**
* Format the given value (in SI units) to a string representation of the value in this
* units. An suitable amount of decimals for the unit are used in the representation.
*/
public String toString(double value) {
double val = toUnit(value);
-
+
if (Math.abs(val) > 1E6) {
return expFormat.format(val);
}
if (Math.abs(val) <= 0.005) {
return "0";
}
-
+
double sign = Math.signum(val);
val = Math.abs(val);
double mul = 1.0;
mul *= 10;
val *= 10;
}
- val = Math.rint(val)/mul * sign;
+ val = Math.rint(val) / mul * sign;
return decFormat.format(val);
}
}
-
+
/**
* Creates a new Value object with the specified value and this unit.
*
* @return Rounded value.
*/
public abstract double round(double value);
-
+
/**
* Return the next rounded value after the given value.
* @param value Value in these units.
return false;
if (this.getClass() != other.getClass())
return false;
- return ((this.multiplier == ((Unit)other).multiplier) &&
- this.unit.equals(((Unit)other).unit));
+ return ((this.multiplier == ((Unit) other).multiplier) && this.unit.equals(((Unit) other).unit));
}
@Override
public int hashCode() {
return this.getClass().hashCode() + this.unit.hashCode();
}
-
+
}
--- /dev/null
+package net.sf.openrocket.util;
+
+import java.awt.Component;
+import java.io.File;
+import java.io.IOException;
+
+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;
+
+/**
+ * Helper methods related to user-initiated file manipulation.
+ * <p>
+ * These methods log the necessary information to the debug log.
+*
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public final class FileHelper {
+ private static final LogHelper log = Application.getLogger();
+ private static final Translator trans = Application.getTranslator();
+
+
+ // TODO: MEDIUM: 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 PDF files (*.pdf) */
+ public static final FileFilter PDF_FILTER =
+ new SimpleFileFilter(trans.get("filetypes.pdf"), ".pdf");
+
+
+
+
+ private FileHelper() {
+ // Prevent instantiation
+ }
+
+ /**
+ * 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 filen
+ */
+ 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;
+ }
+
+
+ /**
+ * 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 <code>true</code> to write, <code>false</code> to abort.
+ */
+ public static boolean confirmWrite(File file, Component parent) {
+ if (file.exists()) {
+ log.info(1, "File " + file + " exists, confirming overwrite from user");
+ int result = JOptionPane.showConfirmDialog(parent,
+ L10N.replace(trans.get("error.fileExists.desc"), "{filename}", file.getName()),
+ trans.get("error.fileExists.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+ if (result != JOptionPane.YES_OPTION) {
+ log.user(1, "User decided not to overwrite the file");
+ return false;
+ }
+ log.user(1, "User decided to overwrite the file");
+ }
+ 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);
+
+ }
+
+}
public class Icons {
private static final LogHelper log = Application.getLogger();
private static final Translator trans = Application.getTranslator();
-
+
static {
log.debug("Starting to load icons");
}
public static final Icon EDIT_COPY = loadImageIcon("pix/icons/edit-copy.png", "Copy");
public static final Icon EDIT_PASTE = loadImageIcon("pix/icons/edit-paste.png", "Paste");
public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete");
+ public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale");
public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in");
public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out");
--- /dev/null
+package net.sf.openrocket.util;
+
+import java.text.Collator;
+
+/**
+ * An object holder that provides a custom toString return value.
+ * <p>
+ * The class supports sorting by the name.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ * @param <T> the holder type
+ */
+public class Named<T> implements Comparable<Named<T>> {
+
+ private final T object;
+ private final String name;
+
+ private Collator collator = null;
+
+ /**
+ * Sole constructor.
+ *
+ * @param object the held object
+ * @param name the value to return by toString().
+ */
+ public Named(T object, String name) {
+ this.object = object;
+ this.name = name;
+ }
+
+
+ /**
+ * Get the held object.
+ *
+ * @return the object.
+ */
+ public T get() {
+ return object;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+
+ @Override
+ public int compareTo(Named<T> other) {
+ if (collator == null) {
+ collator = Collator.getInstance();
+ }
+
+ return collator.compare(this.toString(), other.toString());
+ }
+
+}
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.main.ExceptionHandler;
import net.sf.openrocket.gui.print.PrintSettings;
+import net.sf.openrocket.l10n.L10N;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.material.Material;
private static final String SPLIT_CHARACTER = "|";
+ private static final List<Locale> SUPPORTED_LOCALES;
+ static {
+ List<Locale> list = new ArrayList<Locale>();
+ for (String lang : new String[] { "en", "de", "es", "fr" }) {
+ list.add(new Locale(lang));
+ }
+ SUPPORTED_LOCALES = Collections.unmodifiableList(list);
+ }
+
+
/**
* Whether to use the debug-node instead of the normal node.
*/
//////////////////
-
+ public static List<Locale> getSupportedLocales() {
+ return SUPPORTED_LOCALES;
+ }
+
+ public static Locale getUserLocale() {
+ String locale = getString("locale", null);
+ return L10N.toLocale(locale);
+ }
+
+ public static void setUserLocale(Locale l) {
+ if (l == null) {
+ putString("locale", null);
+ } else {
+ putString("locale", l.toString());
+ }
+ }
+
+
public static boolean getCheckUpdates() {
return PREFNODE.getBoolean(CHECK_UPDATES, BuildPropertyHolder.DEFAULT_CHECK_UPDATES);
--- /dev/null
+package net.sf.openrocket.util;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * A FileFilter similar to FileNameExtensionFilter except that
+ * it allows multipart extensions (.ork.gz), and also implements
+ * the java.io.FileFilter interface.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SimpleFileFilter extends FileFilter implements java.io.FileFilter {
+
+ private final String description;
+ private final boolean acceptDir;
+ private final String[] extensions;
+
+
+ /**
+ * Create filter that accepts files with the provided extensions that
+ * accepts directories as well.
+ *
+ * @param description the description of this file filter.
+ * @param extensions an array of extensions that match this filter.
+ */
+ public SimpleFileFilter(String description, String ... extensions) {
+ this(description, true, extensions);
+ }
+
+
+ /**
+ * Create filter that accepts files with the provided extensions.
+ *
+ * @param description the description of this file filter.
+ * @param acceptDir whether to accept directories
+ * @param extensions an array of extensions that match this filter.
+ */
+ public SimpleFileFilter(String description, boolean acceptDir, String ... extensions) {
+ this.description = description;
+ this.acceptDir = acceptDir;
+ this.extensions = new String[extensions.length];
+ for (int i=0; i<extensions.length; i++) {
+ String ext = extensions[i].toLowerCase();
+ if (ext.charAt(0) == '.') {
+ this.extensions[i] = ext;
+ } else {
+ this.extensions[i] = '.' + ext;
+ }
+ }
+ }
+
+
+ @Override
+ public boolean accept(File file) {
+ if (file == null)
+ return false;
+ if (file.isDirectory())
+ return acceptDir;
+
+ String filename = file.getName();
+ filename = filename.toLowerCase();
+ for (String ext: extensions) {
+ if (filename.endsWith(ext))
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.util;
+
+public class Utils {
+
+ /**
+ * Null-safe equals method.
+ *
+ * @param first the first object to compare
+ * @param second the second object to compare
+ * @return whether the two objects are both equal or both <code>null</code>
+ */
+ public static boolean equals(Object first, Object second) {
+ if (first == null) {
+ return second == null;
+ } else {
+ return first.equals(second);
+ }
+ }
+
+}
import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.file.RocketLoadException;
import net.sf.openrocket.file.motor.GeneralMotorLoader;
+import net.sf.openrocket.l10n.ResourceBundleTranslator;
import net.sf.openrocket.masscalc.BasicMassCalculator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
db.startLoading();
assertEquals(1, db.getMotorSets().size());
Application.setMotorSetDatabase(db);
+ Application.setBaseTranslator(new ResourceBundleTranslator("l10n.messages"));
}
/**