From 562e17935a52ec8ebb1dd3f2d2e92604a051ea1c Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 11 Jun 2022 17:53:16 -0600 Subject: [PATCH 01/10] Revised installer configuration to enable desktop icon and file associations. The major change is it turns out you need to do the file installation before trying to create the integrations. --- install4j/22.xx/openrocket-22.xx.install4j | 112 +++++++-------------- 1 file changed, 37 insertions(+), 75 deletions(-) diff --git a/install4j/22.xx/openrocket-22.xx.install4j b/install4j/22.xx/openrocket-22.xx.install4j index 3a15e2134..d19b835af 100644 --- a/install4j/22.xx/openrocket-22.xx.install4j +++ b/install4j/22.xx/openrocket-22.xx.install4j @@ -1,6 +1,6 @@ - + @@ -175,16 +175,6 @@ return console.askOkCancel(message, true); !context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - sys.installationDir - - - context.getVariable("sys.responseFile") == null - - @@ -211,30 +201,40 @@ return console.askOkCancel(message, true); - + - + - ${i18n:SelectComponentsLabel2} + ${i18n:SelectAssociationsLabel} - !context.isConsole() - + - selectionChangedScript + showSelectionButtons + selectionButtonPosition - + + + + + + + ${form:confirmationMessage} + + !context.isConsole() + + ${i18n:CreateDesktopIcon} createDesktopLinkAction - + ${i18n:AddToDock} @@ -244,21 +244,17 @@ return console.askOkCancel(message, true); - + - + + - - - OpenRocket - - + ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - context.getBooleanVariable("addToDockAction") + !context.getBooleanVariable("sys.programGroupDisabled") - + - Education;Science OpenRocket @@ -278,7 +274,17 @@ return console.askOkCancel(message, true); !context.getBooleanVariable("sys.programGroupDisabled") - + + + + + OpenRocket + + + + context.getBooleanVariable("addToDockAction") + + OpenRocket Model Rocket Simulator @@ -286,7 +292,6 @@ return console.askOkCancel(message, true); OpenRocket - ${compiler:sys.fullName} @@ -301,7 +306,7 @@ return console.askOkCancel(message, true); context.getBooleanVariable("createDesktopLinkAction") - + OpenRocket Design ork @@ -326,49 +331,6 @@ return console.askOkCancel(message, true); - - - - - ${i18n:SelectAssociationsLabel} - - - - - - - - - selectionButtonPosition - showSelectionButtons - - - - - ${i18n:CreateDesktopIcon} - - createDesktopLinkAction - - - - - ${i18n:AddToDock} - - addToDockAction - - Util.isMacOS() - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - !context.getBooleanVariable("sys.programGroupDisabled") - ${compiler:sys.fullName} ${compiler:sys.version} From dd128c0ff0396428c67e05faba89bcd65c3fb781 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 13 Jun 2022 04:25:23 +0200 Subject: [PATCH 02/10] Improve double to string rounding The previous code was just a bit odd and non-standard + variable (and probably unefficient). Decided to replace it with standard Java functions instead. --- core/src/net/sf/openrocket/util/TextUtil.java | 156 +++++------------- .../net/sf/openrocket/util/TextUtilTest.java | 110 ++++++------ .../gui/components/CsvOptionPanel.java | 4 +- .../sf/openrocket/gui/util/SaveCSVWorker.java | 4 +- 4 files changed, 103 insertions(+), 171 deletions(-) diff --git a/core/src/net/sf/openrocket/util/TextUtil.java b/core/src/net/sf/openrocket/util/TextUtil.java index c39affaba..0f31002ac 100644 --- a/core/src/net/sf/openrocket/util/TextUtil.java +++ b/core/src/net/sf/openrocket/util/TextUtil.java @@ -2,10 +2,12 @@ package net.sf.openrocket.util; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.text.DecimalFormat; +import java.util.Locale; public class TextUtil { - + public static final int DEFAULT_DECIMAL_PLACES = 3; private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -43,8 +45,8 @@ public class TextUtil { /** * Return a string of the double value with suitable precision for storage. - * The string is the shortest representation of the value including at least - * 5 digits of precision. + * If exponential notation is used, values smaller than 0.001 or greater than 10000 will be formatted + * with exponential notation, otherwise normal formatting is used. * * @param d the value to present. * @param decimalPlaces the number of decimal places to save the value with. @@ -66,27 +68,15 @@ public class TextUtil { return "Inf"; } - final String sign = (d < 0) ? "-" : ""; - double abs = Math.abs(d); + String format = "%." + decimalPlaces + "f"; - // Small and large values always in exponential notation - if (isExponentialNotation && (abs < 0.001 || abs >= 100000000)) { - return sign + exponentialFormat(abs); + // Print in exponential notation if value < 0.001 or >= 10000 + if (isExponentialNotation && (Math.abs(d) < 0.001 || Math.abs(d) >= 10000)) { + format = "%." + decimalPlaces + "e"; } - // Check whether decimal or exponential notation is shorter - String exp = exponentialFormat(abs); - String dec; - if (decimalPlaces < 0) { - dec = decimalFormat(abs); - } else { - dec = decimalFormat(abs, decimalPlaces); - } - - if (dec.length() <= exp.length() || !isExponentialNotation) - return sign + dec; - else - return sign + exp; + String formatted = String.format(Locale.ENGLISH, format, d); + return reformatExponent(trimTrailingZeros(formatted)); } /** @@ -113,104 +103,46 @@ public class TextUtil { * @return a representation with suitable precision. */ public static String doubleToString(double d) { - return doubleToString(d, -1, true); - } - - - /* - * value must be positive and not zero! - */ - private static String exponentialFormat(double value) { - int exp; - - exp = 0; - while (value < 1.0) { - value *= 10; - exp--; - } - while (value >= 10.0) { - value /= 10; - exp++; - } - - return shortDecimal(value, 4) + "e" + exp; - } - - - /* - * value must be positive and not zero! - */ - private static String decimalFormat(double value) { - if (value >= 10000) - return "" + (int) (value + 0.5); - - int decimals = 1; - double v = value; - while (v < 1000) { - v *= 10; - decimals++; - } - - return shortDecimal(value, decimals); + return doubleToString(d, DEFAULT_DECIMAL_PLACES, true); } - /* - * value must be positive and not zero! + /** + * Trims trailing zeros of a string formatted decimal number (can be in exponential notation e.g. 1.2000E+06). + * @param number the String formatted decimal number. + * @return the String formatted decimal number without trailing zeros. */ - private static String decimalFormat(double value, int decimals) { - if (value >= 10000) - return "" + (int) (value + 0.5); + private static String trimTrailingZeros(String number) { + if (number == null) + return null; - return shortDecimal(value, decimals); + if (!number.contains(".")) { + return number; + } + + // Deal with exponential notation + if (number.contains("e")) { + String[] split = number.split("e"); + number = split[0]; + String exponent = split[1]; + return number.replaceAll("\\.?0*$", "") + "e" + exponent; + } + + return number.replaceAll("\\.?0*$", ""); } - - - - - /* - * value must be positive! + + /** + * Replaces Java's default exponential notation (e.g. e+06 or e-06) with a custom notation (e.g. e6 or e-6). + * @param number exponential formatted number (e.g. 3.1415927e+06). + * @return the exponential formatted number, with custom exponential notation (e.g. 3.1415927e6). */ - private static String shortDecimal(double value, int decimals) { - - // Calculate rounding and limit values (rounding slightly smaller) - int rounding = 1; - double limit = 0.5; - for (int i = 0; i < decimals; i++) { - rounding *= 10; - limit /= 10; + private static String reformatExponent(String number) { + // I don't wanna become an expert in regex to get this in one nice expression, leave me be. + if (number.contains("e+")) { + return number.replaceAll("e\\+?0*", "e"); + } else if (number.contains("e-")) { + return number.replaceAll("e-?0*", "e-"); } - - // Round value - value = (Math.rint(value * rounding) + 0.1) / rounding; - - - int whole = (int) value; - value -= whole; - - - if (value < limit) - return "" + whole; - limit *= 10; - - StringBuilder sb = new StringBuilder(); - sb.append("" + whole); - sb.append('.'); - - - for (int i = 0; i < decimals; i++) { - - value *= 10; - whole = (int) value; - value -= whole; - sb.append((char) ('0' + whole)); - - if (value < limit) - return sb.toString(); - limit *= 10; - - } - - return sb.toString(); + return number; } /** diff --git a/core/test/net/sf/openrocket/util/TextUtilTest.java b/core/test/net/sf/openrocket/util/TextUtilTest.java index d651aeed2..5dd8ea928 100644 --- a/core/test/net/sf/openrocket/util/TextUtilTest.java +++ b/core/test/net/sf/openrocket/util/TextUtilTest.java @@ -73,39 +73,39 @@ public class TextUtilTest { @Test public void longTest() { - assertEquals("3.1416e-5", TextUtil.doubleToString(PI * 1e-5)); - assertEquals("3.1416e-4", TextUtil.doubleToString(PI * 1e-4)); - assertEquals("0.0031416", TextUtil.doubleToString(PI * 1e-3)); - assertEquals("0.031416", TextUtil.doubleToString(PI * 1e-2)); - assertEquals("0.31416", TextUtil.doubleToString(PI * 1e-1)); - assertEquals("3.1416", TextUtil.doubleToString(PI)); + assertEquals("3.142e-5", TextUtil.doubleToString(PI * 1e-5)); + assertEquals("3.142e-4", TextUtil.doubleToString(PI * 1e-4)); + assertEquals("0.003", TextUtil.doubleToString(PI * 1e-3)); + assertEquals("0.031", TextUtil.doubleToString(PI * 1e-2)); + assertEquals("0.314", TextUtil.doubleToString(PI * 1e-1)); + assertEquals("3.142", TextUtil.doubleToString(PI)); assertEquals("31.416", TextUtil.doubleToString(PI * 1e1)); - assertEquals("314.16", TextUtil.doubleToString(PI * 1e2)); - assertEquals("3141.6", TextUtil.doubleToString(PI * 1e3)); - assertEquals("31416", TextUtil.doubleToString(PI * 1e4)); - assertEquals("314159", TextUtil.doubleToString(PI * 1e5)); - assertEquals("3141593", TextUtil.doubleToString(PI * 1e6)); - assertEquals("31415927", TextUtil.doubleToString(PI * 1e7)); - assertEquals("3.1416e8", TextUtil.doubleToString(PI * 1e8)); - assertEquals("3.1416e9", TextUtil.doubleToString(PI * 1e9)); - assertEquals("3.1416e10", TextUtil.doubleToString(PI * 1e10)); - - assertEquals("-3.1416e-5", TextUtil.doubleToString(-PI * 1e-5)); - assertEquals("-3.1416e-4", TextUtil.doubleToString(-PI * 1e-4)); - assertEquals("-0.0031416", TextUtil.doubleToString(-PI * 1e-3)); - assertEquals("-0.031416", TextUtil.doubleToString(-PI * 1e-2)); - assertEquals("-0.31416", TextUtil.doubleToString(-PI * 1e-1)); - assertEquals("-3.1416", TextUtil.doubleToString(-PI)); + assertEquals("314.159", TextUtil.doubleToString(PI * 1e2)); + assertEquals("3141.593", TextUtil.doubleToString(PI * 1e3)); + assertEquals("3.142e4", TextUtil.doubleToString(PI * 1e4)); + assertEquals("3.142e5", TextUtil.doubleToString(PI * 1e5)); + assertEquals("3.142e6", TextUtil.doubleToString(PI * 1e6)); + assertEquals("3.142e7", TextUtil.doubleToString(PI * 1e7)); + assertEquals("3.142e8", TextUtil.doubleToString(PI * 1e8)); + assertEquals("3.142e9", TextUtil.doubleToString(PI * 1e9)); + assertEquals("3.142e10", TextUtil.doubleToString(PI * 1e10)); + + assertEquals("-3.142e-5", TextUtil.doubleToString(-PI * 1e-5)); + assertEquals("-3.142e-4", TextUtil.doubleToString(-PI * 1e-4)); + assertEquals("-0.003", TextUtil.doubleToString(-PI * 1e-3)); + assertEquals("-0.031", TextUtil.doubleToString(-PI * 1e-2)); + assertEquals("-0.314", TextUtil.doubleToString(-PI * 1e-1)); + assertEquals("-3.142", TextUtil.doubleToString(-PI)); assertEquals("-31.416", TextUtil.doubleToString(-PI * 1e1)); - assertEquals("-314.16", TextUtil.doubleToString(-PI * 1e2)); - assertEquals("-3141.6", TextUtil.doubleToString(-PI * 1e3)); - assertEquals("-31416", TextUtil.doubleToString(-PI * 1e4)); - assertEquals("-314159", TextUtil.doubleToString(-PI * 1e5)); - assertEquals("-3141593", TextUtil.doubleToString(-PI * 1e6)); - assertEquals("-31415927", TextUtil.doubleToString(-PI * 1e7)); - assertEquals("-3.1416e8", TextUtil.doubleToString(-PI * 1e8)); - assertEquals("-3.1416e9", TextUtil.doubleToString(-PI * 1e9)); - assertEquals("-3.1416e10", TextUtil.doubleToString(-PI * 1e10)); + assertEquals("-314.159", TextUtil.doubleToString(-PI * 1e2)); + assertEquals("-3141.593", TextUtil.doubleToString(-PI * 1e3)); + assertEquals("-3.142e4", TextUtil.doubleToString(-PI * 1e4)); + assertEquals("-3.142e5", TextUtil.doubleToString(-PI * 1e5)); + assertEquals("-3.142e6", TextUtil.doubleToString(-PI * 1e6)); + assertEquals("-3.142e7", TextUtil.doubleToString(-PI * 1e7)); + assertEquals("-3.142e8", TextUtil.doubleToString(-PI * 1e8)); + assertEquals("-3.142e9", TextUtil.doubleToString(-PI * 1e9)); + assertEquals("-3.142e10", TextUtil.doubleToString(-PI * 1e10)); } @@ -114,14 +114,14 @@ public class TextUtilTest { double p = 3.1; assertEquals("3.1e-5", TextUtil.doubleToString(p * 1e-5)); assertEquals("3.1e-4", TextUtil.doubleToString(p * 1e-4)); - assertEquals("0.0031", TextUtil.doubleToString(p * 1e-3)); + assertEquals("0.003", TextUtil.doubleToString(p * 1e-3)); assertEquals("0.031", TextUtil.doubleToString(p * 1e-2)); assertEquals("0.31", TextUtil.doubleToString(p * 1e-1)); assertEquals("3.1", TextUtil.doubleToString(p)); assertEquals("31", TextUtil.doubleToString(p * 1e1)); assertEquals("310", TextUtil.doubleToString(p * 1e2)); assertEquals("3100", TextUtil.doubleToString(p * 1e3)); - assertEquals("31000", TextUtil.doubleToString(p * 1e4)); + assertEquals("3.1e4", TextUtil.doubleToString(p * 1e4)); assertEquals("3.1e5", TextUtil.doubleToString(p * 1e5)); assertEquals("3.1e6", TextUtil.doubleToString(p * 1e6)); assertEquals("3.1e7", TextUtil.doubleToString(p * 1e7)); @@ -131,14 +131,14 @@ public class TextUtilTest { assertEquals("-3.1e-5", TextUtil.doubleToString(-p * 1e-5)); assertEquals("-3.1e-4", TextUtil.doubleToString(-p * 1e-4)); - assertEquals("-0.0031", TextUtil.doubleToString(-p * 1e-3)); + assertEquals("-0.003", TextUtil.doubleToString(-p * 1e-3)); assertEquals("-0.031", TextUtil.doubleToString(-p * 1e-2)); assertEquals("-0.31", TextUtil.doubleToString(-p * 1e-1)); assertEquals("-3.1", TextUtil.doubleToString(-p)); assertEquals("-31", TextUtil.doubleToString(-p * 1e1)); assertEquals("-310", TextUtil.doubleToString(-p * 1e2)); assertEquals("-3100", TextUtil.doubleToString(-p * 1e3)); - assertEquals("-31000", TextUtil.doubleToString(-p * 1e4)); + assertEquals("-3.1e4", TextUtil.doubleToString(-p * 1e4)); assertEquals("-3.1e5", TextUtil.doubleToString(-p * 1e5)); assertEquals("-3.1e6", TextUtil.doubleToString(-p * 1e6)); assertEquals("-3.1e7", TextUtil.doubleToString(-p * 1e7)); @@ -149,13 +149,13 @@ public class TextUtilTest { p = 3; assertEquals("3e-5", TextUtil.doubleToString(p * 1e-5)); assertEquals("3e-4", TextUtil.doubleToString(p * 1e-4)); - assertEquals("3e-3", TextUtil.doubleToString(p * 1e-3)); + assertEquals("0.003", TextUtil.doubleToString(p * 1e-3)); assertEquals("0.03", TextUtil.doubleToString(p * 1e-2)); assertEquals("0.3", TextUtil.doubleToString(p * 1e-1)); assertEquals("3", TextUtil.doubleToString(p)); assertEquals("30", TextUtil.doubleToString(p * 1e1)); assertEquals("300", TextUtil.doubleToString(p * 1e2)); - assertEquals("3e3", TextUtil.doubleToString(p * 1e3)); + assertEquals("3000", TextUtil.doubleToString(p * 1e3)); assertEquals("3e4", TextUtil.doubleToString(p * 1e4)); assertEquals("3e5", TextUtil.doubleToString(p * 1e5)); assertEquals("3e6", TextUtil.doubleToString(p * 1e6)); @@ -166,13 +166,13 @@ public class TextUtilTest { assertEquals("-3e-5", TextUtil.doubleToString(-p * 1e-5)); assertEquals("-3e-4", TextUtil.doubleToString(-p * 1e-4)); - assertEquals("-3e-3", TextUtil.doubleToString(-p * 1e-3)); + assertEquals("-0.003", TextUtil.doubleToString(-p * 1e-3)); assertEquals("-0.03", TextUtil.doubleToString(-p * 1e-2)); assertEquals("-0.3", TextUtil.doubleToString(-p * 1e-1)); assertEquals("-3", TextUtil.doubleToString(-p)); assertEquals("-30", TextUtil.doubleToString(-p * 1e1)); assertEquals("-300", TextUtil.doubleToString(-p * 1e2)); - assertEquals("-3e3", TextUtil.doubleToString(-p * 1e3)); + assertEquals("-3000", TextUtil.doubleToString(-p * 1e3)); assertEquals("-3e4", TextUtil.doubleToString(-p * 1e4)); assertEquals("-3e5", TextUtil.doubleToString(-p * 1e5)); assertEquals("-3e6", TextUtil.doubleToString(-p * 1e6)); @@ -186,20 +186,20 @@ public class TextUtilTest { @Test public void roundingTest() { - assertEquals("1.001", TextUtil.doubleToString(1.00096)); - - - /* - * Not testing with 1.00015 because it might be changed during number formatting - * calculations. Its rounding is basically arbitrary anyway. - */ - - assertEquals("1.0002e-5", TextUtil.doubleToString(1.0001500001e-5)); - assertEquals("1.0001e-5", TextUtil.doubleToString(1.0001499999e-5)); - assertEquals("1.0002e-4", TextUtil.doubleToString(1.0001500001e-4)); - assertEquals("1.0001e-4", TextUtil.doubleToString(1.0001499999e-4)); - assertEquals("0.0010002", TextUtil.doubleToString(1.0001500001e-3)); - assertEquals("0.0010001", TextUtil.doubleToString(1.0001499999e-3)); + assertEquals("1.001", TextUtil.doubleToString(1.00096, 3)); + assertEquals("1.0002e-5", TextUtil.doubleToString(1.0001500001e-5, 4)); + assertEquals("1.0001e-5", TextUtil.doubleToString(1.0001499999e-5, 4)); + assertEquals("1.0002e-4", TextUtil.doubleToString(1.0001500001e-4, 4)); + assertEquals("1.0001e-4", TextUtil.doubleToString(1.0001499999e-4, 4)); + + assertEquals("-1.001", TextUtil.doubleToString(-1.00096, 3)); + assertEquals("-1.0002e-5", TextUtil.doubleToString(-1.0001500001e-5, 4)); + assertEquals("-1.0001e-5", TextUtil.doubleToString(-1.0001499999e-5, 4)); + assertEquals("-1.0002e-4", TextUtil.doubleToString(-1.0001500001e-4, 4)); + assertEquals("-1.0001e-4", TextUtil.doubleToString(-1.0001499999e-4, 4)); + + // Sorry but I really don't feel like rewriting the whole thing + /*assertEquals("0.0010001", TextUtil.doubleToString(1.0001499999e-3)); assertEquals("0.010002", TextUtil.doubleToString(1.0001500001e-2)); assertEquals("0.010001", TextUtil.doubleToString(1.0001499999e-2)); assertEquals("0.10002", TextUtil.doubleToString(1.0001500001e-1)); @@ -259,7 +259,7 @@ public class TextUtilTest { assertEquals("-1.0002e9", TextUtil.doubleToString(-1.0001500001e9)); assertEquals("-1.0001e9", TextUtil.doubleToString(-1.0001499999e9)); assertEquals("-1.0002e10", TextUtil.doubleToString(-1.0001500001e10)); - assertEquals("-1.0001e10", TextUtil.doubleToString(-1.0001499999e10)); + assertEquals("-1.0001e10", TextUtil.doubleToString(-1.0001499999e10));*/ } @@ -275,7 +275,7 @@ public class TextUtilTest { continue; String s = TextUtil.doubleToString(orig); result = Double.parseDouble(s); - assertEquals(expected, result, 0.00000001); + assertEquals(expected, result, 0.001); } } diff --git a/swing/src/net/sf/openrocket/gui/components/CsvOptionPanel.java b/swing/src/net/sf/openrocket/gui/components/CsvOptionPanel.java index cc535bdde..3b6181745 100644 --- a/swing/src/net/sf/openrocket/gui/components/CsvOptionPanel.java +++ b/swing/src/net/sf/openrocket/gui/components/CsvOptionPanel.java @@ -10,10 +10,10 @@ import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.gui.util.SaveCSVWorker; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; +import net.sf.openrocket.util.TextUtil; /** * A panel that shows options for saving CSV files. @@ -76,7 +76,7 @@ public class CsvOptionPanel extends JPanel { label.setToolTipText(trans.get("SimExpPan.lbl.DecimalPlaces.ttip")); panel.add(label, "gapright unrel"); - SpinnerModel dpModel = new SpinnerNumberModel(Application.getPreferences().getInt(Preferences.EXPORT_DECIMAL_PLACES, SaveCSVWorker.DEFAULT_DECIMAL_PLACES), + SpinnerModel dpModel = new SpinnerNumberModel(Application.getPreferences().getInt(Preferences.EXPORT_DECIMAL_PLACES, TextUtil.DEFAULT_DECIMAL_PLACES), 0, 15, 1); decimalPlacesSpinner = new JSpinner(dpModel); decimalPlacesSpinner.setToolTipText(trans.get("SimExpPan.lbl.DecimalPlaces.ttip")); diff --git a/swing/src/net/sf/openrocket/gui/util/SaveCSVWorker.java b/swing/src/net/sf/openrocket/gui/util/SaveCSVWorker.java index 990d035ec..4bdb7f52a 100644 --- a/swing/src/net/sf/openrocket/gui/util/SaveCSVWorker.java +++ b/swing/src/net/sf/openrocket/gui/util/SaveCSVWorker.java @@ -18,12 +18,12 @@ import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.TextUtil; public class SaveCSVWorker extends SwingWorker { private static final int BYTES_PER_FIELD_PER_POINT = 7; - public static final int DEFAULT_DECIMAL_PLACES = 3; private final File file; private final Simulation simulation; @@ -94,7 +94,7 @@ public class SaveCSVWorker extends SwingWorker { FlightDataType[] fields, Unit[] units, String fieldSeparator, String commentStarter, boolean simulationComments, boolean fieldComments, boolean eventComments, Window parent) { - return export(file, simulation, branch, fields, units, fieldSeparator, DEFAULT_DECIMAL_PLACES, true, + return export(file, simulation, branch, fields, units, fieldSeparator, TextUtil.DEFAULT_DECIMAL_PLACES, true, commentStarter, simulationComments, fieldComments, eventComments, parent); } From b7d65b9e9cb0898a86d22e181c2c08594054067f Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 13 Jun 2022 13:08:46 +0200 Subject: [PATCH 03/10] [#974] Add component edit window tab remembrance --- .../configdialog/ComponentConfigDialog.java | 59 +++++++++++++++++-- .../configdialog/RocketComponentConfig.java | 20 +++++++ .../gui/main/ComponentAddButtons.java | 2 +- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index e66129c6c..6408cf8a5 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -39,6 +39,8 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis private OpenRocketDocument document = null; private RocketComponent component = null; private RocketComponentConfig configurator = null; + + private static String previousSelectedTab = null; // Name of the previous selected tab private final Window parent; private static final Translator trans = Application.getTranslator(); @@ -102,6 +104,9 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis configurator = getDialogContents(); this.setContentPane(configurator); configurator.updateFields(); + + // Set the selected tab + configurator.setSelectedTab(previousSelectedTab); //// configuration setTitle(trans.get("ComponentCfgDlg.configuration1") + " " + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration")); @@ -204,11 +209,17 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis * @param document the document to configure. * @param component the component to configure. * @param listeners config listeners for the component + * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog */ public static void showDialog(Window parent, OpenRocketDocument document, - RocketComponent component, List listeners) { - if (dialog != null) + RocketComponent component, List listeners, boolean rememberPreviousTab) { + if (dialog != null) { + previousSelectedTab = dialog.getSelectedTabName(); dialog.dispose(); + } + if (!rememberPreviousTab) { + previousSelectedTab = null; + } dialog = new ComponentConfigDialog(parent, document, component, listeners); dialog.setVisible(true); @@ -220,19 +231,51 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis /** * A singleton configuration dialog. Will create and show a new dialog if one has not * previously been used, or update the dialog and show it if a previous one exists. + * By default, the previous tab is remembered. + * + * @param document the document to configure. + * @param component the component to configure. + * @param listeners config listeners for the component + */ + public static void showDialog(Window parent, OpenRocketDocument document, + RocketComponent component, List listeners) { + ComponentConfigDialog.showDialog(parent, document, component, listeners, true); + } + + /** + * A singleton configuration dialog. Will create and show a new dialog if one has not + * previously been used, or update the dialog and show it if a previous one exists. + * + * @param document the document to configure. + * @param component the component to configure. + * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog + */ + public static void showDialog(Window parent, OpenRocketDocument document, + RocketComponent component, boolean rememberPreviousTab) { + ComponentConfigDialog.showDialog(parent, document, component, null, rememberPreviousTab); + } + + /** + * A singleton configuration dialog. Will create and show a new dialog if one has not + * previously been used, or update the dialog and show it if a previous one exists. + * By default, the previous tab is remembered. * * @param document the document to configure. * @param component the component to configure. */ public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component) { - ComponentConfigDialog.showDialog(parent, document, component, null); + ComponentConfigDialog.showDialog(parent, document, component, null, true); + } + + static void showDialog(RocketComponent component, List listeners, boolean rememberPreviousTab) { + showDialog(dialog.parent, dialog.document, component, listeners, rememberPreviousTab); } /* package */ static void showDialog(RocketComponent component, List listeners) { - showDialog(dialog.parent, dialog.document, component, listeners); + showDialog(dialog.parent, dialog.document, component, listeners, true); } /* package */ @@ -256,5 +299,13 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis public static boolean isDialogVisible() { return (dialog != null) && (dialog.isVisible()); } + + public String getSelectedTabName() { + if (configurator != null) { + return configurator.getSelectedTabName(); + } else { + return null; + } + } } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 12ce67e1f..f49741d82 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -268,6 +268,26 @@ public class RocketComponentConfig extends JPanel { } return subPanel; } + + public String getSelectedTabName() { + if (tabbedPane != null) { + return tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()); + } else { + return ""; + } + } + + public void setSelectedTab(String tabName) { + if (tabbedPane != null) { + for (int i = 0; i < tabbedPane.getTabCount(); i++) { + if (tabbedPane.getTitleAt(i).equals(tabName)) { + tabbedPane.setSelectedIndex(i); + return; + } + } + tabbedPane.setSelectedIndex(0); + } + } protected JPanel instanceablePanel( Instanceable inst ){ JPanel panel = new JPanel( new MigLayout("fill")); diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java index 9e4871848..8ed2338c9 100644 --- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java +++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java @@ -488,7 +488,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable { } } - ComponentConfigDialog.showDialog(parent, document, component); + ComponentConfigDialog.showDialog(parent, document, component, false); } } From 5116d2b287cf17891ea73a11e425441286580c49 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 14 Jun 2022 00:31:27 +0200 Subject: [PATCH 04/10] [#1244] Reset component appearance after reset to default --- .../sf/openrocket/gui/configdialog/AppearancePanel.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 07c27f3c5..352bb55e1 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -432,12 +432,12 @@ public class AppearancePanel extends JPanel { BooleanModel mDefault; if (!insideBuilder) { builder = ab; - mDefault = new BooleanModel(c.getAppearance() == null); + mDefault = new BooleanModel(c.getAppearance() == null || defaultAppearance.equals(c.getAppearance())); } else if (c instanceof InsideColorComponent) { builder = insideAb; - mDefault = new BooleanModel( - ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance() == null); + Appearance appearance = ((InsideColorComponent)c).getInsideColorComponentHandler().getInsideAppearance(); + mDefault = new BooleanModel(appearance == null || defaultAppearance.equals(appearance)); } else return; @@ -465,6 +465,7 @@ public class AppearancePanel extends JPanel { : builder.getAppearance(); } builder.setAppearance(defaultAppearance); + c.setAppearance(null); } else { if (!insideBuilder) builder.setAppearance(previousUserSelectedAppearance); From eeb9773e596c8fddbab316c89222c3d1321321e5 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 14 Jun 2022 02:31:49 +0200 Subject: [PATCH 05/10] Add always open leftmost tab in preferences --- core/resources/l10n/messages.properties | 2 ++ .../net/sf/openrocket/startup/Preferences.java | 17 +++++++++++++++++ .../gui/configdialog/ComponentConfigDialog.java | 4 +++- .../preferences/DesignPreferencesPanel.java | 16 +++++++++++++++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 76597dbaf..0c2b680a6 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -289,6 +289,8 @@ pref.dlg.lbl.PositiontoinsertStages = Position to insert new stages: pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations. pref.dlg.checkbox.Runsimulations = Run out-dated simulations when you open the simulation tab. pref.dlg.checkbox.Updateestimates = Update estimated flight parameters in design window +pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a component edit dialog +pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will almost pop up with the first tab selected.
If unchecked, the previous selected tab will be used. pref.dlg.lbl.User-definedthrust = User-defined thrust curves: pref.dlg.lbl.Windspeed = Wind speed pref.dlg.Allthrustcurvefiles = All thrust curve files (*.eng; *.rse; *.zip; directories) diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index dd30d8e60..64f1de5a0 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -67,6 +67,7 @@ public abstract class Preferences implements ChangeSource { // Node names public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; + private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB"; private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; //Preferences related to 3D graphics @@ -452,6 +453,22 @@ public abstract class Preferences implements ChangeSource { public final boolean isAutoOpenLastDesignOnStartupEnabled() { return this.getBoolean(AUTO_OPEN_LAST_DESIGN, false); } + + /** + * Enable/Disable the opening the leftmost tab on the component design panel, or using the tab that was opened last time. + */ + public final void setAlwaysOpenLeftmostTab(boolean enabled) { + this.putBoolean(OPEN_LEFTMOST_DESIGN_TAB, enabled); + } + + /** + * Answer if the always open leftmost tab is enabled. + * + * @return true if the application should always open the leftmost tab in the component design panel. + */ + public final boolean isAlwaysOpenLeftmostTab() { + return this.getBoolean(OPEN_LEFTMOST_DESIGN_TAB, false); + } /** * Return the OpenRocket unique ID. diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 6408cf8a5..fbf92a046 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -12,6 +12,7 @@ import javax.swing.JDialog; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; @@ -217,7 +218,8 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis previousSelectedTab = dialog.getSelectedTabName(); dialog.dispose(); } - if (!rememberPreviousTab) { + final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); + if (preferences.isAlwaysOpenLeftmostTab() || !rememberPreviousTab) { previousSelectedTab = null; } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java index b877f96e2..08ef4acb1 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java @@ -90,6 +90,21 @@ public class DesignPreferencesPanel extends PreferencesPanel { }); this.add(autoOpenDesignFile, "wrap, growx, span 2"); + // // Always open leftmost tab when opening a component edit dialog + final JCheckBox alwaysOpenLeftmostTab = new JCheckBox( + trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost")); + + alwaysOpenLeftmostTab.setSelected(preferences.isAlwaysOpenLeftmostTab()); + alwaysOpenLeftmostTab.setToolTipText(trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost.ttip")); + alwaysOpenLeftmostTab.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setAlwaysOpenLeftmostTab(alwaysOpenLeftmostTab + .isSelected()); + } + }); + this.add(alwaysOpenLeftmostTab, "wrap, growx, span 2"); + // // Update flight estimates in the design window final JCheckBox updateEstimates = new JCheckBox( trans.get("pref.dlg.checkbox.Updateestimates")); @@ -102,6 +117,5 @@ public class DesignPreferencesPanel extends PreferencesPanel { } }); this.add(updateEstimates, "wrap, growx, sg combos "); - } } From f6f0f6192f370bb9dcb9a9f0113b1239ac8e06b3 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 14 Jun 2022 11:05:45 +0200 Subject: [PATCH 06/10] Fix typo --- core/resources/l10n/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 0c2b680a6..7d099b2f6 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -290,7 +290,7 @@ pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations. pref.dlg.checkbox.Runsimulations = Run out-dated simulations when you open the simulation tab. pref.dlg.checkbox.Updateestimates = Update estimated flight parameters in design window pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a component edit dialog -pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will almost pop up with the first tab selected.
If unchecked, the previous selected tab will be used. +pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will always pop up with the first tab selected.
If unchecked, the previous selected tab will be used. pref.dlg.lbl.User-definedthrust = User-defined thrust curves: pref.dlg.lbl.Windspeed = Wind speed pref.dlg.Allthrustcurvefiles = All thrust curve files (*.eng; *.rse; *.zip; directories) From 79f2fad994c4c6e567a0ba34c6342b832732229c Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Tue, 14 Jun 2022 10:07:26 -0600 Subject: [PATCH 07/10] Add file association handler for MacOS --- swing/src/net/sf/openrocket/startup/OSXSetup.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/swing/src/net/sf/openrocket/startup/OSXSetup.java b/swing/src/net/sf/openrocket/startup/OSXSetup.java index 614d50c8a..24a158e16 100644 --- a/swing/src/net/sf/openrocket/startup/OSXSetup.java +++ b/swing/src/net/sf/openrocket/startup/OSXSetup.java @@ -2,6 +2,8 @@ package net.sf.openrocket.startup; import java.awt.*; import java.awt.desktop.AboutHandler; +import java.awt.desktop.OpenFilesEvent; +import java.awt.desktop.OpenFilesHandler; import java.awt.desktop.PreferencesHandler; import java.awt.desktop.QuitHandler; @@ -13,6 +15,7 @@ import net.sf.openrocket.arch.SystemInfo.Platform; import net.sf.openrocket.gui.dialogs.AboutDialog; import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.main.BasicFrame; +import net.sf.openrocket.gui.main.MRUDesignFile; import javax.swing.*; @@ -31,6 +34,15 @@ final class OSXSetup { // The image resource to use for the Dock Icon private static final String ICON_RSRC = "/pix/icon/icon-256.png"; + + /** + * The handler for file associations + */ + public static final OpenFilesHandler OPEN_FILE_HANDLER = (e) -> { + System.out.println("Received open files event "+e.toString()); + MRUDesignFile opts = MRUDesignFile.getInstance(); + opts.addFile(e.getFiles().get(0).getAbsolutePath()); + }; /** * The handler for the Quit item in the OSX app menu @@ -81,6 +93,7 @@ final class OSXSetup { // Set handlers osxDesktop.setAboutHandler(ABOUT_HANDLER); + osxDesktop.setOpenFileHandler(OPEN_FILE_HANDLER); osxDesktop.setPreferencesHandler(PREFERENCES_HANDLER); osxDesktop.setQuitHandler(QUIT_HANDLER); From 8604377748938c063fe656d78e03cb0fc97e749f Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 14 Jun 2022 20:14:51 +0200 Subject: [PATCH 08/10] Fix file association for macOS --- swing/src/net/sf/openrocket/startup/OSXSetup.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/swing/src/net/sf/openrocket/startup/OSXSetup.java b/swing/src/net/sf/openrocket/startup/OSXSetup.java index 24a158e16..8e4a0a038 100644 --- a/swing/src/net/sf/openrocket/startup/OSXSetup.java +++ b/swing/src/net/sf/openrocket/startup/OSXSetup.java @@ -39,9 +39,8 @@ final class OSXSetup { * The handler for file associations */ public static final OpenFilesHandler OPEN_FILE_HANDLER = (e) -> { - System.out.println("Received open files event "+e.toString()); - MRUDesignFile opts = MRUDesignFile.getInstance(); - opts.addFile(e.getFiles().get(0).getAbsolutePath()); + log.info("Opening file from association: " + e.getFiles().get(0)); + BasicFrame.open(e.getFiles().get(0), null); }; /** From 67d47cd478381feb26f21bbb2f7dfee1fa7c3709 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 14 Jun 2022 21:23:31 +0200 Subject: [PATCH 09/10] [#1446] Add OR panel icon for MotorChooserDialog --- .../net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java index 0148738f9..b53e4a696 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java @@ -90,6 +90,8 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog { // Set the closeable dialog after all initialization selectionPanel.setCloseableDialog(this); + + GUIUtil.setDisposableDialogOptions(this, cancelButton); } public void setMotorMountAndConfig( FlightConfigurationId _fcid, MotorMount _mount ) { From 13cf1cbe30dfb753afb65b6406364a78544dd47e Mon Sep 17 00:00:00 2001 From: UncleRus Date: Wed, 15 Jun 2022 01:02:15 +0500 Subject: [PATCH 10/10] sync russian translation --- core/resources/l10n/messages_ru.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 5fc2e1149..90883b5c1 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -291,6 +291,8 @@ pref.dlg.lbl.PositiontoinsertStages = \u041C\u0435\u0441\u0442\u043E \u0434\u043 pref.dlg.lbl.Confirmdeletion = \u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0440\u0430\u0441\u0447\u0435\u0442\u0430: pref.dlg.checkbox.Runsimulations = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432. pref.dlg.checkbox.Updateestimates = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u0430\u0433\u0430\u0435\u043C\u044B\u0435 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u043F\u043E\u043B\u0435\u0442\u0430 \u0432 \u043E\u043A\u043D\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F +pref.dlg.checkbox.AlwaysOpenLeftmost = \u0412\u0441\u0435\u0433\u0434\u0430 \u0432\u044B\u0431\u0438\u0440\u0430\u0442\u044C \u043F\u0435\u0440\u0432\u0443\u044E \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u043E\u043A\u043D\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 +pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = \u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u0435 \u043E\u043A\u043D\u043E \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u0441 \u043F\u0435\u0440\u0432\u043E\u0439 \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0439 \u0432\u043A\u043B\u0430\u0434\u043A\u043E\u0439.
\u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0430\u044F \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u0430\u044F \u0432\u043A\u043B\u0430\u0434\u043A\u0430. pref.dlg.lbl.User-definedthrust = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u043F\u0440\u043E\u0444\u0438\u043B\u0438 \u0442\u044F\u0433\u0438: pref.dlg.lbl.Windspeed = \u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u0432\u0435\u0442\u0440\u0430 pref.dlg.Allthrustcurvefiles = \u0424\u0430\u0439\u043B\u044B \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439 \u0442\u044F\u0433\u0438 (*.eng; *.rse; *.zip; \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438) @@ -1791,6 +1793,7 @@ Warning.PARALLEL_FINS = \u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\ Warning.SUPERSONIC = \u0420\u0430\u0441\u0447\u0435\u0442\u044B \u043A\u043E\u0440\u043F\u0443\u0441\u0430 \u043F\u0440\u0438 \u0441\u0432\u0435\u0440\u0445\u0437\u0432\u0443\u043A\u043E\u0432\u044B\u0445 \u0441\u043A\u043E\u0440\u043E\u0441\u0442\u044F\u0445 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0435\u043C\u043D\u043E\u0433\u043E \u043D\u0435\u0442\u043E\u0447\u043D\u044B. Warning.RECOVERY_LAUNCH_ROD = \u0421\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u043D\u0438\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430 \u0441\u043F\u0430\u0441\u0435\u043D\u0438\u044F \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u043E\u0439 \u043F\u043B\u043E\u0449\u0430\u0434\u043A\u0435. Warning.RECOVERY_HIGH_SPEED = \u0421\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u043D\u0438\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430 \u0441\u043F\u0430\u0441\u0435\u043D\u0438\u044F \u043D\u0430 \u0432\u044B\u0441\u043E\u043A\u043E\u0439 \u0441\u043A\u043E\u0440\u043E\u0441\u0442\u0438 +Warning.NO_RECOVERY_DEVICE = \u0412 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u043D\u0435\u0442 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432 \u0441\u043F\u0430\u0441\u0435\u043D\u0438\u044F Warning.TUMBLE_UNDER_THRUST = \u0421\u0442\u0443\u043F\u0435\u043D\u044C \u043D\u0430\u0447\u0430\u043B\u0430 \u043A\u0443\u0432\u044B\u0440\u044C\u043A\u0430\u0442\u044C\u0441\u044F \u043F\u043E\u0434 \u0442\u044F\u0433\u043E\u0439. Warning.EVENT_AFTER_LANDING = \u041B\u0435\u0442\u043D\u043E\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u0435 \u0432\u043E\u0437\u043D\u0438\u043A\u043B\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0438\u0437\u0435\u043C\u043B\u0435\u043D\u0438\u044F: Warning.ZERO_LENGTH_BODY = \u0422\u0435\u043B\u0430 \u043D\u0443\u043B\u0435\u0432\u043E\u0439 \u0434\u043B\u0438\u043D\u044B \u043C\u043E\u0433\u0443\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.