This commit is contained in:
Sampo Niskanen 2009-06-09 16:56:52 +00:00
parent 2f7baaa87c
commit 0d0db9258e
33 changed files with 920 additions and 265 deletions

View File

@ -1,3 +1,12 @@
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

8
build.properties Normal file
View File

@ -0,0 +1,8 @@
# 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

View File

@ -1,18 +1,25 @@
<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 -->
@ -21,26 +28,32 @@
</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"/>
@ -51,13 +64,48 @@
</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>

BIN
dists/OpenRocket-0.9.0.jar Normal file

Binary file not shown.

View File

@ -101,6 +101,19 @@
</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>

View File

@ -175,6 +175,19 @@
</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>

View File

@ -56,7 +56,7 @@
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&amp;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
@ -90,6 +90,19 @@
</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>

View File

@ -139,6 +139,19 @@
</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>

View File

@ -101,6 +101,19 @@
</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>

View File

@ -764,6 +764,19 @@ Public License instead of this License. But first, please read
</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>

View File

@ -55,9 +55,9 @@
<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" /> -->
@ -66,12 +66,12 @@
<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&amp;group_id=260357&amp;atid=1127606">bug
tracker</a>. Follow the instructions provided to fill in the
report.</li>
@ -97,8 +97,8 @@
<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&amp;atid=1127606&amp;artgroup=899287">enhancement requests</a>.</li>
<li>Send the request to the <a href="https://sourceforge.net/tracker/?func=add&amp;group_id=260357&amp;atid=1127606">bug tracker</a> as an
enhancement request. Please send multiple enhancements as
individual items.</li>
</ul>
@ -111,6 +111,19 @@
</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>

View File

@ -90,6 +90,19 @@
</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>

View File

