diff --git a/ReleaseNotes.md b/ReleaseNotes.md index f3a06a816..823d24092 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,6 +1,20 @@ + + + +
+ Release Notes ============= +
+ +
+ OpenRocket 22.02.beta.05 (2022-09-28) ------------------------ (through PR1688) @@ -66,6 +80,10 @@ OpenRocket 22.02.beta.05 (2022-09-28) * Fixed overlapping labels in motor selection diameter filter slider (fixes #1643) * More Russian translation updates +
+ +
+ OpenRocket 22.02.beta.04 (2022-06-17) ------------------------ (through PR1456) @@ -102,6 +120,9 @@ However, the JAR file will still allow Java 11 *or* 17, so if you want to keep e * Updated guided tours * Improved Russian translation +
+ +
OpenRocket 22.02.beta.03 (2022-05-18) ------------------------ @@ -131,6 +152,10 @@ OpenRocket 22.02.beta.03 (2022-05-18) * Improved time step selection for descent * Java 17 is now included in the packaged installers and supported by the JAR file. +
+ +
+ OpenRocket 22.02.beta.02 (2022-03-26) ------------------------ (through PR1261) @@ -161,6 +186,9 @@ NOTE: Tube fin simulation is currently broken, and will be fixed in a future bet * Set default color for all components to #BBBBBB, with Shine=30 (closes issue 1192) * Update Mac installer style to more standard "drag app to Applications folder" +
+ +
OpenRocket 22.02.beta.01 (2022-02-25) ------------------------ @@ -239,6 +267,10 @@ OpenRocket 22.02.beta.01 (2022-02-25) ..._plus many, many additional bug fixes and refinements_ +
+ +
+ OpenRocket 15.03 (2015-03-28) ----------------------------- @@ -258,6 +290,10 @@ Bug Fixes * Always use the correct filename extension when saving +
+ +
+ OpenRocket 14.11 (2014-11-02) ----------------------------- @@ -269,6 +305,10 @@ Bug Fixes * Fixed a couple of bugs. +
+ +
+ OpenRocket 14.06 (2014-06-25) ----------------------------- @@ -282,6 +322,10 @@ Bug Fixes * Fixed annoying IndexOutOfBounds bug in tables. +
+ +
+ OpenRocket 14.05 (2014-05-21) ----------------------------- @@ -296,6 +340,10 @@ Bug Fixes * Updated thrustcurves * Updated 3d libraries to 2.1.5 +
+ +
+ OpenRocket 14.03 (2014-03-20) ----------------------------- @@ -308,6 +356,10 @@ Bug Fixes * Fixes to the flight configuration tab and motor selection dialog * Updated thrustcurves +
+ +
+ OpenRocket 13.11.2 (2014-01-01) ------------------------------- @@ -318,6 +370,10 @@ Bug Fixes * Fix couple of layout issues * Updated Spanish, French and Chinese translations +
+ +
+ OpenRocket 13.11.1 (2013-11-15) ------------------------------- @@ -329,6 +385,10 @@ Bug Fixes * Fixed various exceptions in flight configuration tables due to column reordering * Fixed NPE when deleting a configuration +
+ +
+ OpenRocket 13.11 (2013-11-08) ------------------------------ @@ -343,6 +403,10 @@ Bug Fixes * Updated jogl to correct 3d problems on various platforms * Fixed NPE introduced by changes in Java 1.7.0_45-b18 +
+ +
+ OpenRocket 13.09.1 (2013-10-05) ------------------------------- @@ -350,6 +414,9 @@ This release contains a number of bug fixes, updated 3D JOGL libraries. Added preliminary thrustcurves for Aerotech C3 and D2 18mm reloads. +
+ +
OpenRocket 13.09 (2013-09-08) ----------------------------- @@ -357,6 +424,9 @@ OpenRocket 13.09 (2013-09-08) This release contains a number of bug fixes, updated 3D JOGL libraries, and separates simulation edit and plot dialogs. +
+ +
OpenRocket 13.05 (2013-05-04) ----------------------------- @@ -384,6 +454,9 @@ New Features scroll with mouse wheel. If the alt key is used with either of these, only the domain is zoomed. Richard contributed a more logical mouse controlled zoom - right click and drag will zoom (either domain, range or both). +
+ +
OpenRocket 12.09.1 (2012-06-28) ------------------------------- @@ -392,6 +465,9 @@ Bug-fix release for 12.09, fixing numerous bugs. Only new feature is the possiblity to automatically open the latest design file on startup (in Edit -> Preferences -> Options). +
+ +
OpenRocket 12.09 (2012-09-16) ------------------------------ @@ -411,6 +487,9 @@ Numerous new features by many contributors - Calibration rulers added to printed templates - Translations in Czech and Polish, numerous updates +
+ +
OpenRocket 12.03 (2012-03-17) ------------------------------ @@ -430,6 +509,9 @@ release you can open files and examine existing simulations, stability data and motor files. The Android port is thanks to work by Kevin Ruland. +
+ +
OpenRocket 1.1.9 (2011-11-24) ------------------------------ @@ -440,6 +522,9 @@ to work by Richard Graham. Printing of transitions, nose cone profiles and fin marking guides is available thanks to Doug Pedrick. It also contains some usability features and bug fixes. +
+ +
OpenRocket 1.1.8 (2011-08-25) ------------------------------ @@ -448,6 +533,9 @@ This release contains bug fixes to the optimization methods. It also contains a workaround to a JRE bug that prevents running OpenRocket on Java 7. +
+ +
OpenRocket 1.1.7 (2011-08-12) ------------------------------ @@ -456,6 +544,9 @@ This release contains automatic rocket design optimization functionality. However, be cautious when using it and take the results with a grain of salt. +
+ +
OpenRocket 1.1.6 (2011-07-22) ------------------------------ @@ -465,6 +556,9 @@ translations by Tripoli Spain, Tripoli France and Stefan Lobas (ERIG e.V.). The release also contains rocket design scaling support and numerous bug fixes. +
+ +
OpenRocket 1.1.5 (2011-06-10) ------------------------------ @@ -473,12 +567,18 @@ Removed native printing support. Printing is now handled via PDF viewer, which should make printing much more reliable and less bug-prone. +
+ +
OpenRocket 1.1.4 (2011-03-05) ------------------------------ Initial printing support by Doug Pedrick, and various bug fixes. +
+ +
OpenRocket 1.1.3 (2010-10-06) ------------------------------ @@ -486,12 +586,18 @@ OpenRocket 1.1.3 (2010-10-06) Support for drag-drop moving and copying of components. Fixes a severe bug in the undo system. +
+ +
OpenRocket 1.1.2 (2010-09-07) ------------------------------ Fixes a severe bug that prevented adding stages to rockets. +
+ +
OpenRocket 1.1.1 (2010-09-03) ------------------------------ @@ -501,6 +607,9 @@ curve loading and selection, faster startup time and bug fixes. Old simulation listeners are incompatible with this release. +
+ +
OpenRocket 1.1.0 (2010-03-21) ------------------------------ @@ -508,6 +617,9 @@ OpenRocket 1.1.0 (2010-03-21) Support for loading RockSim rocket design files (.rkt) thanks to Doug Pedrick. +
+ +
OpenRocket 1.0.0 (2010-03-10) ------------------------------ @@ -515,6 +627,9 @@ OpenRocket 1.0.0 (2010-03-10) Added numerous new motor thrustcurves from thrustcurve.org, and fixed a few more bugs. +
+ +
OpenRocket 0.9.6 (2010-02-17) ------------------------------ @@ -523,6 +638,9 @@ Updated aerodynamic calculation methods to be more in line with the Barrowman method and enhanced simulation time step selection. Fixed numerous bugs. +
+ +
OpenRocket 0.9.5 (2009-11-28) ------------------------------ @@ -530,6 +648,9 @@ OpenRocket 0.9.5 (2009-11-28) Fixed a serious defect which prevented adding a tube coupler and centering ring on the same body tube. Other minor improvements. +
+ +
OpenRocket 0.9.4 (2009-11-24) ------------------------------ @@ -538,6 +659,9 @@ Added through-the-wall fin tabs, attaching components to tube couplers, material editing and automatic update checks, and fixed numerous of the most commonly occurring bugs. +
+ +
OpenRocket 0.9.3 (2009-09-01) ------------------------------ @@ -546,6 +670,9 @@ Numerous bug fixes and enhancements including data exporting, showing flight events in plots, example rocket designs, splitting clustered inner tubes and automated bug reporting. +
+ +
OpenRocket 0.9.2 (2009-07-13) ------------------------------ @@ -554,6 +681,9 @@ Fixed imperial unit conversions. Significant UI enhancements to the motor configuration edit dialog, motor selection dialog and file open/save. +
+ +
OpenRocket 0.9.1 (2009-06-09) ------------------------------ @@ -561,8 +691,14 @@ OpenRocket 0.9.1 (2009-06-09) Bug fixes to file dialog and saving; initial support for cut/copy/paste of simulations. +
+ +
OpenRocket 0.9.0 (2009-05-24) ------------------------------ Initial release. + +
+ \ No newline at end of file diff --git a/core/build.xml b/core/build.xml index 007c10508..01893b763 100644 --- a/core/build.xml +++ b/core/build.xml @@ -1,7 +1,8 @@ - + + @@ -34,6 +35,7 @@ + diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 3ecbb3271..919c15396 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -356,6 +356,13 @@ generalprefs.lbl.language = Interface language generalprefs.languages.default = System default generalprefs.lbl.languageEffect = The language will change the next time you start OpenRocket. +! Welcome dialog +welcome.dlg.title = Welcome to OpenRocket +welcome.dlg.lbl.thankYou = Thank you for downloading OpenRocket +welcome.dlg.lbl.seeReleaseNotes = See the release notes below to find what's new. +welcome.dlg.checkbox.dontShowAgain = Don't show this dialog again +welcome.dlg.btn.close.ttip = Cool! Now leave me alone\u2026 + ! Software update checker update.dlg.error.title = Unable to retrieve update information update.dlg.error = An error occurred while communicating with the server. @@ -366,6 +373,8 @@ update.dlg.newerVersion.title = Newer version detected update.dlg.newerVersion = You are either running a test/unofficial release of OpenRocket, or you have a time machine and are running an official release from the future.\n\nYour version: %s\nLatest official release: %s update.dlg.updateAvailable.title = Update available update.dlg.updateAvailable.lbl.title = A new version of OpenRocket is available! +update.dlg.btn.remindMeLater = Remind Me Later +update.dlg.checkbox.skipThisVersion = Skip This Version update.dlg.updateAvailable.lbl.yourVersion = OpenRocket %s is available! \u2015 you have %s. Would you like to download it now? update.dlg.updateAvailable.lbl.releaseNotes = Release notes: update.dlg.updateAvailable.txtPane.readMore = Read more on GitHub diff --git a/core/src/net/sf/openrocket/communication/Communicator.java b/core/src/net/sf/openrocket/communication/Communicator.java index f762f0239..45fef263f 100644 --- a/core/src/net/sf/openrocket/communication/Communicator.java +++ b/core/src/net/sf/openrocket/communication/Communicator.java @@ -9,7 +9,7 @@ public abstract class Communicator { protected static final String BUG_REPORT_URL; protected static final String UPDATE_URL; - protected static final String UPDATE_ADDITIONAL_URL; // Extra URL needed for the update checker + protected static final String UPDATE_URL_LATEST; // Extra URL needed for the latest GitHub release static { String url; @@ -21,10 +21,10 @@ public abstract class Communicator { url = System.getProperty("openrocket.debug.updateurl"); if (url == null) { url = "https://api.github.com/repos/openrocket/openrocket/releases"; - UPDATE_ADDITIONAL_URL = "https://api.github.com/repos/openrocket/openrocket/releases/latest"; + UPDATE_URL_LATEST = "https://api.github.com/repos/openrocket/openrocket/releases/latest"; } else { - UPDATE_ADDITIONAL_URL = null; + UPDATE_URL_LATEST = null; } UPDATE_URL = url; } diff --git a/core/src/net/sf/openrocket/communication/GitHubAPIUtil.java b/core/src/net/sf/openrocket/communication/GitHubAPIUtil.java new file mode 100644 index 000000000..7800baee5 --- /dev/null +++ b/core/src/net/sf/openrocket/communication/GitHubAPIUtil.java @@ -0,0 +1,112 @@ +package net.sf.openrocket.communication; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.json.Json; +import javax.json.JsonReader; +import javax.json.stream.JsonParsingException; +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.Map; + +/** + * Utility functions for the GitHub API. + */ +public class GitHubAPIUtil { + private static final Logger log = LoggerFactory.getLogger(GitHubAPIUtil.class); + private static final Translator trans = Application.getTranslator(); + + /** + * Generate a URL with a set of parameters included. + * E.g. url = github.com/openrocket/openrocket/releases, params = {"lorem", "ipsum"} + * => formatted url: github.com/openrocket/openrocket/releases?lorem=ipsum + * @param url base URL + * @param params parameters to include + * @return formatted URL (= base URL with parameters) + */ + public static String generateUrlWithParameters(String url, Map params) { + StringBuilder formattedUrl = new StringBuilder(url); + formattedUrl.append("?"); // Identifier for start of query string (for parameters) + + // Append the parameters to the URL + int idx = 0; + for (Map.Entry e : params.entrySet()) { + formattedUrl.append(String.format("%s=%s", e.getKey(), e.getValue())); + if (idx < params.size() - 1) { + formattedUrl.append("&"); // Identifier for more parameters + } + idx++; + } + return formattedUrl.toString(); + } + + /** + * Fetches the JSON info from the specified GitHub API URL. + * @param urlLink GitHub API link of the desired releases + * @return JSON-formatted string (can be a JSON-array or JSON-object) + * @throws Exception if an error occurred (e.g. no internet connection) + */ + public static String fetchPageInfo(String urlLink) throws Exception { + HttpsURLConnection connection = null; + try { + // Set up connection info to the GitHub release page + URL url = new URL(urlLink); + connection = (HttpsURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("Accept", "application/json"); + connection.setUseCaches(false); + connection.setAllowUserInteraction(false); + connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT); + connection.setReadTimeout(Communicator.CONNECTION_TIMEOUT); + + // Connect to the GitHub page and get the status response code + connection.connect(); + int status = connection.getResponseCode(); + log.debug("Update checker response code: " + status); + + // Invalid response code + if (status != 200) { + log.warn(String.format("Bad response code from server: %d", status)); + throw new Exception(String.format(trans.get("update.fetcher.badResponse"), status)); + } + + // Read the response JSON data into a StringBuilder + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + while ((line = br.readLine()) != null) { + sb.append(line).append("\n"); + } + br.close(); + + return sb.toString(); + } catch (UnknownHostException | SocketTimeoutException | ConnectException e) { + log.warn(String.format("Could not connect to URL: %s. Please check your internet connection.", urlLink)); + throw new Exception(trans.get("update.fetcher.badConnection")); + } catch (MalformedURLException e) { + log.warn("Malformed URL: " + urlLink); + throw new Exception(String.format(trans.get("update.fetcher.malformedURL"), urlLink)); + } catch (IOException e) { + throw new Exception(String.format("Exception - %s: %s", e, e.getMessage())); + } finally { // Close the connection to the release page + if (connection != null) { + try { + connection.disconnect(); + } catch (Exception ex) { + log.warn("Could not disconnect update checker connection"); + } + } + } + } +} diff --git a/core/src/net/sf/openrocket/communication/ReleaseNotesHandler.java b/core/src/net/sf/openrocket/communication/ReleaseNotesHandler.java new file mode 100644 index 000000000..ca24fd731 --- /dev/null +++ b/core/src/net/sf/openrocket/communication/ReleaseNotesHandler.java @@ -0,0 +1,56 @@ +package net.sf.openrocket.communication; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import org.xml.sax.SAXException; + +import java.util.HashMap; +import java.util.Objects; + +/** + * Class that parses ReleaseNotes.md. + *

+ * Releases are stored in an HTML

object with as id attribute the release version. + * E.g.
...
+ * The content of the div is the release notes for that version. + */ +public class ReleaseNotesHandler extends AbstractElementHandler { + private final String buildVersion; + private String releaseNotes = null; + + /** + * @param buildVersion the build version to search the release notes for (e.g. "22.02") + */ + public ReleaseNotesHandler(String buildVersion) { + this.buildVersion = buildVersion; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) throws SAXException { + if (element.equals("body")) { // The release notes are encapsulated in a root tag (required for XML parsing) + return this; + } + if (element.equals("div") && Objects.equals(attributes.get("id"), this.buildVersion)) { + return PlainTextHandler.INSTANCE; + } + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + super.closeElement(element, attributes, content, warnings); + + if (element.equals("div")) { + this.releaseNotes = content.trim(); + } + } + + /** + * @return the release notes for the build version + */ + public String getReleaseNotes() { + return releaseNotes; + } +} diff --git a/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java b/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java index 692df1834..d587f1c1c 100644 --- a/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java +++ b/core/src/net/sf/openrocket/communication/UpdateInfoRetriever.java @@ -1,14 +1,6 @@ package net.sf.openrocket.communication; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; import java.io.StringReader; -import java.net.ConnectException; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -31,7 +23,6 @@ import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonReader; import javax.json.stream.JsonParsingException; -import javax.net.ssl.HttpsURLConnection; /** * Class that initiates fetching software update information. @@ -79,7 +70,7 @@ public class UpdateInfoRetriever { */ public boolean isRunning() { if (this.fetcher == null) { - throw new IllegalStateException("startFetchUpdateInfo() has not been called"); + throw new IllegalStateException("Fetcher has not been called yet"); } return this.fetcher.isAlive(); } @@ -100,7 +91,7 @@ public class UpdateInfoRetriever { */ public UpdateInfo getUpdateInfo() { if (this.fetcher == null) { - throw new IllegalStateException("startFetchUpdateInfo() has not been called"); + throw new IllegalStateException("Fetcher has not been called yet"); } return this.fetcher.info; } @@ -175,15 +166,15 @@ public class UpdateInfoRetriever { // Get release tags from release page String relUrl = Communicator.UPDATE_URL; - relUrl = generateUrlWithParameters(relUrl, params); + relUrl = GitHubAPIUtil.generateUrlWithParameters(relUrl, params); JsonArray arr1 = retrieveReleaseJSONArr(relUrl); if (arr1 == null) return null; - if (Communicator.UPDATE_ADDITIONAL_URL == null) return arr1; + if (Communicator.UPDATE_URL_LATEST == null) return arr1; // Get release tags from latest release page - String latestRelUrl = Communicator.UPDATE_ADDITIONAL_URL; - latestRelUrl = generateUrlWithParameters(latestRelUrl, params); + String latestRelUrl = Communicator.UPDATE_URL_LATEST; + latestRelUrl = GitHubAPIUtil.generateUrlWithParameters(latestRelUrl, params); JsonArray arr2 = retrieveReleaseJSONArr(latestRelUrl); if (arr2 == null) return null; @@ -209,72 +200,25 @@ public class UpdateInfoRetriever { * @throws UpdateCheckerException if an error occurred (e.g. no internet connection) */ private JsonArray retrieveReleaseJSONArr(String urlLink) throws UpdateCheckerException { - JsonArray jsonArr; - - HttpsURLConnection connection = null; try { - // Set up connection info to the GitHub release page - URL url = new URL(urlLink); - connection = (HttpsURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Accept", "application/json"); - connection.setUseCaches(false); - connection.setAllowUserInteraction(false); - connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT); - connection.setReadTimeout(Communicator.CONNECTION_TIMEOUT); + String pageInfo = GitHubAPIUtil.fetchPageInfo(urlLink); - // Connect to the GitHub page and get the status response code - connection.connect(); - int status = connection.getResponseCode(); - log.debug("Update checker response code: " + status); - - // Invalid response code - if (status != 200) { - log.warn(String.format("Bad response code from server: %d", status)); - throw new UpdateCheckerException(String.format(trans.get("update.fetcher.badResponse"), status)); - } - - // Read the response JSON data into a StringBuilder - StringBuilder sb = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - while ((line = br.readLine()) != null) { - sb.append(line).append("\n"); - } - br.close(); - - // Read the release page as a JSON array - JsonReader reader = Json.createReader(new StringReader(sb.toString())); + // Read the release page as a JSON object + JsonReader reader = Json.createReader(new StringReader(pageInfo)); // The reader-content can be a JSON array or just a JSON object try { // Case: JSON array - jsonArr = reader.readArray(); + return reader.readArray(); } catch (JsonParsingException e) { // Case: JSON object JsonArrayBuilder builder = Json.createArrayBuilder(); - reader = Json.createReader(new StringReader(sb.toString())); + reader = Json.createReader(new StringReader(pageInfo)); JsonObject obj = reader.readObject(); builder.add(obj); - jsonArr = builder.build(); - } - } catch (UnknownHostException | SocketTimeoutException | ConnectException e) { - log.warn(String.format("Could not connect to URL: %s. Please check your internet connection.", urlLink)); - throw new UpdateCheckerException(trans.get("update.fetcher.badConnection")); - } catch (MalformedURLException e) { - log.warn("Malformed URL: " + urlLink); - throw new UpdateCheckerException(String.format(trans.get("update.fetcher.malformedURL"), urlLink)); - } catch (IOException e) { - throw new UpdateCheckerException(String.format("Exception - %s: %s", e, e.getMessage())); - } finally { // Close the connection to the release page - if (connection != null) { - try { - connection.disconnect(); - } catch (Exception ex) { - log.warn("Could not disconnect update checker connection"); - } + return builder.build(); } + } catch (Exception e) { + throw new UpdateCheckerException(e); } - - return jsonArr; } /** @@ -494,30 +438,6 @@ public class UpdateInfoRetriever { } } - /** - * Generate a URL with a set of parameters included. - * E.g. url = github.com/openrocket/openrocket/releases, params = {"lorem", "ipsum"} - * => formatted url: github.com/openrocket/openrocket/releases?lorem=ipsum - * @param url base URL - * @param params parameters to include - * @return formatted URL (= base URL with parameters) - */ - private String generateUrlWithParameters(String url, Map params) { - StringBuilder formattedUrl = new StringBuilder(url); - formattedUrl.append("?"); // Identifier for start of query string (for parameters) - - // Append the parameters to the URL - int idx = 0; - for (Map.Entry e : params.entrySet()) { - formattedUrl.append(String.format("%s=%s", e.getKey(), e.getValue())); - if (idx < params.size() - 1) { - formattedUrl.append("&"); // Identifier for more parameters - } - idx++; - } - return formattedUrl.toString(); - } - /** * Exception for the update checker */ @@ -525,6 +445,10 @@ public class UpdateInfoRetriever { public UpdateCheckerException(String message) { super(message); } + + public UpdateCheckerException(Throwable cause) { + super(cause); + } } } } diff --git a/core/src/net/sf/openrocket/communication/WelcomeInfoRetriever.java b/core/src/net/sf/openrocket/communication/WelcomeInfoRetriever.java new file mode 100644 index 000000000..6b4c579d6 --- /dev/null +++ b/core/src/net/sf/openrocket/communication/WelcomeInfoRetriever.java @@ -0,0 +1,47 @@ +package net.sf.openrocket.communication; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.simplesax.SimpleSAX; +import net.sf.openrocket.util.BuildProperties; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public abstract class WelcomeInfoRetriever { + /** + * Retrieves the release notes of the current build version from the ReleaseNotes.md file. + * @param version the build version to search the release notes for (e.g. "22.02") + * @return the release notes of the current build version + * @throws IOException if the file could not be read + */ + public static String retrieveWelcomeInfo(String version) throws IOException { + ClassLoader cl = WelcomeInfoRetriever.class.getClassLoader(); + InputStream in = cl.getResourceAsStream("ReleaseNotes.md"); + if (in == null) { + throw new FileNotFoundException("ReleaseNotes.md not found"); + } + InputSource source = new InputSource(new InputStreamReader(in)); + ReleaseNotesHandler handler = new ReleaseNotesHandler(version); + WarningSet warnings = new WarningSet(); + + try { + SimpleSAX.readXML(source, handler, warnings); + return handler.getReleaseNotes(); + } catch (SAXException e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * Retrieves the release notes of the current build version from the ReleaseNotes.md file. + * @return the release notes of the current build version + * @throws IOException if the file could not be read + */ + public static String retrieveWelcomeInfo() throws IOException { + return retrieveWelcomeInfo(BuildProperties.getVersion()); + } +} diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index be58fb2a5..26b9b1095 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -55,10 +55,12 @@ public abstract class Preferences implements ChangeSource { public static final String USER_LOCAL = "locale"; public static final String PLOT_SHOW_POINTS = "ShowPlotPoints"; - + + private static final String IGNORE_WELCOME = "IgnoreWelcome"; + private static final String CHECK_UPDATES = "CheckUpdates"; - private static final String IGNORE_VERSIONS = "IgnoreVersions"; + private static final String IGNORE_UPDATE_VERSIONS = "IgnoreUpdateVersions"; private static final String CHECK_BETA_UPDATES = "CheckBetaUpdates"; public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch"; @@ -141,9 +143,31 @@ public abstract class Preferences implements ChangeSource { public abstract void putString(String directory, String key, String value); public abstract java.util.prefs.Preferences getNode(String nodeName); - + /* - * ****************************************************************************************** + * Welcome dialog + */ + + /** + * Sets to ignore opening the welcome dialog for the supplied OpenRocket build version. + * @param version build version to ignore opening the welcome dialog for (e.g. "22.02") + * @param ignore true to ignore, false to show the welcome dialog + */ + public final void setIgnoreWelcome(String version, boolean ignore) { + this.putBoolean(IGNORE_WELCOME + "_" + version, ignore); + } + + /** + * Returns whether to ignore opening the welcome dialog for the supplied OpenRocket build version. + * @param version build version (e.g. "22.02") + * @return true if no welcome dialog should be opened for the supplied version + */ + public final boolean getIgnoreWelcome(String version) { + return this.getBoolean(IGNORE_WELCOME + "_" + version, false); + } + + /* + * Software updater */ public final boolean getCheckUpdates() { return this.getBoolean(CHECK_UPDATES, BuildProperties.getDefaultCheckUpdates()); @@ -153,12 +177,12 @@ public abstract class Preferences implements ChangeSource { this.putBoolean(CHECK_UPDATES, check); } - public final List getIgnoreVersions() { - return List.of(this.getString(IGNORE_VERSIONS, "").split("\n")); + public final List getIgnoreUpdateVersions() { + return List.of(this.getString(IGNORE_UPDATE_VERSIONS, "").split("\n")); } - public final void setIgnoreVersions(List versions) { - this.putString(IGNORE_VERSIONS, String.join("\n", versions)); + public final void setIgnoreUpdateVersions(List versions) { + this.putString(IGNORE_UPDATE_VERSIONS, String.join("\n", versions)); } public final boolean getCheckBetaUpdates() { @@ -168,6 +192,11 @@ public abstract class Preferences implements ChangeSource { public final void setCheckBetaUpdates(boolean check) { this.putBoolean(CHECK_BETA_UPDATES, check); } + + + /* + * ****************************************************************************************** + */ public final boolean getConfirmSimDeletion() { return this.getBoolean(CONFIRM_DELETE_SIMULATION, true); diff --git a/core/test/net/sf/openrocket/communication/WelcomeInfoTest.java b/core/test/net/sf/openrocket/communication/WelcomeInfoTest.java new file mode 100644 index 000000000..529c7ee23 --- /dev/null +++ b/core/test/net/sf/openrocket/communication/WelcomeInfoTest.java @@ -0,0 +1,27 @@ +package net.sf.openrocket.communication; + +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class WelcomeInfoTest extends BaseTestCase { + + /** + * Note: this unit test will fail if you don't run it using 'ant unittest', because otherwise + * it can't load the ReleaseNotes.md file. + */ + @Test + public void testWelcomeInfo() throws Exception { + // Test the welcome info for the current build version + String welcomeInfo = WelcomeInfoRetriever.retrieveWelcomeInfo(); + assertNotNull(welcomeInfo); + assertTrue(welcomeInfo.length() > 0); + + // Test the release info for a bogus release version + welcomeInfo = WelcomeInfoRetriever.retrieveWelcomeInfo("bogus release"); + assertNull(welcomeInfo); + } +} diff --git a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java index e00f96152..50b0ee066 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java @@ -30,7 +30,6 @@ import net.sf.openrocket.communication.AssetHandler.UpdatePlatform; import net.sf.openrocket.communication.ReleaseInfo; import net.sf.openrocket.communication.UpdateInfo; import net.sf.openrocket.gui.components.StyledLabel; -import net.sf.openrocket.gui.configdialog.CommonStrings; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.SwingPreferences; @@ -58,6 +57,7 @@ public class UpdateInfoDialog extends JDialog { JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill")); + // OpenRocket logo on the left panel.add(new JLabel(Icons.getScaledIcon(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket"), 0.6)), "spany, top, gapright 20px, cell 0 0"); @@ -124,7 +124,7 @@ public class UpdateInfoDialog extends JDialog { // Lower row buttons //// Remind me later button - JButton btnLater = new SelectColorButton("Remind Me Later"); + JButton btnLater = new SelectColorButton(trans.get("update.dlg.btn.remindMeLater")); btnLater.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -134,15 +134,15 @@ public class UpdateInfoDialog extends JDialog { panel.add(btnLater, "skip 1, split 2"); //// Skip this version button - JButton btnSkip = new SelectColorButton("Skip This Version"); + JButton btnSkip = new SelectColorButton(trans.get("update.dlg.checkbox.skipThisVersion")); btnSkip.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - List ignoreVersions = new ArrayList<>(preferences.getIgnoreVersions()); + List ignoreVersions = new ArrayList<>(preferences.getIgnoreUpdateVersions()); String ignore = release.getReleaseName(); if (!ignoreVersions.contains(ignore)) { ignoreVersions.add(ignore); - preferences.setIgnoreVersions(ignoreVersions); + preferences.setIgnoreUpdateVersions(ignoreVersions); } UpdateInfoDialog.this.dispose(); } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/WelcomeDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/WelcomeDialog.java new file mode 100644 index 000000000..db19183c1 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/dialogs/WelcomeDialog.java @@ -0,0 +1,115 @@ +package net.sf.openrocket.gui.dialogs; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.Icons; +import net.sf.openrocket.gui.widgets.SelectColorButton; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.BuildProperties; +import net.sf.openrocket.util.MarkdownUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Dialog that opens when you start up OpenRocket. It will display the release notes of the current version. + */ +public class WelcomeDialog extends JDialog { + private static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(WelcomeDialog.class); + + /** + * @param releaseNotes the release notes to display for the current version + */ + public WelcomeDialog(String releaseNotes) { + super(null, trans.get("welcome.dlg.title"), ModalityType.APPLICATION_MODAL); + + JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill")); + + // OpenRocket logo on the left + panel.add(new JLabel(Icons.getScaledIcon(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket"), 0.6)), + "spany, top, gapright 20px, cell 0 0"); + + // Thank you for downloading! + panel.add(new StyledLabel(trans.get("welcome.dlg.lbl.thankYou") + " " + BuildProperties.getVersion() + "!", + 2, StyledLabel.Style.BOLD), "spanx, wrap"); + + // See the release notes below for what's new + panel.add(new StyledLabel(trans.get("welcome.dlg.lbl.seeReleaseNotes"), + 1, StyledLabel.Style.PLAIN), "skip 1, spanx, wrap para"); + + // Release notes + final JTextPane textPane = new JTextPane(); + textPane.setEditable(false); + textPane.setContentType("text/html"); + textPane.setMargin(new Insets(10, 10, 10, 10)); + textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true); + + String sb = "" + + MarkdownUtil.toHtml(releaseNotes) + "

" + + ""; + textPane.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(e.getURL().toURI()); + } catch (Exception ex) { + log.warn("Exception hyperlink: " + ex.getMessage()); + } + } + } + }); + + textPane.setText(sb); + textPane.setCaretPosition(0); // Scroll to the top + + panel.add(new JScrollPane(textPane), "skip 1, left, spanx, grow, push, gapbottom 6px, wrap"); + + // Don't show this dialog again + JCheckBox dontShowAgain = new JCheckBox(trans.get("welcome.dlg.checkbox.dontShowAgain")); + dontShowAgain.setSelected(Application.getPreferences().getIgnoreWelcome(BuildProperties.getVersion())); // Normally this should never be true, but just in case + panel.add(dontShowAgain, "skip 1"); + dontShowAgain.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Application.getPreferences().setIgnoreWelcome(BuildProperties.getVersion(), dontShowAgain.isSelected()); + } + }); + + // Close button + JButton closeBtn = new SelectColorButton(trans.get("button.close")); + closeBtn.setToolTipText(trans.get("welcome.dlg.btn.close.ttip")); + closeBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + WelcomeDialog.this.dispose(); + } + }); + panel.add(closeBtn, "pushx, right, wrap"); + + panel.setPreferredSize(new Dimension(800, 600)); + this.add(panel); + + this.pack(); + this.setLocationRelativeTo(null); + GUIUtil.setDisposableDialogOptions(this, closeBtn); + } +} diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java index 440c21a03..9631603a1 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java @@ -342,7 +342,7 @@ public class GeneralPreferencesPanel extends PreferencesPanel { ReleaseInfo release = info.getLatestRelease(); // Do nothing if the release is part of the ignore versions - if (preferences.getIgnoreVersions().contains(release.getReleaseName())) { + if (preferences.getIgnoreUpdateVersions().contains(release.getReleaseName())) { return; } diff --git a/swing/src/net/sf/openrocket/startup/SwingStartup.java b/swing/src/net/sf/openrocket/startup/SwingStartup.java index 954abcc44..4f00838c5 100644 --- a/swing/src/net/sf/openrocket/startup/SwingStartup.java +++ b/swing/src/net/sf/openrocket/startup/SwingStartup.java @@ -4,6 +4,7 @@ import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.stream.IntStream; @@ -19,10 +20,11 @@ import net.sf.openrocket.arch.SystemInfo.Platform; import net.sf.openrocket.communication.UpdateInfo; import net.sf.openrocket.communication.UpdateInfoRetriever; import net.sf.openrocket.communication.UpdateInfoRetriever.ReleaseStatus; +import net.sf.openrocket.communication.WelcomeInfoRetriever; import net.sf.openrocket.database.Databases; import net.sf.openrocket.gui.dialogs.UpdateInfoDialog; +import net.sf.openrocket.gui.dialogs.WelcomeDialog; import net.sf.openrocket.gui.main.BasicFrame; -import net.sf.openrocket.gui.main.MRUDesignFile; import net.sf.openrocket.gui.main.Splash; import net.sf.openrocket.gui.main.SwingExceptionHandler; import net.sf.openrocket.gui.util.GUIUtil; @@ -217,6 +219,7 @@ public class SwingStartup { if (!handleCommandLine(args)) { BasicFrame startupFrame = BasicFrame.reopen(); BasicFrame.setStartupFrame(startupFrame); + showWelcomeDialog(); } // Check whether update info has been fetched or whether it needs more time @@ -277,7 +280,7 @@ public class SwingStartup { // Only display something when an update is found if (info != null && info.getException() == null && info.getReleaseStatus() == ReleaseStatus.OLDER && - !preferences.getIgnoreVersions().contains(info.getLatestRelease().getReleaseName())) { + !preferences.getIgnoreUpdateVersions().contains(info.getLatestRelease().getReleaseName())) { UpdateInfoDialog infoDialog = new UpdateInfoDialog(info); infoDialog.setVisible(true); } @@ -290,6 +293,34 @@ public class SwingStartup { timer.addActionListener(listener); timer.start(); } + + /** + * Shows a welcome dialog displaying the release notes for this build version. + */ + public static void showWelcomeDialog() { + // Don't show if this build version is ignored + if (Application.getPreferences().getIgnoreWelcome(BuildProperties.getVersion())) { + log.debug("Welcome dialog ignored"); + return; + } + + // Fetch this version's release notes + String releaseNotes; + try { + releaseNotes = WelcomeInfoRetriever.retrieveWelcomeInfo(); + } catch (IOException e) { + log.error("Error retrieving welcome info", e); + return; + } + if (releaseNotes == null) { + log.debug("No release notes found"); + return; + } + + // Show the dialog + WelcomeDialog dialog = new WelcomeDialog(releaseNotes); + dialog.setVisible(true); + } /** * Handles arguments passed from the command line. This may be used either