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.gui.SpinnerEditor;
21 import net.sf.openrocket.gui.adaptors.DoubleModel;
22 import net.sf.openrocket.gui.adaptors.EnumModel;
23 import net.sf.openrocket.gui.adaptors.IntegerModel;
24 import net.sf.openrocket.gui.components.BasicSlider;
25 import net.sf.openrocket.gui.components.StyledLabel;
26 import net.sf.openrocket.gui.components.UnitSelector;
27 import net.sf.openrocket.gui.scalefigure.FinPointFigure;
28 import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
29 import net.sf.openrocket.gui.scalefigure.ScaleSelector;
30 import net.sf.openrocket.l10n.Translator;
31 import net.sf.openrocket.material.Material;
32 import net.sf.openrocket.rocketcomponent.FinSet;
33 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
34 import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
35 import net.sf.openrocket.rocketcomponent.RocketComponent;
36 import net.sf.openrocket.startup.Application;
37 import net.sf.openrocket.unit.UnitGroup;
38 import net.sf.openrocket.util.Coordinate;
40 public class FreeformFinSetConfig extends FinSetConfig {
42 private final FreeformFinSet finset;
43 private JTable table = null;
44 private FinPointTableModel tableModel = null;
46 private FinPointFigure figure = null;
47 private static final Translator trans = Application.getTranslator();
50 public FreeformFinSetConfig(RocketComponent component) {
52 this.finset = (FreeformFinSet)component;
54 //// General and General properties
55 tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.General"), null, generalPane(),
56 trans.get("FreeformFinSetCfg.tab.ttip.General"), 0);
57 //// Shape and Fin shape
58 tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.Shape"), null, shapePane(),
59 trans.get("FreeformFinSetCfg.tab.ttip.Finshape"), 1);
60 tabbedPane.setSelectedIndex(0);
67 private JPanel generalPane() {
73 JPanel mainPanel = new JPanel(new MigLayout("fill"));
75 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel","[][65lp::][30lp::]",""));
80 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Numberoffins")));
82 IntegerModel im = new IntegerModel(component,"FinCount",1,8);
84 spin = new JSpinner(im.getSpinnerModel());
85 spin.setEditor(new SpinnerEditor(spin));
86 panel.add(spin,"growx, wrap");
90 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Finrotation")));
92 m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE,-Math.PI,Math.PI);
94 spin = new JSpinner(m.getSpinnerModel());
95 spin.setEditor(new SpinnerEditor(spin));
96 panel.add(spin,"growx");
98 panel.add(new UnitSelector(m),"growx");
99 panel.add(new BasicSlider(m.getSliderModel(-Math.PI,Math.PI)),"w 100lp, wrap");
104 JLabel label = new JLabel(trans.get("FreeformFinSetCfg.lbl.Fincant"));
105 //// The angle that the fins are canted with respect to the rocket body.
106 label.setToolTipText(trans.get("FreeformFinSetCfg.lbl.ttip.Fincant"));
109 m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
110 -FinSet.MAX_CANT,FinSet.MAX_CANT);
112 spin = new JSpinner(m.getSpinnerModel());
113 spin.setEditor(new SpinnerEditor(spin));
114 panel.add(spin,"growx");
116 panel.add(new UnitSelector(m),"growx");
117 panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT,FinSet.MAX_CANT)),
118 "w 100lp, wrap 40lp");
123 //// Position relative to:
124 panel.add(new JLabel("FreeformFinSetCfg.lbl.Posrelativeto"));
126 combo = new JComboBox(
127 new EnumModel<RocketComponent.Position>(component, "RelativePosition",
128 new RocketComponent.Position[] {
129 RocketComponent.Position.TOP,
130 RocketComponent.Position.MIDDLE,
131 RocketComponent.Position.BOTTOM,
132 RocketComponent.Position.ABSOLUTE
134 panel.add(combo,"spanx 3, growx, wrap");
136 panel.add(new JLabel("FreeformFinSetCfg.lbl.plus"),"right");
138 m = new DoubleModel(component,"PositionValue",UnitGroup.UNITS_LENGTH);
139 spin = new JSpinner(m.getSpinnerModel());
140 spin.setEditor(new SpinnerEditor(spin));
141 panel.add(spin,"growx");
143 panel.add(new UnitSelector(m),"growx");
144 panel.add(new BasicSlider(m.getSliderModel(
145 new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
146 new DoubleModel(component.getParent(), "Length"))),
153 mainPanel.add(panel, "aligny 20%");
154 mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
157 panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
163 //// Fin cross section:
164 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")),"span, split");
165 combo = new JComboBox(
166 new EnumModel<FinSet.CrossSection>(component,"CrossSection"));
167 panel.add(combo,"growx, wrap unrel");
171 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Thickness")));
173 m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
175 spin = new JSpinner(m.getSpinnerModel());
176 spin.setEditor(new SpinnerEditor(spin));
177 panel.add(spin,"growx");
179 panel.add(new UnitSelector(m),"growx");
180 panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
184 materialPanel(panel, Material.Type.BULK);
188 mainPanel.add(panel, "aligny 20%");
195 private JPanel shapePane() {
196 JPanel panel = new JPanel(new MigLayout("fill"));
200 figure = new FinPointFigure(finset);
201 ScaleScrollPane figurePane = new FinPointScrollPane();
202 figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
203 figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
206 tableModel = new FinPointTableModel();
207 table = new JTable(tableModel);
208 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
209 for (int i=0; i < Columns.values().length; i++) {
210 table.getColumnModel().getColumn(i).
211 setPreferredWidth(Columns.values()[i].getWidth());
213 JScrollPane tablePane = new JScrollPane(table);
216 // panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
217 // panel.add(new JLabel(" View:"), "wrap, aligny bottom");
220 panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:");
221 panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
223 panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
225 panel.add(new ScaleSelector(figurePane),"spany 2");
226 panel.add(new StyledLabel("Click+drag: Add and move points " +
227 "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
230 panel.add(new StyledLabel("to edit", -2), "alignx 50%");
240 public void updateFields() {
241 super.updateFields();
243 if (tableModel != null) {
244 tableModel.fireTableDataChanged();
246 if (figure != null) {
247 figure.updateFigure();
254 private class FinPointScrollPane extends ScaleScrollPane {
255 private static final int ANY_MASK =
256 (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK |
257 MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK |
258 MouseEvent.SHIFT_DOWN_MASK);
260 private int dragIndex = -1;
262 public FinPointScrollPane() {
263 super(figure, false); // Disallow fitting as it's buggy
267 public void mousePressed(MouseEvent event) {
268 int mods = event.getModifiersEx();
270 if (event.getButton() != MouseEvent.BUTTON1 ||
271 (mods & ANY_MASK) != 0) {
272 super.mousePressed(event);
276 int index = getPoint(event);
281 index = getSegment(event);
283 Point2D.Double point = getCoordinates(event);
284 finset.addPoint(index);
286 finset.setPoint(index, point.x, point.y);
287 } catch (IllegalFinPointException ignore) { }
293 super.mousePressed(event);
299 public void mouseDragged(MouseEvent event) {
300 int mods = event.getModifiersEx();
302 (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) !=
303 MouseEvent.BUTTON1_DOWN_MASK) {
304 super.mouseDragged(event);
307 Point2D.Double point = getCoordinates(event);
310 finset.setPoint(dragIndex, point.x, point.y);
311 } catch (IllegalFinPointException ignore) {
312 System.out.println("IAE:"+ignore);
318 public void mouseReleased(MouseEvent event) {
320 super.mouseReleased(event);
324 public void mouseClicked(MouseEvent event) {
325 int mods = event.getModifiersEx();
326 if (event.getButton() != MouseEvent.BUTTON1 ||
327 (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) {
328 super.mouseClicked(event);
332 int index = getPoint(event);
334 super.mouseClicked(event);
339 finset.removePoint(index);
340 } catch (IllegalFinPointException ignore) {
345 private int getPoint(MouseEvent event) {
346 Point p0 = event.getPoint();
347 Point p1 = this.getViewport().getViewPosition();
351 return figure.getIndexByPoint(x, y);
354 private int getSegment(MouseEvent event) {
355 Point p0 = event.getPoint();
356 Point p1 = this.getViewport().getViewPosition();
360 return figure.getSegmentByPoint(x, y);
363 private Point2D.Double getCoordinates(MouseEvent event) {
364 Point p0 = event.getPoint();
365 Point p1 = this.getViewport().getViewPosition();
369 return figure.convertPoint(x, y);
379 private enum Columns {
382 // public String toString() {
386 // public String getValue(FreeformFinSet finset, int row) {
387 // return "" + (row+1) + ".";
390 // public int getWidth() {
396 public String toString() {
397 return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
400 public String getValue(FreeformFinSet finset, int row) {
401 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
402 .toString(finset.getFinPoints()[row].x);
407 public String toString() {
408 return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
411 public String getValue(FreeformFinSet finset, int row) {
412 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
413 .toString(finset.getFinPoints()[row].y);
417 public abstract String getValue(FreeformFinSet finset, int row);
419 public abstract String toString();
420 public int getWidth() {
425 private class FinPointTableModel extends AbstractTableModel {
428 public int getColumnCount() {
429 return Columns.values().length;
433 public int getRowCount() {
434 return finset.getPointCount();
438 public Object getValueAt(int rowIndex, int columnIndex) {
439 return Columns.values()[columnIndex].getValue(finset, rowIndex);
443 public String getColumnName(int columnIndex) {
444 return Columns.values()[columnIndex].toString();
448 public boolean isCellEditable(int rowIndex, int columnIndex) {
449 if (rowIndex == 0 || rowIndex == getRowCount()-1) {
450 return (columnIndex == Columns.X.ordinal());
453 return (columnIndex == Columns.X.ordinal() || columnIndex == Columns.Y.ordinal());
457 public void setValueAt(Object o, int rowIndex, int columnIndex) {
458 if (!(o instanceof String))
461 if (rowIndex < 0 || rowIndex >= finset.getFinPoints().length ||
462 columnIndex < 0 || columnIndex >= Columns.values().length) {
463 throw new IllegalArgumentException("Index out of bounds, row="+rowIndex+
464 " column="+columnIndex+" fin point count="+finset.getFinPoints().length);
467 String str = (String)o;
470 double value = UnitGroup.UNITS_LENGTH.fromString(str);
471 Coordinate c = finset.getFinPoints()[rowIndex];
472 if (columnIndex == Columns.X.ordinal())
477 finset.setPoint(rowIndex, c.x, c.y);
479 } catch (NumberFormatException ignore) {
480 } catch (IllegalFinPointException ignore) {