@ -10,6 +10,9 @@ import java.util.List;
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;
@ -29,6 +32,8 @@ public class OpenRocketDocument implements ComponentChangeListener {
public static final int UNDO_MARGIN = 10;
public static final String SIMULATION_NAME_PREFIX = "Simulation ";
private final Rocket rocket;
private final Configuration configuration;
@ -48,6 +53,9 @@ public class OpenRocketDocument implements ComponentChangeListener {
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;
@ -70,8 +78,6 @@ public class OpenRocketDocument implements ComponentChangeListener {
redoAction = new UndoRedoAction(UndoRedoAction.REDO);
rocket.addComponentChangeListener(this);
}
@ -135,17 +141,43 @@ public class OpenRocketDocument implements ComponentChangeListener {
}
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);
}
/**
@ -301,6 +333,24 @@ public class OpenRocketDocument implements ComponentChangeListener {
/////// 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);
}
}
/**

View File

@ -21,7 +21,7 @@ import net.sf.openrocket.simulation.exception.SimulationListenerException;
import net.sf.openrocket.util.ChangeSource;
public class Simulation implements ChangeSource {
public class Simulation implements ChangeSource, Cloneable {
public static enum Status {
/** Up-to-date */
@ -48,9 +48,9 @@ public class Simulation implements ChangeSource {
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;
@ -58,7 +58,7 @@ public class Simulation implements ChangeSource {
/** 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 */
@ -304,7 +304,7 @@ public class Simulation implements ChangeSource {
*
* @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;
@ -321,7 +321,56 @@ public class Simulation implements ChangeSource {
}
/**
* 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;
}

View File

@ -0,0 +1,11 @@
package net.sf.openrocket.document.events;
import javax.swing.event.ChangeEvent;
public class DocumentChangeEvent extends ChangeEvent {
public DocumentChangeEvent(Object source) {
super(source);
}
}

View File

@ -0,0 +1,7 @@
package net.sf.openrocket.document.events;
public interface DocumentChangeListener {
public void documentChanged(DocumentChangeEvent event);
}

View File

@ -0,0 +1,10 @@
package net.sf.openrocket.document.events;
public class SimulationChangeEvent extends DocumentChangeEvent {
public SimulationChangeEvent(Object source) {
super(source);
}
}

View File

@ -181,7 +181,7 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
@Override
public String toString() {
return rocket.getMotorConfigurationDescription(id);
return rocket.getMotorConfigurationNameOrDescription(id);
}
}
@ -219,7 +219,7 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
columnList.add(new Column("Name") {
@Override
public Object getValueAt(int row) {
return rocket.getMotorConfigurationDescription(ids[row]);
return rocket.getMotorConfigurationNameOrDescription(ids[row]);
}
});

View File

@ -16,8 +16,10 @@ import net.sf.openrocket.gui.SpinnerEditor;
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;
@ -142,8 +144,17 @@ public class RingComponentConfig extends RocketComponentConfig {
//// 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;
}

View File

@ -41,6 +41,7 @@ public class BugDialog extends JDialog {
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");

View File

@ -34,6 +34,7 @@ import javax.swing.JSeparator;
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;
@ -96,6 +97,10 @@ public class BasicFrame extends JFrame {
};
public static final int COMPONENT_TAB = 0;
public static final int SIMULATION_TAB = 1;
/**
* List of currently open frames. When the list goes empty
@ -118,10 +123,13 @@ public class BasicFrame extends JFrame {
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;
@ -150,11 +158,21 @@ public class BasicFrame extends JFrame {
});
// 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
@ -164,11 +182,11 @@ public class BasicFrame extends JFrame {
// 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);
@ -334,15 +352,6 @@ public class BasicFrame extends JFrame {
}
/**
* Construct the "Flight simulations" tab.
* @return
*/
private JComponent simulationsTab() {
return new SimulationPanel(document);
}
/**
* Creates the menu for the window.
@ -553,6 +562,16 @@ public class BasicFrame extends JFrame {
/**
* 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();

View File

@ -0,0 +1,7 @@
package net.sf.openrocket.gui.main;
public interface ClipboardListener {
public void clipboardChanged();
}

View File

@ -55,6 +55,19 @@ public class DocumentSelectionModel {
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>
@ -65,6 +78,14 @@ public class DocumentSelectionModel {
public RocketComponent getSelectedComponent() {
return componentSelection;
}
public void setSelectedComponent(RocketComponent component) {
componentSelection = component;
clearSimulationSelection();
TreePath path = ComponentTreeModel.makeTreePath(component);
componentTreeSelectionModel.setSelectionPath(path);
}

View File

@ -1,11 +1,17 @@
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
@ -14,22 +20,51 @@ public class OpenRocketClipboard {
/**
* 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;
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 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();
}
return null;
}
public static Simulation[] getSimulations() {
return null; // TODO
}
}

View File

@ -4,18 +4,18 @@ package net.sf.openrocket.gui.main;
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;
@ -24,6 +24,7 @@ import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.Pair;
import net.sf.openrocket.util.Prefs;
@ -35,15 +36,21 @@ import net.sf.openrocket.util.Pair;
*/
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;
@ -54,8 +61,8 @@ public class RocketActions {
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;
@ -63,6 +70,8 @@ public class RocketActions {
// 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();
@ -71,13 +80,14 @@ public class RocketActions {
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();
}
});
@ -93,28 +103,26 @@ public class RocketActions {
* 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;
@ -152,30 +160,6 @@ public class RocketActions {
//////// 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)
@ -195,7 +179,8 @@ public class RocketActions {
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();
@ -211,6 +196,42 @@ public class RocketActions {
}
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;
}
/**
@ -218,10 +239,11 @@ public class RocketActions {
* 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;
@ -246,39 +268,105 @@ public class RocketActions {
/////// 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 DeleteAction extends RocketAction {
public DeleteAction() {
private class DeleteComponentAction extends RocketAction {
public DeleteComponentAction() {
this.putValue(NAME, "Delete");
this.putValue(SHORT_DESCRIPTION, "Delete the selected component and subcomponents.");
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(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;
RocketComponent c = selectionModel.getSelectedComponent();
ComponentConfigDialog.hideDialog();
if (isDeletable(c)) {
ComponentConfigDialog.hideDialog();
document.addUndoPosition("Delete " + c.getComponentName());
delete(c);
document.addUndoPosition("Delete " + c.getComponentName());
delete(c);
}
}
@Override
public void update() {
this.setEnabled(isDeletable(getSelectedComponent()));
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 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);
clipboardChanged();
}
@Override
public void actionPerformed(ActionEvent e) {
if (isSimulationSelected()) {
deleteSimulationAction.actionPerformed(e);
parentFrame.selectTab(BasicFrame.SIMULATION_TAB);
} else {
deleteComponentAction.actionPerformed(e);
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
}
}
@Override
public void clipboardChanged() {
this.setEnabled(isDeletable(selectionModel.getSelectedComponent()) ||
isSimulationSelected());
}
}
@ -291,32 +379,44 @@ public class RocketActions {
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;
RocketComponent c = selectionModel.getSelectedComponent();
Simulation[] sims = selectionModel.getSelectedSimulations();
ComponentConfigDialog.hideDialog();
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()) {
document.addUndoPosition("Cut " + c.getComponentName());
clipboard = c.copy();
delete(c);
updateClipboardActions();
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());
}
}
@ -329,27 +429,36 @@ public class RocketActions {
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;
RocketComponent c = selectionModel.getSelectedComponent();
Simulation[] sims = selectionModel.getSelectedSimulations();
clipboard = c.copy();
updateClipboardActions();
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());
}
}
@ -365,34 +474,53 @@ public class RocketActions {
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;
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>();
ComponentConfigDialog.hideDialog();
RocketComponent pasted = clipboard.copy();
document.addUndoPosition("Paste " + pasted.getComponentName());
position.getU().addChild(pasted, position.getV());
setSelectedComponent(pasted);
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));
}
}
@ -408,12 +536,12 @@ public class RocketActions {
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;
@ -421,8 +549,8 @@ public class RocketActions {
}
@Override
public void update() {
this.setEnabled(getSelectedComponent() != null);
public void clipboardChanged() {
this.setEnabled(selectionModel.getSelectedComponent() != null);
}
}
@ -439,7 +567,7 @@ public class RocketActions {
public NewStageAction() {
this.putValue(NAME, "New stage");
this.putValue(SHORT_DESCRIPTION, "Add a new stage to the rocket design.");
update();
clipboardChanged();
}
@Override
@ -452,13 +580,13 @@ public class RocketActions {
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);
}
}
@ -473,12 +601,12 @@ public class RocketActions {
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;
@ -487,12 +615,12 @@ public class RocketActions {
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) {
@ -514,12 +642,12 @@ public class RocketActions {
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;
@ -528,12 +656,12 @@ public class RocketActions {
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) {

View File

@ -5,6 +5,7 @@ import java.awt.Color;
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;
@ -17,6 +18,8 @@ import javax.swing.JOptionPane;
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;
@ -25,6 +28,9 @@ import net.sf.openrocket.aerodynamics.Warning;
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;
@ -43,8 +49,6 @@ public class SimulationPanel extends JPanel {
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;
@ -70,21 +74,8 @@ public class SimulationPanel extends JPanel {
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);
@ -231,6 +222,7 @@ public class SimulationPanel extends JPanel {
// 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));
@ -388,11 +380,22 @@ public class SimulationPanel extends JPanel {
}
};
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
@ -411,6 +414,15 @@ public class SimulationPanel extends JPanel {
}
}
});
document.addDocumentChangeListener(new DocumentChangeListener() {
@Override
public void documentChanged(DocumentChangeEvent event) {
if (!(event instanceof SimulationChangeEvent))
return;
simulationTableModel.fireTableDataChanged();
}
});
@ -431,6 +443,10 @@ public class SimulationPanel extends JPanel {
}
public ListSelectionModel getSimulationListSelectionModel() {
return simulationTable.getSelectionModel();
}
private void openDialog(final Simulation sim, int position) {
new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), sim, position)
.setVisible(true);
@ -441,6 +457,8 @@ public class SimulationPanel extends JPanel {
int[] selection = simulationTable.getSelectedRows();
simulationTableModel.fireTableDataChanged();
for (int row: selection) {
if (row >= simulationTableModel.getRowCount())
break;
simulationTable.addRowSelectionInterval(row, row);
}
}

View File

@ -121,7 +121,7 @@ public class RocketFigure extends AbstractScaleFigure {
public void setSelection(RocketComponent[] selection) {
if (selection == null) {
selection = new RocketComponent[0];
this.selection = new RocketComponent[0];
} else {
this.selection = selection;
}

View File

@ -193,7 +193,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
}
public String getMotorConfigurationDescription() {
return rocket.getMotorConfigurationDescription(motorConfiguration);
return rocket.getMotorConfigurationNameOrDescription(motorConfiguration);
}

View File

@ -534,6 +534,42 @@ public class Rocket extends RocketComponent {
}
/**
* 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).
@ -563,9 +599,24 @@ public class Rocket extends RocketComponent {
/**
* 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
@ -579,10 +630,6 @@ public class Rocket extends RocketComponent {
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

View File

@ -80,9 +80,17 @@ public class SimulationConditions implements ChangeSource, Cloneable {
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;
@ -319,14 +327,31 @@ public class SimulationConditions implements ChangeSource, Cloneable {
public void copyFrom(SimulationConditions src) {
if (this.rocket != src.rocket) {
throw new IllegalArgumentException("Unable to copy simulation conditions of "+
"a difference rocket");
}
if (this.equals(src))
return;
this.motorID = src.motorID;
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;
}
}
this.launchAltitude = src.launchAltitude;
this.launchLatitude = src.launchLatitude;
this.launchPressure = src.launchPressure;

View File

@ -35,21 +35,21 @@ public class Startup {
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."});
}
}
@ -73,16 +73,16 @@ public class Startup {
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?"});
}
@ -118,11 +118,11 @@ public class Startup {
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?"});
}
}
@ -135,7 +135,7 @@ public class Startup {
*
* @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:");
@ -163,7 +163,7 @@ public class Startup {
*
* @param message the message Strings to show.
*/
private static void confirm(String ... message) {
private static void confirm(String[] message) {
if (!GraphicsEnvironment.isHeadless()) {
@ -171,10 +171,7 @@ public class Startup {
JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
System.exit(1);
}
}
}
}

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.util;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -18,11 +19,11 @@ public class Icons {
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);
}
@ -34,23 +35,33 @@ public class Icons {
}
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);
}
}

View File

@ -5,8 +5,12 @@ import java.awt.Dimension;
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;
@ -46,7 +50,37 @@ public class Prefs {
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";
@ -112,7 +146,12 @@ public class Prefs {
public static String getVersion() {
return VERSION;
return BUILD_VERSION;
}
public static String getBuildSource() {
return BUILD_SOURCE;
}