Merge pull request #2087 from jppetrakis/dev/jppetrakis/unstable/or_csv_from_simtable
Issue #2077 [Feature Request] Add context menu ability for the simulation table to export all run simulation table rows to a CSV file
This commit is contained in:
commit
79b8d8b506
3
.gitignore
vendored
3
.gitignore
vendored
@ -97,3 +97,6 @@ openrocket.log
|
||||
prime/*
|
||||
swing/resources/datafiles/presets/system.ser
|
||||
swing/resources/datafiles/presets
|
||||
|
||||
# Eclipse - because some of us are stubborn
|
||||
.project
|
||||
|
@ -27,7 +27,6 @@
|
||||
<classpathentry kind="lib" path="lib/logback-classic-1.2.11.jar"/>
|
||||
<classpathentry kind="lib" path="lib/logback-core-1.2.11.jar"/>
|
||||
<classpathentry kind="lib" path="lib/opencsv-5.7.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-lang3-3.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/hamcrest-core-2.2.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/hamcrest-2.2.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/jmock-2.12.0.jar"/>
|
||||
|
@ -242,6 +242,15 @@
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/commons-text-1.10.0!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
|
Binary file not shown.
@ -536,6 +536,9 @@ simpanel.pop.plot = Plot / Export
|
||||
simpanel.pop.run = Run
|
||||
simpanel.pop.delete = Delete
|
||||
simpanel.pop.duplicate = Duplicate
|
||||
simpanel.pop.exportToCSV = Export table as CSV file
|
||||
simpanel.pop.exportToCSV.save.dialog.title = Save as CSV file
|
||||
simpanel.dlg.no.simulation.table.rows = Simulation table has no entries\u2026 Please run a simulation first.
|
||||
simpanel.checkbox.donotask = Do not ask me again
|
||||
simpanel.lbl.defpref = You can change the default operation in the preferences.
|
||||
simpanel.dlg.lbl.DeleteSim1 = Delete the selected simulations?
|
||||
@ -1440,6 +1443,7 @@ main.menu.file.quit = Quit
|
||||
main.menu.file.quit.desc = Quit the program
|
||||
main.menu.file.exportDecal = Save decal image\u2026
|
||||
main.menu.file.exportDecal.desc = Save a decal from the current rocket design to a file for editing.
|
||||
main.menu.file.table.exportToCSV = Export simulations table as CSV file
|
||||
|
||||
main.menu.edit = Edit
|
||||
main.menu.edit.desc = Rocket editing
|
||||
|
BIN
core/resources/pix/icons/sim_table_export.png
Normal file
BIN
core/resources/pix/icons/sim_table_export.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 723 B |
@ -20,7 +20,7 @@ import net.sf.openrocket.simulation.extension.SimulationExtension;
|
||||
import net.sf.openrocket.simulation.extension.SimulationExtensionProvider;
|
||||
import net.sf.openrocket.simulation.extension.impl.JavaCode;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
import com.google.inject.Key;
|
||||
|
||||
@ -85,7 +85,7 @@ class SingleSimulationHandler extends AbstractElementHandler {
|
||||
}
|
||||
} else if (element.equals("listener") && content.trim().length() > 0) {
|
||||
extensions.add(compatibilityExtension(content.trim()));
|
||||
} else if (element.equals("extension") && !StringUtil.isEmpty(attributes.get("extensionid"))) {
|
||||
} else if (element.equals("extension") && !StringUtils.isEmpty(attributes.get("extensionid"))) {
|
||||
String id = attributes.get("extensionid");
|
||||
SimulationExtension extension = null;
|
||||
Set<SimulationExtensionProvider> extensionProviders = Application.getInjector().getInstance(new Key<Set<SimulationExtensionProvider>>() {
|
||||
|
@ -4,7 +4,7 @@ import net.sf.openrocket.preset.TypedKey;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.unit.Unit;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
public class DoubleUnitColumnParser extends BaseUnitColumnParser {
|
||||
|
||||
@ -19,7 +19,7 @@ public class DoubleUnitColumnParser extends BaseUnitColumnParser {
|
||||
@Override
|
||||
protected void doParse(String columnData, String[] data, TypedPropertyMap props) {
|
||||
try {
|
||||
if (StringUtil.isEmpty(columnData)) {
|
||||
if (StringUtils.isEmpty(columnData)) {
|
||||
return;
|
||||
}
|
||||
double value = Double.valueOf(columnData);
|
||||
|
@ -4,7 +4,7 @@ import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.preset.TypedKey;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
public class LineMaterialColumnParser extends BaseColumnParser {
|
||||
|
||||
@ -22,7 +22,7 @@ public class LineMaterialColumnParser extends BaseColumnParser {
|
||||
@Override
|
||||
protected void doParse(String columnData, String[] data, TypedPropertyMap props) {
|
||||
|
||||
if (StringUtil.isEmpty(columnData)) {
|
||||
if (StringUtils.isEmpty(columnData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package net.sf.openrocket.preset.loader;
|
||||
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Special DoubleUnitColumnParser for Mass column. Here we assume that if a mass of 0 is
|
||||
@ -18,7 +18,7 @@ public class MassColumnParser extends DoubleUnitColumnParser {
|
||||
|
||||
@Override
|
||||
protected void doParse(String columnData, String[] data, TypedPropertyMap props) {
|
||||
if ( StringUtil.isEmpty(columnData) || "?".equals(columnData.trim())) {
|
||||
if ( StringUtils.isEmpty(columnData) || "?".equals(columnData.trim())) {
|
||||
return;
|
||||
}
|
||||
double d = Double.valueOf(columnData);
|
||||
|
@ -5,7 +5,7 @@ import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.preset.TypedKey;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
public class MaterialColumnParser extends BaseColumnParser {
|
||||
|
||||
@ -27,7 +27,7 @@ public class MaterialColumnParser extends BaseColumnParser {
|
||||
@Override
|
||||
protected void doParse(String columnData, String[] data, TypedPropertyMap props) {
|
||||
|
||||
if (StringUtil.isEmpty(columnData)) {
|
||||
if (StringUtils.isEmpty(columnData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import net.sf.openrocket.unit.Unit;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
import com.opencsv.CSVReader;
|
||||
|
||||
/**
|
||||
@ -138,7 +138,7 @@ public abstract class RockSimComponentFileLoader {
|
||||
if (data.length == 0) {
|
||||
continue;
|
||||
}
|
||||
if (data.length == 1 && StringUtil.isEmpty(data[0])) {
|
||||
if (data.length == 1 && StringUtils.isEmpty(data[0])) {
|
||||
continue;
|
||||
}
|
||||
parseData(data);
|
||||
|
@ -4,7 +4,7 @@ import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.preset.TypedKey;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
public class SurfaceMaterialColumnParser extends BaseColumnParser {
|
||||
|
||||
@ -22,7 +22,7 @@ public class SurfaceMaterialColumnParser extends BaseColumnParser {
|
||||
@Override
|
||||
protected void doParse(String columnData, String[] data, TypedPropertyMap props) {
|
||||
|
||||
if (StringUtil.isEmpty(columnData)) {
|
||||
if (StringUtils.isEmpty(columnData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A class defining a storable simulation variable type. This class defined numerous ready
|
||||
@ -272,7 +272,7 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
// found it from symbol
|
||||
|
||||
// if name was not given (empty string), can use the one we found
|
||||
if ( s == null || StringUtil.isEmpty(s)){
|
||||
if ( s == null || StringUtils.isEmpty(s)){
|
||||
s = type.getName();
|
||||
}
|
||||
if ( u == null ){
|
||||
|
@ -11,7 +11,7 @@ import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.unit.FixedUnitGroup;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -227,7 +227,7 @@ public class CustomExpression implements Cloneable {
|
||||
}
|
||||
|
||||
public boolean checkSymbol() {
|
||||
if (StringUtil.isEmpty(symbol)) {
|
||||
if (StringUtils.isEmpty(symbol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ public class CustomExpression implements Cloneable {
|
||||
}
|
||||
|
||||
public boolean checkName() {
|
||||
if (StringUtil.isEmpty(name)) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -304,7 +304,7 @@ public class CustomExpression implements Cloneable {
|
||||
* building the expression.
|
||||
*/
|
||||
public boolean checkExpression() {
|
||||
if (StringUtil.isEmpty(expression)) {
|
||||
if (StringUtils.isEmpty(expression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,12 @@ import de.congrace.exp4j.ExpressionBuilder;
|
||||
import de.congrace.exp4j.Variable;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.logging.Markers;
|
||||
import net.sf.openrocket.simulation.customexpression.CustomExpression;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.util.ArrayUtils;
|
||||
import net.sf.openrocket.util.LinearInterpolator;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
public class RangeExpression extends CustomExpression {
|
||||
private static final Logger log = LoggerFactory.getLogger(RangeExpression.class);
|
||||
@ -30,10 +29,10 @@ public class RangeExpression extends CustomExpression {
|
||||
public RangeExpression(OpenRocketDocument doc, String startTime, String endTime, String variableType) {
|
||||
super(doc);
|
||||
|
||||
if (StringUtil.isEmpty(startTime)){
|
||||
if (StringUtils.isEmpty(startTime)){
|
||||
startTime = "0";
|
||||
}
|
||||
if (StringUtil.isEmpty(endTime)){
|
||||
if (StringUtils.isEmpty(endTime)){
|
||||
endTime = "t";
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
@ -19,7 +19,7 @@ public class JavaCode extends AbstractSimulationExtension {
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
String className = getClassName();
|
||||
try {
|
||||
if (!StringUtil.isEmpty(className)) {
|
||||
if (!StringUtils.isEmpty(className)) {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
if (!SimulationListener.class.isAssignableFrom(clazz)) {
|
||||
throw new SimulationException("Class " + className + " does not implement SimulationListener");
|
||||
@ -40,7 +40,7 @@ public class JavaCode extends AbstractSimulationExtension {
|
||||
public String getName() {
|
||||
String name = trans.get("SimulationExtension.javacode.name") + ": ";
|
||||
String className = getClassName();
|
||||
if (!StringUtil.isEmpty(className)) {
|
||||
if (!StringUtils.isEmpty(className)) {
|
||||
name = name + className;
|
||||
} else {
|
||||
name = name + trans.get("SimulationExtension.javacode.name.none");
|
||||
|
@ -18,7 +18,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.util.StringUtil;
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
@ -672,7 +672,7 @@ public class UnitGroup {
|
||||
throw new NumberFormatException("string did not match required pattern");
|
||||
}
|
||||
|
||||
double value = StringUtil.convertToDouble(matcher.group(1));
|
||||
double value = StringUtils.convertToDouble(matcher.group(1));
|
||||
String unit = matcher.group(2).trim();
|
||||
|
||||
if (unit.equals("")) {
|
||||
|
@ -1,42 +0,0 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* Returns true if the argument is null or empty.
|
||||
*
|
||||
* This is implemented without using String.isEmpty() because that method
|
||||
* is not available in Froyo.
|
||||
*
|
||||
* @param s string to check
|
||||
* @return true iff s is null or trims to
|
||||
* an empty string, where trim is defined
|
||||
* by {@link java.lang.String#trim}
|
||||
*/
|
||||
public static boolean isEmpty( String s ) {
|
||||
if ( s == null ) {
|
||||
return true;
|
||||
}
|
||||
return "".equals(s.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a double, but with a more robust locale handling.
|
||||
* Some systems use a comma as a decimal separator, some a dot. This method
|
||||
* should work for both cases
|
||||
* @param input string to convert
|
||||
* @return double converted from string
|
||||
* @throws NumberFormatException if the string cannot be parsed.
|
||||
*/
|
||||
public static double convertToDouble(String input) {
|
||||
input = input.replace(',', '.');
|
||||
int decimalSeparator = input.lastIndexOf('.');
|
||||
|
||||
if (decimalSeparator > -1) {
|
||||
input = input.substring(0, decimalSeparator).replace(".", "") + input.substring(decimalSeparator);
|
||||
}
|
||||
|
||||
return Double.parseDouble(input);
|
||||
}
|
||||
|
||||
}
|
101
core/src/net/sf/openrocket/util/StringUtils.java
Normal file
101
core/src/net/sf/openrocket/util/StringUtils.java
Normal file
@ -0,0 +1,101 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
public static String join(String sep, Object[] values) {
|
||||
if ( values == null || values.length == 0 ) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder value = new StringBuilder();
|
||||
for( Object v : values ) {
|
||||
if( value.length() > 0 ) {
|
||||
value.append(sep);
|
||||
}
|
||||
value.append(String.valueOf(v));
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Join starting with a list of strings rather than an array
|
||||
* @param sep separator
|
||||
* @param listValues list of values
|
||||
* @return joined string
|
||||
*/
|
||||
public static String join(String sep, List<String> listValues) {
|
||||
String[] values = listValues.toArray(new String[0]);
|
||||
return join(sep, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the argument is null or empty.
|
||||
*
|
||||
* This is implemented without using String.isEmpty() because that method
|
||||
* is not available in Froyo.
|
||||
*
|
||||
* @param s string to check
|
||||
* @return true iff s is null or trims to
|
||||
* an empty string, where trim is defined
|
||||
* by {@link java.lang.String#trim}
|
||||
*/
|
||||
public static boolean isEmpty( String s ) {
|
||||
if ( s == null ) {
|
||||
return true;
|
||||
}
|
||||
return "".equals(s.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a double, but with a more robust locale handling.
|
||||
* Some systems use a comma as a decimal separator, some a dot. This method
|
||||
* should work for both cases
|
||||
* @param input string to convert
|
||||
* @return double converted from string
|
||||
* @throws NumberFormatException if the string cannot be parsed.
|
||||
*/
|
||||
public static double convertToDouble(String input) {
|
||||
input = input.replace(',', '.');
|
||||
int decimalSeparator = input.lastIndexOf('.');
|
||||
|
||||
if (decimalSeparator > -1) {
|
||||
input = input.substring(0, decimalSeparator).replace(".", "") + input.substring(decimalSeparator);
|
||||
}
|
||||
|
||||
return Double.parseDouble(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped version of the String so that it can be safely used as a value in a CSV file.
|
||||
* The goal is to surround the input string in double quotes if it contains any double quotes, commas,
|
||||
* newlines, or carriage returns, and to escape any double quotes within the string by doubling them up.
|
||||
* @param input the string to escape
|
||||
* @return the escaped string that can be safely used in a CSV file
|
||||
*/
|
||||
public static String escapeCSV(String input) {
|
||||
final List<Character> CSV_SEARCH_CHARS = new ArrayList<>(Arrays.asList(',', '"', '\r', '\n'));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean quoted = false;
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char c = input.charAt(i);
|
||||
if (CSV_SEARCH_CHARS.contains(c)) {
|
||||
quoted = true;
|
||||
sb.append('\"');
|
||||
}
|
||||
if (c == '\"') {
|
||||
sb.append('\"');
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
if (quoted) {
|
||||
sb.insert(0, '\"');
|
||||
sb.append('\"');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -8,40 +8,40 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* A class that tests
|
||||
* {@link net.sf.openrocket.util.StringUtil}.
|
||||
* {@link StringUtils}.
|
||||
*/
|
||||
public class StringUtilTest {
|
||||
@Test
|
||||
public void testStrings() {
|
||||
assertTrue(StringUtil.isEmpty(""));
|
||||
assertTrue(StringUtil.isEmpty(new StringBuilder().toString())); // ""
|
||||
assertTrue(StringUtil.isEmpty(" "));
|
||||
assertTrue(StringUtil.isEmpty(" "));
|
||||
assertTrue(StringUtil.isEmpty(" "));
|
||||
assertTrue(StringUtil.isEmpty(null));
|
||||
assertTrue(StringUtils.isEmpty(""));
|
||||
assertTrue(StringUtils.isEmpty(new StringBuilder().toString())); // ""
|
||||
assertTrue(StringUtils.isEmpty(" "));
|
||||
assertTrue(StringUtils.isEmpty(" "));
|
||||
assertTrue(StringUtils.isEmpty(" "));
|
||||
assertTrue(StringUtils.isEmpty(null));
|
||||
|
||||
assertFalse(StringUtil.isEmpty("A"));
|
||||
assertFalse(StringUtil.isEmpty(" . "));
|
||||
assertFalse(StringUtils.isEmpty("A"));
|
||||
assertFalse(StringUtils.isEmpty(" . "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToDouble() {
|
||||
assertEquals(0.2, StringUtil.convertToDouble(".2"), MathUtil.EPSILON);
|
||||
assertEquals(0.2, StringUtil.convertToDouble(",2"), MathUtil.EPSILON);
|
||||
assertEquals(1, StringUtil.convertToDouble("1,"), MathUtil.EPSILON);
|
||||
assertEquals(2, StringUtil.convertToDouble("2."), MathUtil.EPSILON);
|
||||
assertEquals(1, StringUtil.convertToDouble("1"), MathUtil.EPSILON);
|
||||
assertEquals(1.52, StringUtil.convertToDouble("1.52"), MathUtil.EPSILON);
|
||||
assertEquals(1.52, StringUtil.convertToDouble("1,52"), MathUtil.EPSILON);
|
||||
assertEquals(1.5, StringUtil.convertToDouble("1.500"), MathUtil.EPSILON);
|
||||
assertEquals(1.5, StringUtil.convertToDouble("1,500"), MathUtil.EPSILON);
|
||||
assertEquals(1500.61, StringUtil.convertToDouble("1.500,61"), MathUtil.EPSILON);
|
||||
assertEquals(1500.61, StringUtil.convertToDouble("1,500.61"), MathUtil.EPSILON);
|
||||
assertEquals(1500.2, StringUtil.convertToDouble("1,500,200"), MathUtil.EPSILON);
|
||||
assertEquals(1500.2, StringUtil.convertToDouble("1.500.200"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtil.convertToDouble("1500200.23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtil.convertToDouble("1500200,23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtil.convertToDouble("1,500,200.23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtil.convertToDouble("1.500.200,23"), MathUtil.EPSILON);
|
||||
assertEquals(0.2, StringUtils.convertToDouble(".2"), MathUtil.EPSILON);
|
||||
assertEquals(0.2, StringUtils.convertToDouble(",2"), MathUtil.EPSILON);
|
||||
assertEquals(1, StringUtils.convertToDouble("1,"), MathUtil.EPSILON);
|
||||
assertEquals(2, StringUtils.convertToDouble("2."), MathUtil.EPSILON);
|
||||
assertEquals(1, StringUtils.convertToDouble("1"), MathUtil.EPSILON);
|
||||
assertEquals(1.52, StringUtils.convertToDouble("1.52"), MathUtil.EPSILON);
|
||||
assertEquals(1.52, StringUtils.convertToDouble("1,52"), MathUtil.EPSILON);
|
||||
assertEquals(1.5, StringUtils.convertToDouble("1.500"), MathUtil.EPSILON);
|
||||
assertEquals(1.5, StringUtils.convertToDouble("1,500"), MathUtil.EPSILON);
|
||||
assertEquals(1500.61, StringUtils.convertToDouble("1.500,61"), MathUtil.EPSILON);
|
||||
assertEquals(1500.61, StringUtils.convertToDouble("1,500.61"), MathUtil.EPSILON);
|
||||
assertEquals(1500.2, StringUtils.convertToDouble("1,500,200"), MathUtil.EPSILON);
|
||||
assertEquals(1500.2, StringUtils.convertToDouble("1.500.200"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtils.convertToDouble("1500200.23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtils.convertToDouble("1500200,23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtils.convertToDouble("1,500,200.23"), MathUtil.EPSILON);
|
||||
assertEquals(1500200.23, StringUtils.convertToDouble("1.500.200,23"), MathUtil.EPSILON);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/hamcrest-core-2.2.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/hamcrest-2.2.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/opencsv-5.7.1.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Core/lib/commons-lang3-3.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/jmock-2.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/jmock-junit4-2.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="/OpenRocket Test Libraries/junit-4.13.2.jar"/>
|
||||
|
@ -117,7 +117,6 @@
|
||||
<zipfileset src="${lib.dir}/jcommon-1.0.18.jar"/>
|
||||
<zipfileset src="${lib.dir}/jfreechart-1.0.15.jar"/>
|
||||
<zipfileset src="${core.dir}/lib/opencsv-5.7.1.jar"/>
|
||||
<zipfileset src="${core.dir}/lib/commons-lang3-3.12.0.jar"/>
|
||||
<zipfileset src="${core.dir}/lib/annotation-detector-3.0.5.jar"/>
|
||||
<zipfileset src="${core.dir}/lib/slf4j-api-1.7.30.jar"/>
|
||||
<zipfileset src="${core.dir}/lib/logback-classic-1.2.11.jar"/>
|
||||
|
181
swing/src/net/sf/openrocket/file/SimulationTableCSVExport.java
Normal file
181
swing/src/net/sf/openrocket/file/SimulationTableCSVExport.java
Normal file
@ -0,0 +1,181 @@
|
||||
package net.sf.openrocket.file;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JTable;
|
||||
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.Warning;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.gui.adaptors.Column;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||
import net.sf.openrocket.gui.adaptors.ValueColumn;
|
||||
import net.sf.openrocket.unit.Value;
|
||||
import net.sf.openrocket.util.TextUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SimulationTableCSVExport {
|
||||
private final OpenRocketDocument document;
|
||||
private final JTable simulationTable;
|
||||
private final ColumnTableModel simulationTableModel;
|
||||
private final HashMap<String, String> valueColumnToUnitString = new HashMap<>();
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimulationTableCSVExport.class);
|
||||
|
||||
public SimulationTableCSVExport (OpenRocketDocument document, JTable simulationTable,
|
||||
ColumnTableModel simulationTableModel) {
|
||||
this.document = document;
|
||||
this.simulationTable = simulationTable;
|
||||
this.simulationTableModel = simulationTableModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* To make a lookup of table header to units. For those columns which are of type Value, the
|
||||
* units will be added to the header...
|
||||
*/
|
||||
private void populateColumnNameToUnitsHashTable() {
|
||||
valueColumnToUnitString.clear(); // Necessary if units changed during session
|
||||
if (simulationTableModel == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < simulationTableModel.getColumnCount(); i++) {
|
||||
Column c = simulationTableModel.getColumn(i);
|
||||
if (c instanceof ValueColumn) {
|
||||
// Only value columns seem to have units that are not zero length strings... These are
|
||||
// the ones we actually want in our lookup table.
|
||||
valueColumnToUnitString.put(c.toString(), c.getUnits().getDefaultUnit().getUnit());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Dump data from sim table to file for run simulations
|
||||
* @param data The CSV data as one string block.
|
||||
* @param CSVFile The file to dump the data to.
|
||||
*/
|
||||
private void dumpDataToFile(String data, File CSVFile) {
|
||||
BufferedWriter bufferedWriter = null;
|
||||
try {
|
||||
CSVFile.createNewFile();
|
||||
bufferedWriter = new BufferedWriter(new FileWriter(CSVFile));
|
||||
bufferedWriter.write(data);
|
||||
} catch (IOException e) {
|
||||
String msg = e.getMessage();
|
||||
JOptionPane.showMessageDialog(simulationTable.getParent(), msg);
|
||||
} finally {
|
||||
if (bufferedWriter != null) {
|
||||
try {
|
||||
bufferedWriter.close();
|
||||
} catch (IOException e) {
|
||||
String msg = e.getMessage();
|
||||
JOptionPane.showMessageDialog(simulationTable.getParent(), msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the CSV data from the simulation table
|
||||
* @param fieldSep The field separator to use in the CSV file.
|
||||
* @param precision The number of decimal places to use in the CSV file.
|
||||
* @return
|
||||
*/
|
||||
public String generateCSVDate(String fieldSep, int precision) {
|
||||
int modelColumnCount = simulationTableModel.getColumnCount();
|
||||
int modelRowCount = simulationTableModel.getRowCount();
|
||||
populateColumnNameToUnitsHashTable();
|
||||
|
||||
String CSVSimResultString;
|
||||
// Obtain the column titles for the first row of the CSV
|
||||
ArrayList<String> rowColumnElement = new ArrayList<>();
|
||||
for (int j = 1; j<modelColumnCount ; j++) {
|
||||
String colName = simulationTable.getColumnName(j);
|
||||
|
||||
// Get the unit string and append to column that it applies to. Columns w/o units will remain unchanged.
|
||||
if (valueColumnToUnitString.containsKey(colName)) {
|
||||
String unitString = valueColumnToUnitString.get(colName);
|
||||
colName += " (" + unitString + ")";
|
||||
}
|
||||
rowColumnElement.add(colName);
|
||||
}
|
||||
|
||||
// ONE difference here is that we'll place any warnings at the last cell in the CSV.
|
||||
CSVSimResultString = StringUtils.join(fieldSep, rowColumnElement) + fieldSep + "Simulation Warnings";
|
||||
|
||||
StringBuilder fullOutputResult = new StringBuilder(CSVSimResultString);
|
||||
|
||||
// Get relevant data and create the comma separated data from it.
|
||||
for (int i = 0; i < modelRowCount; i++) {
|
||||
// Account for sorting... resulting CSV file will be in the same order as shown in the table thanks to this gem.
|
||||
int idx = simulationTable.convertRowIndexToModel(i);
|
||||
|
||||
int nullCnt = 0;
|
||||
rowColumnElement.clear();
|
||||
|
||||
// Get the simulation's warning text if any. This bypasses the need to use
|
||||
// the column 0 stuff which is kind of difficult to use!
|
||||
WarningSet ws = document.getSimulation(idx).getSimulatedWarnings();
|
||||
StringBuilder warningsText = new StringBuilder();
|
||||
for (Warning w : ws) {
|
||||
String warning = w.toString();
|
||||
if (warning != null) {
|
||||
warningsText.append(w).append(" "); // TODO - formatting. inserting a \n does funny things so use " " for now
|
||||
}
|
||||
}
|
||||
|
||||
// Piece together the column data for the index-row, skipping any rows with null counts > 0!
|
||||
for (int j = 1; j < modelColumnCount ; j++) { // skip first column
|
||||
Object o = simulationTableModel.getValueAt(idx, j);
|
||||
if (o != null) {
|
||||
final String valueString;
|
||||
if (o instanceof Value) {
|
||||
double value = ((Value) o).getUnitValue();
|
||||
valueString = TextUtil.doubleToString(value, precision);
|
||||
} else {
|
||||
valueString = o.toString();
|
||||
}
|
||||
rowColumnElement.add(StringUtils.escapeCSV(valueString));
|
||||
} else {
|
||||
rowColumnElement.add("");
|
||||
nullCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// Current "unstable" will have a populated sim table EXCEPT for the optimum delay column on a restart
|
||||
// after a save. That means any row that WAS simulated will have exactly one null column in it... so we'll
|
||||
// skip row export for the case where there are MORE than one nulls. Either way the user should run sims.
|
||||
if (nullCnt > 1) { // ignore rows that have null column fields 1 through 8...
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the column data comma separated string for the ith row...
|
||||
CSVSimResultString = StringUtils.join(fieldSep, rowColumnElement);
|
||||
|
||||
// Piece together all rows into one big ginormous string, adding any warnings to the item
|
||||
fullOutputResult.append("\n").append(CSVSimResultString);
|
||||
fullOutputResult.append(fieldSep).append(warningsText);
|
||||
}
|
||||
|
||||
return fullOutputResult.toString();
|
||||
}
|
||||
|
||||
public void export(File file, String fieldSep, int precision) {
|
||||
if (file == null) {
|
||||
log.warn("No file selected for export");
|
||||
return;
|
||||
}
|
||||
|
||||
String CSVData = generateCSVDate(fieldSep, precision);
|
||||
this.dumpDataToFile(CSVData, file);
|
||||
log.info("Simulation table data exported to " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ import java.util.Comparator;
|
||||
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
public abstract class Column {
|
||||
private final String name;
|
||||
private final String toolTip;
|
||||
@ -59,7 +61,9 @@ public abstract class Column {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public UnitGroup getUnits() {
|
||||
return UnitGroup.UNITS_NONE;
|
||||
}
|
||||
/**
|
||||
* Return the column type class. This is necessary for example for numerical
|
||||
* sorting of Value objects, showing booleans as checkboxes etc.
|
||||
|
@ -31,6 +31,10 @@ public abstract class ValueColumn extends Column {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitGroup getUnits() {
|
||||
return this.unit;
|
||||
}
|
||||
/**
|
||||
* Returns the double value to show in the Value object
|
||||
*
|
||||
|
@ -16,6 +16,7 @@ import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.sf.openrocket.util.StringUtils;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
@ -33,7 +34,6 @@ import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.utils.StringUtils;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class MotorInformationPanel extends JPanel {
|
||||
|
@ -51,6 +51,7 @@ import javax.swing.table.TableColumnModel;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import net.sf.openrocket.arch.SystemInfo;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -1157,6 +1158,14 @@ public class GeneralOptimizationDialog extends JDialog {
|
||||
chooser.setFileFilter(FileHelper.CSV_FILTER);
|
||||
chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
|
||||
chooser.setAccessory(csvOptions);
|
||||
|
||||
// TODO: update this dynamically instead of hard-coded values
|
||||
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
||||
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
|
||||
Dimension currentSize = chooser.getPreferredSize();
|
||||
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
||||
chooser.setPreferredSize(newSize);
|
||||
}
|
||||
|
||||
if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
|
||||
return;
|
||||
|
@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JCheckBox;
|
||||
@ -566,6 +567,11 @@ public class BasicFrame extends JFrame {
|
||||
|
||||
//// END CREATE and implement File > "Encode 3D" menu and submenu
|
||||
*/
|
||||
// export sim table...
|
||||
AbstractAction simTableExportAction = simulationPanel.getSimulationTableAsCSVExportAction();
|
||||
JMenuItem exportSimTableToCSVMenuItem = new JMenuItem(simTableExportAction);
|
||||
menu.add(exportSimTableToCSVMenuItem);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
//// Close
|
||||
@ -1430,7 +1436,6 @@ public class BasicFrame extends JFrame {
|
||||
}
|
||||
// END ROCKSIM Export Action
|
||||
|
||||
|
||||
/**
|
||||
* Perform the writing of the design to the given file in RockSim format.
|
||||
*
|
||||
|
@ -3,6 +3,8 @@ package net.sf.openrocket.gui.main;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
@ -13,6 +15,7 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
@ -21,7 +24,9 @@ import javax.swing.AbstractAction;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
@ -34,8 +39,10 @@ import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
|
||||
import net.sf.openrocket.gui.widgets.IconButton;
|
||||
import net.sf.openrocket.utils.TableRowTraversalPolicy;
|
||||
import net.sf.openrocket.arch.SystemInfo;
|
||||
import net.sf.openrocket.gui.components.CsvOptionPanel;
|
||||
import net.sf.openrocket.gui.util.FileHelper;
|
||||
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -59,20 +66,22 @@ import net.sf.openrocket.gui.simulation.SimulationEditDialog;
|
||||
import net.sf.openrocket.gui.simulation.SimulationRunDialog;
|
||||
import net.sf.openrocket.gui.simulation.SimulationWarningDialog;
|
||||
import net.sf.openrocket.gui.util.Icons;
|
||||
import net.sf.openrocket.gui.widgets.IconButton;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.simulation.FlightData;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.startup.Preferences;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.AlphanumComparator;
|
||||
import net.sf.openrocket.file.SimulationTableCSVExport;
|
||||
import net.sf.openrocket.utils.TableRowTraversalPolicy;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class SimulationPanel extends JPanel {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimulationPanel.class);
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
@ -96,6 +105,7 @@ public class SimulationPanel extends JPanel {
|
||||
private final JButton runButton;
|
||||
private final JButton deleteButton;
|
||||
private final JButton plotButton;
|
||||
private final JButton simTableExportButton;
|
||||
private final JPopupMenu pm;
|
||||
|
||||
private final SimulationAction editSimulationAction;
|
||||
@ -103,8 +113,10 @@ public class SimulationPanel extends JPanel {
|
||||
private final SimulationAction plotSimulationAction;
|
||||
private final SimulationAction duplicateSimulationAction;
|
||||
private final SimulationAction deleteSimulationAction;
|
||||
private final SimulationAction simTableExportAction;
|
||||
|
||||
private int[] previousSelection = null;
|
||||
private JMenuItem exportSimTableToCSVMenuItem;
|
||||
|
||||
public SimulationPanel(OpenRocketDocument doc) {
|
||||
super(new MigLayout("fill", "[grow][][][][][][grow]"));
|
||||
@ -119,6 +131,7 @@ public class SimulationPanel extends JPanel {
|
||||
plotSimulationAction = new PlotSimulationAction();
|
||||
duplicateSimulationAction = new DuplicateSimulationAction();
|
||||
deleteSimulationAction = new DeleteSimulationAction();
|
||||
simTableExportAction = new ExportSimulationTableAsCSVAction();
|
||||
|
||||
//////// The simulation action buttons ////////
|
||||
|
||||
@ -139,7 +152,7 @@ public class SimulationPanel extends JPanel {
|
||||
RocketActions.tieActionToButton(runButton, runSimulationAction, trans.get("simpanel.but.runsimulations"));
|
||||
runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu"));
|
||||
this.add(runButton, "gapright para");
|
||||
|
||||
|
||||
//// Delete simulations button
|
||||
deleteButton = new IconButton();
|
||||
RocketActions.tieActionToButton(deleteButton, deleteSimulationAction, trans.get("simpanel.but.deletesimulations"));
|
||||
@ -151,6 +164,9 @@ public class SimulationPanel extends JPanel {
|
||||
RocketActions.tieActionToButton(plotButton, plotSimulationAction, trans.get("simpanel.but.plotexport"));
|
||||
this.add(plotButton, "wrap para");
|
||||
|
||||
//// Run then Dump simulations
|
||||
simTableExportButton = new IconButton();
|
||||
RocketActions.tieActionToButton(simTableExportButton, simTableExportAction, trans.get("simpanel.but.runsimulations"));
|
||||
|
||||
|
||||
//////// The simulation table
|
||||
@ -174,6 +190,7 @@ public class SimulationPanel extends JPanel {
|
||||
pm.addSeparator();
|
||||
pm.add(runSimulationAction);
|
||||
pm.add(plotSimulationAction);
|
||||
pm.add(simTableExportAction);
|
||||
|
||||
// The normal left/right and tab/shift-tab key action traverses each cell/column of the table instead of going to the next row.
|
||||
TableRowTraversalPolicy.setTableRowTraversalPolicy(simulationTable);
|
||||
@ -437,6 +454,14 @@ public class SimulationPanel extends JPanel {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the action for exporting the simulation table data to a CSV file.
|
||||
* @return
|
||||
*/
|
||||
public AbstractAction getSimulationTableAsCSVExportAction() {
|
||||
return simTableExportAction;
|
||||
}
|
||||
|
||||
protected void doPopup(MouseEvent e) {
|
||||
pm.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
@ -447,6 +472,7 @@ public class SimulationPanel extends JPanel {
|
||||
runSimulationAction.updateEnabledState();
|
||||
plotSimulationAction.updateEnabledState();
|
||||
duplicateSimulationAction.updateEnabledState();
|
||||
simTableExportAction.updateEnabledState();
|
||||
}
|
||||
|
||||
/// when the simulation tab is selected this run outdated simulated if appropriate.
|
||||
@ -598,6 +624,90 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
class ExportSimulationTableAsCSVAction extends SimulationAction {
|
||||
|
||||
public ExportSimulationTableAsCSVAction() {
|
||||
putValue(NAME, trans.get("simpanel.pop.exportToCSV"));
|
||||
putValue(SMALL_ICON, Icons.SIM_TABLE_EXPORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
Container tableParent = simulationTable.getParent();
|
||||
int rowCount = simulationTableModel.getRowCount();
|
||||
|
||||
// I'm pretty sure with the enablement/disablement of the menu item under the File dropdown,
|
||||
// that this would no longer be needed because if there is no sim table yet, the context menu
|
||||
// won't show up. But I'm going to leave this in just in case....
|
||||
if (rowCount <= 0) {
|
||||
log.info("No simulation table rows to export");
|
||||
JOptionPane.showMessageDialog(tableParent, trans.get("simpanel.dlg.no.simulation.table.rows"));
|
||||
return;
|
||||
}
|
||||
|
||||
JFileChooser fch = this.setUpFileChooser();
|
||||
int selectionStatus = fch.showSaveDialog(tableParent);
|
||||
if (selectionStatus != JFileChooser.APPROVE_OPTION) {
|
||||
log.info("User cancelled CSV export");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the info from the file chooser
|
||||
File CSVFile = fch.getSelectedFile();
|
||||
CSVFile = FileHelper.forceExtension(CSVFile, "csv");
|
||||
String separator = ((CsvOptionPanel) fch.getAccessory()).getFieldSeparator();
|
||||
int precision = ((CsvOptionPanel) fch.getAccessory()).getDecimalPlaces();
|
||||
((CsvOptionPanel) fch.getAccessory()).storePreferences();
|
||||
|
||||
// Handle some special separator options from CsvOptionPanel
|
||||
if (separator.equals(trans.get("CsvOptionPanel.separator.space"))) {
|
||||
separator = " ";
|
||||
} else if (separator.equals(trans.get("CsvOptionPanel.separator.tab"))) {
|
||||
separator = "\t";
|
||||
}
|
||||
|
||||
SimulationTableCSVExport exporter = new SimulationTableCSVExport(document, simulationTable, simulationTableModel);
|
||||
exporter.export(CSVFile, separator, precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the file chooser to save the CSV file.
|
||||
* @return The file chooser.
|
||||
*/
|
||||
private JFileChooser setUpFileChooser() {
|
||||
JFileChooser fch = new JFileChooser();
|
||||
fch.setDialogTitle(trans.get("simpanel.pop.exportToCSV.save.dialog.title"));
|
||||
fch.setFileFilter(FileHelper.CSV_FILTER);
|
||||
fch.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
|
||||
fch.setAcceptAllFileFilterUsed(false);
|
||||
|
||||
// Default output CSV to same name as the document's rocket name.
|
||||
String fileName = document.getRocket().getName() + ".csv";
|
||||
fch.setSelectedFile(new File(fileName));
|
||||
|
||||
// Add CSV options to FileChooser
|
||||
CsvOptionPanel CSVOptions = new CsvOptionPanel(SimulationTableCSVExport.class);
|
||||
fch.setAccessory(CSVOptions);
|
||||
fch.revalidate();
|
||||
|
||||
// TODO: update this dynamically instead of hard-coded values
|
||||
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
||||
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
|
||||
Dimension currentSize = fch.getPreferredSize();
|
||||
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
||||
fch.setPreferredSize(newSize);
|
||||
}
|
||||
|
||||
return fch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEnabledState() {
|
||||
setEnabled(simulationTableModel != null && simulationTableModel.getRowCount() > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PlotSimulationAction extends SimulationAction {
|
||||
public PlotSimulationAction() {
|
||||
putValue(NAME, trans.get("simpanel.pop.plot"));
|
||||
|
@ -56,6 +56,7 @@ public class Icons {
|
||||
public static final Icon FILE_PRINT = loadImageIcon("pix/icons/print-design.specs.png", "Print specifications");
|
||||
// public static final Icon FILE_IMPORT = loadImageIcon("pix/icons/model_import.png", "Import");
|
||||
public static final Icon FILE_EXPORT_AS = loadImageIcon("pix/icons/model_export.png", "Export model as");
|
||||
public static final Icon SIM_TABLE_EXPORT = loadImageIcon("pix/icons/sim_table_export.png", "Export simulation table");
|
||||
public static final Icon ENCODE_3D = loadImageIcon("pix/icons/model_encode3d.png", "Encode 3D");
|
||||
public static final Icon FILE_CLOSE = loadImageIcon("pix/icons/document-close.png", "Close document");
|
||||
public static final Icon FILE_QUIT = loadImageIcon("pix/icons/application-exit.png", "Quit OpenRocket");
|
||||
|
@ -1,19 +0,0 @@
|
||||
package net.sf.openrocket.utils;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
public static String join(String sep, Object[] values) {
|
||||
if ( values == null || values.length == 0 ) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder value = new StringBuilder();
|
||||
for( Object v : values ) {
|
||||
if( value.length() > 0 ) {
|
||||
value.append(sep);
|
||||
}
|
||||
value.append(String.valueOf(v));
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user