+2009-05-28 Sampo Niskanen
+
+ * Added startup check for Java 1.6 and OpenJDK
+
+2009-05-28 Sampo Niskanen
+
+ * Fixed FixedPrecisionUnit formatting
+ * Fixed saving of transitions
+ * Fixed file dialog directory browsing
+ * Initial shift-click selects second component from figure
+ * Allow adding body components without selecting stage
+
2009-05-24 Sampo Niskanen <sampo.niskanen@iki.fi>
* Initial release 0.9.0
-
<!-- The main class of the application -->
- <property name="main-class" value="net.sf.openrocket.gui.main.BasicFrame"/>
+ <property name="main-class" value="net.sf.openrocket.startup.Startup"/>
<!-- Classpath definition -->
<p>The source code for OpenRocket is available from the
<a href="http://openrocket.svn.sourceforge.net/viewvc/openrocket/">SourceForge SVN repository</a>.
It can be retrieved simply using the command</p>
- <pre class="quote">$ svn co https://openrocket.svn.sourceforge.net/svnroot/openrocket openrocket</pre>
+ <pre class="quote">$ svn co https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk OpenRocket</pre>
<p>The above URL may be used to connect to the repository with
other Subversion clients as well.</p>
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.security.CodeSource;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import net.sf.openrocket.file.Loader;
import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.JarUtil;
if (!dir.endsWith("/")) {
dir += "/";
}
-
- // Find the jar file this class is contained in and open it
- URL jarUrl = null;
- CodeSource codeSource = Database.class.getProtectionDomain().getCodeSource();
- if (codeSource != null)
- jarUrl = codeSource.getLocation();
- if (jarUrl == null) {
- throw new IOException("Could not find containing JAR file.");
- }
- File file = urlToFile(jarUrl);
+ // Find and open the jar file this class is contained in
+ File file = JarUtil.getCurrentJarFile();
JarFile jarFile = new JarFile(file);
try {
}
- static File urlToFile(URL url) {
- URI uri;
- try {
- uri = url.toURI();
- } catch (URISyntaxException e) {
- try {
- uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(),
- url.getPath(), url.getQuery(), url.getRef());
- } catch (URISyntaxException e1) {
- throw new IllegalArgumentException("Broken URL: " + url);
- }
- }
- return new File(uri);
- }
-
-
public void load(File file) throws IOException {
if (loader == null) {
Transition.Shape shape = trans.getType();
- elements.add("<shape>" + shape.getName().toLowerCase() + "</shape>");
+ elements.add("<shape>" + shape.name().toLowerCase() + "</shape>");
if (shape.isClippable()) {
elements.add("<shapeclipped>" + trans.isClipped() + "</shapeclipped>");
}
+++ /dev/null
-package net.sf.openrocket.gui;
-
-import javax.swing.BoundedRangeModel;
-import javax.swing.JSlider;
-import javax.swing.plaf.basic.BasicSliderUI;
-
-/**
- * A simple slider that does not show the current value. GTK l&f shows the value, and cannot
- * be configured otherwise(!).
- *
- * @author Sampo Niskanen <sampo.niskanen@iki.fi>
- */
-
-public class BasicSlider extends JSlider {
-
- public BasicSlider(BoundedRangeModel brm) {
- this(brm,JSlider.HORIZONTAL,false);
- }
-
- public BasicSlider(BoundedRangeModel brm, int orientation) {
- this(brm,orientation,false);
- }
-
- public BasicSlider(BoundedRangeModel brm, int orientation, boolean inverted) {
- super(brm);
- setOrientation(orientation);
- setInverted(inverted);
- setUI(new BasicSliderUI(this));
- }
-
-}
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.StageSelector;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.FinSet;
+++ /dev/null
-package net.sf.openrocket.gui;
-
-import java.awt.Dimension;
-import java.awt.Rectangle;
-
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.ScrollPaneConstants;
-
-import net.miginfocom.swing.MigLayout;
-
-public class DescriptionArea extends JScrollPane {
-
- private ResizeLabel text;
- private MigLayout layout;
- private JPanel panel;
-
- public DescriptionArea(int rows) {
- this(rows, -2);
- }
-
- public DescriptionArea(int rows, float size) {
- super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
- ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
-
- layout = new MigLayout("ins 0 2px, fill");
- panel = new JPanel(layout);
-
- text = new ResizeLabel(" ",size);
- text.validate();
- Dimension dim = text.getPreferredSize();
- dim.height = (dim.height+2)*rows + 2;
- this.setPreferredSize(dim);
-
- panel.add(text, "growx");
-
- this.setViewportView(panel);
- this.revalidate();
- }
-
- public void setText(String txt) {
- if (!txt.startsWith("<html>"))
- txt = "<html>" + txt;
- text.setText(txt);
- }
-
-
- @Override
- public void validate() {
-
- Rectangle dim = this.getViewportBorderBounds();
- layout.setComponentConstraints(text, "width "+ dim.width + ", growx");
- super.validate();
- text.validate();
-
- }
-
-}
import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil;
+++ /dev/null
-package net.sf.openrocket.gui;
-
-import java.awt.Font;
-import javax.swing.JLabel;
-
-/**
- * A resizeable JLabel. The method resizeFont(float) changes the current font size by the
- * given (positive or negative) amount. The change is relative to the current font size.
- * <p>
- * A nice small text is achievable by <code>new ResizeLabel("My text", -2);</code>
- *
- * @author Sampo Niskanen <sampo.niskanen@iki.fi>
- */
-
-public class ResizeLabel extends JLabel {
-
- public ResizeLabel() {
- super();
- }
-
- public ResizeLabel(String text) {
- super(text);
- }
-
- public ResizeLabel(float size) {
- super();
- resizeFont(size);
- }
-
- public ResizeLabel(String text, float size) {
- super(text);
- resizeFont(size);
- }
-
- public ResizeLabel(String text, int horizontalAlignment, float size) {
- super(text, horizontalAlignment);
- resizeFont(size);
- }
-
-
- public void resizeFont(float size) {
- Font font = this.getFont();
- font = font.deriveFont(font.getSize2D()+size);
- this.setFont(font);
- }
-
-}
+++ /dev/null
-package net.sf.openrocket.gui;
-
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.AbstractAction;
-import javax.swing.JPanel;
-import javax.swing.JToggleButton;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.rocketcomponent.Configuration;
-
-
-public class StageSelector extends JPanel implements ChangeListener {
-
- private final Configuration configuration;
-
- private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
-
- public StageSelector(Configuration configuration) {
- super(new MigLayout("gap 0!"));
- this.configuration = configuration;
-
- JToggleButton button = new JToggleButton(new StageAction(0));
- this.add(button);
- buttons.add(button);
-
- updateButtons();
- configuration.addChangeListener(this);
- }
-
- private void updateButtons() {
- int stages = configuration.getStageCount();
- if (buttons.size() == stages)
- return;
-
- while (buttons.size() > stages) {
- JToggleButton button = buttons.remove(buttons.size()-1);
- this.remove(button);
- }
-
- while (buttons.size() < stages) {
- JToggleButton button = new JToggleButton(new StageAction(buttons.size()));
- this.add(button);
- buttons.add(button);
- }
-
- this.revalidate();
- }
-
-
-
-
- @Override
- public void stateChanged(ChangeEvent e) {
- updateButtons();
- }
-
-
- private class StageAction extends AbstractAction implements ChangeListener {
- private final int stage;
-
- public StageAction(final int stage) {
- this.stage = stage;
- configuration.addChangeListener(this);
- stateChanged(null);
- }
-
- @Override
- public Object getValue(String key) {
- if (key.equals(NAME)) {
- return "Stage "+(stage+1);
- }
- return super.getValue(key);
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- configuration.setToStage(stage);
-
-// boolean state = (Boolean)getValue(SELECTED_KEY);
-// if (state == true) {
-// // Was disabled, now enabled
-// configuration.setToStage(stage);
-// } else {
-// // Was enabled, check what to do
-// if (configuration.isStageActive(stage + 1)) {
-// configuration.setToStage(stage);
-// } else {
-// if (stage == 0)
-// configuration.setAllStages();
-// else
-// configuration.setToStage(stage-1);
-// }
-// }
-// stateChanged(null);
- }
-
-
- @Override
- public void stateChanged(ChangeEvent e) {
- this.putValue(SELECTED_KEY, configuration.isStageActive(stage));
- }
- }
-}
+++ /dev/null
-package net.sf.openrocket.gui;
-
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.ItemSelectable;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.Action;
-import javax.swing.JMenuItem;
-import javax.swing.JPopupMenu;
-import javax.swing.border.Border;
-import javax.swing.border.CompoundBorder;
-import javax.swing.border.EmptyBorder;
-import javax.swing.border.LineBorder;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-import net.sf.openrocket.gui.adaptors.DoubleModel;
-import net.sf.openrocket.unit.Unit;
-import net.sf.openrocket.unit.UnitGroup;
-
-
-/**
- * A Swing component that allows one to choose a unit from a UnitGroup within
- * a DoubleModel model. The current unit of the model is shown as a JLabel, and
- * the unit can be changed by clicking on the label.
- *
- * @author Sampo Niskanen <sampo.niskanen@iki.fi>
- */
-
-public class UnitSelector extends ResizeLabel implements ChangeListener, MouseListener,
- ItemSelectable {
-
- private final DoubleModel model;
- private final Action[] extraActions;
-
- private UnitGroup unitGroup;
- private Unit currentUnit;
-
- private final boolean showValue;
-
- private final Border normalBorder;
- private final Border withinBorder;
-
-
- private final List<ItemListener> itemListeners = new ArrayList<ItemListener>();
-
-
- /**
- * Common private constructor that sets the values and sets up the borders.
- * Either model or group must be null.
- *
- * @param model
- * @param showValue
- * @param group
- * @param actions
- */
- private UnitSelector(DoubleModel model, boolean showValue, UnitGroup group,
- Action[] actions) {
- super();
-
- this.model = model;
- this.showValue = showValue;
-
- if (model != null) {
- this.unitGroup = model.getUnitGroup();
- this.currentUnit = model.getCurrentUnit();
- } else {
- this.unitGroup = group;
- this.currentUnit = group.getDefaultUnit();
- }
-
- this.extraActions = actions;
-
- addMouseListener(this);
-
- // Define borders to use:
-
- normalBorder = new CompoundBorder(
- new LineBorder(new Color(0f, 0f, 0f, 0.08f), 1), new EmptyBorder(1, 1, 1,
- 1));
- withinBorder = new CompoundBorder(new LineBorder(new Color(0f, 0f, 0f, 0.6f)),
- new EmptyBorder(1, 1, 1, 1));
-
- setBorder(normalBorder);
- updateText();
- }
-
-
-
- public UnitSelector(DoubleModel model, Action... actions) {
- this(model, false, actions);
- }
-
- public UnitSelector(DoubleModel model, boolean showValue, Action... actions) {
- this(model, showValue, null, actions);
-
- // Add model listener
- this.model.addChangeListener(this);
- }
-
-
- public UnitSelector(UnitGroup group, Action... actions) {
- this(null, false, group, actions);
- }
-
-
-
-
- /**
- * Return the DoubleModel that is backing this selector up, or <code>null</code>.
- * Either this method or {@link #getUnitGroup()} always returns <code>null</code>.
- *
- * @return the DoubleModel being used, or <code>null</code>.
- */
- public DoubleModel getModel() {
- return model;
- }
-
-
- /**
- * Return the unit group that is being shown, or <code>null</code>. Either this method
- * or {@link #getModel()} always returns <code>null</code>.
- *
- * @return the UnitGroup being used, or <code>null</code>.
- */
- public UnitGroup getUnitGroup() {
- return unitGroup;
- }
-
-
- public void setUnitGroup(UnitGroup group) {
- if (model != null) {
- throw new IllegalStateException(
- "UnitGroup cannot be set when backed up with model.");
- }
-
- if (this.unitGroup == group)
- return;
-
- this.unitGroup = group;
- this.currentUnit = group.getDefaultUnit();
- updateText();
- }
-
-
- /**
- * Return the currently selected unit. Works both when backup up with a DoubleModel
- * and UnitGroup.
- *
- * @return the currently selected unit.
- */
- public Unit getSelectedUnit() {
- return currentUnit;
- }
-
-
- /**
- * Set the currently selected unit. Sets it to the DoubleModel if it is backed up
- * by it.
- *
- * @param unit the unit to select.
- */
- public void setSelectedUnit(Unit unit) {
- if (!unitGroup.contains(unit)) {
- throw new IllegalArgumentException("unit " + unit
- + " not contained in group " + unitGroup);
- }
-
- this.currentUnit = unit;
- if (model != null) {
- model.setCurrentUnit(unit);
- }
- updateText();
- fireItemEvent();
- }
-
-
-
- /**
- * Updates the text of the label
- */
- private void updateText() {
- if (model != null) {
-
- Unit unit = model.getCurrentUnit();
- if (showValue) {
- setText(unit.toStringUnit(model.getValue()));
- } else {
- setText(unit.getUnit());
- }
-
- } else if (unitGroup != null) {
-
- setText(currentUnit.getUnit());
-
- } else {
- throw new IllegalStateException("Both model and unitGroup are null.");
- }
- }
-
-
- /**
- * Update the component when the DoubleModel changes.
- */
- public void stateChanged(ChangeEvent e) {
- updateText();
- }
-
-
-
- //////// ItemListener handling ////////
-
- public void addItemListener(ItemListener listener) {
- itemListeners.add(listener);
- }
-
- public void removeItemListener(ItemListener listener) {
- itemListeners.remove(listener);
- }
-
- protected void fireItemEvent() {
- ItemEvent event = null;
- ItemListener[] listeners = itemListeners.toArray(new ItemListener[0]);
- for (ItemListener l: listeners) {
- if (event == null) {
- event = new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, getSelectedUnit(),
- ItemEvent.SELECTED);
- }
- l.itemStateChanged(event);
- }
- }
-
-
-
- //////// Popup ////////
-
- private void popup() {
- JPopupMenu popup = new JPopupMenu();
-
- for (int i = 0; i < unitGroup.getUnitCount(); i++) {
- Unit unit = unitGroup.getUnit(i);
- JMenuItem item = new JMenuItem(unit.getUnit());
- item.addActionListener(new UnitSelectorItem(unit));
- popup.add(item);
- }
-
- for (int i = 0; i < extraActions.length; i++) {
- if (extraActions[i] == null && i < extraActions.length - 1) {
- popup.addSeparator();
- } else {
- popup.add(new JMenuItem(extraActions[i]));
- }
- }
-
- Dimension d = getSize();
- popup.show(this, 0, d.height);
- }
-
-
- /**
- * ActionListener class that sets the currently selected unit.
- */
- private class UnitSelectorItem implements ActionListener {
- private final Unit unit;
-
- public UnitSelectorItem(Unit u) {
- unit = u;
- }
-
- public void actionPerformed(ActionEvent e) {
- setSelectedUnit(unit);
- }
- }
-
-
- @Override
- public Object[] getSelectedObjects() {
- return new Object[]{ getSelectedUnit() };
- }
-
-
-
- //////// Mouse handling ////////
-
- public void mouseClicked(MouseEvent e) {
- if (unitGroup.getUnitCount() > 1)
- popup();
- }
-
- public void mouseEntered(MouseEvent e) {
- if (unitGroup.getUnitCount() > 1)
- setBorder(withinBorder);
- }
-
- public void mouseExited(MouseEvent e) {
- setBorder(normalBorder);
- }
-
- public void mousePressed(MouseEvent e) {
- } // Ignore
-
- public void mouseReleased(MouseEvent e) {
- } // Ignore
-
-}
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.database.Database;
import net.sf.openrocket.database.Databases;
-import net.sf.openrocket.gui.UnitSelector;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import javax.swing.event.ListSelectionListener;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.ResizeLabel;
import net.sf.openrocket.gui.TextFieldListener;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Motor;
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+import javax.swing.BoundedRangeModel;
+import javax.swing.JSlider;
+import javax.swing.plaf.basic.BasicSliderUI;
+
+/**
+ * A simple slider that does not show the current value. GTK l&f shows the value, and cannot
+ * be configured otherwise(!).
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+
+public class BasicSlider extends JSlider {
+
+ public BasicSlider(BoundedRangeModel brm) {
+ this(brm,JSlider.HORIZONTAL,false);
+ }
+
+ public BasicSlider(BoundedRangeModel brm, int orientation) {
+ this(brm,orientation,false);
+ }
+
+ public BasicSlider(BoundedRangeModel brm, int orientation, boolean inverted) {
+ super(brm);
+ setOrientation(orientation);
+ setInverted(inverted);
+ setUI(new BasicSliderUI(this));
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ScrollPaneConstants;
+
+import net.miginfocom.swing.MigLayout;
+
+public class DescriptionArea extends JScrollPane {
+
+ private ResizeLabel text;
+ private MigLayout layout;
+ private JPanel panel;
+
+ public DescriptionArea(int rows) {
+ this(rows, -2);
+ }
+
+ public DescriptionArea(int rows, float size) {
+ super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ layout = new MigLayout("ins 0 2px, fill");
+ panel = new JPanel(layout);
+
+ text = new ResizeLabel(" ",size);
+ text.validate();
+ Dimension dim = text.getPreferredSize();
+ dim.height = (dim.height+2)*rows + 2;
+ this.setPreferredSize(dim);
+
+ panel.add(text, "growx");
+
+ this.setViewportView(panel);
+ this.revalidate();
+ }
+
+ public void setText(String txt) {
+ if (!txt.startsWith("<html>"))
+ txt = "<html>" + txt;
+ text.setText(txt);
+ }
+
+
+ @Override
+ public void validate() {
+
+ Rectangle dim = this.getViewportBorderBounds();
+ layout.setComponentConstraints(text, "width "+ dim.width + ", growx");
+ super.validate();
+ text.validate();
+
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+import java.awt.Font;
+import javax.swing.JLabel;
+
+/**
+ * A resizeable JLabel. The method resizeFont(float) changes the current font size by the
+ * given (positive or negative) amount. The change is relative to the current font size.
+ * <p>
+ * A nice small text is achievable by <code>new ResizeLabel("My text", -2);</code>
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+
+public class ResizeLabel extends JLabel {
+
+ public ResizeLabel() {
+ super();
+ }
+
+ public ResizeLabel(String text) {
+ super(text);
+ }
+
+ public ResizeLabel(float size) {
+ super();
+ resizeFont(size);
+ }
+
+ public ResizeLabel(String text, float size) {
+ super(text);
+ resizeFont(size);
+ }
+
+ public ResizeLabel(String text, int horizontalAlignment, float size) {
+ super(text, horizontalAlignment);
+ resizeFont(size);
+ }
+
+
+ public void resizeFont(float size) {
+ Font font = this.getFont();
+ font = font.deriveFont(font.getSize2D()+size);
+ this.setFont(font);
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JPanel;
+import javax.swing.JToggleButton;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.rocketcomponent.Configuration;
+
+
+public class StageSelector extends JPanel implements ChangeListener {
+
+ private final Configuration configuration;
+
+ private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
+
+ public StageSelector(Configuration configuration) {
+ super(new MigLayout("gap 0!"));
+ this.configuration = configuration;
+
+ JToggleButton button = new JToggleButton(new StageAction(0));
+ this.add(button);
+ buttons.add(button);
+
+ updateButtons();
+ configuration.addChangeListener(this);
+ }
+
+ private void updateButtons() {
+ int stages = configuration.getStageCount();
+ if (buttons.size() == stages)
+ return;
+
+ while (buttons.size() > stages) {
+ JToggleButton button = buttons.remove(buttons.size()-1);
+ this.remove(button);
+ }
+
+ while (buttons.size() < stages) {
+ JToggleButton button = new JToggleButton(new StageAction(buttons.size()));
+ this.add(button);
+ buttons.add(button);
+ }
+
+ this.revalidate();
+ }
+
+
+
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ updateButtons();
+ }
+
+
+ private class StageAction extends AbstractAction implements ChangeListener {
+ private final int stage;
+
+ public StageAction(final int stage) {
+ this.stage = stage;
+ configuration.addChangeListener(this);
+ stateChanged(null);
+ }
+
+ @Override
+ public Object getValue(String key) {
+ if (key.equals(NAME)) {
+ return "Stage "+(stage+1);
+ }
+ return super.getValue(key);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ configuration.setToStage(stage);
+
+// boolean state = (Boolean)getValue(SELECTED_KEY);
+// if (state == true) {
+// // Was disabled, now enabled
+// configuration.setToStage(stage);
+// } else {
+// // Was enabled, check what to do
+// if (configuration.isStageActive(stage + 1)) {
+// configuration.setToStage(stage);
+// } else {
+// if (stage == 0)
+// configuration.setAllStages();
+// else
+// configuration.setToStage(stage-1);
+// }
+// }
+// stateChanged(null);
+ }
+
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ this.putValue(SELECTED_KEY, configuration.isStageActive(stage));
+ }
+ }
+}
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+import java.awt.Desktop;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.swing.JLabel;
+
+/**
+ * A label of a URL that is clickable. Clicking the URL will launch the URL in
+ * the default browser if the Desktop class is supported.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class URLLabel extends JLabel {
+
+ private final String url;
+
+ public URLLabel(String urlLabel) {
+ super();
+
+ this.url = urlLabel;
+
+
+ if (Desktop.isDesktopSupported()) {
+
+ setText("<html><a href=\"" + url + "\">" + url + "</a>");
+
+ this.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ Desktop d = Desktop.getDesktop();
+ try {
+ d.browse(new URI(url));
+ } catch (URISyntaxException e1) {
+ throw new RuntimeException("BUG: Illegal URL: " + url, e1);
+ } catch (IOException e1) {
+ System.err.println("Unable to launch browser:");
+ e1.printStackTrace();
+ }
+ }
+ });
+
+ } else {
+ setText(url);
+ }
+ }
+}
--- /dev/null
+package net.sf.openrocket.gui.components;
+
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.ItemSelectable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.unit.UnitGroup;
+
+
+/**
+ * A Swing component that allows one to choose a unit from a UnitGroup within
+ * a DoubleModel model. The current unit of the model is shown as a JLabel, and
+ * the unit can be changed by clicking on the label.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+
+public class UnitSelector extends ResizeLabel implements ChangeListener, MouseListener,
+ ItemSelectable {
+
+ private final DoubleModel model;
+ private final Action[] extraActions;
+
+ private UnitGroup unitGroup;
+ private Unit currentUnit;
+
+ private final boolean showValue;
+
+ private final Border normalBorder;
+ private final Border withinBorder;
+
+
+ private final List<ItemListener> itemListeners = new ArrayList<ItemListener>();
+
+
+ /**
+ * Common private constructor that sets the values and sets up the borders.
+ * Either model or group must be null.
+ *
+ * @param model
+ * @param showValue
+ * @param group
+ * @param actions
+ */
+ private UnitSelector(DoubleModel model, boolean showValue, UnitGroup group,
+ Action[] actions) {
+ super();
+
+ this.model = model;
+ this.showValue = showValue;
+
+ if (model != null) {
+ this.unitGroup = model.getUnitGroup();
+ this.currentUnit = model.getCurrentUnit();
+ } else {
+ this.unitGroup = group;
+ this.currentUnit = group.getDefaultUnit();
+ }
+
+ this.extraActions = actions;
+
+ addMouseListener(this);
+
+ // Define borders to use:
+
+ normalBorder = new CompoundBorder(
+ new LineBorder(new Color(0f, 0f, 0f, 0.08f), 1), new EmptyBorder(1, 1, 1,
+ 1));
+ withinBorder = new CompoundBorder(new LineBorder(new Color(0f, 0f, 0f, 0.6f)),
+ new EmptyBorder(1, 1, 1, 1));
+
+ setBorder(normalBorder);
+ updateText();
+ }
+
+
+
+ public UnitSelector(DoubleModel model, Action... actions) {
+ this(model, false, actions);
+ }
+
+ public UnitSelector(DoubleModel model, boolean showValue, Action... actions) {
+ this(model, showValue, null, actions);
+
+ // Add model listener
+ this.model.addChangeListener(this);
+ }
+
+
+ public UnitSelector(UnitGroup group, Action... actions) {
+ this(null, false, group, actions);
+ }
+
+
+
+
+ /**
+ * Return the DoubleModel that is backing this selector up, or <code>null</code>.
+ * Either this method or {@link #getUnitGroup()} always returns <code>null</code>.
+ *
+ * @return the DoubleModel being used, or <code>null</code>.
+ */
+ public DoubleModel getModel() {
+ return model;
+ }
+
+
+ /**
+ * Return the unit group that is being shown, or <code>null</code>. Either this method
+ * or {@link #getModel()} always returns <code>null</code>.
+ *
+ * @return the UnitGroup being used, or <code>null</code>.
+ */
+ public UnitGroup getUnitGroup() {
+ return unitGroup;
+ }
+
+
+ public void setUnitGroup(UnitGroup group) {
+ if (model != null) {
+ throw new IllegalStateException(
+ "UnitGroup cannot be set when backed up with model.");
+ }
+
+ if (this.unitGroup == group)
+ return;
+
+ this.unitGroup = group;
+ this.currentUnit = group.getDefaultUnit();
+ updateText();
+ }
+
+
+ /**
+ * Return the currently selected unit. Works both when backup up with a DoubleModel
+ * and UnitGroup.
+ *
+ * @return the currently selected unit.
+ */
+ public Unit getSelectedUnit() {
+ return currentUnit;
+ }
+
+
+ /**
+ * Set the currently selected unit. Sets it to the DoubleModel if it is backed up
+ * by it.
+ *
+ * @param unit the unit to select.
+ */
+ public void setSelectedUnit(Unit unit) {
+ if (!unitGroup.contains(unit)) {
+ throw new IllegalArgumentException("unit " + unit
+ + " not contained in group " + unitGroup);
+ }
+
+ this.currentUnit = unit;
+ if (model != null) {
+ model.setCurrentUnit(unit);
+ }
+ updateText();
+ fireItemEvent();
+ }
+
+
+
+ /**
+ * Updates the text of the label
+ */
+ private void updateText() {
+ if (model != null) {
+
+ Unit unit = model.getCurrentUnit();
+ if (showValue) {
+ setText(unit.toStringUnit(model.getValue()));
+ } else {
+ setText(unit.getUnit());
+ }
+
+ } else if (unitGroup != null) {
+
+ setText(currentUnit.getUnit());
+
+ } else {
+ throw new IllegalStateException("Both model and unitGroup are null.");
+ }
+ }
+
+
+ /**
+ * Update the component when the DoubleModel changes.
+ */
+ public void stateChanged(ChangeEvent e) {
+ updateText();
+ }
+
+
+
+ //////// ItemListener handling ////////
+
+ public void addItemListener(ItemListener listener) {
+ itemListeners.add(listener);
+ }
+
+ public void removeItemListener(ItemListener listener) {
+ itemListeners.remove(listener);
+ }
+
+ protected void fireItemEvent() {
+ ItemEvent event = null;
+ ItemListener[] listeners = itemListeners.toArray(new ItemListener[0]);
+ for (ItemListener l: listeners) {
+ if (event == null) {
+ event = new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, getSelectedUnit(),
+ ItemEvent.SELECTED);
+ }
+ l.itemStateChanged(event);
+ }
+ }
+
+
+
+ //////// Popup ////////
+
+ private void popup() {
+ JPopupMenu popup = new JPopupMenu();
+
+ for (int i = 0; i < unitGroup.getUnitCount(); i++) {
+ Unit unit = unitGroup.getUnit(i);
+ JMenuItem item = new JMenuItem(unit.getUnit());
+ item.addActionListener(new UnitSelectorItem(unit));
+ popup.add(item);
+ }
+
+ for (int i = 0; i < extraActions.length; i++) {
+ if (extraActions[i] == null && i < extraActions.length - 1) {
+ popup.addSeparator();
+ } else {
+ popup.add(new JMenuItem(extraActions[i]));
+ }
+ }
+
+ Dimension d = getSize();
+ popup.show(this, 0, d.height);
+ }
+
+
+ /**
+ * ActionListener class that sets the currently selected unit.
+ */
+ private class UnitSelectorItem implements ActionListener {
+ private final Unit unit;
+
+ public UnitSelectorItem(Unit u) {
+ unit = u;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ setSelectedUnit(unit);
+ }
+ }
+
+
+ @Override
+ public Object[] getSelectedObjects() {
+ return new Object[]{ getSelectedUnit() };
+ }
+
+
+
+ //////// Mouse handling ////////
+
+ public void mouseClicked(MouseEvent e) {
+ if (unitGroup.getUnitCount() > 1)
+ popup();
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ if (unitGroup.getUnitCount() > 1)
+ setBorder(withinBorder);
+ }
+
+ public void mouseExited(MouseEvent e) {
+ setBorder(normalBorder);
+ }
+
+ public void mousePressed(MouseEvent e) {
+ } // Ignore
+
+ public void mouseReleased(MouseEvent e) {
+ } // Ignore
+
+}
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import javax.swing.table.AbstractTableModel;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.ResizeLabel;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.scalefigure.FinPointFigure;
import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
import net.sf.openrocket.gui.scalefigure.ScaleSelector;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.Resettable;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
import net.sf.openrocket.rocketcomponent.Clusterable;
import net.sf.openrocket.rocketcomponent.InnerTube;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
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.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.unit.UnitGroup;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
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.UnitSelector;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.unit.UnitGroup;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.main.MotorChooserDialog;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Motor;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.DescriptionArea;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+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.NoseCone;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel;
import net.sf.openrocket.gui.adaptors.MaterialModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.Parachute;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
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.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.RingComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.ResizeLabel;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.MaterialModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
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.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.unit.UnitGroup;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.ResizeLabel;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.MaterialModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.DescriptionArea;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+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.RocketComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import javax.swing.SwingConstants;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.BasicSlider;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
--- /dev/null
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.URLLabel;
+import net.sf.openrocket.gui.main.AboutDialog;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.JarUtil;
+import net.sf.openrocket.util.Prefs;
+
+public class BugDialog extends JDialog {
+
+ public BugDialog(JFrame parent) {
+ super(parent, "Bug reporing", true);
+
+ JPanel panel = new JPanel(new MigLayout("fill"));
+
+ panel.add(new JLabel("Please report any bugs you encounter as instructed at "),
+ "gap para, split 2");
+ panel.add(new URLLabel(AboutDialog.OPENROCKET_URL), "wrap rel");
+ panel.add(new JLabel("This allows us to make OpenRocket an even better simulator."),
+ "gap para, wrap para");
+
+ panel.add(new JLabel("<html><em>Please copy and paste the following information " +
+ "to the end of your bug report:</em>"), "gap para, wrap");
+
+
+ StringBuilder sb = new StringBuilder();
+ sb.append('\n');
+ sb.append("---------- Included system information ----------\n");
+ sb.append("OpenRocket version: " + Prefs.getVersion() + "\n");
+ sb.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n");
+ sb.append("System properties:\n");
+
+ // Sort the keys
+ SortedSet<String> keys = new TreeSet<String>();
+ for (Object key: System.getProperties().keySet()) {
+ keys.add((String)key);
+ }
+
+ for (String key: keys) {
+ String value = System.getProperty(key);
+ sb.append(" " + key + "=");
+ if (key.equals("line.separator")) {
+ for (char c: value.toCharArray()) {
+ sb.append(String.format("\\u%04x", (int)c));
+ }
+ } else {
+ sb.append(value);
+ }
+ sb.append('\n');
+ }
+
+ sb.append("---------- End system information ----------\n");
+ sb.append('\n');
+
+ JTextArea text = new JTextArea(sb.toString(), 15, 70);
+ text.setEditable(false);
+ panel.add(new JScrollPane(text), "grow, wrap para");
+
+ JButton close = new JButton("Close");
+ close.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ BugDialog.this.dispose();
+ }
+ });
+ panel.add(close, "right");
+
+ this.add(panel);
+
+ this.pack();
+ this.setLocationRelativeTo(parent);
+ GUIUtil.installEscapeCloseOperation(this);
+ GUIUtil.setDefaultButton(close);
+ }
+
+}
package net.sf.openrocket.gui.main;
-import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
-import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.ResizeLabel;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.URLLabel;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Prefs;
panel.add(new ResizeLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"), "ax 50%, wrap para");
- JLabel link;
-
- if (Desktop.isDesktopSupported()) {
-
- link = new JLabel("<html><a href=\"" + OPENROCKET_URL + "\">" +
- OPENROCKET_URL + "</a>");
- link.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- Desktop d = Desktop.getDesktop();
- try {
- d.browse(new URI(OPENROCKET_URL));
-
- } catch (URISyntaxException e1) {
- throw new RuntimeException("BUG: Illegal OpenRocket URL: "+OPENROCKET_URL,
- e1);
- } catch (IOException e1) {
- System.err.println("Unable to launch browser:");
- e1.printStackTrace();
- }
- }
- });
-
- } else {
- link = new JLabel(OPENROCKET_URL);
- }
- panel.add(link, "ax 50%, wrap para");
+ panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, wrap para");
JButton close = new JButton("Close");
import net.sf.openrocket.gui.PreferencesDialog;
import net.sf.openrocket.gui.StorageOptionChooser;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
+import net.sf.openrocket.gui.dialogs.BugDialog;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
}
@Override
public boolean accept(File f) {
+ if (f.isDirectory())
+ return true;
String name = f.getName().toLowerCase();
return name.endsWith(".ork") || name.endsWith(".ork.gz");
}
private RocketPanel rocketpanel;
private ComponentTree tree = null;
- private final TreeSelectionModel selectionModel;
+ private final TreeSelectionModel componentSelectionModel;
+ // private final ListSelectionModel simulationSelectionModel; ...
/** Actions available for rocket modifications */
private final RocketActions actions;
// Create the selection model that will be used
- selectionModel = new DefaultTreeSelectionModel();
- selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ componentSelectionModel = new DefaultTreeSelectionModel();
+ componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
- actions = new RocketActions(document, selectionModel, this);
+ actions = new RocketActions(document, componentSelectionModel, this);
// The main vertical split pane
JPanel panel = new JPanel(new MigLayout("fill, flowy","","[grow]"));
tree = new ComponentTree(rocket);
- tree.setSelectionModel(selectionModel);
+ tree.setSelectionModel(componentSelectionModel);
// Remove JTree key events that interfere with menu accelerators
InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED);
tree.addMouseListener(ml);
// Update dialog when selection is changed
- selectionModel.addTreeSelectionListener(new TreeSelectionListener() {
+ componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
// Scroll tree to the selected item
- TreePath path = selectionModel.getSelectionPath();
+ TreePath path = componentSelectionModel.getSelectionPath();
if (path == null)
return;
tree.scrollPathToVisible(path);
scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
- scroll.setViewportView(new ComponentAddButtons(document, selectionModel,
+ scroll.setViewportView(new ComponentAddButtons(document, componentSelectionModel,
scroll.getViewport()));
scroll.setBorder(null);
scroll.setViewportBorder(null);
menu.getAccessibleContext().setAccessibleDescription("Information about OpenRocket");
menubar.add(menu);
+
+
item = new JMenuItem("License",KeyEvent.VK_L);
item.getAccessibleContext().setAccessibleDescription("OpenRocket license information");
item.addActionListener(new ActionListener() {
});
menu.add(item);
+ item = new JMenuItem("Bug report",KeyEvent.VK_B);
+ item.getAccessibleContext().setAccessibleDescription("Information about reporting " +
+ "bugs in OpenRocket");
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ new BugDialog(BasicFrame.this).setVisible(true);
+ }
+ });
+ menu.add(item);
+
item = new JMenuItem("About",KeyEvent.VK_A);
item.getAccessibleContext().setAccessibleDescription("About OpenRocket");
item.addActionListener(new ActionListener() {
- // TODO: HIGH: Remember last directory on open/save
private void openAction() {
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(ROCKET_DESIGN_FILTER);
chooser.setMultiSelectionEnabled(true);
chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
- if (chooser.showOpenDialog(BasicFrame.this) != JFileChooser.APPROVE_OPTION)
+ if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;
Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.ResizeLabel;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.rocketcomponent.BodyComponent;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Parachute;
+import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.ShockCord;
import net.sf.openrocket.rocketcomponent.Streamer;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
+import net.sf.openrocket.util.Pair;
import net.sf.openrocket.util.Prefs;
/**
////////////////////////////////////////////
-// addButtonRow("Body components",row,
-// new ComponentButton(NoseCone.class,"Nose cone") {
-// @Override
-// public boolean isAddable(RocketComponent c) {
-// if (!(c instanceof ComponentAssembly))
-// return false;
-// if (c.getSiblingCount() == 0)
-// return true;
-// return false;
-// }
-// },
-// new BodyComponentButton(BodyTube.class,"Body tube"),
-// new BodyComponentButton(null,"Transition"));
-
addButtonRow("Body components and fin sets",row,
new BodyComponentButton(NoseCone.class,"Nose cone"),
);
row++;
-/////
/////////////////////////////////////////////
/**
* Return the position to add the component if component c is selected currently.
* The first element of the returned array is the RocketComponent to add the component
- * to, and the second (in any) an Integer telling the position of the component.
+ * to, and the second (if non-null) an Integer telling the position of the component.
* A return value of null means that the user cancelled addition of the component.
- * If the array has only one element, the component is added at the end of the sibling
+ * If the Integer is null, the component is added at the end of the sibling
* list. By default returns the end of the currently selected component.
*
* @param c The component currently selected
* @return The position to add the new component to, or null if should not add.
*/
- public Object[] getAdditionPosition(RocketComponent c) {
- return new Object[] { c };
+ public Pair<RocketComponent, Integer> getAdditionPosition(RocketComponent c) {
+ return new Pair<RocketComponent, Integer>(c, null);
}
/**
TreePath p = selectionModel.getSelectionPath();
if (p!= null)
c = (RocketComponent)p.getLastPathComponent();
- if (c != null) {
- Object[] pos = getAdditionPosition(c);
- if (pos==null || pos.length==0) {
- // Cancel addition
- return;
- }
- c = (RocketComponent)pos[0];
- if (pos.length>1)
- position = (Integer)pos[1];
+ Pair<RocketComponent, Integer> pos = getAdditionPosition(c);
+ if (pos==null) {
+ // Cancel addition
+ return;
}
+ c = pos.getU();
+ position = pos.getV();
+
if (c == null) {
// Should not occur
public boolean isAddable(RocketComponent c) {
if (super.isAddable(c))
return true;
- if (c instanceof BodyComponent) // Handled separately
+ // Handled separately:
+ if (c instanceof BodyComponent)
+ return true;
+ if (c == null || c instanceof Rocket)
return true;
return false;
}
@Override
- public Object[] getAdditionPosition(RocketComponent c) {
+ public Pair<RocketComponent, Integer> getAdditionPosition(RocketComponent c) {
if (super.isAddable(c)) // Handled automatically
return super.getAdditionPosition(c);
- // Handle BodyComponent separately
+
+ if (c == null || c instanceof Rocket) {
+ // Add as last body component of the last stage
+ Rocket rocket = document.getRocket();
+ return new Pair<RocketComponent,Integer>(rocket.getChild(rocket.getStageCount()-1),
+ null);
+ }
+
if (!(c instanceof BodyComponent))
return null;
RocketComponent parent = c.getParent();
return null;
case 1:
// Insert after current position
- return new Object[] { parent, new Integer(parent.getChildPosition(c)+1) };
+ return new Pair<RocketComponent,Integer>(parent, parent.getChildPosition(c)+1);
case 2:
// Insert at the end of the parent
- return new Object[] { parent };
+ return new Pair<RocketComponent,Integer>(parent, null);
default:
System.err.println("ERROR: Bad position type: "+pos);
Thread.dumpStack();
--- /dev/null
+package net.sf.openrocket.gui.main;
+
+public interface DocumentSelectionListener {
+
+ public static final int COMPONENT_SELECTION_CHANGE = 1;
+ public static final int SIMULATION_SELECTION_CHANGE = 2;
+
+ /**
+ * Called when the selection changes.
+ *
+ * @param changeType a bitmask of the type of change.
+ */
+ public void valueChanged(int changeType);
+
+}
--- /dev/null
+package net.sf.openrocket.gui.main;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+public class DocumentSelectionModel {
+
+ private static final Simulation[] NO_SIMULATION = new Simulation[0];
+
+ private final ComponentTreeSelectionListener componentTreeSelectionListener =
+ new ComponentTreeSelectionListener();
+ private final SimulationListSelectionListener simulationListSelectionListener =
+ new SimulationListSelectionListener();
+
+
+ private final OpenRocketDocument document;
+
+ private RocketComponent componentSelection = null;
+ private Simulation[] simulationSelection = NO_SIMULATION;
+
+ private TreeSelectionModel componentTreeSelectionModel = null;
+ private ListSelectionModel simulationListSelectionModel = null;
+
+ private final List<DocumentSelectionListener> listeners =
+ new ArrayList<DocumentSelectionListener>();
+
+
+
+ public DocumentSelectionModel(OpenRocketDocument document) {
+ this.document = document;
+ }
+
+
+
+
+ /**
+ * Return the currently selected simulations. Returns an empty array if none
+ * are selected.
+ *
+ * @return an array of the currently selected simulations, may be of zero length.
+ */
+ public Simulation[] getSelectedSimulations() {
+ return Arrays.copyOf(simulationSelection, simulationSelection.length);
+ }
+
+ /**
+ * Return the currently selected rocket component. Returns <code>null</code>
+ * if no rocket component is selected.
+ *
+ * @return the currently selected rocket component, or <code>null</code>.
+ */
+ public RocketComponent getSelectedComponent() {
+ return componentSelection;
+ }
+
+
+
+
+
+ public void attachComponentTreeSelectionModel(TreeSelectionModel model) {
+ if (componentTreeSelectionModel != null)
+ componentTreeSelectionModel.removeTreeSelectionListener(
+ componentTreeSelectionListener);
+
+ componentTreeSelectionModel = model;
+ if (model != null)
+ model.addTreeSelectionListener(componentTreeSelectionListener);
+ clearComponentSelection();
+ }
+
+
+
+ public void attachSimulationListSelectionModel(ListSelectionModel model) {
+ if (simulationListSelectionModel != null)
+ simulationListSelectionModel.removeListSelectionListener(
+ simulationListSelectionListener);
+
+ simulationListSelectionModel = model;
+ if (model != null)
+ model.addListSelectionListener(simulationListSelectionListener);
+ clearSimulationSelection();
+ }
+
+
+
+ public void clearSimulationSelection() {
+ if (simulationSelection.length == 0)
+ return;
+
+ simulationSelection = NO_SIMULATION;
+ if (simulationListSelectionModel != null)
+ simulationListSelectionModel.clearSelection();
+
+ fireDocumentSelection(DocumentSelectionListener.SIMULATION_SELECTION_CHANGE);
+ }
+
+
+ public void clearComponentSelection() {
+ if (componentSelection == null)
+ return;
+
+ componentSelection = null;
+ if (componentTreeSelectionModel != null)
+ componentTreeSelectionModel.clearSelection();
+
+ fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
+ }
+
+
+
+ public void addDocumentSelectionListener(DocumentSelectionListener l) {
+ listeners.add(l);
+ }
+
+ public void removeDocumentSelectionListener(DocumentSelectionListener l) {
+ listeners.remove(l);
+ }
+
+ protected void fireDocumentSelection(int type) {
+ DocumentSelectionListener[] array =
+ listeners.toArray(new DocumentSelectionListener[0]);
+
+ for (DocumentSelectionListener l: array) {
+ l.valueChanged(type);
+ }
+ }
+
+
+
+ private class ComponentTreeSelectionListener implements TreeSelectionListener {
+
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ TreePath path = componentTreeSelectionModel.getSelectionPath();
+ if (path == null) {
+ componentSelection = null;
+ fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
+ return;
+ }
+
+ componentSelection = (RocketComponent)path.getLastPathComponent();
+
+ clearSimulationSelection();
+ fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
+ }
+
+ }
+
+ private class SimulationListSelectionListener implements ListSelectionListener {
+
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ int min = simulationListSelectionModel.getMinSelectionIndex();
+ int max = simulationListSelectionModel.getMaxSelectionIndex();
+ if (min < 0 || max < 0) {
+ simulationSelection = NO_SIMULATION;
+ fireDocumentSelection(DocumentSelectionListener.SIMULATION_SELECTION_CHANGE);
+ return;
+ }
+
+ ArrayList<Simulation> list = new ArrayList<Simulation>();
+ for (int i = min; i <= max; i++) {
+ if (simulationListSelectionModel.isSelectedIndex(i) &&
+ (i < document.getSimulationCount())) {
+ list.add(document.getSimulation(i));
+ }
+ }
+ simulationSelection = list.toArray(NO_SIMULATION);
+
+ clearComponentSelection();
+ fireDocumentSelection(DocumentSelectionListener.SIMULATION_SELECTION_CHANGE);
+ }
+
+ }
+
+}
import javax.swing.JTextArea;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.ResizeLabel;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.util.GUIUtil;
public class LicenseDialog extends JDialog {
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.database.Databases;
-import net.sf.openrocket.gui.ResizeLabel;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.rocketcomponent.Motor;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil;
--- /dev/null
+package net.sf.openrocket.gui.main;
+
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+public class OpenRocketClipboard {
+
+ private static Object clipboard = null;
+
+ private OpenRocketClipboard() {
+ // Disallow instantiation
+ }
+
+
+ /**
+ * Return the <code>RocketComponent</code> contained in the clipboard, or
+ * <code>null</code>.
+ *
+ * @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 Simulation[] getSimulations() {
+ return null; // TODO
+ }
+
+
+}
/**
- * A class that holds Actions for common rocket operations such as
+ * A class that holds Actions for common rocket and simulation operations such as
* cut/copy/paste/delete etc.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.ExtendedISAModel;
import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.DescriptionArea;
import net.sf.openrocket.gui.SpinnerEditor;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
+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.gui.plot.Axis;
import net.sf.openrocket.gui.plot.PlotConfiguration;
import net.sf.openrocket.gui.plot.PlotPanel;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.ResizeLabel;
import net.sf.openrocket.gui.adaptors.Column;
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
+import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.simulation.FlightData;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.ResizeLabel;
-import net.sf.openrocket.gui.UnitSelector;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataBranch.Type;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.BasicSlider;
-import net.sf.openrocket.gui.StageSelector;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.StageSelector;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.gui.figureelements.CGCaret;
import net.sf.openrocket.gui.figureelements.CPCaret;
*
* Get the components clicked.
* If no component is clicked, do nothing.
- * If the primary currently selected component is in the set, keep it,
+ * If the currently selected component is in the set, keep it,
* unless the selector specified is pressed. If it is pressed, cycle to
* the next component. Otherwise select the first component in the list.
*/
// Currently selected component not clicked
if (path == null) {
- path = ComponentTreeModel.makeTreePath(clicked[0]);
+ if (event.isShiftDown() && event.getClickCount()==1 && clicked.length>1) {
+ path = ComponentTreeModel.makeTreePath(clicked[1]);
+ } else {
+ path = ComponentTreeModel.makeTreePath(clicked[0]);
+ }
}
// Set selection and check for double-click
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-import net.sf.openrocket.gui.UnitSelector;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.unit.Tick;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
--- /dev/null
+package net.sf.openrocket.startup;
+
+import java.awt.GraphicsEnvironment;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.swing.JOptionPane;
+
+
+/**
+ * A startup class that checks that a suitable JRE environment is being run.
+ * If the environment is too old the execution is canceled, and if OpenJDK is being
+ * used warns the user of problems and confirms whether to continue.
+ * <p>
+ * Note: This class must be Java 1.4 compatible and calls the next class using
+ * only reflection.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class Startup {
+
+ public static final String START_CLASS = "net.sf.openrocket.gui.main.BasicFrame";
+
+ public static final int REQUIRED_MAJOR_VERSION = 1;
+ public static final int REQUIRED_MINOR_VERSION = 6;
+
+
+ public static void main(String[] args) {
+
+ checkVersion();
+ checkHead();
+ checkOpenJDK();
+
+ // Load and execute START_CLASS
+ try {
+
+ Class cls = Class.forName(START_CLASS);
+ Method m = cls.getMethod("main", String[].class);
+ m.invoke(null, new Object[] { args });
+
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ error("Error starting main class!", "Please report a bug.");
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ error("Error starting main class!", "Please report a bug.");
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ error("Error starting main class!", "Please report a bug.");
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ error("Error starting main class!", "Please report a bug.");
+ }
+
+ }
+
+
+ /**
+ * Check that the JRE version is high enough.
+ */
+ private static void checkVersion() {
+ String[] version = System.getProperty("java.specification.version", "").split("\\.");
+
+ String jreName = System.getProperty("java.vm.name", "(unknown)");
+ String jreVersion = System.getProperty("java.runtime.version", "(unknown)");
+ String jreVendor = System.getProperty("java.vendor", "(unknown)");
+
+ int major, minor;
+
+ try {
+ major = Integer.parseInt(version[0]);
+ minor = Integer.parseInt(version[1]);
+
+ if (major < REQUIRED_MAJOR_VERSION ||
+ (major == REQUIRED_MAJOR_VERSION && minor < REQUIRED_MINOR_VERSION)) {
+ error("Java SE version 6 is required to run OpenRocket.",
+ "You are currently running " + jreName + " version " +
+ jreVersion + " by " + jreVendor);
+ }
+
+ } catch (RuntimeException e) {
+
+ confirm("The Java version in use could not be detected.",
+ "OpenRocket requires at least Java SE 6.",
+ "Continue anyway?");
+
+ }
+
+ }
+
+
+ /**
+ * Check that the JRE is not running headless.
+ */
+ private static void checkHead() {
+
+ if (GraphicsEnvironment.isHeadless()) {
+ System.err.println();
+ System.err.println("OpenRocket cannot currently be run without the graphical " +
+ "user interface.");
+ System.err.println();
+ System.exit(1);
+ }
+
+ }
+
+
+ /**
+ * Check whether OpenJDK is being used, and if it is warn the user about
+ * problems and confirm whether to continue.
+ */
+ private static void checkOpenJDK() {
+
+ if (System.getProperty("java.runtime.name", "").toLowerCase().indexOf("icedtea")>=0 ||
+ System.getProperty("java.vm.name", "").toLowerCase().indexOf("openjdk")>=0) {
+
+ String jreName = System.getProperty("java.vm.name", "(unknown)");
+ String jreVersion = System.getProperty("java.runtime.version", "(unknown)");
+ String jreVendor = System.getProperty("java.vendor", "(unknown)");
+
+ confirm("OpenJDK is known to have problems running OpenRocket.",
+ " ",
+ "You are currently running " + jreName + " version " +
+ jreVersion + " by " + jreVendor,
+ "Do you want to continue?");
+
+ }
+ }
+
+
+
+
+ /**
+ * Presents an error message to the user and exits the application.
+ *
+ * @param message an array of messages to present.
+ */
+ private static void error(String ... message) {
+
+ System.err.println();
+ System.err.println("Error starting OpenRocket:");
+ System.err.println();
+ for (int i=0; i < message.length; i++) {
+ System.err.println(message[i]);
+ }
+ System.err.println();
+
+
+ if (!GraphicsEnvironment.isHeadless()) {
+
+ JOptionPane.showMessageDialog(null, message, "Error starting OpenRocket",
+ JOptionPane.ERROR_MESSAGE);
+
+ }
+
+ System.exit(1);
+ }
+
+
+ /**
+ * Presents the user with a message dialog and asks whether to continue.
+ * If the user does not select "Yes" the the application exits.
+ *
+ * @param message the message Strings to show.
+ */
+ private static void confirm(String ... message) {
+
+ if (!GraphicsEnvironment.isHeadless()) {
+
+ if (JOptionPane.showConfirmDialog(null, message, "Error starting OpenRocket",
+ JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
+ System.exit(1);
+ }
+
+ }
+
+ }
+
+
+}
@Override
public String toString(double value) {
- return String.format(formatString, value);
+ return String.format(formatString, this.toUnit(value));
}
--- /dev/null
+package net.sf.openrocket.util;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.CodeSource;
+
+import net.sf.openrocket.database.Database;
+
+public class JarUtil {
+
+ /**
+ * Return the a File object pointing to the JAR file that this class belongs to,
+ * or <code>null</code> if it cannot be found.
+ *
+ * @return a File object of the current Java archive, or <code>null</code>
+ */
+ public static File getCurrentJarFile() {
+ // Find the jar file this class is contained in
+ URL jarUrl = null;
+ CodeSource codeSource = Database.class.getProtectionDomain().getCodeSource();
+ if (codeSource != null)
+ jarUrl = codeSource.getLocation();
+
+ if (jarUrl == null) {
+ return null;
+ }
+ return urlToFile(jarUrl);
+ }
+
+
+
+ public static File urlToFile(URL url) {
+ URI uri;
+ try {
+ uri = url.toURI();
+ } catch (URISyntaxException e) {
+ try {
+ uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(),
+ url.getPort(), url.getPath(), url.getQuery(), url.getRef());
+ } catch (URISyntaxException e1) {
+ throw new IllegalArgumentException("Broken URL: " + url);
+ }
+ }
+ return new File(uri);
+ }
+
+
+}
--- /dev/null
+package net.sf.openrocket.util;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class PrintProperties {
+
+ public static void main(String[] args) {
+
+ // Sort the keys
+ SortedSet<String> keys = new TreeSet<String>();
+ for (Object key: System.getProperties().keySet()) {
+ keys.add((String)key);
+ }
+
+ for (String key: keys) {
+ System.out.println(key + "=" + System.getProperty((String)key));
+ }
+
+ }
+
+}