+2009-06-08 Sampo Niskanen
+
+ * Fixed loading of icons from JAR
+
+2009-06-06 Sampo Niskanen
+
+ * Cut/Copy/Paste of simulations
+ * Improved build scripts
+
2009-05-28 Sampo Niskanen
* Added startup check for Java 1.6 and OpenJDK
--- /dev/null
+
+# The OpenRocket build version
+build.version=0.9.1pre
+
+# The source of the package. When building a package for a specific
+# distribution (Debian, Fedora etc.), this should be changed appropriately!
+build.source=default
+
<project name="OpenRocket" basedir=".">
+ <property file="build.properties" />
+
<property name="src.dir" value="src"/> <!-- Source directory -->
<property name="build.dir" value="build"/> <!-- Build directory -->
<!-- Distribution directory, from which stuff is jar'ed -->
<property name="dist.dir" value="${build.dir}/dist"/>
+ <property name="test.dir" value="${build.dir}/test"/>
<property name="classes.dir" value="${dist.dir}"/> <!-- Directory for classes -->
<property name="jar.dir" value="${build.dir}/jar"/> <!-- Directory for built jar's -->
<property name="lib.dir" value="lib"/> <!-- Library source directory -->
+ <property name="jar.file" value="${jar.dir}/${ant.project.name}.jar"/>
+ <property name="dist.bin" value="${jar.dir}/${ant.project.name}-${build.version}.jar"/>
+ <property name="dist.src" value="${jar.dir}/${ant.project.name}-src-${build.version}.zip"/>
<!-- The main class of the application -->
<property name="main-class" value="net.sf.openrocket.startup.Startup"/>
+ <property name="main-dir" value="net/sf/openrocket/startup"/>
<!-- Classpath definition -->
</path>
+
+
<!-- CLEAN -->
<target name="clean">
<delete dir="${build.dir}"/>
</target>
+
<!-- BUILD -->
<target name="build">
<mkdir dir="${classes.dir}"/>
- <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
+ <echo>Compiling main classes</echo>
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" excludes="${main-dir}/*" classpathref="classpath"/>
+ <echo>Compiling startup classes</echo>
+ <javac srcdir="${src.dir}/${main-dir}" destdir="${classes.dir}" source="1.4" classpathref="classpath"/>
</target>
+
<!-- JAR -->
<target name="jar" depends="build">
<copy todir="${dist.dir}/">
- <fileset dir="." includes="LICENSE.TXT" />
- <fileset dir="." includes="README.TXT" />
- <fileset dir="." includes="datafiles/**/* pix/**/*" />
+ <fileset dir="." includes="LICENSE.TXT README.TXT build.properties" />
+ <fileset dir="." includes="datafiles/ pix/" />
</copy>
<mkdir dir="${jar.dir}"/>
- <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${dist.dir}">
+ <jar destfile="${jar.file}" basedir="${dist.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
<attribute name="SplashScreen-Image" value="pix/splashscreen.png"/>
</jar>
</target>
- <!-- RUN -->
- <target name="run" depends="jar">
- <java fork="true" classname="${main-class}">
- <classpath>
- <path location="${jar.dir}/${ant.project.name}.jar"/>
- </classpath>
- </java>
+
+ <!-- DIST-SRC -->
+ <target name="dist-src" depends="clean">
+ <echo>
+ Building source distribution
+ </echo>
+ <mkdir dir="${jar.dir}"/>
+ <zip destfile="${dist.src}">
+ <!-- Base directory: -->
+ <fileset dir="." includes="*">
+ <type type="file"/>
+ </fileset>
+ <fileset dir="." includes="datafiles/ lib/ pix/ src/"/>
+ </zip>
+ </target>
+
+
+ <!-- DIST-SRC-TEST -->
+ <target name="dist-src-test" depends="dist-src">
+ <echo>
+ Testing source distribution
+ </echo>
+ <delete dir="${test.dir}"/>
+ <mkdir dir="${test.dir}"/>
+ <unzip dest="${test.dir}" src="${dist.src}"/>
+ <ant dir="${test.dir}" antfile="build.xml" target="jar"/>
+ <delete dir="${test.dir}"/>
+ <echo>
+ Test successful
+ </echo>
+ </target>
+
+
+ <!-- DIST-BIN -->
+ <target name="dist-bin" depends="clean,jar">
+ <move file="${jar.file}" tofile="${dist.bin}"/>
</target>
+
+ <!-- DIST -->
+ <target name="dist" depends="dist-bin,dist-src,dist-src-test">
+ <echo>Distribution ${build.version} (${build.source}) built into directory ${jar.dir}</echo>
+ </target>
+
</project>
\ No newline at end of file
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
later. The Sun JRE is recommended.</em></p>
<p class="download">
- <a href="https://sourceforge.net/project/downloading.php?group_id=260357&filename=OpenRocket-0.9.0.jar">Download OpenRocket 0.9.0</a></p>
+ <a href="https://sourceforge.net/project/downloading.php?group_id=260357&filename=OpenRocket-0.9.0.jar">Download OpenRocket 0.9.0</a></p>
<p>OpenRocket is still considered <strong>beta software</strong>.
If you encounter any problems, please
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
<ul>
<li>Search the bug repository to see if the bug has already been
reported. If it is, please add extra information to that bug
- report:<br/>
+ report:
<form action="https://sourceforge.net/search/index.php" method="get">
- <input type="hidden" name="group_id" value="260357" />
+ <p><input type="hidden" name="group_id" value="260357" />
<input type="hidden" name="type_of_search" value="artifact"/>
<!-- <input type="hidden" name="group_artifact_id" value="1127606" /> -->
<!-- <input type="hidden" name="artifact_group" value="Bug" /> -->
<input type="hidden" name="search_comments" value="1" />
<input type="text" name="all_words" value="" />
- <input type="submit" name="form_submit" value="Search" />
+ <input type="submit" name="form_submit" value="Search" /></p>
</form>
</li>
<li>Report the bug using the
- <a href="https://sourceforge.net/tracker/?func=add&group_id=260357&atid=1127606">bug
+ <a href="https://sourceforge.net/tracker/?func=add&group_id=260357&atid=1127606">bug
tracker</a>. Follow the instructions provided to fill in the
report.</li>
<ul>
<li>Check that the feature is not already in the
<a href="features.html#future">planned future features</a> or
- the <a href="https://sourceforge.net/tracker/?group_id=260357&atid=1127606&artgroup=899287">enhancement requests</a>.</li>
- <li>Send the request to the <a href="https://sourceforge.net/tracker/?func=add&group_id=260357&atid=1127606">bug tracker</a> as an
+ the <a href="https://sourceforge.net/tracker/?group_id=260357&atid=1127606&artgroup=899287">enhancement requests</a>.</li>
+ <li>Send the request to the <a href="https://sourceforge.net/tracker/?func=add&group_id=260357&atid=1127606">bug tracker</a> as an
enhancement request. Please send multiple enhancements as
individual items.</li>
</ul>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
</p>
</div>
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/openrocket/" : "http://apps.sourceforge.net/piwik/openrocket/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 1;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<noscript><p><img src="http://apps.sourceforge.net/piwik/openrocket/piwik.php?idsite=1" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+
</body>
</html>
import javax.swing.AbstractAction;
import javax.swing.Action;
+import net.sf.openrocket.document.events.DocumentChangeEvent;
+import net.sf.openrocket.document.events.DocumentChangeListener;
+import net.sf.openrocket.document.events.SimulationChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Configuration;
public static final int UNDO_MARGIN = 10;
+ public static final String SIMULATION_NAME_PREFIX = "Simulation ";
+
private final Rocket rocket;
private final Configuration configuration;
private final StorageOptions storageOptions = new StorageOptions();
+ private final List<DocumentChangeListener> listeners =
+ new ArrayList<DocumentChangeListener>();
+
/* These must be initialized after undo history is set up. */
private final UndoRedoAction undoAction;
private final UndoRedoAction redoAction;
redoAction = new UndoRedoAction(UndoRedoAction.REDO);
rocket.addComponentChangeListener(this);
-
-
}
}
public void addSimulation(Simulation simulation) {
simulations.add(simulation);
+ fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
}
public void addSimulation(Simulation simulation, int n) {
simulations.add(n, simulation);
+ fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
}
public void removeSimulation(Simulation simulation) {
simulations.remove(simulation);
+ fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
}
public Simulation removeSimulation(int n) {
- return simulations.remove(n);
+ Simulation simulation = simulations.remove(n);
+ fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
+ return simulation;
}
+ /**
+ * Return a unique name suitable for the next simulation. The name begins
+ * with {@link #SIMULATION_NAME_PREFIX} and has a unique number larger than any
+ * previous simulation.
+ *
+ * @return the new name.
+ */
+ public String getNextSimulationName() {
+ // Generate unique name for the simulation
+ int maxValue = 0;
+ for (Simulation s: simulations) {
+ String name = s.getName();
+ if (name.startsWith(SIMULATION_NAME_PREFIX)) {
+ try {
+ maxValue = Math.max(maxValue,
+ Integer.parseInt(name.substring(SIMULATION_NAME_PREFIX.length())));
+ } catch (NumberFormatException ignore) { }
+ }
+ }
+ return SIMULATION_NAME_PREFIX + (maxValue+1);
+ }
/**
+ /////// Listeners
+
+ public void addDocumentChangeListener(DocumentChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeDocumentChangeListener(DocumentChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ protected void fireDocumentChangeEvent(DocumentChangeEvent event) {
+ DocumentChangeListener[] array = listeners.toArray(new DocumentChangeListener[0]);
+ for (DocumentChangeListener l: array) {
+ l.documentChanged(event);
+ }
+ }
+
+
/**
import net.sf.openrocket.util.ChangeSource;
-public class Simulation implements ChangeSource {
+public class Simulation implements ChangeSource, Cloneable {
public static enum Status {
/** Up-to-date */
private Status status = Status.NOT_SIMULATED;
/** The conditions to use */
- private final SimulationConditions conditions;
+ private SimulationConditions conditions;
- private List<String> simulationListeners = new ArrayList<String>();
+ private ArrayList<String> simulationListeners = new ArrayList<String>();
private Class<? extends FlightSimulator> simulatorClass = RK4Simulator.class;
private Class<? extends AerodynamicCalculator> calculatorClass = BarrowmanCalculator.class;
/** Listeners for this object */
- private final List<ChangeListener> listeners = new ArrayList<ChangeListener>();
+ private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
/** The conditions actually used in the previous simulation, or null */
*
* @return a description of the motor configuration of the previous simulation, or
* <code>null</code>.
- * @see Rocket#getMotorConfigurationDescription(String)
+ * @see Rocket#getMotorConfigurationNameOrDescription(String)
*/
public String getSimulatedMotorDescription() {
return simulatedMotors;
}
+
+ /**
+ * Returns a copy of this simulation suitable for cut/copy/paste operations.
+ * This excludes any simulated data.
+ *
+ * @return a copy of this simulation and its conditions.
+ */
+ @SuppressWarnings("unchecked")
+ public Simulation copy() {
+ try {
+
+ Simulation copy = (Simulation)super.clone();
+
+ copy.status = Status.NOT_SIMULATED;
+ copy.conditions = this.conditions.clone();
+ copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
+ copy.listeners = new ArrayList<ChangeListener>();
+ copy.simulatedConditions = null;
+ copy.simulatedMotors = null;
+ copy.simulatedData = null;
+ copy.simulatedRocketID = -1;
+
+ return copy;
+
+
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Clone not supported, BUG", e);
+ }
+ }
+
+
+ /**
+ * Create a duplicate of this simulation with the specified rocket. The new
+ * simulation is in non-simulated state.
+ *
+ * @param newRocket the rocket for the new simulation.
+ * @return a new simulation with the same conditions and properties.
+ */
+ @SuppressWarnings("unchecked")
+ public Simulation duplicateSimulation(Rocket newRocket) {
+ Simulation copy = new Simulation(newRocket);
+
+ copy.name = this.name;
+ copy.conditions.copyFrom(this.conditions);
+ copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
+ copy.simulatorClass = this.simulatorClass;
+ copy.calculatorClass = this.calculatorClass;
+ return copy;
+ }
--- /dev/null
+package net.sf.openrocket.document.events;
+
+import javax.swing.event.ChangeEvent;
+
+public class DocumentChangeEvent extends ChangeEvent {
+
+ public DocumentChangeEvent(Object source) {
+ super(source);
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.document.events;
+
+public interface DocumentChangeListener {
+
+ public void documentChanged(DocumentChangeEvent event);
+
+}
--- /dev/null
+package net.sf.openrocket.document.events;
+
+
+public class SimulationChangeEvent extends DocumentChangeEvent {
+
+ public SimulationChangeEvent(Object source) {
+ super(source);
+ }
+
+}
@Override
public String toString() {
- return rocket.getMotorConfigurationDescription(id);
+ return rocket.getMotorConfigurationNameOrDescription(id);
}
}
columnList.add(new Column("Name") {
@Override
public Object getValueAt(int row) {
- return rocket.getMotorConfigurationDescription(ids[row]);
+ return rocket.getMotorConfigurationNameOrDescription(ids[row]);
}
});
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.RingComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.unit.UnitGroup;
//// Material
- panel.add(materialPanel(new JPanel(new MigLayout()), Material.Type.BULK),
- "cell 4 0, gapleft paragraph, aligny 0%, spany");
+ JPanel sub = materialPanel(new JPanel(new MigLayout()), Material.Type.BULK);
+
+ if (component instanceof EngineBlock) {
+ DescriptionArea desc = new DescriptionArea(6,-1);
+ desc.setText("<html>An engine block stops the motor from moving forwards in " +
+ "the motor mount tube.<br><br>In order to add a motor, create a body tube " +
+ "or inner tube and mark it as a motor mount in the <em>Motor</em> " +
+ "tab.");
+ sub.add(desc, "growx");
+ }
+ panel.add(sub,"cell 4 0, gapleft paragraph, aligny 0%, spany");
return panel;
}
sb.append('\n');
sb.append("---------- Included system information ----------\n");
sb.append("OpenRocket version: " + Prefs.getVersion() + "\n");
+ sb.append("OpenRocket source: " + Prefs.getBuildSource() + "\n");
sb.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n");
sb.append("System properties:\n");
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
};
+
+ public static final int COMPONENT_TAB = 0;
+ public static final int SIMULATION_TAB = 1;
+
/**
* List of currently open frames. When the list goes empty
private final OpenRocketDocument document;
private final Rocket rocket;
+ private JTabbedPane tabbedPane;
private RocketPanel rocketpanel;
private ComponentTree tree = null;
+
+ private final DocumentSelectionModel selectionModel;
private final TreeSelectionModel componentSelectionModel;
- // private final ListSelectionModel simulationSelectionModel; ...
+ private final ListSelectionModel simulationSelectionModel;
/** Actions available for rocket modifications */
private final RocketActions actions;
});
- // Create the selection model that will be used
+ // Create the component tree selection model that will be used
componentSelectionModel = new DefaultTreeSelectionModel();
componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
- actions = new RocketActions(document, componentSelectionModel, this);
+ // Obtain the simulation selection model that will be used
+ SimulationPanel simulationPanel = new SimulationPanel(document);
+ simulationSelectionModel = simulationPanel.getSimulationListSelectionModel();
+
+ // Combine into a DocumentSelectionModel
+ selectionModel = new DocumentSelectionModel(document);
+ selectionModel.attachComponentTreeSelectionModel(componentSelectionModel);
+ selectionModel.attachSimulationListSelectionModel(simulationSelectionModel);
+
+
+ actions = new RocketActions(document, selectionModel, this);
// The main vertical split pane
// The top tabbed pane
- JTabbedPane tabbed = new JTabbedPane();
- tabbed.addTab("Rocket design", null, designTab());
- tabbed.addTab("Flight simulations", null, simulationsTab());
+ tabbedPane = new JTabbedPane();
+ tabbedPane.addTab("Rocket design", null, designTab());
+ tabbedPane.addTab("Flight simulations", null, simulationPanel);
- vertical.setTopComponent(tabbed);
+ vertical.setTopComponent(tabbedPane);
}
- /**
- * Construct the "Flight simulations" tab.
- * @return
- */
- private JComponent simulationsTab() {
- return new SimulationPanel(document);
- }
-
-
/**
* Creates the menu for the window.
+ /**
+ * Select the tab on the main pane.
+ *
+ * @param tab one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}.
+ */
+ public void selectTab(int tab) {
+ tabbedPane.setSelectedIndex(tab);
+ }
+
+
private void openAction() {
JFileChooser chooser = new JFileChooser();
--- /dev/null
+package net.sf.openrocket.gui.main;
+
+public interface ClipboardListener {
+
+ public void clipboardChanged();
+
+}
public Simulation[] getSelectedSimulations() {
return Arrays.copyOf(simulationSelection, simulationSelection.length);
}
+
+ public void setSelectedSimulations(Simulation[] sims) {
+ simulationSelection = sims;
+ clearComponentSelection();
+
+ simulationListSelectionModel.clearSelection();
+ for (Simulation s: sims) {
+ int index = document.getSimulationIndex(s);
+ if (index >= 0) {
+ simulationListSelectionModel.addSelectionInterval(index, index);
+ }
+ }
+ }
/**
* Return the currently selected rocket component. Returns <code>null</code>
public RocketComponent getSelectedComponent() {
return componentSelection;
}
+
+ public void setSelectedComponent(RocketComponent component) {
+ componentSelection = component;
+ clearSimulationSelection();
+
+ TreePath path = ComponentTreeModel.makeTreePath(component);
+ componentTreeSelectionModel.setSelectionPath(path);
+ }
package net.sf.openrocket.gui.main;
+import java.util.ArrayList;
+import java.util.List;
+
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.rocketcomponent.RocketComponent;
-public class OpenRocketClipboard {
+public final class OpenRocketClipboard {
- private static Object clipboard = null;
+ private static RocketComponent clipboardComponent = null;
+ private static Simulation[] clipboardSimulations = null;
+
+ private static List<ClipboardListener> listeners = new ArrayList<ClipboardListener>();
private OpenRocketClipboard() {
// Disallow instantiation
/**
* Return the <code>RocketComponent</code> contained in the clipboard, or
- * <code>null</code>.
+ * <code>null</code>. The component is returned verbatim, and must be copied
+ * before attaching to any rocket design!
*
* @return the rocket component contained in the clipboard, or <code>null</code>
* if the clipboard does not currently contain a rocket component.
*/
- public static RocketComponent getComponent() {
- if (clipboard instanceof RocketComponent) {
- return (RocketComponent) clipboard;
- }
- return null;
+ public static RocketComponent getClipboardComponent() {
+ return clipboardComponent;
+ }
+
+
+ public static void setClipboard(RocketComponent component) {
+ clipboardComponent = component;
+ clipboardSimulations = null;
+ fireClipboardChanged();
+ }
+
+
+ public static Simulation[] getClipboardSimulations() {
+ if (clipboardSimulations == null || clipboardSimulations.length == 0)
+ return null;
+ return clipboardSimulations.clone();
+ }
+
+ public static void setClipboard(Simulation[] simulations) {
+ clipboardSimulations = simulations.clone();
+ clipboardComponent = null;
+ fireClipboardChanged();
}
- public static Simulation[] getSimulations() {
- return null; // TODO
+
+ public static void addClipboardListener(ClipboardListener listener) {
+ listeners.add(listener);
}
+ public static void removeClipboardListener(ClipboardListener listener) {
+ listeners.remove(listener);
+ }
+
+ private static void fireClipboardChanged() {
+ ClipboardListener[] array = listeners.toArray(new ClipboardListener[0]);
+ for (ClipboardListener l: array) {
+ l.clipboardChanged();
+ }
+ }
}
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
-import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
-import javax.swing.JFrame;
+import javax.swing.JCheckBox;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
import javax.swing.KeyStroke;
-import javax.swing.event.TreeSelectionEvent;
-import javax.swing.event.TreeSelectionListener;
-import javax.swing.tree.TreePath;
-import javax.swing.tree.TreeSelectionModel;
+import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.Pair;
+import net.sf.openrocket.util.Prefs;
*/
public class RocketActions {
- private static RocketComponent clipboard = null;
- private static List<RocketAction> clipboardListeners = new ArrayList<RocketAction>();
-
+ public static final KeyStroke CUT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_X,
+ ActionEvent.CTRL_MASK);
+ public static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C,
+ ActionEvent.CTRL_MASK);
+ public static final KeyStroke PASTE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_V,
+ ActionEvent.CTRL_MASK);
+
private final OpenRocketDocument document;
private final Rocket rocket;
- private final JFrame parentFrame;
- private final TreeSelectionModel selectionModel;
+ private final BasicFrame parentFrame;
+ private final DocumentSelectionModel selectionModel;
+ private final RocketAction deleteComponentAction;
+ private final RocketAction deleteSimulationAction;
private final RocketAction deleteAction;
private final RocketAction cutAction;
private final RocketAction copyAction;
private final RocketAction moveDownAction;
- public RocketActions(OpenRocketDocument document, TreeSelectionModel selectionModel,
- JFrame parentFrame) {
+ public RocketActions(OpenRocketDocument document, DocumentSelectionModel selectionModel,
+ BasicFrame parentFrame) {
this.document = document;
this.rocket = document.getRocket();
this.selectionModel = selectionModel;
// Add action also to updateActions()
this.deleteAction = new DeleteAction();
+ this.deleteComponentAction = new DeleteComponentAction();
+ this.deleteSimulationAction = new DeleteSimulationAction();
this.cutAction = new CutAction();
this.copyAction = new CopyAction();
this.pasteAction = new PasteAction();
this.moveUpAction = new MoveUpAction();
this.moveDownAction = new MoveDownAction();
+ OpenRocketClipboard.addClipboardListener(this.pasteAction);
updateActions();
// Update all actions when tree selection or rocket changes
- selectionModel.addTreeSelectionListener(new TreeSelectionListener() {
+ selectionModel.addDocumentSelectionListener(new DocumentSelectionListener() {
@Override
- public void valueChanged(TreeSelectionEvent e) {
+ public void valueChanged(int changeType) {
updateActions();
}
});
* Update the state of all of the actions.
*/
private void updateActions() {
- deleteAction.update();
- cutAction.update();
- copyAction.update();
- pasteAction.update();
- editAction.update();
- newStageAction.update();
- moveUpAction.update();
- moveDownAction.update();
+ deleteAction.clipboardChanged();
+ cutAction.clipboardChanged();
+ copyAction.clipboardChanged();
+ pasteAction.clipboardChanged();
+ editAction.clipboardChanged();
+ newStageAction.clipboardChanged();
+ moveUpAction.clipboardChanged();
+ moveDownAction.clipboardChanged();
}
- /**
- * Update the state of all actions that depend on the clipboard.
- */
- private void updateClipboardActions() {
- RocketAction[] array = clipboardListeners.toArray(new RocketAction[0]);
- for (RocketAction a: array) {
- a.update();
- }
+
+
+ public Action getDeleteComponentAction() {
+ return deleteAction;
}
-
+ public Action getDeleteSimulationAction() {
+ return deleteAction;
+ }
public Action getDeleteAction() {
return deleteAction;
//////// Helper methods for the actions
- /**
- * Return the currently selected rocket component, or null if none selected.
- *
- * @return the currently selected component.
- */
- private RocketComponent getSelectedComponent() {
- RocketComponent c = null;
- TreePath p = selectionModel.getSelectionPath();
- if (p != null)
- c = (RocketComponent) p.getLastPathComponent();
-
- if (c != null && c.getRocket() != rocket) {
- throw new IllegalStateException("Selection not same as document rocket, "
- + "report bug!");
- }
- return c;
- }
-
- private void setSelectedComponent(RocketComponent component) {
- TreePath path = ComponentTreeModel.makeTreePath(component);
- selectionModel.setSelectionPath(path);
- }
-
-
private boolean isDeletable(RocketComponent c) {
// Sanity check
if (c == null || c.getParent() == null)
private void delete(RocketComponent c) {
if (!isDeletable(c)) {
- throw new IllegalArgumentException("Report bug! Component " + c + " not deletable.");
+ throw new IllegalArgumentException("Report bug! Component " + c +
+ " not deletable.");
}
RocketComponent parent = c.getParent();
}
+ private boolean isSimulationSelected() {
+ Simulation[] selection = selectionModel.getSelectedSimulations();
+ return (selection != null && selection.length > 0);
+ }
+
+
+
+ private boolean verifyDeleteSimulation() {
+ boolean verify = Prefs.NODE.getBoolean(Prefs.CONFIRM_DELETE_SIMULATION, true);
+ if (verify) {
+ JPanel panel = new JPanel(new MigLayout());
+ JCheckBox dontAsk = new JCheckBox("Do not ask me again");
+ panel.add(dontAsk,"wrap");
+ panel.add(new ResizeLabel("You can change the default operation in the " +
+ "preferences.",-2));
+
+ int ret = JOptionPane.showConfirmDialog(
+ parentFrame,
+ new Object[] {
+ "Delete the selected simulations?",
+ "<html><i>This operation cannot be undone.</i>",
+ "",
+ panel },
+ "Delete simulations",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE);
+ if (ret != JOptionPane.OK_OPTION)
+ return false;
+
+ if (dontAsk.isSelected()) {
+ Prefs.NODE.putBoolean(Prefs.CONFIRM_DELETE_SIMULATION, false);
+ }
+ }
+
+ return true;
+ }
/**
* should be pasted. Returns null if the clipboard is empty or if the
* clipboard cannot be pasted to the current selection.
*
+ * @param clipboard the component on the clipboard.
* @return a Pair with both components defined, or null.
*/
- private Pair<RocketComponent, Integer> getPastePosition() {
- RocketComponent selected = getSelectedComponent();
+ private Pair<RocketComponent, Integer> getPastePosition(RocketComponent clipboard) {
+ RocketComponent selected = selectionModel.getSelectedComponent();
if (selected == null)
return null;
/////// Action classes
- private abstract class RocketAction extends AbstractAction {
- public abstract void update();
+ private abstract class RocketAction extends AbstractAction implements ClipboardListener {
+ public abstract void clipboardChanged();
+ }
+
+
+ /**
+ * Action that deletes the selected component.
+ */
+ private class DeleteComponentAction extends RocketAction {
+ public DeleteComponentAction() {
+ this.putValue(NAME, "Delete");
+ this.putValue(SHORT_DESCRIPTION, "Delete the selected component.");
+ this.putValue(MNEMONIC_KEY, KeyEvent.VK_D);
+// this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
+ this.putValue(SMALL_ICON, Icons.EDIT_DELETE);
+ clipboardChanged();
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ RocketComponent c = selectionModel.getSelectedComponent();
+
+ if (isDeletable(c)) {
+ ComponentConfigDialog.hideDialog();
+
+ document.addUndoPosition("Delete " + c.getComponentName());
+ delete(c);
+ }
+ }
+
+ @Override
+ public void clipboardChanged() {
+ this.setEnabled(isDeletable(selectionModel.getSelectedComponent()));
+ }
+ }
+
+
+
+ /**
+ * Action that deletes the selected component.
+ */
+ private class DeleteSimulationAction extends RocketAction {
+ public DeleteSimulationAction() {
+ this.putValue(NAME, "Delete");
+ this.putValue(SHORT_DESCRIPTION, "Delete the selected simulation.");
+ this.putValue(MNEMONIC_KEY, KeyEvent.VK_D);
+// this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
+ this.putValue(SMALL_ICON, Icons.EDIT_DELETE);
+ clipboardChanged();
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Simulation[] sims = selectionModel.getSelectedSimulations();
+ if (sims.length > 0) {
+ if (verifyDeleteSimulation()) {
+ for (Simulation s: sims) {
+ document.removeSimulation(s);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void clipboardChanged() {
+ this.setEnabled(isSimulationSelected());
+ }
}
+
/**
* Action that deletes the selected component.
*/
private class DeleteAction extends RocketAction {
public DeleteAction() {
this.putValue(NAME, "Delete");
- this.putValue(SHORT_DESCRIPTION, "Delete the selected component and subcomponents.");
+ this.putValue(SHORT_DESCRIPTION, "Delete the selected component or simulation.");
this.putValue(MNEMONIC_KEY, KeyEvent.VK_D);
this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
this.putValue(SMALL_ICON, Icons.EDIT_DELETE);
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent c = getSelectedComponent();
- if (!isDeletable(c))
- return;
-
- ComponentConfigDialog.hideDialog();
-
- document.addUndoPosition("Delete " + c.getComponentName());
- delete(c);
+ if (isSimulationSelected()) {
+ deleteSimulationAction.actionPerformed(e);
+ parentFrame.selectTab(BasicFrame.SIMULATION_TAB);
+ } else {
+ deleteComponentAction.actionPerformed(e);
+ parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
+ }
}
@Override
- public void update() {
- this.setEnabled(isDeletable(getSelectedComponent()));
+ public void clipboardChanged() {
+ this.setEnabled(isDeletable(selectionModel.getSelectedComponent()) ||
+ isSimulationSelected());
}
}
public CutAction() {
this.putValue(NAME, "Cut");
this.putValue(MNEMONIC_KEY, KeyEvent.VK_T);
- this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X,
- ActionEvent.CTRL_MASK));
- this.putValue(SHORT_DESCRIPTION, "Cut this component (and subcomponents) to "
+ this.putValue(ACCELERATOR_KEY, CUT_KEY_STROKE);
+ this.putValue(SHORT_DESCRIPTION, "Cut this component or simulation to "
+ "the clipboard and remove from this design");
this.putValue(SMALL_ICON, Icons.EDIT_CUT);
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent c = getSelectedComponent();
- if (!isDeletable(c) || !isCopyable(c))
- return;
-
- ComponentConfigDialog.hideDialog();
-
- document.addUndoPosition("Cut " + c.getComponentName());
- clipboard = c.copy();
- delete(c);
- updateClipboardActions();
+ RocketComponent c = selectionModel.getSelectedComponent();
+ Simulation[] sims = selectionModel.getSelectedSimulations();
+
+ if (isDeletable(c) && isCopyable(c)) {
+ ComponentConfigDialog.hideDialog();
+
+ document.addUndoPosition("Cut " + c.getComponentName());
+ OpenRocketClipboard.setClipboard(c.copy());
+ delete(c);
+ parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
+ } else if (isSimulationSelected()) {
+
+ Simulation[] simsCopy = new Simulation[sims.length];
+ for (int i=0; i < sims.length; i++) {
+ simsCopy[i] = sims[i].copy();
+ }
+ OpenRocketClipboard.setClipboard(simsCopy);
+
+ for (Simulation s: sims) {
+ document.removeSimulation(s);
+ }
+ parentFrame.selectTab(BasicFrame.SIMULATION_TAB);
+ }
}
@Override
- public void update() {
- RocketComponent c = getSelectedComponent();
- this.setEnabled(isDeletable(c) && isCopyable(c));
+ public void clipboardChanged() {
+ RocketComponent c = selectionModel.getSelectedComponent();
+ this.setEnabled((isDeletable(c) && isCopyable(c)) || isSimulationSelected());
}
}
public CopyAction() {
this.putValue(NAME, "Copy");
this.putValue(MNEMONIC_KEY, KeyEvent.VK_C);
- this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C,
- ActionEvent.CTRL_MASK));
+ this.putValue(ACCELERATOR_KEY, COPY_KEY_STROKE);
this.putValue(SHORT_DESCRIPTION, "Copy this component (and subcomponents) to "
+ "the clipboard.");
this.putValue(SMALL_ICON, Icons.EDIT_COPY);
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent c = getSelectedComponent();
- if (!isCopyable(c))
- return;
-
- clipboard = c.copy();
- updateClipboardActions();
+ RocketComponent c = selectionModel.getSelectedComponent();
+ Simulation[] sims = selectionModel.getSelectedSimulations();
+
+ if (isCopyable(c)) {
+ OpenRocketClipboard.setClipboard(c.copy());
+ parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
+ } else if (sims.length >= 0) {
+
+ Simulation[] simsCopy = new Simulation[sims.length];
+ for (int i=0; i < sims.length; i++) {
+ simsCopy[i] = sims[i].copy();
+ }
+ OpenRocketClipboard.setClipboard(simsCopy);
+ parentFrame.selectTab(BasicFrame.SIMULATION_TAB);
+ }
}
@Override
- public void update() {
- this.setEnabled(isCopyable(getSelectedComponent()));
+ public void clipboardChanged() {
+ this.setEnabled(isCopyable(selectionModel.getSelectedComponent()) ||
+ isSimulationSelected());
}
}
public PasteAction() {
this.putValue(NAME, "Paste");
this.putValue(MNEMONIC_KEY, KeyEvent.VK_P);
- this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V,
- ActionEvent.CTRL_MASK));
- this.putValue(SHORT_DESCRIPTION, "Paste the component (and subcomponents) on "
+ this.putValue(ACCELERATOR_KEY, PASTE_KEY_STROKE);
+ this.putValue(SHORT_DESCRIPTION, "Paste the component or simulation on "
+ "the clipboard to the design.");
this.putValue(SMALL_ICON, Icons.EDIT_PASTE);
- update();
-
- // Listen to when the clipboard changes
- clipboardListeners.add(this);
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- Pair<RocketComponent, Integer> position = getPastePosition();
- if (position == null)
- return;
-
- ComponentConfigDialog.hideDialog();
-
- RocketComponent pasted = clipboard.copy();
- document.addUndoPosition("Paste " + pasted.getComponentName());
- position.getU().addChild(pasted, position.getV());
- setSelectedComponent(pasted);
+ RocketComponent clipboard = OpenRocketClipboard.getClipboardComponent();
+ Simulation[] sims = OpenRocketClipboard.getClipboardSimulations();
+
+ Pair<RocketComponent, Integer> position = getPastePosition(clipboard);
+ if (position != null) {
+ ComponentConfigDialog.hideDialog();
+
+ RocketComponent pasted = clipboard.copy();
+ document.addUndoPosition("Paste " + pasted.getComponentName());
+ position.getU().addChild(pasted, position.getV());
+ selectionModel.setSelectedComponent(pasted);
+
+ parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
+
+ } else if (sims != null) {
+
+ ArrayList<Simulation> copySims = new ArrayList<Simulation>();
+
+ for (Simulation s: sims) {
+ Simulation copy = s.duplicateSimulation(rocket);
+ String name = copy.getName();
+ if (name.matches(OpenRocketDocument.SIMULATION_NAME_PREFIX + "[0-9]+ *")) {
+ copy.setName(document.getNextSimulationName());
+ }
+ document.addSimulation(copy);
+ copySims.add(copy);
+ }
+ selectionModel.setSelectedSimulations(copySims.toArray(new Simulation[0]));
+
+ parentFrame.selectTab(BasicFrame.SIMULATION_TAB);
+ }
}
@Override
- public void update() {
- this.setEnabled(getPastePosition() != null);
+ public void clipboardChanged() {
+ this.setEnabled(
+ (getPastePosition(OpenRocketClipboard.getClipboardComponent()) != null) ||
+ (OpenRocketClipboard.getClipboardSimulations() != null));
}
}
public EditAction() {
this.putValue(NAME, "Edit");
this.putValue(SHORT_DESCRIPTION, "Edit the selected component.");
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent c = getSelectedComponent();
+ RocketComponent c = selectionModel.getSelectedComponent();
if (c == null)
return;
}
@Override
- public void update() {
- this.setEnabled(getSelectedComponent() != null);
+ public void clipboardChanged() {
+ this.setEnabled(selectionModel.getSelectedComponent() != null);
}
}
public NewStageAction() {
this.putValue(NAME, "New stage");
this.putValue(SHORT_DESCRIPTION, "Add a new stage to the rocket design.");
- update();
+ clipboardChanged();
}
@Override
document.addUndoPosition("Add stage");
rocket.addChild(stage);
rocket.getDefaultConfiguration().setAllStages();
- setSelectedComponent(stage);
+ selectionModel.setSelectedComponent(stage);
ComponentConfigDialog.showDialog(parentFrame, document, stage);
}
@Override
- public void update() {
+ public void clipboardChanged() {
this.setEnabled(true);
}
}
public MoveUpAction() {
this.putValue(NAME, "Move up");
this.putValue(SHORT_DESCRIPTION, "Move this component upwards.");
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent selected = getSelectedComponent();
+ RocketComponent selected = selectionModel.getSelectedComponent();
if (!canMove(selected))
return;
RocketComponent parent = selected.getParent();
document.addUndoPosition("Move "+selected.getComponentName());
parent.moveChild(selected, parent.getChildPosition(selected)-1);
- setSelectedComponent(selected);
+ selectionModel.setSelectedComponent(selected);
}
@Override
- public void update() {
- this.setEnabled(canMove(getSelectedComponent()));
+ public void clipboardChanged() {
+ this.setEnabled(canMove(selectionModel.getSelectedComponent()));
}
private boolean canMove(RocketComponent c) {
public MoveDownAction() {
this.putValue(NAME, "Move down");
this.putValue(SHORT_DESCRIPTION, "Move this component downwards.");
- update();
+ clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
- RocketComponent selected = getSelectedComponent();
+ RocketComponent selected = selectionModel.getSelectedComponent();
if (!canMove(selected))
return;
RocketComponent parent = selected.getParent();
document.addUndoPosition("Move "+selected.getComponentName());
parent.moveChild(selected, parent.getChildPosition(selected)+1);
- setSelectedComponent(selected);
+ selectionModel.setSelectedComponent(selected);
}
@Override
- public void update() {
- this.setEnabled(canMove(getSelectedComponent()));
+ public void clipboardChanged() {
+ this.setEnabled(canMove(selectionModel.getSelectedComponent()));
}
private boolean canMove(RocketComponent c) {
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.document.events.DocumentChangeEvent;
+import net.sf.openrocket.document.events.DocumentChangeListener;
+import net.sf.openrocket.document.events.SimulationChangeEvent;
import net.sf.openrocket.gui.adaptors.Column;
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
import net.sf.openrocket.gui.components.ResizeLabel;
private static final Color OK_COLOR = new Color(60,150,0);
private static final String OK_TEXT = "\u2714"; // Heavy check mark
- private static final String NAME_PREFIX = "Simulation ";
-
private final OpenRocketDocument document;
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
-
- // Generate unique name for the simulation
- int maxValue = 0;
- for (Simulation s: document.getSimulations()) {
- String name = s.getName();
- if (name.startsWith(NAME_PREFIX)) {
- try {
- maxValue = Math.max(maxValue,
- Integer.parseInt(name.substring(NAME_PREFIX.length())));
- } catch (NumberFormatException ignore) { }
- }
- }
-
Simulation sim = new Simulation(document.getRocket());
- sim.setName(NAME_PREFIX + (maxValue+1));
+ sim.setName(document.getNextSimulationName());
int n = document.getSimulationCount();
document.addSimulation(sim);
// Set simulation status icon
Simulation.Status status = document.getSimulation(row).getStatus();
+ System.out.println("status=" + status);
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
}
};
- simulationTable = new JTable(simulationTableModel);
+ // Override processKeyBinding so that the JTable does not catch
+ // key bindings used in menu accelerators
+ simulationTable = new JTable(simulationTableModel) {
+ @Override
+ protected boolean processKeyBinding(KeyStroke ks,
+ KeyEvent e,
+ int condition,
+ boolean pressed) {
+ return false;
+ }
+ };
simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
+
// Mouse listener to act on double-clicks
simulationTable.addMouseListener(new MouseAdapter() {
@Override
}
}
});
+
+ document.addDocumentChangeListener(new DocumentChangeListener() {
+ @Override
+ public void documentChanged(DocumentChangeEvent event) {
+ if (!(event instanceof SimulationChangeEvent))
+ return;
+ simulationTableModel.fireTableDataChanged();
+ }
+ });
}
+ public ListSelectionModel getSimulationListSelectionModel() {
+ return simulationTable.getSelectionModel();
+ }
+
private void openDialog(final Simulation sim, int position) {
new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), sim, position)
.setVisible(true);
int[] selection = simulationTable.getSelectedRows();
simulationTableModel.fireTableDataChanged();
for (int row: selection) {
+ if (row >= simulationTableModel.getRowCount())
+ break;
simulationTable.addRowSelectionInterval(row, row);
}
}
public void setSelection(RocketComponent[] selection) {
if (selection == null) {
- selection = new RocketComponent[0];
+ this.selection = new RocketComponent[0];
} else {
this.selection = selection;
}
}
public String getMotorConfigurationDescription() {
- return rocket.getMotorConfigurationDescription(motorConfiguration);
+ return rocket.getMotorConfigurationNameOrDescription(motorConfiguration);
}
}
+ /**
+ * Check whether <code>id</code> is a valid motor configuration ID.
+ *
+ * @param id the configuration ID.
+ * @return whether a motor configuration with that ID exists.
+ */
+ public boolean isMotorConfigurationID(String id) {
+ return motorConfigurationIDs.contains(id);
+ }
+
+
+
+ /**
+ * Check whether the given motor configuration ID has motors defined for it.
+ *
+ * @param id the motor configuration ID (may be invalid).
+ * @return whether any motors are defined for it.
+ */
+ public boolean hasMotors(String id) {
+ Iterator<RocketComponent> iterator = this.deepIterator();
+ while (iterator.hasNext()) {
+ RocketComponent c = iterator.next();
+
+ if (c instanceof MotorMount) {
+ MotorMount mount = (MotorMount) c;
+ if (!mount.isMotorMount())
+ continue;
+ if (mount.getMotor(id) != null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
/**
* Return the user-set name of the motor configuration. If no name has been set,
* returns an empty string (not null).
/**
- * Return a description for the motor configuration. This is either the
- * name previously set by {@link #setMotorConfigurationName(String, String)} or
- * a string generated from the motor designations of the components.
+ * Return either the motor configuration name (if set) or its description.
+ *
+ * @param id the motor configuration ID.
+ * @return a textual representation of the configuration
+ */
+ public String getMotorConfigurationNameOrDescription(String id) {
+ String name;
+
+ name = motorConfigurationNames.get(id);
+ if (name != null && !name.equals(""))
+ return name;
+
+ return getMotorConfigurationDescription(id);
+ }
+
+ /**
+ * Return a description for the motor configuration, generated from the motor
+ * designations of the components.
*
* @param id the motor configuration ID.
* @return a textual representation of the configuration
throw new IllegalArgumentException("Motor configuration ID does not exist: "+id);
}
- name = motorConfigurationNames.get(id);
- if (name != null && !name.equals(""))
- return name;
-
// Generate the description
// First iterate over each stage and store the designations of each motor
return motorID;
}
+ /**
+ * Set the motor configuration ID. This must be a valid motor configuration ID of
+ * the rocket, otherwise the configuration is set to <code>null</code>.
+ *
+ * @param id the configuration to set.
+ */
public void setMotorConfigurationID(String id) {
if (id != null)
id = id.intern();
+ if (!rocket.isMotorConfigurationID(id))
+ id = null;
if (id == motorID)
return;
motorID = id;
public void copyFrom(SimulationConditions src) {
- if (this.rocket != src.rocket) {
- throw new IllegalArgumentException("Unable to copy simulation conditions of "+
- "a difference rocket");
+
+ if (this.rocket == src.rocket) {
+
+ this.motorID = src.motorID;
+
+ } else {
+
+ if (src.rocket.hasMotors(src.motorID)) {
+ // Try to find a matching motor ID
+ String motorDesc = src.rocket.getMotorConfigurationDescription(src.motorID);
+ String matchID = null;
+
+ for (String id: this.rocket.getMotorConfigurationIDs()) {
+ if (motorDesc.equals(this.rocket.getMotorConfigurationDescription(id))) {
+ matchID = id;
+ break;
+ }
+ }
+
+ this.motorID = matchID;
+ } else {
+ this.motorID = null;
+ }
}
- if (this.equals(src))
- return;
- this.motorID = src.motorID;
this.launchAltitude = src.launchAltitude;
this.launchLatitude = src.launchLatitude;
this.launchPressure = src.launchPressure;
try {
Class cls = Class.forName(START_CLASS);
- Method m = cls.getMethod("main", String[].class);
+ Method m = cls.getMethod("main", new Class[] {String[].class});
m.invoke(null, new Object[] { args });
} catch (ClassNotFoundException e) {
e.printStackTrace();
- error("Error starting main class!", "Please report a bug.");
+ error(new String[] {"Error starting main class!", "Please report a bug."});
} catch (NoSuchMethodException e) {
e.printStackTrace();
- error("Error starting main class!", "Please report a bug.");
+ error(new String[] {"Error starting main class!", "Please report a bug."});
} catch (InvocationTargetException e) {
e.printStackTrace();
- error("Error starting main class!", "Please report a bug.");
+ error(new String[] {"Error starting main class!", "Please report a bug."});
} catch (IllegalAccessException e) {
e.printStackTrace();
- error("Error starting main class!", "Please report a bug.");
+ error(new String[] {"Error starting main class!", "Please report a bug."});
}
}
if (major < REQUIRED_MAJOR_VERSION ||
(major == REQUIRED_MAJOR_VERSION && minor < REQUIRED_MINOR_VERSION)) {
- error("Java SE version 6 is required to run OpenRocket.",
+ error(new String[] {"Java SE version 6 is required to run OpenRocket.",
"You are currently running " + jreName + " version " +
- jreVersion + " by " + jreVendor);
+ jreVersion + " by " + jreVendor});
}
} catch (RuntimeException e) {
- confirm("The Java version in use could not be detected.",
+ confirm(new String[] {"The Java version in use could not be detected.",
"OpenRocket requires at least Java SE 6.",
- "Continue anyway?");
+ "Continue anyway?"});
}
String jreVersion = System.getProperty("java.runtime.version", "(unknown)");
String jreVendor = System.getProperty("java.vendor", "(unknown)");
- confirm("OpenJDK is known to have problems running OpenRocket.",
+ confirm(new String[] {"OpenJDK is known to have problems running OpenRocket.",
" ",
"You are currently running " + jreName + " version " +
jreVersion + " by " + jreVendor,
- "Do you want to continue?");
+ "Do you want to continue?"});
}
}
*
* @param message an array of messages to present.
*/
- private static void error(String ... message) {
+ private static void error(String[] message) {
System.err.println();
System.err.println("Error starting OpenRocket:");
*
* @param message the message Strings to show.
*/
- private static void confirm(String ... message) {
+ private static void confirm(String[] message) {
if (!GraphicsEnvironment.isHeadless()) {
JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
System.exit(1);
}
-
}
-
}
-
}
package net.sf.openrocket.util;
+import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public static final Map<Simulation.Status, Icon> SIMULATION_STATUS_ICON_MAP;
static {
HashMap<Simulation.Status, Icon> map = new HashMap<Simulation.Status, Icon>();
- map.put(Simulation.Status.NOT_SIMULATED, new ImageIcon("pix/spheres/gray-16x16.png", "Not simulated"));
- map.put(Simulation.Status.UPTODATE, new ImageIcon("pix/spheres/green-16x16.png", "Up to date"));
- map.put(Simulation.Status.LOADED, new ImageIcon("pix/spheres/yellow-16x16.png", "Loaded from file"));
- map.put(Simulation.Status.OUTDATED, new ImageIcon("pix/spheres/red-16x16.png", "Out-of-date"));
- map.put(Simulation.Status.EXTERNAL, new ImageIcon("pix/spheres/blue-16x16.png", "Imported data"));
+ map.put(Simulation.Status.NOT_SIMULATED, loadImageIcon("pix/spheres/gray-16x16.png", "Not simulated"));
+ 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.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);
}
}
- public static final Icon FILE_NEW = new ImageIcon(ClassLoader.getSystemResource("pix/icons/document-new.png"), "New document");
- public static final Icon FILE_OPEN = new ImageIcon(ClassLoader.getSystemResource("pix/icons/document-open.png"), "Open document");
- public static final Icon FILE_SAVE = new ImageIcon(ClassLoader.getSystemResource("pix/icons/document-save.png"), "Save document");
- public static final Icon FILE_SAVE_AS = new ImageIcon(ClassLoader.getSystemResource("pix/icons/document-save-as.png"), "Save document as");
- public static final Icon FILE_CLOSE = new ImageIcon(ClassLoader.getSystemResource("pix/icons/document-close.png"), "Close document");
- public static final Icon FILE_QUIT = new ImageIcon(ClassLoader.getSystemResource("pix/icons/application-exit.png"), "Quit OpenRocket");
+ public static final Icon FILE_NEW = loadImageIcon("pix/icons/document-new.png", "New document");
+ public static final Icon FILE_OPEN = loadImageIcon("pix/icons/document-open.png", "Open document");
+ public static final Icon FILE_SAVE = loadImageIcon("pix/icons/document-save.png", "Save document");
+ public static final Icon FILE_SAVE_AS = loadImageIcon("pix/icons/document-save-as.png", "Save document as");
+ public static final Icon FILE_CLOSE = loadImageIcon("pix/icons/document-close.png", "Close document");
+ public static final Icon FILE_QUIT = loadImageIcon("pix/icons/application-exit.png", "Quit OpenRocket");
- public static final Icon EDIT_UNDO = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-undo.png"), "Undo");
- public static final Icon EDIT_REDO = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-redo.png"), "Redo");
- public static final Icon EDIT_CUT = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-cut.png"), "Cut");
- public static final Icon EDIT_COPY = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-copy.png"), "Copy");
- public static final Icon EDIT_PASTE = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-paste.png"), "Paste");
- public static final Icon EDIT_DELETE = new ImageIcon(ClassLoader.getSystemResource("pix/icons/edit-delete.png"), "Delete");
+ public static final Icon EDIT_UNDO = loadImageIcon("pix/icons/edit-undo.png", "Undo");
+ public static final Icon EDIT_REDO = loadImageIcon("pix/icons/edit-redo.png", "Redo");
+ public static final Icon EDIT_CUT = loadImageIcon("pix/icons/edit-cut.png", "Cut");
+ public static final Icon EDIT_COPY = loadImageIcon("pix/icons/edit-copy.png", "Copy");
+ public static final Icon EDIT_PASTE = loadImageIcon("pix/icons/edit-paste.png", "Paste");
+ public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete");
- public static final Icon ZOOM_IN = new ImageIcon(ClassLoader.getSystemResource("pix/icons/zoom-in.png"), "Zoom in");
- public static final Icon ZOOM_OUT = new ImageIcon(ClassLoader.getSystemResource("pix/icons/zoom-out.png"), "Zoom out");
+ public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in");
+ public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out");
- public static final Icon PREFERENCES = new ImageIcon(ClassLoader.getSystemResource("pix/icons/preferences.png"), "Preferences");
+ public static final Icon PREFERENCES = loadImageIcon("pix/icons/preferences.png", "Preferences");
+
+
+ private static ImageIcon loadImageIcon(String file, String name) {
+ URL url = ClassLoader.getSystemResource(file);
+ if (url == null) {
+ System.err.println("Resource "+file+" not found! Ignoring...");
+ return null;
+ }
+ return new ImageIcon(url, name);
+ }
}
import java.awt.Point;
import java.awt.Toolkit;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
- private static final String VERSION = "0.9.0";
+ private static final String BUILD_VERSION;
+ private static final String BUILD_SOURCE;
+
+ static {
+ try {
+ InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
+ if (is == null) {
+ throw new MissingResourceException(
+ "build.properties not found, distribution built wrong",
+ "build.properties", "build.version");
+ }
+
+ Properties props = new Properties();
+ props.load(is);
+ is.close();
+
+ BUILD_VERSION = props.getProperty("build.version");
+ if (BUILD_VERSION == null) {
+ throw new MissingResourceException(
+ "build.version not found in property file",
+ "build.properties", "build.version");
+ }
+
+ BUILD_SOURCE = props.getProperty("build.source");
+
+ } catch (IOException e) {
+ throw new MissingResourceException(
+ "Error reading build.properties",
+ "build.properties", "build.version");
+ }
+ }
public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
public static String getVersion() {
- return VERSION;
+ return BUILD_VERSION;
+ }
+
+
+ public static String getBuildSource() {
+ return BUILD_SOURCE;
}