diff --git a/.github/OpenRocket_home.png b/.github/OpenRocket_home.png
new file mode 100644
index 000000000..613f283a2
Binary files /dev/null and b/.github/OpenRocket_home.png differ
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 000000000..02fe919e8
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,9 @@
+cff-version: 1.2.0
+message: "If you use this software, please cite it as below."
+authors:
+- family-names: "Niskanen"
+ given-names: "Sampo"
+title: "OpenRocket"
+version: 15.03
+date-released: 2015-03-28
+url: "https://github.com/openrocket/openrocket"
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..5cf0455e6
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,133 @@
+# Contributor Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement by sending them a
+private email, message on Slack, or any other communication method that you find
+suitable.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..c1c17c594
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,80 @@
+# Contributing to OpenRocket 🚀
+Hi, thank you for your interest in OpenRocket! 😊
+
+I will guide you to contributing to OpenRocket, be it as a developer, tester or any other type of help that will launch - *pun intended* - OpenRocket to the next level.
+
+Before I move on: time is money, so to save you time, get used to how OpenRocket is abbreviated with _OR_.
+
+#### Table Of Contents
+[Testing](#testing)
+* [Reporting bugs](#reporting-bugs)
+* [Suggesting new features](#suggesting-new-features)
+
+[Development](#development)
+* [Commit etiquette](#commit-etiquette)
+* [Pull requests](#pull-requests)
+
+[Translation](#translation)
+
+[Documentation](#documentation)
+
+[Anything else](#anything-else)
+
+## Testing
+OpenRocket is not perfect, but we need people to discover and clearly document all of its imperfections. The job of a tester is to discover bugs, formulate new feature requests and to test out software updates. 📝
+
+### Reporting bugs
+Please be very concise when you post a new issue. Give a short and appropriate title, preferably with the '[Bug]'-tag in the beginning to indicate a bug.
+
+When explaining the issue, the following elements are important:
+* Explain how you expected OpenRocket to behave, and how it behaved instead
+* Go through the different steps that you took to (re)create the issue
+* Include information about your operating system (e.g. 'macOS Monterey version 12.1') and which version of OpenRocket you are using (e.g. 'the latest unstable branch')
+* If applicable, include a Bug Report (preferably in a separate .txt file) of the exception that OpenRocket threw
+
+Providing extra information like a screenshot, a screen recording, the .ork file that produced an error etc. really help understand and solve the issue more quickly.
+
+### Suggesting new features
+If you would like to see a new feature implemented in OR, make a new issue for it. Preferably include the tag '[Feature Request]' in the issue's title.
+
+Explain the new feature in detail:
+* Which new behavior would you like OR to have
+* Why is this new feature important
+
+## Development
+Please read our [Developer's Guide](https://github.com/openrocket/openrocket/wiki/Developer%27s-Guide). If you still have questions about how to set up your environment, with which issues you should start etc., then don't be afraid to send us a message on [Slack](https://join.slack.com/t/openrocket/shared_invite/zt-dh0wtpc4-WmkSK1ysqAOqHa6eFN7zgA).
+
+Developing OpenRocket may be daunting at first, but if you keep Google, your IDE's search and debug features, and the other developers as close friends, then you will easily create your first pull request.
+
+If you want to work on a certain issue, you should first communicate that you want to work on that issue. This can be done by commenting on the issue something like 'I would like to work on this issue'. This ensures that no more than one person works on a given issue.
+
+### Commit etiquette
+Please make use of **atomic commits**. This means: don't fix 10 different issues and cram them in one commit. Split up commits into smaller commits that fix only one issue/feature.
+
+For example: I fixed an issue where a button was displayed as red instead of blue, but I also found that there was a typo in a text somewhere else. Then put the button-fix in a one commit, give it an appropriate name, and put the typo-fix in another commit. Atomic commits make it much easier for code reviewers to review the code changes.
+
+Also give **useful names** to your commits. A good naming convention of a commit is in the form of '[#{GitHub issue number of the issue you are trying to fix}] {Commit subject}'.
+
+Take the example of fixing the red button from issue #123: '[#123] Display red button as blue'. Mentioning '#123' will also automatically link your pull request to the corresponding issue. The commit subject should be short and precise. It is also very useful to include a git commit message body besides just the commit subject to explain why and how you made that commit.
+
+### Pull requests
+Right, you've dug into the codebase, found that one nasty line that caused all your troubles and fixed it. It is now time to push your code and create a pull request of the branch from your own repository to the official repository. As your PR (Pull Request) text, it is good to have the following structure:
+
+1. Explain briefly which issue that you are trying to solve, e.g. 'This PR solves #123 in which buttons were displayed as red instead of blue'
+2. Next explain what the underlying issue was, e.g. 'The problem was that by default Java swing displays buttons as red.'
+3. Next is how you fixed the issue, e.g. 'Fixed it by overriding the default button color to blue'
+4. Finally, for other people to test your code, it is good to include a jar file of your fixed OpenRocket. This can be done using ant ([more info here](https://github.com/openrocket/openrocket/wiki/Instructions-to-Build-from-Terminal)). You can upload this jar-file to e.g. Dropbox or Google Drive and include it in your PR, e.g. 'Here is a jar file for testing: ' or if you're really GitHub-savvy, you can add a hyperlink to the 'jar file'-text. If necessary, you can also include information on how to recreate the original issue so that testers can check whether your code solved the issue. If needed, you can also included information about the expected behavior so that others know what your solution should do.
+
+You can take a look at example PR [#979](https://github.com/openrocket/openrocket/pull/979).
+
+## Translation
+Both the OpenRocket software and the end-user documentation wiki site are multilingual. The job of a translator is to maintain the existing languages, or to make a new translation of an unlisted language. During the development sometimes new translation keys get added in the English language that are not simultaneously translated to other languages. The translator must therefor check which translation keys are still missing in his/her/they language.
+
+How you can make/edit a translation can be found on [this site](http://openrocket.trans.free.fr/index.php?lang=en) or the [GitHub wiki](https://github.com/openrocket/openrocket/wiki/Instructions-for-translators).
+
+## Documentation
+We have two main documentation channels: a [GitHub wiki](https://github.com/openrocket/openrocket/wiki) and an [OpenRocket wiki page](http://wiki.openrocket.info/Main_Page), both of which require regular updates to keep up to date with the latest developments in the software.
+
+## Anything else
+Do you have the perfect voice for making OpenRocket tutorials, are you a graphical designer that screams to improve OR's design, or are you the salesman that can grow OR's influence? Then go for it! We highly appreciate any help that we get, in any shape or form. 🙃
+
diff --git a/core/LICENSE.TXT b/LICENSE.TXT
similarity index 99%
rename from core/LICENSE.TXT
rename to LICENSE.TXT
index a06056587..05af4172b 100644
--- a/core/LICENSE.TXT
+++ b/LICENSE.TXT
@@ -1,6 +1,6 @@
OpenRocket - A model rocket simulator
-Copyright (C) 2007-2020 Sampo Niskanen and others
+Copyright (C) 2007-2022 Sampo Niskanen and others
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/README.md b/README.md
index 862d4b574..66f04db7f 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@ Overview
OpenRocket is a free, fully featured model rocket simulator that allows you to design and simulate your rockets before actually building and flying them.
+
+
The main features include:
* Six-degree-of-freedom flight simulation
@@ -20,15 +22,16 @@ Read more about it on the [OpenRocket Wiki](http://wiki.openrocket.info).
Installers
----------
-OpenRocket maintains an installer for installing the software and Java runtime. You can find the installer for Windows at the link below. Installers for macOS and Linux are coming and will be added to this page when ready.
+OpenRocket maintains an installer for installing the software and Java runtime. You can find the installer below.
-[OpenRocket 15.03 Installer (Windows)](https://github.com/openrocket/openrocket/releases/download/release-15.03/OpenRocket-15.03-installer.exe)
+* [OpenRocket 15.03 Installer (Windows)](https://github.com/openrocket/openrocket/releases/download/release-15.03/OpenRocket-15.03-installer.exe)
+* [OpenRocket 15.03 Installer (Mac OS)](https://github.com/openrocket/openrocket/releases/download/release-15.03/OpenRocket-15.03.dmg)
+* [OpenRocket 15.03 Installer (Linux)](https://github.com/openrocket/openrocket/releases/download/release-15.03/OpenRocket-15.03.AppImage)
+* [OpenRocket 15.03 JAR file](https://github.com/openrocket/openrocket/releases/download/release-15.03/OpenRocket-15.03.jar)
Release Notes
-------------
-Release notes for all releases of OpenRocket are available at the following link.
-
-[OpenRocket Release Notes](https://github.com/openrocket/openrocket/wiki/Release-Notes)
+Release notes are available on each [release's page](https://github.com/openrocket/openrocket/releases) or on [our website](https://openrocket.info/release_notes.html).
License
-------
@@ -37,7 +40,7 @@ OpenRocket is an Open Source project licensed under the [GNU GPL](https://www.gn
Contributing
------------
-OpenRocket needs help to become even better. Implementing features, writing documentation and creating example designs are just a few ways of helping. If you are interested in helping make OpenRocket the best rocket simulator out there, please [click here for information on how to get involved!](http://openrocket.sourceforge.net/getinvolved.html)
+OpenRocket needs help to become even better. Implementing features, writing documentation and creating example designs are just a few ways of helping. If you are interested in helping make OpenRocket the best rocket simulator out there, please [click here for information on how to get involved](http://openrocket.sourceforge.net/getinvolved.html) and [read the practicalities of contributing here](.github/CONTRIBUTING.md).
**Contributors**
- Sampo Niskanen, main developer
@@ -47,6 +50,13 @@ OpenRocket needs help to become even better. Implementing features, writing docu
- Richard Graham, geodetic computations
- Jason Blood, freeform fin set import
- Boris du Reau, internationalization
+- Daniel Williams, pod support, maintainer
+- Joe Pfeiffer (maintainer)
+- Billy Olsen (maintainer)
+- Sibo Van Gool (maintainer)
+- Neil Weinstock (tester, icons, forum support)
+- H. Craig Miller (tester)
+
**Translators**
- Tripoli France
@@ -56,3 +66,4 @@ OpenRocket needs help to become even better. Implementing features, writing docu
- Sky Dart Team
- Vladimir Beran
- Polish Rocketry Society / Łukasz & Alex Kazanski
+- Sibo Van Gool
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index f13737151..2eb7f9c79 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -76,7 +76,6 @@ OpenRocket 22.00.beta.01
..._plus many, many additional bug fixes and refinements_
-
OpenRocket 15.03 (2015-03-28)
-----------------------------
diff --git a/SUPPORT.md b/SUPPORT.md
new file mode 100644
index 000000000..aabc1ede2
--- /dev/null
+++ b/SUPPORT.md
@@ -0,0 +1,12 @@
+# OpenRocket support
+
+OpenRocket has two main Wiki-pages for documentation:
+* [GitHub wiki](https://github.com/openrocket/openrocket/wiki)
+* [OpenRocket wiki](http://wiki.openrocket.info/Main_Page)
+* [OpenRocket website](https://openrocket.info/index.html)
+
+Our main communication channel are:
+* [Slack](https://www.rocketryforum.com/forums/rocketry-electronics-software.36/) = **primary communication channel**
+* [OpenRocket forum](https://www.rocketryforum.com/forums/rocketry-electronics-software.36/)
+* [OpenRocket-devel mailing list](https://sourceforge.net/projects/openrocket/lists/openrocket-devel) for discussion related to OpenRocket development, documentation and upcoming features
+* [OpenRocket-announce mailing list](https://sourceforge.net/projects/openrocket/lists/openrocket-announce) for announcements of new OpenRocket versions and developments
\ No newline at end of file
diff --git a/core/build.xml b/core/build.xml
index 27a1b4ecc..cab35e15b 100644
--- a/core/build.xml
+++ b/core/build.xml
@@ -69,7 +69,7 @@
-
+
diff --git a/core/resources-src/pix/splashscreen-1.4.png b/core/resources-src/pix/splashscreen-1.4.png
new file mode 100644
index 000000000..32b5c97c5
Binary files /dev/null and b/core/resources-src/pix/splashscreen-1.4.png differ
diff --git a/core/resources-src/pix/splashscreen-1.4.xcf.gz b/core/resources-src/pix/splashscreen-1.4.xcf.gz
new file mode 100644
index 000000000..941d535b9
Binary files /dev/null and b/core/resources-src/pix/splashscreen-1.4.xcf.gz differ
diff --git a/core/resources/build.properties b/core/resources/build.properties
index 98a12aa25..31cd9fb23 100644
--- a/core/resources/build.properties
+++ b/core/resources/build.properties
@@ -1,10 +1,10 @@
# The OpenRocket build version
-build.version=20.11.alpha.16
+build.version=22.00.beta.01
# The copyright year for the build. Displayed in the about dialog.
# Will show as Copyright 2013-${build.copyright}
-build.copyright=2021
+build.copyright=2022
# The source of the package. When building a package for a specific
# distribution (Debian, Fedora etc.), this should be changed appropriately!
diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index 468de4992..67d9018fb 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -503,7 +503,6 @@ simpanel.col.Timetoapogee = Time to apogee
simpanel.col.Flighttime = Flight time
simpanel.col.Groundhitvelocity = Ground hit velocity
simpanel.ttip.uptodate = Up to date
-simpanel.ttip.loaded = Data loaded from a file
simpanel.ttip.outdated = Data is out of date
Click Run simulations to simulate.
simpanel.ttip.external = Imported data
simpanel.ttip.notSimulated = Not simulated yet
Click Run simulations to simulate.
diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties
index 138835bf9..0d61ffa10 100644
--- a/core/resources/l10n/messages_es.properties
+++ b/core/resources/l10n/messages_es.properties
@@ -1835,7 +1835,6 @@ simpanel.dlg.lbl.DeleteSim2 = Esta operaci\u00f3n no puede deshacer
simpanel.dlg.lbl.DeleteSim3 = Borrar las simulaciones
simpanel.lbl.defpref = Puede cambiar la operaci\u00f3n por defecto por las preferencias
simpanel.ttip.external = Datos importados
-simpanel.ttip.loaded = Datos recientes.
simpanel.ttip.noData = No hay datos de simulaci\u00f3n disponibles.
simpanel.ttip.noWarnings = Sin alertas.
simpanel.ttip.notSimulated = A\u00fan no ejecutada
Seleccione y haga Click en Lanzar las simulaciones para obtener datos.
diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties
index 380d7de73..f625beb03 100644
--- a/core/resources/l10n/messages_fr.properties
+++ b/core/resources/l10n/messages_fr.properties
@@ -1827,7 +1827,6 @@ simpanel.dlg.lbl.DeleteSim2 = Cette op\u00E9ration n'est pas r\u00E
simpanel.dlg.lbl.DeleteSim3 = Effacer les simulations
simpanel.lbl.defpref = Vous pouvez changer le mode op\u00E9ratoire par d\u00E9faut dans pr\u00E9f\u00E9rences.
simpanel.ttip.external = Donn\u00E9es import\u00E9es
-simpanel.ttip.loaded = Donn\u00E9es charg\u00E9es depuis un fichier
simpanel.ttip.noData = Pas de donn\u00E9es de simulations disponible.
simpanel.ttip.noWarnings = Pas d'avertissements.
simpanel.ttip.notSimulated = Pas encore simul\u00E9
Cliquez Lancer simulations pour simuler.
diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties
index d355b1874..185f2791b 100644
--- a/core/resources/l10n/messages_ja.properties
+++ b/core/resources/l10n/messages_ja.properties
@@ -395,7 +395,6 @@ simpanel.col.Timetoapogee = \u9060\u5730\u70B9\u306E\u6642\u523B
simpanel.col.Flighttime = \u30D5\u30E9\u30A4\u30C8\u6642\u9593
simpanel.col.Groundhitvelocity = \u5730\u9762\u885D\u7A81\u901F\u5EA6
simpanel.ttip.uptodate = Up to date
-simpanel.ttip.loaded = Data loaded from a file
simpanel.ttip.outdated = Imported data
simpanel.ttip.notSimulated = Not simulated yet
Click Run simulations to simulate.
diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties
index d028eab0b..f017b73d1 100644
--- a/core/resources/l10n/messages_nl.properties
+++ b/core/resources/l10n/messages_nl.properties
@@ -487,7 +487,6 @@ simpanel.col.Timetoapogee = Tijd tot apogee
simpanel.col.Flighttime = Vluchttijd
simpanel.col.Groundhitvelocity = Snelheid bij grondcontact
simpanel.ttip.uptodate = Up-to-date
-simpanel.ttip.loaded = Uit een bestand geladen gegevens
simpanel.ttip.outdated = Gegevens zijn verouderd
Klik Voer simulaties uit om te simuleren.
simpanel.ttip.external = Geïmporteerde data
simpanel.ttip.notSimulated = Nog niet gesimuleerd
Klik Voer simulaties uit om te simuleren.
diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties
index 0528481d4..1982ed4ad 100644
--- a/core/resources/l10n/messages_pt.properties
+++ b/core/resources/l10n/messages_pt.properties
@@ -1780,7 +1780,6 @@ simpanel.dlg.lbl.DeleteSim2 = Esta opera\u00e7\u00e3o n\u00e3o pode
simpanel.dlg.lbl.DeleteSim3 = Excluir simula\u00e7\u00f5es
simpanel.lbl.defpref = Voc\u00ea pode alterar a opera\u00e7\u00e3o padr\u00e3o em Prefer\u00eancias.
simpanel.ttip.external = Dados importados
-simpanel.ttip.loaded = Dados carregado de um arquivo
simpanel.ttip.noData = N\u00e3o h\u00e1 dados dispon\u00edveis para simula\u00e7\u00e3o.
simpanel.ttip.noWarnings = Nenhuma advert\u00eancia.
simpanel.ttip.notSimulated = N\u00e3o simulado ainda
CliqueExecutar simula\u00e7\u00f5es para simular.
diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties
index c707e1568..3628b1067 100644
--- a/core/resources/l10n/messages_ru.properties
+++ b/core/resources/l10n/messages_ru.properties
@@ -440,7 +440,6 @@ simpanel.col.Timetoapogee = \u0412\u0440\u0435\u043c\u044f \u0434\u043e \u0430\u
simpanel.col.Flighttime = \u0412\u0440\u0435\u043c\u044f \u043f\u043e\u043b\u0435\u0442\u0430
simpanel.col.Groundhitvelocity = \u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u044f
simpanel.ttip.uptodate = \u0414\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b
-simpanel.ttip.loaded = \u0414\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0438\u0437 \u0444\u0430\u0439\u043b\u0430
simpanel.ttip.outdated = \u0414\u0430\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0430\u0440\u0435\u043b\u0438
\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0440\u0430\u0441\u0447\u0435\u0442\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430.
simpanel.ttip.external = \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435
simpanel.ttip.notSimulated = \u0420\u0430\u0441\u0447\u0435\u0442 \u0435\u0449\u0435 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u0441\u044f
\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0440\u0430\u0441\u0447\u0435\u0442\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430.
diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties
index d28af6c71..eb0de6f43 100644
--- a/core/resources/l10n/messages_uk_UA.properties
+++ b/core/resources/l10n/messages_uk_UA.properties
@@ -444,7 +444,6 @@ simpanel.col.Timetoapogee = Time to apogee
simpanel.col.Flighttime = Flight time
simpanel.col.Groundhitvelocity = Ground hit velocity
simpanel.ttip.uptodate = Up to date
-simpanel.ttip.loaded = Data loaded from a file
simpanel.ttip.outdated = Data is out of date
Click Run simulations to simulate.
simpanel.ttip.external = Imported data
simpanel.ttip.notSimulated = Not simulated yet
Click Run simulations to simulate.
diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties
index 0ec1c6d31..0c8732e03 100644
--- a/core/resources/l10n/messages_zh_CN.properties
+++ b/core/resources/l10n/messages_zh_CN.properties
@@ -1928,7 +1928,6 @@ simpanel.dlg.lbl.DeleteSim2 = \u8BE5\u64CD\u4F5C\u65E0\u6CD5\u64
simpanel.dlg.lbl.DeleteSim3 = \u5220\u9664\u4EFF\u771F
simpanel.lbl.defpref = \u60A8\u53EF\u5728\u9996\u9009\u9879\u4E2D\u4FEE\u6539\u9ED8\u8BA4\u64CD\u4F5C.
simpanel.ttip.external = \u5BFC\u5165\u7684\u6570\u636E
-simpanel.ttip.loaded = \u4ECE\u6587\u4EF6\u8F7D\u5165\u6570\u636E
simpanel.ttip.noData = \u6CA1\u6709\u53EF\u7528\u7684\u4EFF\u771F\u6570\u636E.
simpanel.ttip.noWarnings = \u6CA1\u6709\u8B66\u544A.
simpanel.ttip.notSimulated = \u672A\u8FDB\u884C\u8FC7\u4EFF\u771F
\u70B9\u51FB\u8FD0\u884C\u4EFF\u771F.
diff --git a/core/resources/pix/splashscreen.png b/core/resources/pix/splashscreen.png
index 0909a8758..32b5c97c5 100644
Binary files a/core/resources/pix/splashscreen.png and b/core/resources/pix/splashscreen.png differ
diff --git a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
index e55f1d366..6e32873e4 100644
--- a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
@@ -318,8 +318,11 @@ public class OpenRocketSaver extends RocketSaver {
private void saveSimulation(Simulation simulation, double timeSkip) throws IOException {
SimulationOptions cond = simulation.getOptions();
-
- writeln("");
+
+ Simulation.Status simStatus;
+ simStatus = timeSkip != StorageOptions.SIMULATION_DATA_NONE ? simulation.getStatus() : Simulation.Status.NOT_SIMULATED;
+
+ writeln("");
indent++;
writeln("" + TextUtil.escapeXML(simulation.getName()) + "");
diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java
index 34c1d528d..6d90c7050 100644
--- a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java
+++ b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java
@@ -91,7 +91,13 @@ class FlightDataHandler extends AbstractElementHandler {
@Override
public void endHandler(String element, HashMap attributes,
String content, WarningSet warnings) {
-
+
+ // If no tag in XML, then there is no sim data
+ if (dataHandler == null) {
+ data = null;
+ return;
+ }
+
if (branches.size() > 0) {
data = new FlightData(branches.toArray(new FlightDataBranch[0]));
} else {
diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java
index 7834c285e..c559a2fca 100644
--- a/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java
+++ b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java
@@ -134,6 +134,10 @@ class SingleSimulationHandler extends AbstractElementHandler {
data = null;
else
data = dataHandler.getFlightData();
+
+ if (data == null) {
+ status = Status.NOT_SIMULATED;
+ }
Simulation simulation = new Simulation(doc.getRocket(), status, name,
options, extensions, data);
diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java
index ef6c25212..5da1cd877 100644
--- a/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java
@@ -3,6 +3,8 @@ package net.sf.openrocket.file.openrocket.savers;
import java.util.List;
import java.util.Locale;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.util.MathUtil;
public class FinSetSaver extends ExternalComponentSaver {
@@ -28,6 +30,22 @@ public class FinSetSaver extends ExternalComponentSaver {
elements.add("" + fins.getTabHeight() + "");
elements.add("" + fins.getTabLength() + "");
+ // TODO: delete this when no backward compatibility with OR 15.03 is needed anymore
+ String offset = "center";
+ double offsetVal = fins.getTabOffset();
+ switch (fins.getTabOffsetMethod()) {
+ case TOP:
+ offset = "front";
+ break;
+ case BOTTOM:
+ offset = "end";
+ break;
+ case MIDDLE:
+ offset = "center";
+ break;
+ }
+ elements.add("" +
+ offsetVal + "");
elements.add("" +
fins.getTabOffset() + "");
diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
index 176993f18..97dfd97eb 100644
--- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
@@ -83,6 +83,10 @@ public class RocketComponentSaver {
// no-op. Instance counts are set via named cluster configurations
} else {
emitInteger(elements, "instancecount", c.getInstanceCount());
+ // TODO: delete this when no backward compatibility with OR 15.03 is needed anymore
+ if (c instanceof FinSet || c instanceof TubeFinSet) {
+ emitInteger(elements, "fincount", c.getInstanceCount());
+ }
}
if (c instanceof LineInstanceable) {
@@ -103,6 +107,13 @@ public class RocketComponentSaver {
final String angleMethod = anglePos.getAngleMethod().name().toLowerCase(Locale.ENGLISH);
final double angleOffset = anglePos.getAngleOffset()*180.0/Math.PI;
elements.add("" + angleOffset + "");
+ // TODO: delete this when no backward compatibility with OR 15.03 is needed anymore
+ if (c instanceof FinSet || c instanceof TubeFinSet) {
+ elements.add("" + angleOffset + "");
+ }
+ else if (!(c instanceof RailButton)) {
+ elements.add("" + angleOffset + "");
+ }
}
// Save position unless "AFTER"
@@ -110,6 +121,8 @@ public class RocketComponentSaver {
// The type names are currently equivalent to the enum names except for case.
String axialMethod = c.getAxialMethod().name().toLowerCase(Locale.ENGLISH);
elements.add("" + c.getAxialOffset() + "");
+ // TODO: delete this when no backward compatibility with OR 15.03 is needed anymore
+ elements.add("" + c.getAxialOffset() + "");
}
// Overrides
diff --git a/core/fileformat.txt b/fileformat.txt
similarity index 100%
rename from core/fileformat.txt
rename to fileformat.txt
diff --git a/swing/build.xml b/swing/build.xml
index c6a0678b1..18fd76bc3 100644
--- a/swing/build.xml
+++ b/swing/build.xml
@@ -200,6 +200,10 @@ the .jar file
+
+
diff --git a/swing/resources/datafiles/examples/A simple model rocket.ork b/swing/resources/datafiles/examples/A simple model rocket.ork
index 2932f25e3..42aa47599 100644
Binary files a/swing/resources/datafiles/examples/A simple model rocket.ork and b/swing/resources/datafiles/examples/A simple model rocket.ork differ
diff --git a/swing/resources/datafiles/examples/Boosted Dart.ork b/swing/resources/datafiles/examples/Boosted Dart.ork
index 8bd8df078..18e755700 100644
Binary files a/swing/resources/datafiles/examples/Boosted Dart.ork and b/swing/resources/datafiles/examples/Boosted Dart.ork differ
diff --git a/swing/resources/datafiles/examples/Clustered rocket design.ork b/swing/resources/datafiles/examples/Clustered rocket design.ork
index b16f089c5..21785f0b1 100644
Binary files a/swing/resources/datafiles/examples/Clustered rocket design.ork and b/swing/resources/datafiles/examples/Clustered rocket design.ork differ
diff --git a/swing/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork b/swing/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork
index d3c120da4..267d5d2d8 100644
Binary files a/swing/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork and b/swing/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork differ
diff --git a/swing/resources/datafiles/examples/Three-stage rocket.ork b/swing/resources/datafiles/examples/Three-stage rocket.ork
index 39b3a02f5..04deb759c 100644
Binary files a/swing/resources/datafiles/examples/Three-stage rocket.ork and b/swing/resources/datafiles/examples/Three-stage rocket.ork differ
diff --git a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
index 2c68b129c..844c4909d 100644
--- a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
+++ b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
@@ -1,10 +1,21 @@
package net.sf.openrocket.gui.components;
import java.awt.Color;
+import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
+import java.net.URI;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JTextPane;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
@@ -65,7 +76,71 @@ public class DescriptionArea extends JScrollPane {
Font font = editorPane.getFont();
editorPane.setFont(font.deriveFont(font.getSize2D() + size));
editorPane.setEditable(false);
-
+ editorPane.addHyperlinkListener(new HyperlinkListener() {
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ URI uri = null;
+ try {
+ uri = e.getURL().toURI();
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+
+ // If the uri scheme indicates this is a resource in a jar file,
+ // extract and write to a temporary file
+ if (uri.getScheme().equals("jar")) {
+
+ // get the resource
+ final String uriString = uri.toString();
+ final String resourceName = uriString.substring(uriString.indexOf("!") + 1);
+ final BufferedInputStream is = new BufferedInputStream(getClass().getResourceAsStream(resourceName));
+
+ // construct filename from resource name
+ String prefix = resourceName.substring(1);
+ String suffix;
+ final int dotIndex = prefix.lastIndexOf(".");
+ if (dotIndex > 0) {
+ prefix = resourceName.substring(0, dotIndex);
+ suffix = resourceName.substring(dotIndex+1);
+ } else {
+ // if there is no suffix, assume it's a raw text file.
+ suffix = ".txt";
+ }
+
+
+ // create temporary file and copy resource to it
+ File of = null;
+ BufferedOutputStream os = null;
+ try {
+ of = File.createTempFile(prefix, suffix);
+ os = new BufferedOutputStream(new FileOutputStream(of));
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ of.deleteOnExit();
+ uri = of.toURI();
+
+ try {
+ byte buffer[] = is.readAllBytes();
+ os.write(buffer);
+ os.close();
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ try {
+ Desktop.getDesktop().browse(uri);
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+ });
if (!opaque) {
Color bg = new JPanel().getBackground();
editorPane.setBackground(new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
@@ -103,5 +178,17 @@ public class DescriptionArea extends JScrollPane {
});
editorPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
+
+ /**
+ * Set the font to use for the text pane. If null, then the default font from OR is used.
+ * @param font font to use
+ */
+ public void setTextFont(Font font) {
+ if (editorPane == null) return;
+ editorPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
+ if (font != null) {
+ editorPane.setFont(font);
+ }
+ }
}
diff --git a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
index de9782608..a26222798 100644
--- a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
+++ b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
@@ -56,7 +56,7 @@ public class CustomExpressionPanel extends JPanel {
//expressionSelectorPanel.add(scroll);
//this.add(expressionSelectorPanel, "spany 1, height 10px, wmin 600lp, grow 100, gapright para");
- this.add(scroll, "hmin 200lp, wmin 700lp, grow 100, wrap");
+ this.add(scroll, "hmin 200lp, wmin 700lp, grow, pushy, wrap");
//DescriptionArea desc = new DescriptionArea(trans.get("customExpressionPanel.lbl.UpdateNote")+"\n\n"+trans.get("customExpressionPanel.lbl.CalcNote"), 8, -2f);
//desc.setViewportBorder(BorderFactory.createEmptyBorder());
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
index e62419684..452a617dc 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
@@ -8,6 +8,7 @@ import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
+import javax.swing.UIManager;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.DescriptionArea;
@@ -25,42 +26,60 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
@SuppressWarnings("serial")
public class AboutDialog extends JDialog {
- public static final String OPENROCKET_URL = "http://openrocket.info/";
- private static final Translator trans = Application.getTranslator();
+ public final String OPENROCKET_URL = "http://openrocket.info/";
+
+ private final Translator trans = Application.getTranslator();
- private static final String CREDITS = "" +
- "OpenRocket has been developed by:
" +
- "Sampo Niskanen (main developer)
" +
- "Doug Pedrick (RockSim file format, printing)
" +
- "Kevin Ruland (Android version)
" +
- "Bill Kuker (3D visualization)
" +
- "Boris du Reau (internationalization, translation lead)
" +
- "Richard Graham (geodetic computations)
" +
- "Jason Blood (finset import)
" +
- "Daniel Williams (pod support, maintainer)
" +
- "Joe Pfeiffer (maintainer)
" +
- "Billy Olsen (maintainer)
" +
- "Neil Weinstock (tester, icons, forum support)
" +
- "H. Craig Miller (tester)
" +
- "Translations by:
" +
- "Tripoli France (French)
" +
- "Stefan Lobas / ERIG e.V. (German)
" +
- "Tripoli Spain (Spanish)
" +
- "Sky Dart Team (Russian)
" +
- "Mauro Biasutti (Italian)
" +
- "Vladimir Beran (Czech)
" +
- "Polish Rocketry Society / \u0141ukasz & Alex Kazanski (Polish)
" +
- "Sibo Van Gool (Dutch)
" +
- "See all contributors at
https://github.com/openrocket/openrocket/graphs/contributors
" +
- "OpenRocket utilizes the following libraries:
" +
- "MiG Layout (http://www.miglayout.com/)
" +
- "JFreeChart (http://www.jfree.org/jfreechart/)
" +
- "iText (http://www.itextpdf.com/)
" +
- "exp4j (http://projects.congrace.de/exp4j/index.html)
" +
- "JOGL (http://jogamp.org/jogl/www/)
" +
- "Guava (https://github.com/google/guava)
" +
- "Opencsv (http://opencsv.sourceforge.net/)
" +
- "Simple Logging Facade for Java (http://www.slf4j.org/)";
+ private final String CREDITS = "" +
+ "OpenRocket has been developed by:
" +
+ "
" +
+ "Sampo Niskanen (main developer)
" +
+ "Doug Pedrick (RockSim file format, printing)
" +
+ "Kevin Ruland (Android version)
" +
+ "Bill Kuker (3D visualization)
" +
+ "Boris du Reau (internationalization, translation lead)
" +
+ "Richard Graham (geodetic computations)
" +
+ "Jason Blood (finset import)
" +
+ "Daniel Williams (pod support, maintainer)
" +
+ "Joe Pfeiffer (maintainer)
" +
+ "Billy Olsen (maintainer)
" +
+ "Sibo Van Gool (maintainer)
" +
+ "Justin Hanney (maintainer)
" +
+ "Neil Weinstock (tester, icons, forum support)
" +
+ "H. Craig Miller (tester)
" +
+ "Translations by:
" +
+ "Tripoli France (French)
" +
+ "Stefan Lobas / ERIG e.V. (German)
" +
+ "Tripoli Spain (Spanish)
" +
+ "Sky Dart Team (Russian)
" +
+ "Mauro Biasutti (Italian)
" +
+ "Vladimir Beran (Czech)
" +
+ "Polish Rocketry Society / \u0141ukasz & Alex Kazanski (Polish)
" +
+ "Sibo Van Gool (Dutch)
" +
+ "
" +
+ "See all contributors at
" +
+ href("https://github.com/openrocket/openrocket/graphs/contributors") + "
" +
+ "
" +
+ "OpenRocket utilizes the following libraries:
" +
+ "
" +
+ "MiG Layout (" + href("http://www.miglayout.com/") + ")
" +
+ "JFreeChart (" + href("http://www.jfree.org/jfreechart/") + ")
" +
+ "iText (" + href("http://www.itextpdf.com/") + ")
" +
+ "exp4j (" + href("http://projects.congrace.de/exp4j/index.html") + ")
" +
+ "JOGL (" + href("http://jogamp.org/jogl/www/") + ")
" +
+ "Guava (" + href("https://github.com/google/guava") + ")
" +
+ "Opencsv (" + href("http://opencsv.sourceforge.net/") + ")
" +
+ "Simple Logging Facade for Java (" + href("http://www.slf4j.org/") + ")
" +
+ "Java library for parsing and rendering CommonMark (" + href("https://github.com/commonmark/commonmark-java") + ")
" +
+ "
" +
+ "OpenRocket gratefully acknowledges our use of the following databases:
" +
+ "
" +
+ "Rocket Motor Data (" + href("https://www.thrustcurve.org/") + ")
" +
+ "Enhanced components database for OpenRocket" + href("https://github.com/dbcook/openrocket-database/") + ")
";
+
+ private String href(String url) {
+ return "" + url + "";
+ }
public AboutDialog(JFrame parent) {
super(parent, true);
@@ -85,7 +104,7 @@ public class AboutDialog extends JDialog {
sub.add(new StyledLabel(copyright), "ax 50%, growy, wrap para");
sub.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para");
- panel.add(sub, "grow");
+ panel.add(sub, "grow, pushx");
// Translation information (if present)
@@ -116,7 +135,8 @@ public class AboutDialog extends JDialog {
DescriptionArea info = new DescriptionArea(5);
info.setText(CREDITS);
- panel.add(info, "newline, width 10px, height 150lp, grow, spanx, wrap para");
+ info.setTextFont(UIManager.getFont("Label.font"));
+ panel.add(info, "newline, width 10px, height 250lp, pushy, grow, spanx, wrap para");
// JTextArea area = new JTextArea(CREATORS);
// area.setEditable(false);
@@ -138,7 +158,6 @@ public class AboutDialog extends JDialog {
this.add(panel);
this.setTitle("OpenRocket " + version);
this.pack();
- this.setResizable(false);
this.setLocationRelativeTo(parent);
GUIUtil.setDisposableDialogOptions(this, close);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
index 6b1817990..946ecd224 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
@@ -18,8 +18,8 @@ import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
import javax.swing.JTextPane;
+import javax.swing.UIManager;
import com.jogamp.opengl.JoglVersion;
@@ -73,6 +73,7 @@ public class BugReportDialog extends JDialog {
final JEditorPane editorPane = new JEditorPane("text/html", formatNewlineHTML(message));
editorPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
+ editorPane.setFont(UIManager.getFont("Label.font"));
editorPane.setPreferredSize(new Dimension(600, 400));
editorPane.setEditable(true);
editorPane.setCaretPosition(0); // Scroll to the top by default
@@ -180,7 +181,8 @@ public class BugReportDialog extends JDialog {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
- sb.append(sw.getBuffer());
+ String stackTrace = unformatHTML(String.valueOf(sw.getBuffer()));
+ sb.append(stackTrace);
sb.append('\n');
@@ -207,39 +209,47 @@ public class BugReportDialog extends JDialog {
}
private static void addSystemInformation(StringBuilder sb) {
- sb.append("OpenRocket version: " + BuildProperties.getVersion() + "\n");
- sb.append("OpenRocket source: " + BuildProperties.getBuildSource() + "\n");
- sb.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n");
- sb.append("JOGL version: " + JoglVersion.getInstance().getImplementationVersion() + "\n");
- sb.append("Current default locale: " + Locale.getDefault() + "\n");
- sb.append("System properties:\n");
-
+ StringBuilder sbTemp = new StringBuilder();
+ sbTemp.append("OpenRocket version: " + BuildProperties.getVersion() + "\n");
+ sbTemp.append("OpenRocket source: " + BuildProperties.getBuildSource() + "\n");
+ sbTemp.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n");
+ sbTemp.append("JOGL version: " + JoglVersion.getInstance().getImplementationVersion() + "\n");
+ sbTemp.append("Current default locale: " + Locale.getDefault() + "\n");
+ sbTemp.append("System properties:\n");
+
// Sort the keys
SortedSet keys = new TreeSet();
for (Object key : System.getProperties().keySet()) {
keys.add((String) key);
}
-
+
for (String key : keys) {
String value = System.getProperty(key);
- sb.append(" " + key + "=");
+ sbTemp.append(" " + key + "=");
if (key.equals("line.separator")) {
for (char c : value.toCharArray()) {
- sb.append(String.format("\\u%04x", (int) c));
+ sbTemp.append(String.format("\\u%04x", (int) c));
}
} else {
- sb.append(value);
+ sbTemp.append(value);
}
- sb.append('\n');
+ sbTemp.append('\n');
}
+
+ String message = unformatHTML(sbTemp.toString());
+ sb.append(message);
}
-
+
private static void addErrorLog(StringBuilder sb) {
+ StringBuilder sbTemp = new StringBuilder();
LogLevelBufferLogger buffer = LoggingSystemSetup.getBufferLogger();
List logs = buffer.getLogs();
for (LogLine l : logs) {
- sb.append(l.toString()).append('\n');
+ sbTemp.append(l.toString()).append('\n');
}
+
+ String message = unformatHTML(sbTemp.toString());
+ sb.append(message);
}
/**
@@ -252,5 +262,14 @@ public class BugReportDialog extends JDialog {
private static String formatNewlineHTML(String text) {
return text.replaceAll("\n(.*?)(?=(\n|$))", "$1
");
}
+
+ /**
+ * Makes text HTML unformatted by replacing '<' and '>' by the HTML character equivalent
+ * @param text text to be replaced
+ * @return HTML unformatted text
+ */
+ private static String unformatHTML(String text) {
+ return text.replace("<", "<").replace(">", ">");
+ }
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
index 865b0a67e..1452daad0 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
@@ -11,6 +11,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
@@ -82,6 +83,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
private final JToggleButton worstToggle;
private boolean fakeChange = false;
private AerodynamicCalculator aerodynamicCalculator;
+ private double initTheta;
private final ColumnTableModel longitudeStabilityTableModel;
private final ColumnTableModel dragTableModel;
@@ -115,6 +117,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
aoa = new DoubleModel(rocketPanel, "CPAOA", UnitGroup.UNITS_ANGLE, 0, Math.PI);
rocketPanel.setCPMach(Application.getPreferences().getDefaultMach());
mach = new DoubleModel(rocketPanel, "CPMach", UnitGroup.UNITS_COEFFICIENT, 0);
+ initTheta = rocketPanel.getFigure().getRotation();
rocketPanel.setCPTheta(rocketPanel.getFigure().getRotation());
theta = new DoubleModel(rocketPanel, "CPTheta", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
rocketPanel.setCPRoll(0);
@@ -148,7 +151,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
JScrollPane scrollPane = new JScrollPane(warningList);
////Warnings:
scrollPane.setBorder(BorderFactory.createTitledBorder(trans.get("componentanalysisdlg.TitledBorder.warnings")));
- panel.add(scrollPane, "gap paragraph, spany 4, width 300lp!, growy 1, height :100lp:, wrap");
+ panel.add(scrollPane, "gap paragraph, spany 4, wmin 300lp, grow, height :100lp:, wrap");
////Angle of attack:
panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.angleofattack")), "width 120lp!");
@@ -183,7 +186,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
// Tabbed pane
JTabbedPane tabbedPane = new JTabbedPane();
- panel.add(tabbedPane, "spanx, growx, growy");
+ panel.add(tabbedPane, "spanx, growx, growy, pushy");
// Create the Longitudinal Stability (CM vs CP) data table
@@ -428,6 +431,8 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
+ theta.setValue(initTheta);
+
//System.out.println("Closing method called: " + this);
theta.removeChangeListener(ComponentAnalysisDialog.this);
aoa.removeChangeListener(ComponentAnalysisDialog.this);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java
index 7ac41ce05..31fc47290 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java
@@ -1,68 +1,147 @@
package net.sf.openrocket.gui.dialogs;
-import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
+import javax.swing.JLabel;
import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
+import javax.swing.UIManager;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.gui.widgets.SelectColorButton;
+import net.sf.openrocket.util.BuildProperties;
+import net.sf.openrocket.util.Chars;
public class LicenseDialog extends JDialog {
- private static final String LICENSE_FILENAME = "LICENSE.TXT";
private static final Translator trans = Application.getTranslator();
- private static final String DEFAULT_LICENSE_TEXT =
- "\n" +
- "Error: Unable to load " + LICENSE_FILENAME + "!\n" +
- "\n" +
- "OpenRocket is licensed under the GNU GPL version 3, with additional permissions.\n" +
- "See http://openrocket.sourceforge.net/ for details.";
-
public LicenseDialog(JFrame parent) {
super(parent, true);
JPanel panel = new JPanel(new MigLayout("fill"));
- panel.add(new StyledLabel("OpenRocket license", 10), "ax 50%, wrap para");
-
- String licenseText;
- try {
-
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(ClassLoader.getSystemResourceAsStream(LICENSE_FILENAME)));
- StringBuffer sb = new StringBuffer();
- for (String s = reader.readLine(); s != null; s = reader.readLine()) {
- sb.append(s);
- sb.append('\n');
- }
- licenseText = sb.toString();
-
- } catch (IOException e) {
-
- licenseText = DEFAULT_LICENSE_TEXT;
-
- }
+ // OpenRocket logo
+ panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), "top");
- JTextArea text = new JTextArea(licenseText);
- text.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
- text.setRows(20);
- text.setColumns(80);
- text.setEditable(false);
- panel.add(new JScrollPane(text),"grow, wrap para");
+ panel.add(new StyledLabel("Software Licenses", 10), "ax 50%, pushx, wrap para");
+
+ final String jarUrl = "jar:" + getClass().getProtectionDomain().getCodeSource().getLocation().toString();
+ final String copyrightYear = BuildProperties.getCopyrightYear();
+
+ /*****************************************************************************************************************************/
+ /* */
+ /* LICENSE TEXT: each of the licenses we're using is described here. At the end, they are all concatenated for insertion */
+ /* in the description window */
+ /* */
+ /*****************************************************************************************************************************/
+
+ /*****************************************************************************************************************************/
+ /* GPL: overall project */
+ /*****************************************************************************************************************************/
+ final String orLicense = "GNU GENERAL PUBLIC LICENSE" + "
" +
+ "
" +
+ "OpenRocket - A model rocket simulator
" +
+ "Copyright " + Chars.COPY + " 2007-" + copyrightYear + " Sampo Niskanen and others
" +
+ "Project page: https://openrocket.info/
" +
+ "
" +
+ "This program is free software: you can redistribute it and/or modify it under the terms of the " +
+ "GNU General Public License as published by the Free Software Foundation, either version 3 " +
+ "of the License, or any later version. " +
+ "The license may be viewed " +
+ "here.
" +
+ "
" +
+ "This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; " +
+ "without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. " +
+ "See the GNU General Public License for more details.
" +
+ "
" +
+ "You should have received a copy of the GNU Public License along with this program. If not, you may obtain a copy at " +
+ "https://www.gnu.org/licenses/gpl-3.0.html
" +
+ "
" +
+ "OpenRocket developers may be contacted electronically at:
" +
+ "mailto:openrocket-devel@lists.sourceforge.net
" +
+ "https://openrocket.slack.com
" +
+ "https://github.com/openrocket
" +
+ "
";
+
+ /*****************************************************************************************************************************/
+ /* APACHE: components library */
+ /*****************************************************************************************************************************/
+ final String componentsLicense =
+ "APACHE LICENSE
" +
+ "
" +
+ "OpenRocket features the enhanced components database created by David B. Cook
" +
+ "Copyright " + Chars.COPY + " 2015-" + copyrightYear + " David B. Cook
" +
+ "Project page: https://github.com/dbcook/openrocket-database
" +
+ "
" +
+ "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this work except in compliance with the License. " +
+ "You may view the License " +
+ "here.
" +
+ "You may also obtain a copy of the License at " +
+ "http://www.apache.org/licenses/LICENSE-2.0
" +
+ "
" +
+ "OpenRocket uses the Work or Derivative Works of Ant, a product which includes software developed by the Apache " +
+ "Software Foundation
" +
+ "
" +
+ "Ant also includes software developed by:" +
+ "" +
+ "The names \"Ant\" and \"Apache Software Foundation\" must not be used to endorse or " +
+ "promote products derived from this software without prior written permission. For written permission, "+
+ "please contact apache@apache.org.
" +
+ "
";
+
+ /*****************************************************************************************************************************/
+ /* BITSTREAM VERA: Deja Vu font */
+ /*****************************************************************************************************************************/
+ final String fontLicense =
+ "BITSTREAM VERA FONT LICENSE
" +
+ "
" +
+ "OpenRocket makes use of the DejaVu Serif Font
" +
+ "Fonts are Copyright " + Chars.COPY + " 2003 by Bitstream, Inc. All Rights Reserved. " +
+ "Bitstream Vera is a trademark of Bitstream, Inc.
" +
+ "DejaVu changes are in the public domain
" +
+ "Glyphs imported from Arev Fonts Copyright " + Chars.COPY + " 2006 by Tavmjong Bah. All Rights Reserved.
" +
+ "Project page: https://github.com/dejavu-fonts/dejavu-fonts/
" +
+ "
" +
+ "Licensed according to the Bitstream Vera Font License which may be found " +
+ "here." +
+ "
" +
+ "You may also obtain a copy of the License at " +
+ "https://github.com/dejavu-fonts/dejavu-fonts/blob/master/LICENSE
" +
+ "
";
+
+ /*****************************************************************************************************************************/
+ /* BSD 2-Clause: commonmark-java library */
+ /*****************************************************************************************************************************/
+ final String commonmarkLicense =
+ "BSD 2-Clause License
" +
+ "
" +
+ "OpenRocket makes use of the Commonmark-Java Library
" +
+ "Copyright " + Chars.COPY + " 2015-2016 Atlassian Pty Ltd. All rights reserved.
" +
+ "Project page: https://github.com/commonmark/commonmark-java/
" +
+ "
" +
+ "You may obtain a copy of the License at https://github.com/commonmark/commonmark-java/blob/main/LICENSE.txt." +
+ "
";
+
+ /*****************************************************************************************************************************/
+ /* End of license text */
+ /*****************************************************************************************************************************/
+
+ DescriptionArea info = new DescriptionArea(20);
+ info.setTextFont(UIManager.getFont("Label.font"));
+ info.setText(orLicense + componentsLicense + fontLicense + commonmarkLicense);
+ panel.add(info, "newline, width 700lp, height 250lp, pushy, grow, spanx, wrap para");
//Close button
JButton close = new SelectColorButton(trans.get("dlg.but.close"));
@@ -72,7 +151,7 @@ public class LicenseDialog extends JDialog {
LicenseDialog.this.dispose();
}
});
- panel.add(close, "right");
+ panel.add(close, "spanx, right");
this.add(panel);
this.setTitle("OpenRocket license");
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
index 545aa0a47..755bab3d8 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
@@ -1,6 +1,8 @@
package net.sf.openrocket.gui.dialogs.optimization;
import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -205,7 +207,7 @@ public class GeneralOptimizationDialog extends JDialog {
JScrollPane scroll;
String tip;
- JPanel panel = new JPanel(new MigLayout("fill"));
+ JPanel panel = new JPanel(new MigLayout("fill, w 1200"));
ChangeListener clearHistoryChangeListener = e -> clearHistory();
ActionListener clearHistoryActionListener = e -> clearHistory();
@@ -251,10 +253,10 @@ public class GeneralOptimizationDialog extends JDialog {
label = new StyledLabel(trans.get("lbl.paramsToOptimize"), Style.BOLD);
disableComponents.add(label);
panel.add(label, "split 3, flowy");
- panel.add(scroll, "wmin 300lp, height 200lp, grow");
+ panel.add(scroll, "wmin 300lp, height 150lp, grow");
selectedModifierDescription = new DescriptionArea(2, -3);
disableComponents.add(selectedModifierDescription);
- panel.add(selectedModifierDescription, "growx");
+ panel.add(selectedModifierDescription, "hmin 20lp, growx");
// // Add/remove buttons
sub = new JPanel(new MigLayout("fill"));
@@ -601,16 +603,16 @@ public class GeneralOptimizationDialog extends JDialog {
});
panel.add(closeButton, "right");
- this.add(panel);
+ this.add(new JScrollPane(panel));
clearHistory();
updateComponents();
GUIUtil.setDisposableDialogOptions(this, null);
- // seem like a reasonable defaults
- this.setSize(1200, 600);
- // System.err.println("OptimizationDialog.size: " + this.getSize());
- this.setLocation(100, 100);
- // System.err.println("OptimizationDialog.location: " + this.getLocation());
+ int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
+ this.setSize(new Dimension(this.getWidth(), Math.min(this.getHeight(), screenHeight - 150)));
+ this.pack();
+
+ this.setLocation((parent.getWidth() - 1200)/2, 100);
}
private void startOptimization() {
diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
index d3182f3e4..ee9b99b6f 100644
--- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -209,7 +209,7 @@ public class BasicFrame extends JFrame {
//// Rocket design
tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designTab());
//// Flight configurations
- tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, new FlightConfigurationPanel(document));
+ tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, new FlightConfigurationPanel(this, document));
//// Flight simulations
tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel);
@@ -1690,10 +1690,14 @@ public class BasicFrame extends JFrame {
}
}
+ public void setSelectedComponent(RocketComponent component) {
+ this.selectionModel.setSelectedComponent(component);
+ }
+
public void stateChanged(ChangeEvent e) {
JTabbedPane tabSource = (JTabbedPane) e.getSource();
- String tab = tabSource.getTitleAt(tabSource.getSelectedIndex());
- if (tab.equals(trans.get("BasicFrame.tab.Flightsim"))) {
+ int tab = tabSource.getSelectedIndex();
+ if (tab == SIMULATION_TAB) {
simulationPanel.activating();
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
index 7657bba57..74c2b30e8 100644
--- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
@@ -814,14 +814,11 @@ public class SimulationPanel extends JPanel {
case CANT_RUN:
tip += trans.get("simpanel.ttip.noData")+"
";
break;
+ case LOADED:
case UPTODATE:
tip += trans.get("simpanel.ttip.uptodate") + "
";
break;
- case LOADED:
- tip += trans.get("simpanel.ttip.loaded") + "
";
- break;
-
case OUTDATED:
tip += trans.get("simpanel.ttip.outdated") + "
";
break;
diff --git a/swing/src/net/sf/openrocket/gui/main/Splash.java b/swing/src/net/sf/openrocket/gui/main/Splash.java
index c3ef0bf64..a985e5beb 100644
--- a/swing/src/net/sf/openrocket/gui/main/Splash.java
+++ b/swing/src/net/sf/openrocket/gui/main/Splash.java
@@ -25,8 +25,8 @@ public class Splash {
// The right edge of the text base line for the version string
private static final int VERSION_POSITION_X = 617;
- private static final int VERSION_POSITION_Y = 138;
- private static final Font VERSION_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 9);
+ private static final int VERSION_POSITION_Y = 150;
+ private static final Font VERSION_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 14);
private static final Color VERSION_COLOR = Color.WHITE;
diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java
index ccf6ed8c4..b8a602869 100644
--- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java
+++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java
@@ -138,7 +138,7 @@ public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
@Override
public void componentChanged(ComponentChangeEvent e) {
- if (e.isTreeChange() || e.isUndoChange() || e.isMassChange()) {
+ if (e.isTreeChange() || e.isUndoChange()) {
// Tree must be fully updated also in case of an undo change
fireTreeStructureChanged(e.getSource());
if (e.isTreeChange() && e.isUndoChange()) {
diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
index a0a0f4fc4..a937c56dd 100644
--- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
@@ -132,7 +132,7 @@ public abstract class FlightConfigurablePanel
return configurationTable;
}
+ @Override
+ protected void installTableListener() {
+ super.installTableListener();
+
+ table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ updateComponentSelection(e);
+ }
+ });
+
+ table.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ updateComponentSelection(new ListSelectionEvent(this, 0, 0, false));
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+
+ }
+ });
+ }
+
+ public void updateComponentSelection(ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ MotorMount mount = getSelectedComponent();
+ if (mount instanceof RocketComponent) {
+ flightConfigurationPanel.setSelectedComponent((RocketComponent) mount);
+ }
+ }
+
protected void updateButtonState() {
if( configurationTableModel.getColumnCount() > 1 ) {
showContent();
diff --git a/swing/src/net/sf/openrocket/gui/print/DesignReport.java b/swing/src/net/sf/openrocket/gui/print/DesignReport.java
index 5f09daad5..cb281336e 100644
--- a/swing/src/net/sf/openrocket/gui/print/DesignReport.java
+++ b/swing/src/net/sf/openrocket/gui/print/DesignReport.java
@@ -616,13 +616,13 @@ public class DesignReport {
log.warn("Simulation " + simulation.getId() + " has no motors, skipping");
// Continue so we don't simulate
continue;
+ case LOADED:
case UPTODATE:
log.trace("Simulation " + simulation.getId() + "is up to date, not running simulation");
simulate = false;
break;
case NOT_SIMULATED:
case OUTDATED:
- case LOADED:
log.trace("Running simulation for " + simulation.getId());
simulate = true;
break;
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java
index 7dce2f69b..d6e86640d 100644
--- a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java
@@ -3,6 +3,7 @@ package net.sf.openrocket.gui.rocketfigure;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.util.ArrayList;
+import java.util.Arrays;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
@@ -89,37 +90,72 @@ public class FinSetShapes extends RocketComponentShape {
double thickness = finset.getThickness();
double height = finset.getSpan();
+ double tabHeight = finset.getTabHeight();
// Generate base coordinates for a single fin
- Coordinate c[] = new Coordinate[4];
+ Coordinate[] c = new Coordinate[4];
c[0]=new Coordinate(0, 0,-thickness/2);
c[1]=new Coordinate(0, 0,thickness/2);
c[2]=new Coordinate(0,height,thickness/2);
c[3]=new Coordinate(0,height,-thickness/2);
+ // Generate base coordinates for a single fin tab
+ Coordinate[] cTab = new Coordinate[4];
+ cTab[0]=new Coordinate(0, 0,-thickness/2);
+ cTab[1]=new Coordinate(0, 0,thickness/2);
+ cTab[2]=new Coordinate(0, -tabHeight,thickness/2);
+ cTab[3]=new Coordinate(0, -tabHeight,-thickness/2);
+
// Apply base rotation
c = transformation.transform(c);
+ cTab = transformation.transform(cTab);
// Make polygon
+ Path2D.Double p = createUncantedPolygon(c);
+
+ if (tabHeight != 0 && finset.getTabLength() != 0) {
+ Path2D.Double pTab = createUncantedPolygon(cTab);
+ return new Shape[]{p, pTab};
+ }
+ else {
+ return new Shape[]{p};
+ }
+ }
+
+ private static Path2D.Double createUncantedPolygon(Coordinate[] c) {
Coordinate a;
Path2D.Double p = new Path2D.Double();
-
- a = c[0];
+
+ a = c[0];
p.moveTo(a.z, a.y);
a = c[1];
- p.lineTo(a.z, a.y);
+ p.lineTo(a.z, a.y);
a = c[2];
- p.lineTo(a.z, a.y);
+ p.lineTo(a.z, a.y);
a = c[3];
p.lineTo(a.z, a.y);
p.closePath();
-
- return new Shape[]{p};
+ return p;
}
-
- private static Shape[] cantedShapesBack(FinSet finset,
- Transformation transformation) {
+ private static Shape[] cantedShapesBack(FinSet finset,
+ Transformation transformation) {
+ if (finset.getTabHeight() == 0 || finset.getTabLength() == 0) {
+ return cantedShapesBackFins(finset, transformation);
+ }
+
+ Shape[] toReturn;
+ Shape[] shapesFin = cantedShapesBackFins(finset, transformation);
+ Shape[] shapesTab = cantedShapesBackTabs(finset, transformation);
+
+ toReturn = Arrays.copyOf(shapesFin, shapesFin.length + shapesTab.length);
+ System.arraycopy(shapesTab, 0, toReturn, shapesFin.length, shapesTab.length);
+
+ return toReturn;
+ }
+
+ private static Shape[] cantedShapesBackFins(FinSet finset,
+ Transformation transformation) {
double thickness = finset.getThickness();
Coordinate[] sidePoints;
@@ -168,6 +204,57 @@ public class FinSetShapes extends RocketComponentShape {
return s;
}
+
+ private static Shape[] cantedShapesBackTabs(FinSet finset,
+ Transformation transformation) {
+ double thickness = finset.getThickness();
+
+ Coordinate[] sidePoints;
+ Coordinate[] backPoints;
+ int minIndex;
+
+ Coordinate[] points = finset.getTabPoints();
+
+ // this loop finds the index @ min-y, as visible from the back
+ for (minIndex = points.length-1; minIndex > 0; minIndex--) {
+ if (points[minIndex-1].y > points[minIndex].y)
+ break;
+ }
+
+ Transformation cantTransform = finset.getCantRotation();
+ final Transformation compositeTransform = transformation.applyTransformation(cantTransform);
+
+ sidePoints = new Coordinate[points.length];
+ backPoints = new Coordinate[2*(points.length-minIndex)];
+ double sign = Math.copySign(1.0, finset.getCantAngle());
+
+ // Calculate points for the visible side panel
+ for (int i=0; i < points.length; i++) {
+ sidePoints[i] = points[i].add(0,0,sign*thickness/2);
+ }
+
+ // Calculate points for the back portion
+ int i=0;
+ for (int j=points.length-1; j >= minIndex; j--, i++) {
+ backPoints[i] = points[j].add(0,0,sign*thickness/2);
+ }
+ for (int j=minIndex; j <= points.length-1; j++, i++) {
+ backPoints[i] = points[j].add(0,0,-sign*thickness/2);
+ }
+
+ // Generate shapes
+ Shape[] s;
+ if (thickness > 0.0005) {
+ s = new Shape[2];
+ s[0] = makePolygonBack(sidePoints,compositeTransform);
+ s[1] = makePolygonBack(backPoints,compositeTransform);
+ } else {
+ s = new Shape[1];
+ s[0] = makePolygonBack(sidePoints,compositeTransform);
+ }
+
+ return s;
+ }
private static Shape makePolygonBack(Coordinate[] array, final Transformation t) {
Path2D.Float p;
diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java
index 48f1ccf0e..76ed87fdd 100644
--- a/swing/src/net/sf/openrocket/gui/util/Icons.java
+++ b/swing/src/net/sf/openrocket/gui/util/Icons.java
@@ -33,7 +33,7 @@ public class Icons {
map.put(Simulation.Status.NOT_SIMULATED, loadImageIcon("pix/spheres/gray-16x16.png", "Not simulated"));
map.put(Simulation.Status.CANT_RUN, loadImageIcon("pix/spheres/yellow-16x16.png", "Can't run, no motors assigned."));
map.put(Simulation.Status.UPTODATE, loadImageIcon("pix/spheres/green-16x16.png", "Up to date"));
- map.put(Simulation.Status.LOADED, loadImageIcon("pix/spheres/yellow-16x16.png", "Loaded from file"));
+ map.put(Simulation.Status.LOADED, loadImageIcon("pix/spheres/green-16x16.png", "Up to date"));
map.put(Simulation.Status.OUTDATED, loadImageIcon("pix/spheres/red-16x16.png", "Out-of-date"));
map.put(Simulation.Status.EXTERNAL, loadImageIcon("pix/spheres/blue-16x16.png", "Imported data"));
SIMULATION_STATUS_ICON_MAP = Collections.unmodifiableMap(map);