1 package net.sf.openrocket.gui.configdialog;
5 import java.awt.event.MouseEvent;
6 import java.awt.geom.Point2D;
8 import javax.swing.JComboBox;
9 import javax.swing.JLabel;
10 import javax.swing.JPanel;
11 import javax.swing.JScrollPane;
12 import javax.swing.JSeparator;
13 import javax.swing.JSpinner;
14 import javax.swing.JTable;
15 import javax.swing.ListSelectionModel;
16 import javax.swing.SwingConstants;
17 import javax.swing.table.AbstractTableModel;
19 import net.miginfocom.swing.MigLayout;
20 import net.sf.openrocket.document.OpenRocketDocument;
21 import net.sf.openrocket.gui.SpinnerEditor;
22 import net.sf.openrocket.gui.adaptors.DoubleModel;
23 import net.sf.openrocket.gui.adaptors.EnumModel;
24 import net.sf.openrocket.gui.adaptors.IntegerModel;
25 import net.sf.openrocket.gui.components.BasicSlider;
26 import net.sf.openrocket.gui.components.StyledLabel;
27 import net.sf.openrocket.gui.components.UnitSelector;
28 import net.sf.openrocket.gui.scalefigure.FinPointFigure;
29 import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
30 import net.sf.openrocket.gui.scalefigure.ScaleSelector;
31 import net.sf.openrocket.l10n.Translator;
32 import net.sf.openrocket.logging.LogHelper;
33 import net.sf.openrocket.material.Material;
34 import net.sf.openrocket.rocketcomponent.FinSet;
35 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
36 import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
37 import net.sf.openrocket.rocketcomponent.RocketComponent;
38 import net.sf.openrocket.startup.Application;
39 import net.sf.openrocket.unit.UnitGroup;
40 import net.sf.openrocket.util.Coordinate;
42 public class FreeformFinSetConfig extends FinSetConfig {
44 private static final LogHelper log = Application.getLogger();
45 private static final Translator trans = Application.getTranslator();
47 private final FreeformFinSet finset;
48 private JTable table = null;
49 private FinPointTableModel tableModel = null;
51 private FinPointFigure figure = null;
54 public FreeformFinSetConfig(OpenRocketDocument d, RocketComponent component) {
56 this.finset = (FreeformFinSet) component;
58 //// General and General properties
59 tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.General"), null, generalPane(),
60 trans.get("FreeformFinSetCfg.tab.ttip.General"), 0);
61 //// Shape and Fin shape
62 tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.Shape"), null, shapePane(),
63 trans.get("FreeformFinSetCfg.tab.ttip.Finshape"), 1);
64 tabbedPane.setSelectedIndex(0);
71 private JPanel generalPane() {
77 JPanel mainPanel = new JPanel(new MigLayout("fill"));
79 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel", "[][65lp::][30lp::]", ""));
84 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Numberoffins")));
86 IntegerModel im = new IntegerModel(component, "FinCount", 1, 8);
88 spin = new JSpinner(im.getSpinnerModel());
89 spin.setEditor(new SpinnerEditor(spin));
90 panel.add(spin, "growx, wrap");
94 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Finrotation")));
96 m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI);
98 spin = new JSpinner(m.getSpinnerModel());
99 spin.setEditor(new SpinnerEditor(spin));
100 panel.add(spin, "growx");
102 panel.add(new UnitSelector(m), "growx");
103 panel.add(new BasicSlider(m.getSliderModel(-Math.PI, Math.PI)), "w 100lp, wrap");
108 JLabel label = new JLabel(trans.get("FreeformFinSetCfg.lbl.Fincant"));
109 //// The angle that the fins are canted with respect to the rocket body.
110 label.setToolTipText(trans.get("FreeformFinSetCfg.lbl.ttip.Fincant"));
113 m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
114 -FinSet.MAX_CANT, FinSet.MAX_CANT);
116 spin = new JSpinner(m.getSpinnerModel());
117 spin.setEditor(new SpinnerEditor(spin));
118 panel.add(spin, "growx");
120 panel.add(new UnitSelector(m), "growx");
121 panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)),
122 "w 100lp, wrap 40lp");
127 //// Position relative to:
128 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Posrelativeto")));
130 combo = new JComboBox(
131 new EnumModel<RocketComponent.Position>(component, "RelativePosition",
132 new RocketComponent.Position[] {
133 RocketComponent.Position.TOP,
134 RocketComponent.Position.MIDDLE,
135 RocketComponent.Position.BOTTOM,
136 RocketComponent.Position.ABSOLUTE
138 panel.add(combo, "spanx 3, growx, wrap");
140 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.plus")), "right");
142 m = new DoubleModel(component, "PositionValue", UnitGroup.UNITS_LENGTH);
143 spin = new JSpinner(m.getSpinnerModel());
144 spin.setEditor(new SpinnerEditor(spin));
145 panel.add(spin, "growx");
147 panel.add(new UnitSelector(m), "growx");
148 panel.add(new BasicSlider(m.getSliderModel(
149 new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
150 new DoubleModel(component.getParent(), "Length"))),
157 mainPanel.add(panel, "aligny 20%");
158 mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
161 panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
167 //// Fin cross section:
168 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")), "span, split");
169 combo = new JComboBox(
170 new EnumModel<FinSet.CrossSection>(component, "CrossSection"));
171 panel.add(combo, "growx, wrap unrel");
175 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Thickness")));
177 m = new DoubleModel(component, "Thickness", UnitGroup.UNITS_LENGTH, 0);
179 spin = new JSpinner(m.getSpinnerModel());
180 spin.setEditor(new SpinnerEditor(spin));
181 panel.add(spin, "growx");
183 panel.add(new UnitSelector(m), "growx");
184 panel.add(new BasicSlider(m.getSliderModel(0, 0.01)), "w 100lp, wrap 30lp");
188 materialPanel(panel, Material.Type.BULK);
192 mainPanel.add(panel, "aligny 20%");
199 private JPanel shapePane() {
200 JPanel panel = new JPanel(new MigLayout("fill"));
204 figure = new FinPointFigure(finset);
205 ScaleScrollPane figurePane = new FinPointScrollPane();
206 figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
207 figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
210 tableModel = new FinPointTableModel();
211 table = new JTable(tableModel);
212 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
213 for (int i = 0; i < Columns.values().length; i++) {
214 table.getColumnModel().getColumn(i).
215 setPreferredWidth(Columns.values()[i].getWidth());
217 JScrollPane tablePane = new JScrollPane(table);
220 // panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
221 // panel.add(new JLabel(" View:"), "wrap, aligny bottom");
224 panel.add(tablePane, "growy, width 100lp:100lp:, height 100lp:250lp:");
225 panel.add(figurePane, "gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
227 panel.add(new StyledLabel(trans.get("lbl.doubleClick1"), -2), "alignx 50%");
229 panel.add(new ScaleSelector(figurePane), "spany 2");
230 panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.clickDrag") + " " +
231 trans.get("FreeformFinSetConfig.lbl.ctrlClick"), -2), "spany 2, right, wrap");
234 panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.doubleClick2"), -2), "alignx 50%");
244 public void updateFields() {
245 super.updateFields();
247 if (tableModel != null) {
248 tableModel.fireTableDataChanged();
250 if (figure != null) {
251 figure.updateFigure();
258 private class FinPointScrollPane extends ScaleScrollPane {
259 private static final int ANY_MASK =
260 (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK |
261 MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK |
262 MouseEvent.SHIFT_DOWN_MASK);
264 private int dragIndex = -1;
266 public FinPointScrollPane() {
267 super(figure, false); // Disallow fitting as it's buggy
271 public void mousePressed(MouseEvent event) {
272 int mods = event.getModifiersEx();
274 if (event.getButton() != MouseEvent.BUTTON1 ||
275 (mods & ANY_MASK) != 0) {
276 super.mousePressed(event);
280 int index = getPoint(event);
285 index = getSegment(event);
287 Point2D.Double point = getCoordinates(event);
288 finset.addPoint(index);
290 finset.setPoint(index, point.x, point.y);
291 } catch (IllegalFinPointException ignore) {
298 super.mousePressed(event);
304 public void mouseDragged(MouseEvent event) {
305 int mods = event.getModifiersEx();
307 (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) !=
308 MouseEvent.BUTTON1_DOWN_MASK) {
309 super.mouseDragged(event);
312 Point2D.Double point = getCoordinates(event);
315 finset.setPoint(dragIndex, point.x, point.y);
316 } catch (IllegalFinPointException ignore) {
317 log.debug("Ignoring IllegalFinPointException while dragging, dragIndex=" + dragIndex +
318 " x=" + point.x + " y=" + point.y);
324 public void mouseReleased(MouseEvent event) {
326 super.mouseReleased(event);
330 public void mouseClicked(MouseEvent event) {
331 int mods = event.getModifiersEx();
332 if (event.getButton() != MouseEvent.BUTTON1 ||
333 (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) {
334 super.mouseClicked(event);
338 int index = getPoint(event);
340 super.mouseClicked(event);
345 finset.removePoint(index);
346 } catch (IllegalFinPointException ignore) {
351 private int getPoint(MouseEvent event) {
352 Point p0 = event.getPoint();
353 Point p1 = this.getViewport().getViewPosition();
357 return figure.getIndexByPoint(x, y);
360 private int getSegment(MouseEvent event) {
361 Point p0 = event.getPoint();
362 Point p1 = this.getViewport().getViewPosition();
366 return figure.getSegmentByPoint(x, y);
369 private Point2D.Double getCoordinates(MouseEvent event) {
370 Point p0 = event.getPoint();
371 Point p1 = this.getViewport().getViewPosition();
375 return figure.convertPoint(x, y);
385 private enum Columns {
388 // public String toString() {
392 // public String getValue(FreeformFinSet finset, int row) {
393 // return "" + (row+1) + ".";
396 // public int getWidth() {
402 public String toString() {
403 return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
407 public String getValue(FreeformFinSet finset, int row) {
408 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
409 .toString(finset.getFinPoints()[row].x);
414 public String toString() {
415 return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
419 public String getValue(FreeformFinSet finset, int row) {
420 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
421 .toString(finset.getFinPoints()[row].y);
425 public abstract String getValue(FreeformFinSet finset, int row);
428 public abstract String toString();
430 public int getWidth() {
435 private class FinPointTableModel extends AbstractTableModel {
438 public int getColumnCount() {
439 return Columns.values().length;
443 public int getRowCount() {
444 return finset.getPointCount();
448 public Object getValueAt(int rowIndex, int columnIndex) {
449 return Columns.values()[columnIndex].getValue(finset, rowIndex);
453 public String getColumnName(int columnIndex) {
454 return Columns.values()[columnIndex].toString();
458 public boolean isCellEditable(int rowIndex, int columnIndex) {
459 if (rowIndex == 0 || rowIndex == getRowCount() - 1) {
460 return (columnIndex == Columns.X.ordinal());
463 return (columnIndex == Columns.X.ordinal() || columnIndex == Columns.Y.ordinal());
467 public void setValueAt(Object o, int rowIndex, int columnIndex) {
468 if (!(o instanceof String))
471 if (rowIndex < 0 || rowIndex >= finset.getFinPoints().length ||
472 columnIndex < 0 || columnIndex >= Columns.values().length) {
473 throw new IllegalArgumentException("Index out of bounds, row=" + rowIndex +
474 " column=" + columnIndex + " fin point count=" + finset.getFinPoints().length);
477 String str = (String) o;
480 double value = UnitGroup.UNITS_LENGTH.fromString(str);
481 Coordinate c = finset.getFinPoints()[rowIndex];
482 if (columnIndex == Columns.X.ordinal())
487 finset.setPoint(rowIndex, c.x, c.y);
489 } catch (NumberFormatException ignore) {
490 } catch (IllegalFinPointException ignore) {