1 package net.sf.openrocket.startup;
3 import java.awt.GraphicsEnvironment;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
8 import java.util.concurrent.atomic.AtomicInteger;
10 import javax.swing.SwingUtilities;
11 import javax.swing.Timer;
12 import javax.swing.ToolTipManager;
14 import net.sf.openrocket.communication.UpdateInfo;
15 import net.sf.openrocket.communication.UpdateInfoRetriever;
16 import net.sf.openrocket.database.Databases;
17 import net.sf.openrocket.database.ThrustCurveMotorSet;
18 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
19 import net.sf.openrocket.file.iterator.DirectoryIterator;
20 import net.sf.openrocket.file.iterator.FileIterator;
21 import net.sf.openrocket.file.motor.MotorLoaderHelper;
22 import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
23 import net.sf.openrocket.gui.main.BasicFrame;
24 import net.sf.openrocket.gui.main.ExceptionHandler;
25 import net.sf.openrocket.gui.main.Splash;
26 import net.sf.openrocket.logging.LogHelper;
27 import net.sf.openrocket.motor.Motor;
28 import net.sf.openrocket.motor.ThrustCurveMotor;
29 import net.sf.openrocket.util.GUIUtil;
30 import net.sf.openrocket.util.Prefs;
31 import net.sf.openrocket.util.SimpleFileFilter;
34 * The second class in the OpenRocket startup sequence. This class can assume the
35 * Application class to be properly set up, and can use any classes safely.
37 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
39 public class Startup2 {
40 private static final LogHelper log = Application.getLogger();
43 private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
45 /** Block motor loading for this many milliseconds */
46 private static AtomicInteger blockLoading = new AtomicInteger(Integer.MAX_VALUE);
51 * Run when starting up OpenRocket after Application has been set up.
53 * @param args command line arguments
55 static void runMain(final String[] args) throws Exception {
57 log.info("Starting up OpenRocket version " + Prefs.getVersion());
59 // Check that we're not running headless
60 log.info("Checking for graphics head");
63 // Check that we're running a good version of a JRE
64 log.info("Checking JRE compatibility");
65 VersionHelper.checkVersion();
66 VersionHelper.checkOpenJDK();
68 // Run the actual startup method in the EDT since it can use progress dialogs etc.
69 log.info("Moving startup to EDT");
70 SwingUtilities.invokeAndWait(new Runnable() {
77 log.info("Startup complete");
82 * Run in the EDT when starting up OpenRocket.
84 * @param args command line arguments
86 private static void runInEDT(String[] args) {
88 // Initialize the splash screen with version info
89 log.info("Initializing the splash screen");
92 // Setup the uncaught exception handler
93 log.info("Registering exception handler");
94 ExceptionHandler.registerExceptionHandler();
96 // Start update info fetching
97 final UpdateInfoRetriever updateInfo;
98 if (Prefs.getCheckUpdates()) {
99 log.info("Starting update check");
100 updateInfo = new UpdateInfoRetriever();
103 log.info("Update check disabled");
107 // Set the best available look-and-feel
108 log.info("Setting best LAF");
109 GUIUtil.setBestLAF();
111 // Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
112 ToolTipManager.sharedInstance().setDismissDelay(30000);
115 Prefs.loadDefaultUnits();
118 log.info("Loading databases");
120 Databases.fakeMethod();
122 // Starting action (load files or open new document)
123 log.info("Opening main application window");
124 if (!handleCommandLine(args)) {
125 BasicFrame.newAction();
128 // Check whether update info has been fetched or whether it needs more time
129 log.info("Checking update status");
130 checkUpdateStatus(updateInfo);
132 // Block motor loading for 1.5 seconds to allow window painting to be faster
133 blockLoading.set(1500);
138 * Check that the JRE is not running headless.
140 private static void checkHead() {
142 if (GraphicsEnvironment.isHeadless()) {
143 log.error("Application is headless.");
144 System.err.println();
145 System.err.println("OpenRocket cannot currently be run without the graphical " +
147 System.err.println();
154 private static void loadMotor() {
156 log.info("Starting motor loading from " + THRUSTCURVE_DIRECTORY + " in background thread.");
157 ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
160 protected void loadMotors() {
162 // Block loading until timeout occurs or database is taken into use
163 log.info("Blocking motor loading while starting up");
164 while (!inUse && blockLoading.addAndGet(-100) > 0) {
167 } catch (InterruptedException e) {
170 log.info("Blocking ended, inUse=" + inUse + " blockLoading=" + blockLoading.get());
173 log.info("Loading motors from " + THRUSTCURVE_DIRECTORY);
174 long t0 = System.currentTimeMillis();
176 int thrustCurveCount;
178 // Load the packaged thrust curves
180 FileIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
181 new SimpleFileFilter("", false, "eng", "rse"));
182 if (iterator == null) {
183 throw new IllegalStateException("Thrust curve directory " + THRUSTCURVE_DIRECTORY +
184 "not found, distribution built wrong");
186 list = MotorLoaderHelper.load(iterator);
187 for (Motor m : list) {
188 this.addMotor((ThrustCurveMotor) m);
190 fileCount = iterator.getFileCount();
192 thrustCurveCount = list.size();
194 // Load the user-defined thrust curves
195 for (File file : Prefs.getUserThrustCurveFiles()) {
196 log.info("Loading motors from " + file);
197 list = MotorLoaderHelper.load(file);
198 for (Motor m : list) {
199 this.addMotor((ThrustCurveMotor) m);
202 thrustCurveCount += list.size();
205 long t1 = System.currentTimeMillis();
208 int distinctMotorCount = 0;
209 int distinctThrustCurveCount = 0;
210 distinctMotorCount = motorSets.size();
211 for (ThrustCurveMotorSet set : motorSets) {
212 distinctThrustCurveCount += set.getMotorCount();
214 log.info("Motor loading done, took " + (t1 - t0) + " ms to load "
215 + fileCount + " files/directories containing "
216 + thrustCurveCount + " thrust curves which contained "
217 + distinctMotorCount + " distinct motors with "
218 + distinctThrustCurveCount + " distinct thrust curves.");
223 Application.setMotorSetDatabase(db);
226 private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
227 if (updateInfo == null)
231 if (!updateInfo.isRunning())
234 final Timer timer = new Timer(delay, null);
236 ActionListener listener = new ActionListener() {
237 private int count = 5;
240 public void actionPerformed(ActionEvent e) {
241 if (!updateInfo.isRunning()) {
244 String current = Prefs.getVersion();
245 String last = Prefs.getString(Prefs.LAST_UPDATE, "");
247 UpdateInfo info = updateInfo.getUpdateInfo();
248 if (info != null && info.getLatestVersion() != null &&
249 !current.equals(info.getLatestVersion()) &&
250 !last.equals(info.getLatestVersion())) {
252 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
253 infoDialog.setVisible(true);
254 if (infoDialog.isReminderSelected()) {
255 Prefs.putString(Prefs.LAST_UPDATE, "");
257 Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
266 timer.addActionListener(listener);
271 * Handles arguments passed from the command line. This may be used either
272 * when starting the first instance of OpenRocket or later when OpenRocket is
273 * executed again while running.
275 * @param args the command-line arguments.
276 * @return whether a new frame was opened or similar user desired action was
277 * performed as a result.
279 private static boolean handleCommandLine(String[] args) {
281 // Check command-line for files
282 boolean opened = false;
283 for (String file : args) {
284 if (BasicFrame.open(new File(file), null)) {