Motor chooser search field and new edit motor config dialog
This commit is contained in:
		
							parent
							
								
									a65e061445
								
							
						
					
					
						commit
						6e14883374
					
				
							
								
								
									
										13
									
								
								ChangeLog
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								ChangeLog
									
									
									
									
									
								
							@ -1,3 +1,16 @@
 | 
			
		||||
2009-06-20  Sampo Niskanen
 | 
			
		||||
 | 
			
		||||
	* New edit motor configurations dialog
 | 
			
		||||
	* Changed FreeformFinSet to throw checked exceptions
 | 
			
		||||
 | 
			
		||||
2009-06-11  Sampo Niskanen
 | 
			
		||||
 | 
			
		||||
	* Added search field to motor chooser dialog
 | 
			
		||||
 | 
			
		||||
2009-06-09  Sampo Niskanen
 | 
			
		||||
 | 
			
		||||
	* Release 0.9.1
 | 
			
		||||
 | 
			
		||||
2009-06-08  Sampo Niskanen
 | 
			
		||||
 | 
			
		||||
	* Fixed loading of icons from JAR
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,11 @@
 | 
			
		||||
 | 
			
		||||
OpenRocket 0.9.2  (future):
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
- a new and enhanced "Edit motor configurations" dialog
 | 
			
		||||
- a search field in the motor selection dialog
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OpenRocket 0.9.1  (2009-06-09):
 | 
			
		||||
-------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										131
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								TODO
									
									
									
									
									
								
							@ -1,126 +1,35 @@
 | 
			
		||||
 | 
			
		||||
GUI:
 | 
			
		||||
 | 
			
		||||
- Preferences dialog
 | 
			
		||||
Feature roadmap for OpenRocket 1.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUGS:
 | 
			
		||||
Must-have:
 | 
			
		||||
 | 
			
		||||
- Exporting flight data
 | 
			
		||||
- Store custom materials
 | 
			
		||||
- Read more thrust curve formats / go through thrust curves and correct errors
 | 
			
		||||
- Create application icon and take into use
 | 
			
		||||
- Fix engine block icons
 | 
			
		||||
- Progress and error dialogs when reading/writing files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COMPUTATION:
 | 
			
		||||
Maybe:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FILE/STORAGE:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OTHER:
 | 
			
		||||
 | 
			
		||||
- web-sivut
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DIPPA:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
LATER:
 | 
			
		||||
 | 
			
		||||
- Simulation delete/copy/paste hotkeys
 | 
			
		||||
  (either component or simulation selected, but not both)
 | 
			
		||||
- Add BodyComponent at end of rocket when no component is selected
 | 
			
		||||
- Showing events in plot (maybe future)
 | 
			
		||||
- Search field in motor selection dialog
 | 
			
		||||
- Reading (writing) .RKT format
 | 
			
		||||
- Showing events in plots
 | 
			
		||||
- Through-the-wall fins
 | 
			
		||||
- Store materials
 | 
			
		||||
 | 
			
		||||
- Streamer CD estimation
 | 
			
		||||
 | 
			
		||||
- exporting (maybe later)
 | 
			
		||||
 | 
			
		||||
- Make ThicknessRingComponent implement RadialParent and allow
 | 
			
		||||
  attaching components to a TubeCoupler
 | 
			
		||||
- Reading thrust curves from external directory
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Postponed:
 | 
			
		||||
 | 
			
		||||
- Importing flight data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DONE:
 | 
			
		||||
Done:
 | 
			
		||||
 | 
			
		||||
- Automatic diameters of body components
 | 
			
		||||
- Copy/paste
 | 
			
		||||
- Search field in motor selection dialog
 | 
			
		||||
- Motor selection/editing from Edit configurations dialog
 | 
			
		||||
- Change FreeformFinSet to throw checked exceptions
 | 
			
		||||
 | 
			
		||||
18.4.:
 | 
			
		||||
- Esc, Ctrl-Z and Y etc.
 | 
			
		||||
- Look and feel
 | 
			
		||||
 | 
			
		||||
19.4.:
 | 
			
		||||
- Nose cone and transition shoulders in GUI
 | 
			
		||||
- zoom, cut/copy/paste etc. icons
 | 
			
		||||
 | 
			
		||||
23.4.:
 | 
			
		||||
- Figure or rocket not updating when using a new BasicFrame
 | 
			
		||||
 | 
			
		||||
24.4.:
 | 
			
		||||
- File save and load
 | 
			
		||||
- Motor configuration editing		(pre-alpha)
 | 
			
		||||
- Save simulations
 | 
			
		||||
 | 
			
		||||
25.4.:
 | 
			
		||||
- Multi-stages simulation		(pre-alpha)
 | 
			
		||||
- Make sure simulations end
 | 
			
		||||
- Mass and CG overrides			(pre-alpha)
 | 
			
		||||
- General loader
 | 
			
		||||
 | 
			
		||||
26.4.:
 | 
			
		||||
- Centering ring inner diameter automatics	(pre-alpha)
 | 
			
		||||
- Landing simulation			(pre-alpha ??)
 | 
			
		||||
- Parachute/Streamer editing in GUI	(pre-alpha)
 | 
			
		||||
- Launch lug editing in GUI		(pre-alpha)
 | 
			
		||||
 | 
			
		||||
29.4.:
 | 
			
		||||
- Actual plotting done
 | 
			
		||||
- Refactored source code packages
 | 
			
		||||
 | 
			
		||||
2.5.:
 | 
			
		||||
- Plotting				(pre-alpha)
 | 
			
		||||
- Gravity model
 | 
			
		||||
- More units and specific custom units (angle, temperature, ...)
 | 
			
		||||
- Transition/Nose cone description text wrapping
 | 
			
		||||
- Fin set CP jumps at Mach 0.9
 | 
			
		||||
 | 
			
		||||
- Error dialogs for load/save/etc
 | 
			
		||||
 | 
			
		||||
3.5.:
 | 
			
		||||
- More materials			(pre-alpha)
 | 
			
		||||
- File opening from command line
 | 
			
		||||
 | 
			
		||||
9.5.:
 | 
			
		||||
- Rocket configuration dialog
 | 
			
		||||
- Warnings in poor conditions (transition supersonic)
 | 
			
		||||
- New or old fin-body interference?
 | 
			
		||||
- poista tiedot laminaarisesta vastuksesta
 | 
			
		||||
- vertailuosio
 | 
			
		||||
 | 
			
		||||
11.5.:
 | 
			
		||||
- Better default values for components
 | 
			
		||||
- Component analysis dialog show zero total mass and CG
 | 
			
		||||
- Compression support in save
 | 
			
		||||
- Simulation storage options
 | 
			
		||||
 | 
			
		||||
12.5.:
 | 
			
		||||
- Load simulations
 | 
			
		||||
- Update file version to 1.0
 | 
			
		||||
 | 
			
		||||
13.5.:
 | 
			
		||||
- statistiikat softasta
 | 
			
		||||
 | 
			
		||||
17.5.:
 | 
			
		||||
- jonkin verran TODOja
 | 
			
		||||
- conclusion
 | 
			
		||||
- viitteet
 | 
			
		||||
- Draw the component icons
 | 
			
		||||
- splashscreen
 | 
			
		||||
 | 
			
		||||
18.5.:
 | 
			
		||||
- About dialog + version number
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
 | 
			
		||||
# The OpenRocket build version
 | 
			
		||||
build.version=0.9.1
 | 
			
		||||
build.version=0.9.2pre
 | 
			
		||||
 | 
			
		||||
# The source of the package.  When building a package for a specific
 | 
			
		||||
# distribution (Debian, Fedora etc.), this should be changed appropriately!
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
 | 
			
		||||
	private LinkedList<String> undoDescription = new LinkedList<String>();
 | 
			
		||||
	
 | 
			
		||||
	private String nextDescription = null;
 | 
			
		||||
	private String storedDescription = null;
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private File file = null;
 | 
			
		||||
@ -70,9 +71,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
 | 
			
		||||
		this.configuration = configuration;
 | 
			
		||||
		this.rocket = configuration.getRocket();
 | 
			
		||||
		
 | 
			
		||||
		undoHistory.add(rocket.copy());
 | 
			
		||||
		undoDescription.add(null);
 | 
			
		||||
		undoPosition = 0;
 | 
			
		||||
		clearUndo();
 | 
			
		||||
		
 | 
			
		||||
		undoAction = new UndoRedoAction(UndoRedoAction.UNDO);
 | 
			
		||||
		redoAction = new UndoRedoAction(UndoRedoAction.REDO);
 | 
			
		||||
