Merge branch 'upstream' into debian
[debian/openrocket] / core / src / net / sf / openrocket / startup / Startup2.java
1 package net.sf.openrocket.startup;
2
3 import java.awt.GraphicsEnvironment;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.io.File;
7 import java.util.List;
8 import java.util.concurrent.atomic.AtomicInteger;
9
10 import javax.swing.SwingUtilities;
11 import javax.swing.Timer;
12 import javax.swing.ToolTipManager;
13
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.Splash;
25 import net.sf.openrocket.gui.main.SwingExceptionHandler;
26 import net.sf.openrocket.gui.util.GUIUtil;
27 import net.sf.openrocket.gui.util.SimpleFileFilter;
28 import net.sf.openrocket.gui.util.SwingPreferences;
29 import net.sf.openrocket.logging.LogHelper;
30 import net.sf.openrocket.motor.Motor;
31 import net.sf.openrocket.motor.ThrustCurveMotor;
32 import net.sf.openrocket.util.BuildProperties;
33
34 /**
35  * The second class in the OpenRocket startup sequence.  This class can assume the
36  * Application class to be properly set up, and can use any classes safely.
37  * 
38  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
39  */
40 public class Startup2 {
41         private static final LogHelper log = Application.getLogger();
42         
43
44         private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
45         
46         /** Block motor loading for this many milliseconds */
47         private static AtomicInteger blockLoading = new AtomicInteger(Integer.MAX_VALUE);
48         
49         
50
51         /**
52          * Run when starting up OpenRocket after Application has been set up.
53          * 
54          * @param args  command line arguments
55          */
56         static void runMain(final String[] args) throws Exception {
57                 
58                 log.info("Starting up OpenRocket version " + BuildProperties.getVersion());
59                 
60                 // Check that we're not running headless
61                 log.info("Checking for graphics head");
62                 checkHead();
63                 
64                 // Check that we're running a good version of a JRE
65                 log.info("Checking JRE compatibility");
66                 VersionHelper.checkVersion();
67                 VersionHelper.checkOpenJDK();
68                 
69                 // Run the actual startup method in the EDT since it can use progress dialogs etc.
70                 log.info("Moving startup to EDT");
71                 SwingUtilities.invokeAndWait(new Runnable() {
72                         @Override
73                         public void run() {
74                                 runInEDT(args);
75                         }
76                 });
77                 
78                 log.info("Startup complete");
79         }
80         
81         
82         /**
83          * Run in the EDT when starting up OpenRocket.
84          * 
85          * @param args  command line arguments
86          */
87         private static void runInEDT(String[] args) {
88                 
89                 // Initialize the splash screen with version info
90                 log.info("Initializing the splash screen");
91                 Splash.init();
92                 
93                 // Setup the uncaught exception handler
94                 log.info("Registering exception handler");
95                 SwingExceptionHandler exceptionHandler = new SwingExceptionHandler();
96                 Application.setExceptionHandler(exceptionHandler);
97                 exceptionHandler.registerExceptionHandler();
98                 
99                 // Start update info fetching
100                 final UpdateInfoRetriever updateInfo;
101                 if ( Application.getPreferences().getCheckUpdates()) {
102                         log.info("Starting update check");
103                         updateInfo = new UpdateInfoRetriever();
104                         updateInfo.start();
105                 } else {
106                         log.info("Update check disabled");
107                         updateInfo = null;
108                 }
109                 
110                 // Set the best available look-and-feel
111                 log.info("Setting best LAF");
112                 GUIUtil.setBestLAF();
113                 
114                 // Set tooltip delay time.  Tooltips are used in MotorChooserDialog extensively.
115                 ToolTipManager.sharedInstance().setDismissDelay(30000);
116                 
117                 // Load defaults
118                 ((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
119                 
120                 // Load motors etc.
121                 log.info("Loading databases");
122                 loadMotor();
123                 Databases.fakeMethod();
124                 
125                 // Starting action (load files or open new document)
126                 log.info("Opening main application window");
127                 if (!handleCommandLine(args)) {
128                         BasicFrame.newAction();
129                 }
130                 
131                 // Check whether update info has been fetched or whether it needs more time
132                 log.info("Checking update status");
133                 checkUpdateStatus(updateInfo);
134                 
135                 // Block motor loading for 1.5 seconds to allow window painting to be faster
136                 blockLoading.set(1500);
137         }
138         
139         
140         /**
141          * Check that the JRE is not running headless.
142          */
143         private static void checkHead() {
144                 
145                 if (GraphicsEnvironment.isHeadless()) {
146                         log.error("Application is headless.");
147                         System.err.println();
148                         System.err.println("OpenRocket cannot currently be run without the graphical " +
149                                         "user interface.");
150                         System.err.println();
151                         System.exit(1);
152                 }
153                 
154         }
155         
156         
157         private static void loadMotor() {
158                 
159                 log.info("Starting motor loading from " + THRUSTCURVE_DIRECTORY + " in background thread.");
160                 ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
161                         
162                         @Override
163                         protected void loadMotors() {
164                                 
165                                 // Block loading until timeout occurs or database is taken into use
166                                 log.info("Blocking motor loading while starting up");
167                                 while (!inUse && blockLoading.addAndGet(-100) > 0) {
168                                         try {
169                                                 Thread.sleep(100);
170                                         } catch (InterruptedException e) {
171                                         }
172                                 }
173                                 log.info("Blocking ended, inUse=" + inUse + " blockLoading=" + blockLoading.get());
174                                 
175                                 // Start loading
176                                 log.info("Loading motors from " + THRUSTCURVE_DIRECTORY);
177                                 long t0 = System.currentTimeMillis();
178                                 int fileCount;
179                                 int thrustCurveCount;
180                                 
181                                 // Load the packaged thrust curves
182                                 List<Motor> list;
183                                 FileIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
184                                                                 new SimpleFileFilter("", false, "eng", "rse"));
185                                 if (iterator == null) {
186                                         throw new IllegalStateException("Thrust curve directory " + THRUSTCURVE_DIRECTORY +
187                                                         "not found, distribution built wrong");
188                                 }
189                                 list = MotorLoaderHelper.load(iterator);
190                                 for (Motor m : list) {
191                                         this.addMotor((ThrustCurveMotor) m);
192                                 }
193                                 fileCount = iterator.getFileCount();
194                                 
195                                 thrustCurveCount = list.size();
196                                 
197                                 // Load the user-defined thrust curves
198                                 for (File file : ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles()) {
199                                         log.info("Loading motors from " + file);
200                                         list = MotorLoaderHelper.load(file);
201                                         for (Motor m : list) {
202                                                 this.addMotor((ThrustCurveMotor) m);
203                                         }
204                                         fileCount++;
205                                         thrustCurveCount += list.size();
206                                 }
207                                 
208                                 long t1 = System.currentTimeMillis();
209                                 
210                                 // Count statistics
211                                 int distinctMotorCount = 0;
212                                 int distinctThrustCurveCount = 0;
213                                 distinctMotorCount = motorSets.size();
214                                 for (ThrustCurveMotorSet set : motorSets) {
215                                         distinctThrustCurveCount += set.getMotorCount();
216                                 }
217                                 log.info("Motor loading done, took " + (t1 - t0) + " ms to load "
218                                                 + fileCount + " files/directories containing "
219                                                 + thrustCurveCount + " thrust curves which contained "
220                                                 + distinctMotorCount + " distinct motors with "
221                                                 + distinctThrustCurveCount + " distinct thrust curves.");
222                         }
223                         
224                 };
225                 db.startLoading();
226                 Application.setMotorSetDatabase(db);
227         }
228         
229         private static void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
230                 if (updateInfo == null)
231                         return;
232                 
233                 int delay = 1000;
234                 if (!updateInfo.isRunning())
235                         delay = 100;
236                 
237                 final Timer timer = new Timer(delay, null);
238                 
239                 ActionListener listener = new ActionListener() {
240                         private int count = 5;
241                         
242                         @Override
243                         public void actionPerformed(ActionEvent e) {
244                                 if (!updateInfo.isRunning()) {
245                                         timer.stop();
246                                         
247                                         String current = BuildProperties.getVersion();
248                                         String last = Application.getPreferences().getString(Preferences.LAST_UPDATE, "");
249                                         
250                                         UpdateInfo info = updateInfo.getUpdateInfo();
251                                         if (info != null && info.getLatestVersion() != null &&
252                                                         !current.equals(info.getLatestVersion()) &&
253                                                         !last.equals(info.getLatestVersion())) {
254                                                 
255                                                 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
256                                                 infoDialog.setVisible(true);
257                                                 if (infoDialog.isReminderSelected()) {
258                                                         Application.getPreferences().putString(Preferences.LAST_UPDATE, "");
259                                                 } else {
260                                                         Application.getPreferences().putString(Preferences.LAST_UPDATE, info.getLatestVersion());
261                                                 }
262                                         }
263                                 }
264                                 count--;
265                                 if (count <= 0)
266                                         timer.stop();
267                         }
268                 };
269                 timer.addActionListener(listener);
270                 timer.start();
271         }
272         
273         /**
274          * Handles arguments passed from the command line.  This may be used either
275          * when starting the first instance of OpenRocket or later when OpenRocket is
276          * executed again while running.
277          * 
278          * @param args  the command-line arguments.
279          * @return              whether a new frame was opened or similar user desired action was
280          *                              performed as a result.
281          */
282         private static boolean handleCommandLine(String[] args) {
283                 
284                 // Check command-line for files
285                 boolean opened = false;
286                 for (String file : args) {
287                         if (BasicFrame.open(new File(file), null)) {
288                                 opened = true;
289                         }
290                 }
291                 return opened;
292         }
293         
294 }