package net.sf.openrocket.startup;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.PrintStream;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import net.miginfocom.layout.LayoutUtil;
import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.arch.SystemInfo.Platform;
import net.sf.openrocket.communication.UpdateInfo;
import net.sf.openrocket.communication.UpdateInfoRetriever;
import net.sf.openrocket.database.Databases;
import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.main.MRUDesignFile;
import net.sf.openrocket.gui.main.Splash;
import net.sf.openrocket.gui.main.SwingExceptionHandler;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.logging.LoggingSystemSetup;
import net.sf.openrocket.logging.PrintStreamToSLF4J;
import net.sf.openrocket.plugin.PluginModule;
import net.sf.openrocket.util.BuildProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* Start the OpenRocket swing application.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class SwingStartup {
private final static Logger log = LoggerFactory.getLogger(SwingStartup.class);
/**
* OpenRocket startup main method.
*/
public static void main(final String[] args) throws Exception {
// Check for "openrocket.debug" property before anything else
checkDebugStatus();
if (System.getProperty("openrocket.debug.layout") != null) {
LayoutUtil.setGlobalDebugMillis(100);
}
// Initialize logging first so we can use it
initializeLogging();
log.info("Starting up OpenRocket version {}", BuildProperties.getVersion());
// Check that we're not running headless
log.info("Checking for graphics head");
checkHead();
// If running on a MAC set up OSX UI Elements.
if (SystemInfo.getPlatform() == Platform.MAC_OS) {
OSXSetup.setupOSX();
}
final SwingStartup runner = new SwingStartup();
// Run the actual startup method in the EDT since it can use progress dialogs etc.
log.info("Moving startup to EDT");
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
runner.runInEDT(args);
}
});
log.info("Startup complete");
}
/**
* Set proper system properties if openrocket.debug is defined.
*/
private static void checkDebugStatus() {
if (System.getProperty("openrocket.debug") != null) {
setPropertyIfNotSet("openrocket.debug.menu", "true");
setPropertyIfNotSet("openrocket.debug.mutexlocation", "true");
setPropertyIfNotSet("openrocket.debug.motordigest", "true");
setPropertyIfNotSet("jogl.debug", "all");
}
}
private static void setPropertyIfNotSet(String key, String value) {
if (System.getProperty(key) == null) {
System.setProperty(key, value);
}
}
/**
* Initializes the logging system.
*/
public static void initializeLogging() {
LoggingSystemSetup.setupLoggingAppender();
if (System.getProperty("openrocket.debug") != null) {
LoggingSystemSetup.addConsoleAppender();
}
//Replace System.err with a PrintStream that logs lines to DEBUG, or VBOSE if they are indented.
//If debug info is not being output to the console then the data is both logged and written to
//stderr.
final PrintStream stdErr = System.err;
System.setErr(PrintStreamToSLF4J.getPrintStream("STDERR", stdErr));
}
/**
* Run in the EDT when starting up OpenRocket.
*
* @param args command line arguments
*/
private void runInEDT(String[] args) {
// Initialize the splash screen with version info
log.info("Initializing the splash screen");
Splash.init();
// Setup the uncaught exception handler
log.info("Registering exception handler");
SwingExceptionHandler exceptionHandler = new SwingExceptionHandler();
Application.setExceptionHandler(exceptionHandler);
exceptionHandler.registerExceptionHandler();
// Load motors etc.
log.info("Loading databases");
GuiModule guiModule = new GuiModule();
Module pluginModule = new PluginModule();
Injector injector = Guice.createInjector(guiModule, pluginModule);
Application.setInjector(injector);
guiModule.startLoader();
// Start update info fetching
final UpdateInfoRetriever updateInfo;
if (Application.getPreferences().getCheckUpdates()) {
log.info("Starting update check");
updateInfo = new UpdateInfoRetriever();
updateInfo.start();
} else {
log.info("Update check disabled");
updateInfo = null;
}
// Set the best available look-and-feel
log.info("Setting best LAF");
GUIUtil.setBestLAF();
// Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
ToolTipManager.sharedInstance().setDismissDelay(30000);
// Load defaults
((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
Databases.fakeMethod();
// Starting action (load files or open new document)
log.info("Opening main application window");
if (!handleCommandLine(args)) {
if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) {
BasicFrame.newAction();
} else {
String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile();
if (lastFile != null) {
if (!BasicFrame.open(new File(lastFile), null)) {
MRUDesignFile.getInstance().removeFile(lastFile);
BasicFrame.newAction();
}
else {
MRUDesignFile.getInstance().addFile(lastFile);
}
}
else {
BasicFrame.newAction();
}
}
}
// Check whether update info has been fetched or whether it needs more time
log.info("Checking update status");
checkUpdateStatus(updateInfo);
}
/**
* Check that the JRE is not running headless.
*/
private static void checkHead() {
if (GraphicsEnvironment.isHeadless()) {
log.error("Application is headless.");
System.err.println();
System.err.println("OpenRocket cannot currently be run without the graphical " +
"user interface.");
System.err.println();
System.exit(1);
}
}
private void checkUpdateStatus(final UpdateInfoRetriever updateInfo) {
if (updateInfo == null)
return;
int delay = 1000;
if (!updateInfo.isRunning())
delay = 100;
final Timer timer = new Timer(delay, null);
ActionListener listener = new ActionListener() {
private int count = 5;
@Override
public void actionPerformed(ActionEvent e) {
if (!updateInfo.isRunning()) {
timer.stop();
String current = BuildProperties.getVersion();
String last = Application.getPreferences().getString(Preferences.LAST_UPDATE, "");
UpdateInfo info = updateInfo.getUpdateInfo();
if (info != null && info.getLatestVersion() != null &&
!current.equals(info.getLatestVersion()) &&
!last.equals(info.getLatestVersion())) {
UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
infoDialog.setVisible(true);
if (infoDialog.isReminderSelected()) {
Application.getPreferences().putString(Preferences.LAST_UPDATE, "");
} else {
Application.getPreferences().putString(Preferences.LAST_UPDATE, info.getLatestVersion());
}
}
}
count--;
if (count <= 0)
timer.stop();
}
};
timer.addActionListener(listener);
timer.start();
}
/**
* Handles arguments passed from the command line. This may be used either
* when starting the first instance of OpenRocket or later when OpenRocket is
* executed again while running.
*
* @param args the command-line arguments.
* @return whether a new frame was opened or similar user desired action was
* performed as a result.
*/
private boolean handleCommandLine(String[] args) {
// Check command-line for files
boolean opened = false;
for (String file : args) {
if (BasicFrame.open(new File(file), null)) {
opened = true;
}
}
return opened;
}
}