@ -234,6 +233,31 @@ public class OpenRocketDocument implements ComponentChangeListener {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Start a time-limited undoable operation.  After the operation {@link #stopUndo()}
 | 
			
		||||
	 * must be called, which will restore the previous undo description into effect.
 | 
			
		||||
	 * Only one level of start-stop undo descriptions is supported, i.e. start-stop
 | 
			
		||||
	 * undo cannot be nested, and no other undo operations may be called between
 | 
			
		||||
	 * the start and stop calls.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param description	Description of the following undoable operations.
 | 
			
		||||
	 */
 | 
			
		||||
	public void startUndo(String description) {
 | 
			
		||||
		storedDescription = nextDescription;
 | 
			
		||||
		addUndoPosition(description);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * End the previous time-limited undoable operation.  This must be called after
 | 
			
		||||
	 * {@link #startUndo(String)} has been called before any other undo operations are
 | 
			
		||||
	 * performed.
 | 
			
		||||
	 */
 | 
			
		||||
	public void stopUndo() {
 | 
			
		||||
		addUndoPosition(storedDescription);
 | 
			
		||||
		storedDescription = null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	public Action getUndoAction() {
 | 
			
		||||
		return undoAction;
 | 
			
		||||
	}
 | 
			
		||||
@ -244,6 +268,24 @@ public class OpenRocketDocument implements ComponentChangeListener {
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Clear the undo history.
 | 
			
		||||
	 */
 | 
			
		||||
	public void clearUndo() {
 | 
			
		||||
		undoHistory.clear();
 | 
			
		||||
		undoDescription.clear();
 | 
			
		||||
		
 | 
			
		||||
		undoHistory.add(rocket.copy());
 | 
			
		||||
		undoDescription.add(null);
 | 
			
		||||
		undoPosition = 0;
 | 
			
		||||
		
 | 
			
		||||
		if (undoAction != null)
 | 
			
		||||
			undoAction.setAllValues();
 | 
			
		||||
		if (redoAction != null)
 | 
			
		||||
			redoAction.setAllValues();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public void componentChanged(ComponentChangeEvent e) {
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import net.sf.openrocket.rocketcomponent.EngineBlock;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.ExternalComponent;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.FinSet;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.InnerTube;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.InternalComponent;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
 | 
			
		||||
@ -137,6 +138,8 @@ public class OpenRocketLoader extends RocketLoader {
 | 
			
		||||
		doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip);
 | 
			
		||||
		doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed
 | 
			
		||||
		doc.getDefaultStorageOptions().setExplicitlySet(false);
 | 
			
		||||
		
 | 
			
		||||
		doc.clearUndo();
 | 
			
		||||
		return doc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -977,7 +980,7 @@ class FinSetPointHandler extends ElementHandler {
 | 
			
		||||
			String content, WarningSet warnings) {
 | 
			
		||||
		try {
 | 
			
		||||
			finset.setPoints(coordinates.toArray(new Coordinate[0]));
 | 
			
		||||
		} catch (IllegalArgumentException e) {
 | 
			
		||||
		} catch (IllegalFinPointException e) {
 | 
			
		||||
			warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring."));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package net.sf.openrocket.file;
 | 
			
		||||
 | 
			
		||||
import java.awt.Component;
 | 
			
		||||
import java.io.BufferedInputStream;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
@ -7,6 +8,8 @@ import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import javax.swing.ProgressMonitorInputStream;
 | 
			
		||||
 | 
			
		||||
import net.sf.openrocket.aerodynamics.WarningSet;
 | 
			
		||||
import net.sf.openrocket.document.OpenRocketDocument;
 | 
			
		||||
 | 
			
		||||
@ -15,6 +18,19 @@ public abstract class RocketLoader {
 | 
			
		||||
	protected final WarningSet warnings = new WarningSet();
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	public final OpenRocketDocument load(File source, Component parent) 
 | 
			
		||||
	throws RocketLoadException {
 | 
			
		||||
		warnings.clear();
 | 
			
		||||
		
 | 
			
		||||
		try {
 | 
			
		||||
			return load(new BufferedInputStream(new ProgressMonitorInputStream(
 | 
			
		||||
					parent, "Loading " + source.getName(),
 | 
			
		||||
					new FileInputStream(source))));
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			throw new RocketLoadException("File not found: " + source);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Loads a rocket from the specified File object.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
@ -1,44 +1,22 @@
 | 
			
		||||
package net.sf.openrocket.gui.adaptors;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.awt.event.ActionListener;
 | 
			
		||||
import java.awt.event.MouseAdapter;
 | 
			
		||||
import java.awt.event.MouseEvent;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import javax.swing.ComboBoxModel;
 | 
			
		||||
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.JTable;
 | 
			
		||||
import javax.swing.JTextField;
 | 
			
		||||
import javax.swing.ListSelectionModel;
 | 
			
		||||
import javax.swing.SwingUtilities;
 | 
			
		||||
import javax.swing.event.ChangeEvent;
 | 
			
		||||
import javax.swing.event.ChangeListener;
 | 
			
		||||
import javax.swing.event.EventListenerList;
 | 
			
		||||
import javax.swing.event.ListDataEvent;
 | 
			
		||||
import javax.swing.event.ListDataListener;
 | 
			
		||||
import javax.swing.event.ListSelectionEvent;
 | 
			
		||||
import javax.swing.event.ListSelectionListener;
 | 
			
		||||
 | 
			
		||||
import net.miginfocom.swing.MigLayout;
 | 
			
		||||
import net.sf.openrocket.gui.TextFieldListener;
 | 
			
		||||
import net.sf.openrocket.gui.components.ResizeLabel;
 | 
			
		||||
import net.sf.openrocket.gui.dialogs.EditMotorConfigurationDialog;
 | 
			
		||||
import net.sf.openrocket.gui.main.BasicFrame;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Configuration;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Motor;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.MotorMount;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Rocket;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
 | 
			
		||||
import net.sf.openrocket.util.GUIUtil;
 | 
			
		||||
 | 
			
		||||
public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
 | 
			
		||||
 | 
			
		||||
@ -91,13 +69,8 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
 | 
			
		||||
			SwingUtilities.invokeLater(new Runnable() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void run() {
 | 
			
		||||
					EditConfigurationDialog dialog = new EditConfigurationDialog();
 | 
			
		||||
					dialog.setVisible(true);
 | 
			
		||||
					
 | 
			
		||||
					if (dialog.isRowSelected()) {
 | 
			
		||||
						rocket.getDefaultConfiguration().setMotorConfigurationID(
 | 
			
		||||
								dialog.getSelectedID());
 | 
			
		||||
					}
 | 
			
		||||
					new EditMotorConfigurationDialog(rocket, BasicFrame.findFrame(rocket))
 | 
			
		||||
						.setVisible(true);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
@ -185,198 +158,5 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private class EditConfigurationDialog extends JDialog {
 | 
			
		||||
		private final ColumnTableModel tableModel;
 | 
			
		||||
		private String[] ids;
 | 
			
		||||
		int selection = -1;
 | 
			
		||||
		
 | 
			
		||||
		private final JButton addButton;
 | 
			
		||||
		private final JButton removeButton;
 | 
			
		||||
		private final JTextField nameField;
 | 
			
		||||
		
 | 
			
		||||
		private final JTable table;
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		public boolean isRowSelected() {
 | 
			
		||||
			return selection >= 0;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public String getSelectedID() {
 | 
			
		||||
			if (selection >= 0)
 | 
			
		||||
				return ids[selection];
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		public EditConfigurationDialog() {
 | 
			
		||||
			super((JFrame)null, "Edit configurations", true);
 | 
			
		||||
 | 
			
		||||
			ids = rocket.getMotorConfigurationIDs();
 | 
			
		||||
			
 | 
			
		||||
			// Create columns
 | 
			
		||||
			ArrayList<Column> columnList = new ArrayList<Column>();
 | 
			
		||||
			columnList.add(new Column("Name") {
 | 
			
		||||
				@Override
 | 
			
		||||
				public Object getValueAt(int row) {
 | 
			
		||||
					return rocket.getMotorConfigurationNameOrDescription(ids[row]);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			
 | 
			
		||||
			// Create columns from the motor mounts
 | 
			
		||||
			Iterator<RocketComponent> iterator = rocket.deepIterator();
 | 
			
		||||
			while (iterator.hasNext()) {
 | 
			
		||||
				RocketComponent c = iterator.next();
 | 
			
		||||
				if (!(c instanceof MotorMount))
 | 
			
		||||
					continue;
 | 
			
		||||
				
 | 
			
		||||
				final MotorMount mount = (MotorMount)c;
 | 
			
		||||
				if (!mount.isMotorMount())
 | 
			
		||||
					continue;
 | 
			
		||||
				
 | 
			
		||||
				Column col = new Column(c.getName()) {
 | 
			
		||||
					@Override
 | 
			
		||||
					public Object getValueAt(int row) {
 | 
			
		||||
						Motor motor = mount.getMotor(ids[row]);
 | 
			
		||||
						if (motor == null)
 | 
			
		||||
							return "";
 | 
			
		||||
						return motor.getDesignation(mount.getMotorDelay(ids[row]));
 | 
			
		||||
					}
 | 
			
		||||
				};
 | 
			
		||||
				columnList.add(col);
 | 
			
		||||
			}
 | 
			
		||||
			tableModel = new ColumnTableModel(columnList.toArray(new Column[0])) {
 | 
			
		||||
				@Override
 | 
			
		||||
				public int getRowCount() {
 | 
			
		||||
					return ids.length;
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			// Create the panel
 | 
			
		||||
			JPanel panel = new JPanel(new MigLayout("fill","[shrink][grow]"));
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			panel.add(new JLabel("Configuration name:"), "gapright para");
 | 
			
		||||
			nameField = new JTextField();
 | 
			
		||||
			new TextFieldListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void setText(String text) {
 | 
			
		||||
					if (selection < 0 || ids[selection] == null)
 | 
			
		||||
						return;
 | 
			
		||||
					rocket.setMotorConfigurationName(ids[selection], text);
 | 
			
		||||
					fireChange();
 | 
			
		||||
				}
 | 
			
		||||
			}.listenTo(nameField);
 | 
			
		||||
			panel.add(nameField, "growx, wrap");
 | 
			
		||||
			
 | 
			
		||||
			panel.add(new ResizeLabel("Leave empty for default description", -2), 
 | 
			
		||||
					"skip, growx, wrap para");
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			table = new JTable(tableModel);
 | 
			
		||||
			table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 | 
			
		||||
			table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void valueChanged(ListSelectionEvent e) {
 | 
			
		||||
					updateSelection();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			// Mouse listener to act on double-clicks
 | 
			
		||||
			table.addMouseListener(new MouseAdapter() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void mouseClicked(MouseEvent e) {
 | 
			
		||||
					if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
 | 
			
		||||
						EditConfigurationDialog.this.dispose();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			 
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			JScrollPane scrollpane = new JScrollPane(table);
 | 
			
		||||
			panel.add(scrollpane, "spanx, height 150lp, width 400lp, grow, wrap");
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			addButton = new JButton("New");
 | 
			
		||||
			addButton.addActionListener(new ActionListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void actionPerformed(ActionEvent e) {
 | 
			
		||||
					String id = rocket.newMotorConfigurationID();
 | 
			
		||||
					ids = rocket.getMotorConfigurationIDs();
 | 
			
		||||
					tableModel.fireTableDataChanged();
 | 
			
		||||
					int sel;
 | 
			
		||||
					for (sel=0; sel < ids.length; sel++) {
 | 
			
		||||
						if (id.equals(ids[sel]))
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					table.getSelectionModel().addSelectionInterval(sel, sel);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			panel.add(addButton, "growx, spanx, split 2");
 | 
			
		||||
			
 | 
			
		||||
			removeButton = new JButton("Remove");
 | 
			
		||||
			removeButton.addActionListener(new ActionListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void actionPerformed(ActionEvent e) {
 | 
			
		||||
					int sel = table.getSelectedRow();
 | 
			
		||||
					if (sel < 0 || sel >= ids.length || ids[sel] == null)
 | 
			
		||||
						return;
 | 
			
		||||
					rocket.removeMotorConfigurationID(ids[sel]);
 | 
			
		||||
					ids = rocket.getMotorConfigurationIDs();
 | 
			
		||||
					tableModel.fireTableDataChanged();
 | 
			
		||||
					if (sel >= ids.length)
 | 
			
		||||
						sel--;
 | 
			
		||||
					table.getSelectionModel().addSelectionInterval(sel, sel);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			panel.add(removeButton, "growx, wrap para");
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			JButton close = new JButton("Close");
 | 
			
		||||
			close.addActionListener(new ActionListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void actionPerformed(ActionEvent e) {
 | 
			
		||||
					EditConfigurationDialog.this.dispose();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			panel.add(close, "spanx, alignx 100%");
 | 
			
		||||
			
 | 
			
		||||
			this.getRootPane().setDefaultButton(close);
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
			this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 | 
			
		||||
			GUIUtil.installEscapeCloseOperation(this);
 | 
			
		||||
			this.setLocationByPlatform(true);
 | 
			
		||||
			
 | 
			
		||||
			updateSelection();
 | 
			
		||||
			
 | 
			
		||||
			this.add(panel);
 | 
			
		||||
			this.validate();
 | 
			
		||||
			this.pack();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		private void fireChange() {
 | 
			
		||||
			int sel = table.getSelectedRow();
 | 
			
		||||
			tableModel.fireTableDataChanged();
 | 
			
		||||
			table.getSelectionModel().addSelectionInterval(sel, sel);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		private void updateSelection() {
 | 
			
		||||
			selection = table.getSelectedRow();
 | 
			
		||||
			if (selection < 0  ||  ids[selection] == null) {
 | 
			
		||||
				removeButton.setEnabled(false);
 | 
			
		||||
				nameField.setEnabled(false);
 | 
			
		||||
				nameField.setText("");
 | 
			
		||||
			} else {
 | 
			
		||||
				removeButton.setEnabled(true);
 | 
			
		||||
				nameField.setEnabled(true);
 | 
			
		||||
				nameField.setText(rocket.getMotorConfigurationName(ids[selection]));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import net.sf.openrocket.gui.scalefigure.ScaleSelector;
 | 
			
		||||
import net.sf.openrocket.material.Material;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.FinSet;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
 | 
			
		||||
import net.sf.openrocket.unit.UnitGroup;
 | 
			
		||||
import net.sf.openrocket.util.Coordinate;
 | 
			
		||||
@ -275,7 +276,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
 | 
			
		||||
				finset.addPoint(index);
 | 
			
		||||
				try {
 | 
			
		||||
					finset.setPoint(index, point.x, point.y);
 | 
			
		||||
				} catch (IllegalArgumentException ignore) { }
 | 
			
		||||
				} catch (IllegalFinPointException ignore) { }
 | 
			
		||||
				dragIndex = index;
 | 
			
		||||
				
 | 
			
		||||
				return;
 | 
			
		||||
@ -299,7 +300,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
 | 
			
		||||
			
 | 
			
		||||
			try {
 | 
			
		||||
				finset.setPoint(dragIndex, point.x, point.y);
 | 
			
		||||
			} catch (IllegalArgumentException ignore) {
 | 
			
		||||
			} catch (IllegalFinPointException ignore) {
 | 
			
		||||
				System.out.println("IAE:"+ignore);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@ -328,7 +329,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				finset.removePoint(index);
 | 
			
		||||
			} catch (IllegalArgumentException ignore) {
 | 
			
		||||
			} catch (IllegalFinPointException ignore) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
@ -462,6 +463,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
 | 
			
		||||
				finset.setPoint(rowIndex, c.x, c.y);
 | 
			
		||||
			
 | 
			
		||||
			} catch (NumberFormatException ignore) {
 | 
			
		||||
			} catch (IllegalFinPointException ignore) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
@ -50,11 +50,11 @@ public class InnerTubeConfig extends ThicknessRingComponentConfig {
 | 
			
		||||
		tab = positionTab();
 | 
			
		||||
		tabbedPane.insertTab("Radial position", null, tab, "Radial position", 1);
 | 
			
		||||
		
 | 
			
		||||
		tab = clusterTab();
 | 
			
		||||
		tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 2);
 | 
			
		||||
		
 | 
			
		||||
		tab = new MotorConfig((MotorMount)c);
 | 
			
		||||
		tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 3);
 | 
			
		||||
		tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 2);
 | 
			
		||||
 | 
			
		||||
		tab = clusterTab();
 | 
			
		||||
		tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 3);
 | 
			
		||||
		
 | 
			
		||||
		tabbedPane.setSelectedIndex(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package net.sf.openrocket.gui;
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import static net.sf.openrocket.unit.Unit.NOUNIT2;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package net.sf.openrocket.gui;
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import java.awt.Component;
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,479 @@
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import java.awt.Window;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.awt.event.ActionListener;
 | 
			
		||||
import java.awt.event.MouseAdapter;
 | 
			
		||||
import java.awt.event.MouseEvent;
 | 
			
		||||
import java.awt.event.WindowAdapter;
 | 
			
		||||
import java.awt.event.WindowEvent;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JButton;
 | 
			
		||||
import javax.swing.JDialog;
 | 
			
		||||
import javax.swing.JLabel;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
import javax.swing.JScrollPane;
 | 
			
		||||
import javax.swing.JTable;
 | 
			
		||||
import javax.swing.JTextField;
 | 
			
		||||
import javax.swing.ListSelectionModel;
 | 
			
		||||
import javax.swing.event.DocumentEvent;
 | 
			
		||||
import javax.swing.event.DocumentListener;
 | 
			
		||||
import javax.swing.table.AbstractTableModel;
 | 
			
		||||
import javax.swing.table.TableColumn;
 | 
			
		||||
import javax.swing.table.TableColumnModel;
 | 
			
		||||
 | 
			
		||||
import net.miginfocom.swing.MigLayout;
 | 
			
		||||
import net.sf.openrocket.document.OpenRocketDocument;
 | 
			
		||||
import net.sf.openrocket.gui.main.BasicFrame;
 | 
			
		||||
import net.sf.openrocket.gui.main.MotorChooserDialog;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Motor;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.MotorMount;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Rocket;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
 | 
			
		||||
import net.sf.openrocket.util.GUIUtil;
 | 
			
		||||
 | 
			
		||||
public class EditMotorConfigurationDialog extends JDialog {
 | 
			
		||||
	
 | 
			
		||||
	private final Rocket rocket;
 | 
			
		||||
	
 | 
			
		||||
	private final MotorMount[] mounts;
 | 
			
		||||
 | 
			
		||||
	private final JTable configurationTable;
 | 
			
		||||
	private final MotorConfigurationTableModel configurationTableModel;
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private final JButton newConfButton, removeConfButton;
 | 
			
		||||
	private final JButton selectMotorButton, removeMotorButton;
 | 
			
		||||
	private final JTextField configurationNameField;
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private String currentID = null;
 | 
			
		||||
	private MotorMount currentMount = null;
 | 
			
		||||
	
 | 
			
		||||
	public EditMotorConfigurationDialog(final Rocket rocket, Window parent) {
 | 
			
		||||
		super(parent, "Edit motor configurations");
 | 
			
		||||
		
 | 
			
		||||
		if (parent != null)
 | 
			
		||||
			this.setModalityType(ModalityType.DOCUMENT_MODAL);
 | 
			
		||||
		else
 | 
			
		||||
			this.setModalityType(ModalityType.APPLICATION_MODAL);
 | 
			
		||||
		
 | 
			
		||||
		this.rocket = rocket;
 | 
			
		||||
		
 | 
			
		||||
		ArrayList<MotorMount> mountList = new ArrayList<MotorMount>();
 | 
			
		||||
		Iterator<RocketComponent> iterator = rocket.deepIterator();
 | 
			
		||||
		while (iterator.hasNext()) {
 | 
			
		||||
			RocketComponent c = iterator.next();
 | 
			
		||||
			if (c instanceof MotorMount) {
 | 
			
		||||
				mountList.add((MotorMount)c);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		mounts = mountList.toArray(new MotorMount[0]);
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		JPanel panel = new JPanel(new MigLayout("fill, flowy"));
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		////  Motor mount selection
 | 
			
		||||
		
 | 
			
		||||
		JLabel label = new JLabel("<html><b>Motor mounts:</b>");
 | 
			
		||||
		panel.add(label, "gapbottom para");
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("<html>Select which components function as motor mounts:");
 | 
			
		||||
		panel.add(label,"ay 100%, w 1px, growx");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		JTable table = new JTable(new MotorMountTableModel());
 | 
			
		||||
		table.setTableHeader(null);
 | 
			
		||||
		table.setShowVerticalLines(false);
 | 
			
		||||
		table.setRowSelectionAllowed(false);
 | 
			
		||||
		table.setColumnSelectionAllowed(false);
 | 
			
		||||
		
 | 
			
		||||
		TableColumnModel columnModel = table.getColumnModel();
 | 
			
		||||
		TableColumn col0 = columnModel.getColumn(0);
 | 
			
		||||
		int w = table.getRowHeight() + 2;
 | 
			
		||||
		col0.setMinWidth(w);
 | 
			
		||||
		col0.setPreferredWidth(w);
 | 
			
		||||
		col0.setMaxWidth(w);
 | 
			
		||||
		
 | 
			
		||||
		JScrollPane scroll = new JScrollPane(table);
 | 
			
		||||
		panel.add(scroll, "w 200lp, h 150lp, grow, wrap 20lp");
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		//// Motor selection
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("<html><b>Motor configurations:</b>");
 | 
			
		||||
		panel.add(label, "spanx, gapbottom para");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("Configuration name:");
 | 
			
		||||
		String tip = "Leave name empty for default.";
 | 
			
		||||
		label.setToolTipText(tip);
 | 
			
		||||
		panel.add(label, "");
 | 
			
		||||
		
 | 
			
		||||
		configurationNameField = new JTextField(10);
 | 
			
		||||
		configurationNameField.setToolTipText(tip);
 | 
			
		||||
		configurationNameField.getDocument().addDocumentListener(new DocumentListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void changedUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			@Override
 | 
			
		||||
			public void insertUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			@Override
 | 
			
		||||
			public void removeUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			private void update() {
 | 
			
		||||
				String text = configurationNameField.getText();
 | 
			
		||||
				if (currentID != null) {
 | 
			
		||||
					rocket.setMotorConfigurationName(currentID, text);
 | 
			
		||||
					int row = configurationTable.getSelectedRow();
 | 
			
		||||
					configurationTableModel.fireTableCellUpdated(row, 0);
 | 
			
		||||
					updateEnabled();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(configurationNameField, "cell 2 1, gapright para");
 | 
			
		||||
		
 | 
			
		||||
		newConfButton = new JButton("New configuration");
 | 
			
		||||
		newConfButton.addActionListener(new ActionListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent e) {
 | 
			
		||||
				String id = rocket.newMotorConfigurationID();
 | 
			
		||||
				rocket.getDefaultConfiguration().setMotorConfigurationID(id);
 | 
			
		||||
				configurationTableModel.fireTableDataChanged();
 | 
			
		||||
				updateEnabled();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(newConfButton, "cell 3 1");
 | 
			
		||||
		
 | 
			
		||||
		removeConfButton = new JButton("Remove configuration");
 | 
			
		||||
		removeConfButton.addActionListener(new ActionListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent e) {
 | 
			
		||||
				if (currentID == null)
 | 
			
		||||
					return;
 | 
			
		||||
				rocket.removeMotorConfigurationID(currentID);
 | 
			
		||||
				rocket.getDefaultConfiguration().setMotorConfigurationID(null);
 | 
			
		||||
				configurationTableModel.fireTableDataChanged();
 | 
			
		||||
				updateEnabled();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(removeConfButton, "cell 4 1");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		configurationTableModel = new MotorConfigurationTableModel();
 | 
			
		||||
		configurationTable = new JTable(configurationTableModel);
 | 
			
		||||
		configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 | 
			
		||||
		configurationTable.setCellSelectionEnabled(true);
 | 
			
		||||
		
 | 
			
		||||
		configurationTable.addMouseListener(new MouseAdapter() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void mouseClicked(MouseEvent e) {
 | 
			
		||||
				
 | 
			
		||||
				if (e.getClickCount() == 1) {
 | 
			
		||||
					
 | 
			
		||||
					// Single click updates selection
 | 
			
		||||
					updateEnabled();
 | 
			
		||||
					
 | 
			
		||||
				} else if (e.getClickCount() == 2) {
 | 
			
		||||
					
 | 
			
		||||
					// Double-click edits motor
 | 
			
		||||
					selectMotor();
 | 
			
		||||
					
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		scroll = new JScrollPane(configurationTable);
 | 
			
		||||
		panel.add(scroll, "cell 1 2, spanx, w 500lp, h 150lp, grow");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		selectMotorButton = new JButton("Select motor");
 | 
			
		||||
		selectMotorButton.addActionListener(new ActionListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent e) {
 | 
			
		||||
				selectMotor();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(selectMotorButton, "spanx, flowx, split 2, ax 50%");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		removeMotorButton = new JButton("Remove motor");
 | 
			
		||||
		removeMotorButton.addActionListener(new ActionListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent e) {
 | 
			
		||||
				removeMotor();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(removeMotorButton, "ax 50%");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		//// Close button
 | 
			
		||||
		
 | 
			
		||||
		JButton close = new JButton("Close");
 | 
			
		||||
		close.addActionListener(new ActionListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent e) {
 | 
			
		||||
				EditMotorConfigurationDialog.this.dispose();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(close, "spanx, right");
 | 
			
		||||
		
 | 
			
		||||
		this.add(panel);
 | 
			
		||||
		this.validate();
 | 
			
		||||
		this.pack();
 | 
			
		||||
		
 | 
			
		||||
		updateEnabled();
 | 
			
		||||
		
 | 
			
		||||
		GUIUtil.installEscapeCloseOperation(this);
 | 
			
		||||
		GUIUtil.setDefaultButton(close);
 | 
			
		||||
		
 | 
			
		||||
		// Undo description
 | 
			
		||||
		final OpenRocketDocument document = BasicFrame.findDocument(rocket);
 | 
			
		||||
		if (document != null) {
 | 
			
		||||
			document.startUndo("Edit motor configurations");
 | 
			
		||||
			this.addWindowListener(new WindowAdapter() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void windowClosed(WindowEvent e) {
 | 
			
		||||
					document.stopUndo();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private void updateEnabled() {
 | 
			
		||||
		int column = configurationTable.getSelectedColumn();
 | 
			
		||||
		int row = configurationTable.getSelectedRow();
 | 
			
		||||
		
 | 
			
		||||
		if (column < 0 || row < 0) {
 | 
			
		||||
			currentID = null;
 | 
			
		||||
			currentMount = null;
 | 
			
		||||
		} else {
 | 
			
		||||
			
 | 
			
		||||
			currentID = findID(row);
 | 
			
		||||
			if (column == 0) {
 | 
			
		||||
				currentMount = null;
 | 
			
		||||
			} else {
 | 
			
		||||
				currentMount = findMount(column);
 | 
			
		||||
			}
 | 
			
		||||
			rocket.getDefaultConfiguration().setMotorConfigurationID(currentID);
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		configurationNameField.setEnabled(currentID != null);
 | 
			
		||||
		if (currentID == null) {
 | 
			
		||||
			configurationNameField.setText("");
 | 
			
		||||
		} else {
 | 
			
		||||
			configurationNameField.setText(rocket.getMotorConfigurationName(currentID));
 | 
			
		||||
		}
 | 
			
		||||
		removeConfButton.setEnabled(currentID != null);
 | 
			
		||||
		selectMotorButton.setEnabled(currentMount != null && currentID != null);
 | 
			
		||||
		removeMotorButton.setEnabled(currentMount != null && currentID != null);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private void selectMotor() {
 | 
			
		||||
		if (currentID == null || currentMount == null)
 | 
			
		||||
			return;
 | 
			
		||||
		
 | 
			
		||||
		MotorChooserDialog dialog = new MotorChooserDialog(currentMount.getMotor(currentID),
 | 
			
		||||
				currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter());
 | 
			
		||||
		dialog.setVisible(true);
 | 
			
		||||
		Motor m = dialog.getSelectedMotor();
 | 
			
		||||
		double d = dialog.getSelectedDelay();
 | 
			
		||||
		
 | 
			
		||||
		if (m != null) {
 | 
			
		||||
			currentMount.setMotor(currentID, m);
 | 
			
		||||
			currentMount.setMotorDelay(currentID, d);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int row = configurationTable.getSelectedRow();
 | 
			
		||||
		configurationTableModel.fireTableRowsUpdated(row, row);
 | 
			
		||||
		updateEnabled();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private void removeMotor() {
 | 
			
		||||
		if (currentID == null || currentMount == null)
 | 
			
		||||
			return;
 | 
			
		||||
		
 | 
			
		||||
		currentMount.setMotor(currentID, null);
 | 
			
		||||
		
 | 
			
		||||
		int row = configurationTable.getSelectedRow();
 | 
			
		||||
		configurationTableModel.fireTableRowsUpdated(row, row);
 | 
			
		||||
		updateEnabled();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private String findID(int row) {
 | 
			
		||||
		return rocket.getMotorConfigurationIDs()[row+1];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private MotorMount findMount(int column) {
 | 
			
		||||
		MotorMount mount = null;
 | 
			
		||||
 | 
			
		||||
		int count = column;
 | 
			
		||||
		for (MotorMount m: mounts) {
 | 
			
		||||
			if (m.isMotorMount())
 | 
			
		||||
				count--;
 | 
			
		||||
			if (count <= 0) {
 | 
			
		||||
				mount = m;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (mount == null) {
 | 
			
		||||
			throw new IndexOutOfBoundsException("motor mount not found, column="+column);
 | 
			
		||||
		}
 | 
			
		||||
		return mount;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * The table model for selecting whether components are motor mounts or not.
 | 
			
		||||
	 */
 | 
			
		||||
	private class MotorMountTableModel extends AbstractTableModel {
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public int getColumnCount() {
 | 
			
		||||
			return 2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public int getRowCount() {
 | 
			
		||||
			return mounts.length;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		public Class<?> getColumnClass(int column) {
 | 
			
		||||
			switch (column) {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return Boolean.class;
 | 
			
		||||
				
 | 
			
		||||
			case 1:
 | 
			
		||||
				return String.class;
 | 
			
		||||
				
 | 
			
		||||
			default:
 | 
			
		||||
				throw new IndexOutOfBoundsException("column="+column);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Object getValueAt(int row, int column) {
 | 
			
		||||
			switch (column) {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return new Boolean(mounts[row].isMotorMount());
 | 
			
		||||
				
 | 
			
		||||
			case 1:
 | 
			
		||||
				return mounts[row].toString();
 | 
			
		||||
				
 | 
			
		||||
			default:
 | 
			
		||||
				throw new IndexOutOfBoundsException("column="+column);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean isCellEditable(int row, int column) {
 | 
			
		||||
			return column == 0;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		public void setValueAt(Object value, int row, int column) {
 | 
			
		||||
			if (column != 0 || !(value instanceof Boolean)) {
 | 
			
		||||
				throw new IllegalArgumentException("column="+column+", value="+value);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			mounts[row].setMotorMount((Boolean)value);
 | 
			
		||||
			configurationTableModel.fireTableStructureChanged();
 | 
			
		||||
			updateEnabled();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * The table model for selecting and editing the motor configurations.
 | 
			
		||||
	 */
 | 
			
		||||
	private class MotorConfigurationTableModel extends AbstractTableModel {
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		public int getColumnCount() {
 | 
			
		||||
			int count = 1;
 | 
			
		||||
			for (MotorMount m: mounts) {
 | 
			
		||||
				if (m.isMotorMount())
 | 
			
		||||
					count++;
 | 
			
		||||
			}
 | 
			
		||||
			return count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public int getRowCount() {
 | 
			
		||||
			return rocket.getMotorConfigurationIDs().length-1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Object getValueAt(int row, int column) {
 | 
			
		||||
			
 | 
			
		||||
			String id = findID(row);
 | 
			
		||||
 | 
			
		||||
			if (column == 0) {
 | 
			
		||||
				return rocket.getMotorConfigurationNameOrDescription(id);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			MotorMount mount = findMount(column);
 | 
			
		||||
			Motor motor = mount.getMotor(id);
 | 
			
		||||
			if (motor == null)
 | 
			
		||||
				return "None";
 | 
			
		||||
			
 | 
			
		||||
			String str = motor.getDesignation(mount.getMotorDelay(id)); 
 | 
			
		||||
			int count = mount.getMotorCount();
 | 
			
		||||
			if (count > 1) {
 | 
			
		||||
				str = "" + count + "\u00d7 " + str;
 | 
			
		||||
			}
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String getColumnName(int column) {
 | 
			
		||||
			if (column == 0) {
 | 
			
		||||
				return "Configuration name";
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			MotorMount mount = findMount(column);
 | 
			
		||||
			String name = mount.toString();
 | 
			
		||||
			int count = mount.getMotorCount();
 | 
			
		||||
			if (count > 1) {
 | 
			
		||||
				name = name + " (\u00d7" + count + ")";
 | 
			
		||||
			}
 | 
			
		||||
			return name;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package net.sf.openrocket.gui.main;
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import java.awt.Font;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package net.sf.openrocket.gui;
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.awt.event.ActionListener;
 | 
			
		||||
							
								
								
									
										118
									
								
								src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,118 @@
 | 
			
		||||
package net.sf.openrocket.gui.dialogs;
 | 
			
		||||
 | 
			
		||||
import java.awt.Window;
 | 
			
		||||
import java.beans.PropertyChangeEvent;
 | 
			
		||||
import java.beans.PropertyChangeListener;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JButton;
 | 
			
		||||
import javax.swing.JDialog;
 | 
			
		||||
import javax.swing.JLabel;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
import javax.swing.JProgressBar;
 | 
			
		||||
import javax.swing.SwingWorker;
 | 
			
		||||
 | 
			
		||||
import net.miginfocom.swing.MigLayout;
 | 
			
		||||
import net.sf.openrocket.util.Pair;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A modal dialog that runs specific SwingWorkers and waits until they complete.
 | 
			
		||||
 * A message and progress bar is provided and a cancel button.  If the cancel button
 | 
			
		||||
 * is pressed, the currently running worker is interrupted and the later workers are not
 | 
			
		||||
 * executed.
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
 | 
			
		||||
 */
 | 
			
		||||
public class SwingWorkerDialog extends JDialog implements PropertyChangeListener {
 | 
			
		||||
 | 
			
		||||
	private final JLabel label;
 | 
			
		||||
	private final JProgressBar progressBar;
 | 
			
		||||
	
 | 
			
		||||
	private int position;
 | 
			
		||||
	private Pair<String, SwingWorker<?,?>>[] workers;
 | 
			
		||||
	
 | 
			
		||||
	private boolean cancelled = false;
 | 
			
		||||
	
 | 
			
		||||
	public SwingWorkerDialog(Window parent, String title) {
 | 
			
		||||
		super(parent, title, ModalityType.APPLICATION_MODAL);
 | 
			
		||||
 | 
			
		||||
		JPanel panel = new JPanel(new MigLayout("fill"));
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("");
 | 
			
		||||
		panel.add(label, "wrap para");
 | 
			
		||||
		
 | 
			
		||||
		progressBar = new JProgressBar();
 | 
			
		||||
		panel.add(progressBar, "growx, wrap para");
 | 
			
		||||
		
 | 
			
		||||
		JButton cancel = new JButton("Cancel");
 | 
			
		||||
		// TODO: CRITICAL: Implement cancel
 | 
			
		||||
		panel.add(cancel, "right");
 | 
			
		||||
		
 | 
			
		||||
		this.add(panel);
 | 
			
		||||
		this.pack();
 | 
			
		||||
		this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Execute the provided workers one after another.  When this call returns
 | 
			
		||||
	 * the workers will all have completed.
 | 
			
		||||
	 *   
 | 
			
		||||
	 * @param workers	pairs of description texts and workers to run.
 | 
			
		||||
	 */
 | 
			
		||||
	public void runWorkers(Pair<String, SwingWorker<?,?>> ... workers) {
 | 
			
		||||
		if (workers.length == 0) {
 | 
			
		||||
			throw new IllegalArgumentException("No workers provided.");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		this.workers = workers;
 | 
			
		||||
		position = -1;
 | 
			
		||||
		
 | 
			
		||||
		for (int i=0; i < workers.length; i++) {
 | 
			
		||||
			workers[i].getV().addPropertyChangeListener(this);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		nextWorker();
 | 
			
		||||
		this.setVisible(true);  // Waits until all have ended
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Starts the execution of the next worker in the queue.  If the last worker
 | 
			
		||||
	 * has completed or the operation has been cancelled, closes the dialog.
 | 
			
		||||
	 */
 | 
			
		||||
	private void nextWorker() {
 | 
			
		||||
		if ((position >= workers.length-1) || cancelled) {
 | 
			
		||||
			close();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		position++;
 | 
			
		||||
		
 | 
			
		||||
		label.setText(workers[position].getU());
 | 
			
		||||
		workers[position].getV().execute();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void propertyChange(PropertyChangeEvent evt) {
 | 
			
		||||
		if (workers[position].getV().getState() == SwingWorker.StateValue.DONE) {
 | 
			
		||||
			nextWorker();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		int value = workers[position].getV().getProgress();
 | 
			
		||||
		value = (value + position*100 ) / workers.length;
 | 
			
		||||
		progressBar.setValue(value);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private void close() {
 | 
			
		||||
		for (int i=0; i < workers.length; i++) {
 | 
			
		||||
			workers[i].getV().removePropertyChangeListener(this);
 | 
			
		||||
		}
 | 
			
		||||
		this.setVisible(false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package net.sf.openrocket.gui.main;
 | 
			
		||||
 | 
			
		||||
import java.awt.Component;
 | 
			
		||||
import java.awt.Dimension;
 | 
			
		||||
import java.awt.Font;
 | 
			
		||||
import java.awt.Toolkit;
 | 
			
		||||
@ -14,6 +15,7 @@ import java.awt.event.MouseListener;
 | 
			
		||||
import java.awt.event.WindowAdapter;
 | 
			
		||||
import java.awt.event.WindowEvent;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
@ -38,6 +40,7 @@ import javax.swing.ListSelectionModel;
 | 
			
		||||
import javax.swing.LookAndFeel;
 | 
			
		||||
import javax.swing.ScrollPaneConstants;
 | 
			
		||||
import javax.swing.SwingUtilities;
 | 
			
		||||
import javax.swing.SwingWorker;
 | 
			
		||||
import javax.swing.ToolTipManager;
 | 
			
		||||
import javax.swing.UIManager;
 | 
			
		||||
import javax.swing.border.TitledBorder;
 | 
			
		||||
@ -56,17 +59,20 @@ import net.sf.openrocket.file.OpenRocketSaver;
 | 
			
		||||
import net.sf.openrocket.file.RocketLoadException;
 | 
			
		||||
import net.sf.openrocket.file.RocketLoader;
 | 
			
		||||
import net.sf.openrocket.file.RocketSaver;
 | 
			
		||||
import net.sf.openrocket.gui.ComponentAnalysisDialog;
 | 
			
		||||
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.dialogs.ComponentAnalysisDialog;
 | 
			
		||||
import net.sf.openrocket.gui.dialogs.LicenseDialog;
 | 
			
		||||
import net.sf.openrocket.gui.dialogs.PreferencesDialog;
 | 
			
		||||
import net.sf.openrocket.gui.scalefigure.RocketPanel;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Rocket;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Stage;
 | 
			
		||||
import net.sf.openrocket.util.ConcurrentProgressMonitor;
 | 
			
		||||
import net.sf.openrocket.util.ConcurrentProgressMonitorInputStream;
 | 
			
		||||
import net.sf.openrocket.util.Icons;
 | 
			
		||||
import net.sf.openrocket.util.Prefs;
 | 
			
		||||
 | 
			
		||||
@ -588,7 +594,7 @@ public class BasicFrame extends JFrame {
 | 
			
		||||
	    
 | 
			
		||||
	    for (File file: files) {
 | 
			
		||||
	    	System.out.println("Opening file: " + file);
 | 
			
		||||
	    	if (open(file)) {
 | 
			
		||||
	    	if (open(file, this)) {
 | 
			
		||||
	    		opened = true;
 | 
			
		||||
	    	}
 | 
			
		||||
	    }
 | 
			
		||||
@ -605,12 +611,15 @@ public class BasicFrame extends JFrame {
 | 
			
		||||
	 * is shown and <code>false</code> is returned.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param file		the file to open.
 | 
			
		||||
	 * @param parent	the parent component for which a progress dialog is opened.
 | 
			
		||||
	 * @return			whether the file was successfully loaded and opened.
 | 
			
		||||
	 */
 | 
			
		||||
	private static boolean open(File file) {
 | 
			
		||||
	private static boolean open(File file, Component parent) {
 | 
			
		||||
	    OpenRocketDocument doc = null;
 | 
			
		||||
	    
 | 
			
		||||
	    
 | 
			
		||||
		try {
 | 
			
		||||
			doc = ROCKET_LOADER.load(file);
 | 
			
		||||
			doc = ROCKET_LOADER.load(file, parent);
 | 
			
		||||
		} catch (RocketLoadException e) {
 | 
			
		||||
			JOptionPane.showMessageDialog(null, "Unable to open file '" + file.getName() 
 | 
			
		||||
					+"': " + e.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
@ -643,6 +652,33 @@ public class BasicFrame extends JFrame {
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private static class OpenWorker extends SwingWorker<OpenRocketDocument, Void> {
 | 
			
		||||
		private final File file;
 | 
			
		||||
		private final Component parent;
 | 
			
		||||
		private ConcurrentProgressMonitor monitor = null;
 | 
			
		||||
		
 | 
			
		||||
		public OpenWorker(File file, Component parent) {
 | 
			
		||||
			this.file = file;
 | 
			
		||||
			this.parent = parent;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		protected OpenRocketDocument doInBackground() throws Exception {
 | 
			
		||||
			ConcurrentProgressMonitorInputStream is = 
 | 
			
		||||
				new ConcurrentProgressMonitorInputStream(parent, 
 | 
			
		||||
						"Loading " + file.getName(), new FileInputStream(file));
 | 
			
		||||
			monitor = is.getProgressMonitor();
 | 
			
		||||
			return ROCKET_LOADER.load(is);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public ConcurrentProgressMonitor getMonitor() {
 | 
			
		||||
			return monitor;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private boolean saveAction() {
 | 
			
		||||
		File file = document.getFile();
 | 
			
		||||
		if (file==null) {
 | 
			
		||||
@ -804,6 +840,35 @@ public class BasicFrame extends JFrame {
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Find a currently open BasicFrame containing the specified rocket.  This method
 | 
			
		||||
	 * can be used to map a Rocket to a BasicFrame from GUI methods.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param rocket the Rocket.
 | 
			
		||||
	 * @return		 the corresponding BasicFrame, or <code>null</code> if none found.
 | 
			
		||||
	 */
 | 
			
		||||
	public static BasicFrame findFrame(Rocket rocket) {
 | 
			
		||||
		for (BasicFrame f: frames) {
 | 
			
		||||
			if (f.rocket == rocket)
 | 
			
		||||
				return f;
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Find a currently open document by the rocket object.  This method can be used
 | 
			
		||||
	 * to map a Rocket to OpenRocketDocument from GUI methods.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param rocket the Rocket.
 | 
			
		||||
	 * @return		 the corresponding OpenRocketDocument, or <code>null</code> if not found.
 | 
			
		||||
	 */
 | 
			
		||||
	public static OpenRocketDocument findDocument(Rocket rocket) {
 | 
			
		||||
		for (BasicFrame f: frames) {
 | 
			
		||||
			if (f.rocket == rocket)
 | 
			
		||||
				return f.document;
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
@ -861,7 +926,7 @@ public class BasicFrame extends JFrame {
 | 
			
		||||
		// Check command-line for files
 | 
			
		||||
		boolean opened = false;
 | 
			
		||||
		for (String file: args) {
 | 
			
		||||
			if (open(new File(file))) {
 | 
			
		||||
			if (open(new File(file), null)) {
 | 
			
		||||
				opened = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import java.awt.event.ActionListener;
 | 
			
		||||
import java.awt.event.MouseAdapter;
 | 
			
		||||
import java.awt.event.MouseEvent;
 | 
			
		||||
import java.text.Collator;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
@ -22,8 +23,11 @@ import javax.swing.JLabel;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
import javax.swing.JScrollPane;
 | 
			
		||||
import javax.swing.JTable;
 | 
			
		||||
import javax.swing.JTextField;
 | 
			
		||||
import javax.swing.ListSelectionModel;
 | 
			
		||||
import javax.swing.RowFilter;
 | 
			
		||||
import javax.swing.event.DocumentEvent;
 | 
			
		||||
import javax.swing.event.DocumentListener;
 | 
			
		||||
import javax.swing.event.ListSelectionEvent;
 | 
			
		||||
import javax.swing.event.ListSelectionListener;
 | 
			
		||||
import javax.swing.table.AbstractTableModel;
 | 
			
		||||
@ -50,6 +54,8 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
	};
 | 
			
		||||
	private static final int SHOW_MAX = 2;
 | 
			
		||||
 | 
			
		||||
	private final JTextField searchField; 
 | 
			
		||||
	private String[] searchTerms = new String[0];
 | 
			
		||||
 | 
			
		||||
	private final double diameter;
 | 
			
		||||
 | 
			
		||||
@ -81,16 +87,16 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
		this.selectedDelay = delay;
 | 
			
		||||
		this.diameter = diameter;
 | 
			
		||||
		
 | 
			
		||||
		JPanel panel = new JPanel(new MigLayout("fill"));
 | 
			
		||||
		JPanel panel = new JPanel(new MigLayout("fill", "[grow][]"));
 | 
			
		||||
 | 
			
		||||
		// Label
 | 
			
		||||
		JLabel label = new JLabel("Select a rocket motor:");
 | 
			
		||||
		label.setFont(label.getFont().deriveFont(Font.BOLD));
 | 
			
		||||
		panel.add(label,"split 2, growx");
 | 
			
		||||
		panel.add(label,"growx");
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("Motor mount diameter: " +
 | 
			
		||||
				UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter));
 | 
			
		||||
		panel.add(label,"alignx 100%, wrap paragraph");
 | 
			
		||||
		panel.add(label,"gapleft para, wrap paragraph");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		// Diameter selection
 | 
			
		||||
@ -104,17 +110,14 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
					sel = SHOW_ALL;
 | 
			
		||||
				switch (sel) {
 | 
			
		||||
				case SHOW_ALL:
 | 
			
		||||
					System.out.println("Setting filter: all");
 | 
			
		||||
					sorter.setRowFilter(new MotorRowFilterAll());
 | 
			
		||||
					break;
 | 
			
		||||
					
 | 
			
		||||
				case SHOW_SMALLER:
 | 
			
		||||
					System.out.println("Setting filter: smaller");
 | 
			
		||||
					sorter.setRowFilter(new MotorRowFilterSmaller());
 | 
			
		||||
					break;
 | 
			
		||||
					
 | 
			
		||||
				case SHOW_EXACT:
 | 
			
		||||
					System.out.println("Setting filter: exact");
 | 
			
		||||
					sorter.setRowFilter(new MotorRowFilterExact());
 | 
			
		||||
					break;
 | 
			
		||||
					
 | 
			
		||||
@ -125,7 +128,44 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
				setSelectionVisible();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(combo,"growx, wrap");
 | 
			
		||||
		panel.add(combo,"growx 1000");
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		label = new JLabel("Search:");
 | 
			
		||||
		panel.add(label, "gapleft para, split 2");
 | 
			
		||||
		
 | 
			
		||||
		searchField = new JTextField();
 | 
			
		||||
		searchField.getDocument().addDocumentListener(new DocumentListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void changedUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			@Override
 | 
			
		||||
			public void insertUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			@Override
 | 
			
		||||
			public void removeUpdate(DocumentEvent e) {
 | 
			
		||||
				update();
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			private void update() {
 | 
			
		||||
				String text = searchField.getText().trim();
 | 
			
		||||
				String[] split = text.split("\\s+");
 | 
			
		||||
				ArrayList<String> list = new ArrayList<String>();
 | 
			
		||||
				for (String s: split) {
 | 
			
		||||
					s = s.trim().toLowerCase();
 | 
			
		||||
					if (s.length() > 0) {
 | 
			
		||||
						list.add(s);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				searchTerms = list.toArray(new String[0]);
 | 
			
		||||
				sorter.sort();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(searchField, "growx 1, wrap");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		// Table, overridden to show meaningful tooltip texts
 | 
			
		||||
@ -186,11 +226,11 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
		
 | 
			
		||||
		JScrollPane scrollpane = new JScrollPane();
 | 
			
		||||
		scrollpane.setViewportView(table);
 | 
			
		||||
		panel.add(scrollpane,"grow, width :700:, height :300:, wrap paragraph");
 | 
			
		||||
		panel.add(scrollpane,"spanx, grow, width :700:, height :300:, wrap paragraph");
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		// Ejection delay
 | 
			
		||||
		panel.add(new JLabel("Select ejection charge delay:"), "split 3, gap rel");
 | 
			
		||||
		panel.add(new JLabel("Select ejection charge delay:"), "spanx, split 3, gap rel");
 | 
			
		||||
		
 | 
			
		||||
		delayBox = new JComboBox();
 | 
			
		||||
		delayBox.setEditable(true);
 | 
			
		||||
@ -222,7 +262,7 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
				MotorChooserDialog.this.setVisible(false);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		panel.add(okButton,"split, tag ok");
 | 
			
		||||
		panel.add(okButton,"spanx, split, tag ok");
 | 
			
		||||
 | 
			
		||||
		button = new JButton("Cancel");
 | 
			
		||||
		button.addActionListener(new ActionListener() {
 | 
			
		||||
@ -248,6 +288,9 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
		
 | 
			
		||||
		// Table can be scrolled only after pack() has been called
 | 
			
		||||
		setSelectionVisible();
 | 
			
		||||
		
 | 
			
		||||
		// Focus the search field
 | 
			
		||||
		searchField.grabFocus();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private void setSelectionVisible() {
 | 
			
		||||
@ -567,14 +610,26 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
	 */
 | 
			
		||||
	private abstract class MotorRowFilter extends RowFilter<TableModel,Integer> {
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean include(
 | 
			
		||||
				RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
 | 
			
		||||
		public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
 | 
			
		||||
			int index = entry.getIdentifier();
 | 
			
		||||
			Motor m = model.getMotor(index);
 | 
			
		||||
			return include(m);
 | 
			
		||||
			return filterByDiameter(m) && filterByString(m);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public abstract boolean include(Motor m);
 | 
			
		||||
		public abstract boolean filterByDiameter(Motor m);
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		public boolean filterByString(Motor m) {
 | 
			
		||||
			main: for (String s : searchTerms) {
 | 
			
		||||
				for (MotorColumns col : MotorColumns.values()) {
 | 
			
		||||
					String str = col.getValue(m).toLowerCase();
 | 
			
		||||
					if (str.indexOf(s) >= 0)
 | 
			
		||||
						continue main;
 | 
			
		||||
				}
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
@ -582,7 +637,7 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
	 */
 | 
			
		||||
	private class MotorRowFilterAll extends MotorRowFilter {
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean include(Motor m) {
 | 
			
		||||
		public boolean filterByDiameter(Motor m) {
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -592,7 +647,7 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
	 */
 | 
			
		||||
	private class MotorRowFilterSmaller extends MotorRowFilter {
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean include(Motor m) {
 | 
			
		||||
		public boolean filterByDiameter(Motor m) {
 | 
			
		||||
			return (m.getDiameter() <= diameter + 0.0004);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -602,7 +657,7 @@ public class MotorChooserDialog extends JDialog {
 | 
			
		||||
	 */
 | 
			
		||||
	private class MotorRowFilterExact extends MotorRowFilter {
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean include(Motor m) {
 | 
			
		||||
		public boolean filterByDiameter(Motor m) {
 | 
			
		||||
			return ((m.getDiameter() <= diameter + 0.0004) &&
 | 
			
		||||
					(m.getDiameter() >= diameter - 0.0015));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ import javax.swing.JProgressBar;
 | 
			
		||||
 | 
			
		||||
import net.miginfocom.swing.MigLayout;
 | 
			
		||||
import net.sf.openrocket.document.Simulation;
 | 
			
		||||
import net.sf.openrocket.gui.DetailDialog;
 | 
			
		||||
import net.sf.openrocket.gui.dialogs.DetailDialog;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Configuration;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.MotorMount;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;
 | 
			
		||||
 | 
			
		||||
@ -56,9 +56,11 @@ public class ClusterConfiguration {
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private final List<Double> points;
 | 
			
		||||
	private final String xmlName;
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private ClusterConfiguration(String xmlName, double... points) {
 | 
			
		||||
		this.xmlName = xmlName;
 | 
			
		||||
		if (points.length == 0 || points.length%2 == 1) {
 | 
			
		||||
@ -100,4 +102,10 @@ public class ClusterConfiguration {
 | 
			
		||||
		}
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return xmlName;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,10 +59,11 @@ public class FreeformFinSet extends FinSet {
 | 
			
		||||
	 * if attempted.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param index   the fin point index to remove
 | 
			
		||||
	 * @throws IllegalFinPointException if removing the first or last fin point was attempted.
 | 
			
		||||
	 */
 | 
			
		||||
	public void removePoint(int index) {
 | 
			
		||||
	public void removePoint(int index) throws IllegalFinPointException {
 | 
			
		||||
		if (index == 0  ||  index == points.size()-1) {
 | 
			
		||||
			throw new IllegalArgumentException("cannot remove first or last point");
 | 
			
		||||
			throw new IllegalFinPointException("cannot remove first or last point");
 | 
			
		||||
		}
 | 
			
		||||
		points.remove(index);
 | 
			
		||||
		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
 | 
			
		||||
@ -73,19 +74,19 @@ public class FreeformFinSet extends FinSet {
 | 
			
		||||
		return points.size();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public void setPoints(Coordinate[] p) {
 | 
			
		||||
	public void setPoints(Coordinate[] p) throws IllegalFinPointException {
 | 
			
		||||
		if (p[0].x != 0 || p[0].y != 0 || p[p.length-1].y != 0) {
 | 
			
		||||
			throw new IllegalArgumentException("Start or end point illegal.");
 | 
			
		||||
			throw new IllegalFinPointException("Start or end point illegal.");
 | 
			
		||||
		}
 | 
			
		||||
		for (int i=0; i < p.length-1; i++) {
 | 
			
		||||
			for (int j=i+2; j < p.length-1; j++) {
 | 
			
		||||
				if (intersects(p[i].x, p[i].y, p[i+1].x, p[i+1].y,
 | 
			
		||||
						       p[j].x, p[j].y, p[j+1].x, p[j+1].y)) {
 | 
			
		||||
					throw new IllegalArgumentException("segments intersect");
 | 
			
		||||
					throw new IllegalFinPointException("segments intersect");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (p[i].z != 0) {
 | 
			
		||||
				throw new IllegalArgumentException("z-coordinate not zero");
 | 
			
		||||
				throw new IllegalFinPointException("z-coordinate not zero");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
@ -112,8 +113,10 @@ public class FreeformFinSet extends FinSet {
 | 
			
		||||
	 * @param index	the point index to modify.
 | 
			
		||||
	 * @param x		the x-coordinate.
 | 
			
		||||
	 * @param y		the y-coordinate.
 | 
			
		||||
	 * @throws IllegalFinPointException	if the specified fin point would cause intersecting
 | 
			
		||||
	 * 									segments
 | 
			
		||||
	 */
 | 
			
		||||
	public void setPoint(int index, double x, double y) {
 | 
			
		||||
	public void setPoint(int index, double x, double y) throws IllegalFinPointException {
 | 
			
		||||
		if (y < 0)
 | 
			
		||||
			y = 0;
 | 
			
		||||
		
 | 
			
		||||
@ -160,12 +163,12 @@ public class FreeformFinSet extends FinSet {
 | 
			
		||||
			
 | 
			
		||||
			if (i != index-1 && i != index && i != index+1) {
 | 
			
		||||
				if (intersects(x0,y0,x,y,px0,py0,px1,py1)) {
 | 
			
		||||
					throw new IllegalArgumentException("segments intersect");
 | 
			
		||||
					throw new IllegalFinPointException("segments intersect");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (i != index && i != index+1 && i != index+2) {
 | 
			
		||||
				if (intersects(x,y,x1,y1,px0,py0,px1,py1)) {
 | 
			
		||||
					throw new IllegalArgumentException("segments intersect");
 | 
			
		||||
					throw new IllegalFinPointException("segments intersect");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
package net.sf.openrocket.rocketcomponent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An exception signifying that an operation on the freeform fin set points was
 | 
			
		||||
 * illegal (segments intersect, removing first or last point, etc).
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
 | 
			
		||||
 */
 | 
			
		||||
public class IllegalFinPointException extends Exception {
 | 
			
		||||
 | 
			
		||||
	public IllegalFinPointException() {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public IllegalFinPointException(String message) {
 | 
			
		||||
		super(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public IllegalFinPointException(Throwable cause) {
 | 
			
		||||
		super(cause);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public IllegalFinPointException(String message, Throwable cause) {
 | 
			
		||||
		super(message, cause);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -7,7 +7,6 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import javax.swing.event.ChangeListener;
 | 
			
		||||
@ -72,8 +71,8 @@ public class Rocket extends RocketComponent {
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	// Motor configuration list
 | 
			
		||||
	private List<String> motorConfigurationIDs = new ArrayList<String>();
 | 
			
		||||
	private Map<String, String> motorConfigurationNames = new HashMap<String, String>();
 | 
			
		||||
	private ArrayList<String> motorConfigurationIDs = new ArrayList<String>();
 | 
			
		||||
	private HashMap<String, String> motorConfigurationNames = new HashMap<String, String>();
 | 
			
		||||
	{
 | 
			
		||||
		motorConfigurationIDs.add(null);
 | 
			
		||||
	}
 | 
			
		||||
@ -267,22 +266,25 @@ public class Rocket extends RocketComponent {
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Make a deep copy of the Rocket structure.  This is a helper method which simply 
 | 
			
		||||
	 * casts the result of the superclass method to a Rocket.
 | 
			
		||||
	 */
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	@Override
 | 
			
		||||
	public Rocket copy() {
 | 
			
		||||
		Rocket copy = (Rocket)super.copy();
 | 
			
		||||
		copy.motorConfigurationIDs = (ArrayList<String>) this.motorConfigurationIDs.clone();
 | 
			
		||||
		copy.motorConfigurationNames = 
 | 
			
		||||
			(HashMap<String, String>) this.motorConfigurationNames.clone();
 | 
			
		||||
		copy.resetListeners();
 | 
			
		||||
		
 | 
			
		||||
		return copy;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Load the rocket structure from the source.  The method loads the fields of this
 | 
			
		||||
	 * Rocket object and copies the references to siblings from the <code>source</code>.
 | 
			
		||||
@ -293,6 +295,7 @@ public class Rocket extends RocketComponent {
 | 
			
		||||
	 * and therefore fires an UNDO_EVENT, masked with all applicable mass/aerodynamic/tree
 | 
			
		||||
	 * changes.
 | 
			
		||||
	 */
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	public void loadFrom(Rocket r) {
 | 
			
		||||
		super.copyFrom(r);
 | 
			
		||||
		
 | 
			
		||||
@ -312,10 +315,15 @@ public class Rocket extends RocketComponent {
 | 
			
		||||
		this.refType = r.refType;
 | 
			
		||||
		this.customReferenceLength = r.customReferenceLength;
 | 
			
		||||
		
 | 
			
		||||
		this.motorConfigurationIDs = r.motorConfigurationIDs;
 | 
			
		||||
		this.motorConfigurationNames = r.motorConfigurationNames;
 | 
			
		||||
		this.motorConfigurationIDs = (ArrayList<String>) r.motorConfigurationIDs.clone();
 | 
			
		||||
		this.motorConfigurationNames = 
 | 
			
		||||
			(HashMap<String, String>) r.motorConfigurationNames.clone();
 | 
			
		||||
		this.perfectFinish = r.perfectFinish;
 | 
			
		||||
		
 | 
			
		||||
		String id = defaultConfiguration.getMotorConfigurationID();
 | 
			
		||||
		if (!this.motorConfigurationIDs.contains(id))
 | 
			
		||||
			defaultConfiguration.setMotorConfigurationID(null);
 | 
			
		||||
		
 | 
			
		||||
		fireComponentChangeEvent(type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -594,7 +602,7 @@ public class Rocket extends RocketComponent {
 | 
			
		||||
	 */
 | 
			
		||||
	public void setMotorConfigurationName(String id, String name) {
 | 
			
		||||
		motorConfigurationNames.put(id,name);
 | 
			
		||||
		fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
 | 
			
		||||
		fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										45
									
								
								src/net/sf/openrocket/util/ConcurrentProgressMonitor.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/net/sf/openrocket/util/ConcurrentProgressMonitor.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
package net.sf.openrocket.util;
 | 
			
		||||
 | 
			
		||||
import java.awt.Component;
 | 
			
		||||
 | 
			
		||||
import javax.swing.ProgressMonitor;
 | 
			
		||||
import javax.swing.SwingUtilities;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A thread-safe <code>ProgressMonitor</code>.  This class may be instantiated
 | 
			
		||||
 * and the method {@link #setProgress(int)} called safely from any thread.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Why the FSCK&!¤#&%¤ isn't the default API version thread-safe?!?!
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
 | 
			
		||||
 */
 | 
			
		||||
public class ConcurrentProgressMonitor extends ProgressMonitor {
 | 
			
		||||
 | 
			
		||||
	public ConcurrentProgressMonitor(Component parentComponent, Object message,
 | 
			
		||||
			String note, int min, int max) {
 | 
			
		||||
		super(parentComponent, message, note, min, max);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void setProgress(final int nv) {
 | 
			
		||||
		
 | 
			
		||||
		if (SwingUtilities.isEventDispatchThread()) {
 | 
			
		||||
			super.setProgress(nv);
 | 
			
		||||
		} else {
 | 
			
		||||
			
 | 
			
		||||
			SwingUtilities.invokeLater(new Runnable() {
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void run() {
 | 
			
		||||
					ConcurrentProgressMonitor.super.setProgress(nv);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
			});
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,146 @@
 | 
			
		||||
/*
 | 
			
		||||
 * TODO: CRITICAL: Licensing
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package net.sf.openrocket.util;
 | 
			
		||||
 | 
			
		||||
import java.awt.Component;
 | 
			
		||||
import java.io.FilterInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.InterruptedIOException;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A functional equivalent of <code>ProgressMonitorInputStream</code> which 
 | 
			
		||||
 * uses {@link ConcurrentProgressMonitor} and leaves the progress dialog open
 | 
			
		||||
 * to be manually closed later on.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
public class ConcurrentProgressMonitorInputStream extends FilterInputStream {
 | 
			
		||||
	private ConcurrentProgressMonitor monitor;
 | 
			
		||||
	private int nread = 0;
 | 
			
		||||
	private int size = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs an object to monitor the progress of an input stream.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param message Descriptive text to be placed in the dialog box
 | 
			
		||||
	 *                if one is popped up.
 | 
			
		||||
	 * @param parentComponent The component triggering the operation
 | 
			
		||||
	 *                        being monitored.
 | 
			
		||||
	 * @param in The input stream to be monitored.
 | 
			
		||||
	 */
 | 
			
		||||
	public ConcurrentProgressMonitorInputStream(Component parentComponent,
 | 
			
		||||
			Object message, InputStream in) {
 | 
			
		||||
		super(in);
 | 
			
		||||
		try {
 | 
			
		||||
			size = in.available();
 | 
			
		||||
		} catch (IOException ioe) {
 | 
			
		||||
			size = 0;
 | 
			
		||||
		}
 | 
			
		||||
		monitor = new ConcurrentProgressMonitor(parentComponent, message, null, 0,
 | 
			
		||||
				size + 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the ProgressMonitor object being used by this stream. Normally
 | 
			
		||||
	 * this isn't needed unless you want to do something like change the
 | 
			
		||||
	 * descriptive text partway through reading the file.
 | 
			
		||||
	 * @return the ProgressMonitor object used by this object 
 | 
			
		||||
	 */
 | 
			
		||||
	public ConcurrentProgressMonitor getProgressMonitor() {
 | 
			
		||||
		return monitor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.read</code> 
 | 
			
		||||
	 * to update the progress monitor after the read.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public int read() throws IOException {
 | 
			
		||||
		int c = in.read();
 | 
			
		||||
		if (c >= 0)
 | 
			
		||||
			monitor.setProgress(++nread);
 | 
			
		||||
		if (monitor.isCanceled()) {
 | 
			
		||||
			InterruptedIOException exc = new InterruptedIOException("progress");
 | 
			
		||||
			exc.bytesTransferred = nread;
 | 
			
		||||
			throw exc;
 | 
			
		||||
		}
 | 
			
		||||
		return c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.read</code> 
 | 
			
		||||
	 * to update the progress monitor after the read.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public int read(byte b[]) throws IOException {
 | 
			
		||||
		int nr = in.read(b);
 | 
			
		||||
		if (nr > 0)
 | 
			
		||||
			monitor.setProgress(nread += nr);
 | 
			
		||||
		if (monitor.isCanceled()) {
 | 
			
		||||
			InterruptedIOException exc = new InterruptedIOException("progress");
 | 
			
		||||
			exc.bytesTransferred = nread;
 | 
			
		||||
			throw exc;
 | 
			
		||||
		}
 | 
			
		||||
		return nr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.read</code> 
 | 
			
		||||
	 * to update the progress monitor after the read.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public int read(byte b[], int off, int len) throws IOException {
 | 
			
		||||
		int nr = in.read(b, off, len);
 | 
			
		||||
		if (nr > 0)
 | 
			
		||||
			monitor.setProgress(nread += nr);
 | 
			
		||||
		if (monitor.isCanceled()) {
 | 
			
		||||
			InterruptedIOException exc = new InterruptedIOException("progress");
 | 
			
		||||
			exc.bytesTransferred = nread;
 | 
			
		||||
			throw exc;
 | 
			
		||||
		}
 | 
			
		||||
		return nr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.skip</code> 
 | 
			
		||||
	 * to update the progress monitor after the skip.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public long skip(long n) throws IOException {
 | 
			
		||||
		long nr = in.skip(n);
 | 
			
		||||
		if (nr > 0)
 | 
			
		||||
			monitor.setProgress(nread += nr);
 | 
			
		||||
		return nr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.close</code> 
 | 
			
		||||
	 * to close the progress monitor as well as the stream.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void close() throws IOException {
 | 
			
		||||
		in.close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Overrides <code>FilterInputStream.reset</code> 
 | 
			
		||||
	 * to reset the progress monitor as well as the stream.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public synchronized void reset() throws IOException {
 | 
			
		||||
		in.reset();
 | 
			
		||||
		nread = size - in.available();
 | 
			
		||||
		monitor.setProgress(nread);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -6,6 +6,7 @@ import net.sf.openrocket.rocketcomponent.BodyTube;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.Bulkhead;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.CenteringRing;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.InnerTube;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
 | 
			
		||||
import net.sf.openrocket.rocketcomponent.MassComponent;
 | 
			
		||||
@ -144,6 +145,7 @@ public class Test {
 | 
			
		||||
		bodytube = new BodyTube(0.69,0.033,0.001);
 | 
			
		||||
 | 
			
		||||
		finset = new FreeformFinSet();
 | 
			
		||||
		try {
 | 
			
		||||
			finset.setPoints(new Coordinate[] {
 | 
			
		||||
					new Coordinate(0, 0),
 | 
			
		||||
					new Coordinate(0.115, 0.072),
 | 
			
		||||
@ -151,6 +153,9 @@ public class Test {
 | 
			
		||||
					new Coordinate(0.255, 0.037),
 | 
			
		||||
					new Coordinate(0.150, 0)
 | 
			
		||||
			});
 | 
			
		||||
		} catch (IllegalFinPointException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
		finset.setThickness(0.003);
 | 
			
		||||
		finset.setFinCount(4);
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user