Initial commit
[debian/openrocket] / src / net / sf / openrocket / gui / configdialog / FreeformFinSetConfig.java
1 package net.sf.openrocket.gui.configdialog;
2
3
4 import java.awt.Point;
5 import java.awt.event.MouseEvent;
6 import java.awt.geom.Point2D;
7
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;
18
19 import net.miginfocom.swing.MigLayout;
20 import net.sf.openrocket.gui.BasicSlider;
21 import net.sf.openrocket.gui.ResizeLabel;
22 import net.sf.openrocket.gui.SpinnerEditor;
23 import net.sf.openrocket.gui.UnitSelector;
24 import net.sf.openrocket.gui.adaptors.DoubleModel;
25 import net.sf.openrocket.gui.adaptors.EnumModel;
26 import net.sf.openrocket.gui.adaptors.IntegerModel;
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.material.Material;
31 import net.sf.openrocket.rocketcomponent.FinSet;
32 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
33 import net.sf.openrocket.rocketcomponent.RocketComponent;
34 import net.sf.openrocket.unit.UnitGroup;
35 import net.sf.openrocket.util.Coordinate;
36
37 public class FreeformFinSetConfig extends FinSetConfig {
38
39         private final FreeformFinSet finset;
40         private JTable table = null;
41         private FinPointTableModel tableModel = null;
42         
43         private FinPointFigure figure = null;
44         
45         
46         public FreeformFinSetConfig(RocketComponent component) {
47                 super(component);
48                 this.finset = (FreeformFinSet)component;
49
50
51                 tabbedPane.insertTab("General", null, generalPane(), "General properties", 0);
52                 tabbedPane.insertTab("Shape", null, shapePane(), "Fin shape", 1);
53                 tabbedPane.setSelectedIndex(0);
54                 
55                 addFinSetButtons();
56         }
57         
58         
59         
60         private JPanel generalPane() {
61
62                 DoubleModel m;
63                 JSpinner spin;
64                 JComboBox combo;
65                 
66                 JPanel mainPanel = new JPanel(new MigLayout("fill"));
67                 
68                 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel","[][65lp::][30lp::]",""));
69                 
70                 
71                 
72                 ////  Number of fins
73                 panel.add(new JLabel("Number of fins:"));
74                 
75                 IntegerModel im = new IntegerModel(component,"FinCount",1,8);
76                 
77                 spin = new JSpinner(im.getSpinnerModel());
78                 spin.setEditor(new SpinnerEditor(spin));
79                 panel.add(spin,"growx, wrap");
80                 
81                 
82                 ////  Base rotation
83                 panel.add(new JLabel("Fin rotation:"));
84                 
85                 m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE,-Math.PI,Math.PI);
86                 
87                 spin = new JSpinner(m.getSpinnerModel());
88                 spin.setEditor(new SpinnerEditor(spin));
89                 panel.add(spin,"growx");
90                 
91                 panel.add(new UnitSelector(m),"growx");
92                 panel.add(new BasicSlider(m.getSliderModel(-Math.PI,Math.PI)),"w 100lp, wrap");
93                 
94                 
95
96                 ////  Fin cant
97                 JLabel label = new JLabel("Fin cant:");
98                 label.setToolTipText("The angle that the fins are canted with respect to the rocket " +
99                                 "body.");
100                 panel.add(label);
101                 
102                 m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
103                                 -FinSet.MAX_CANT,FinSet.MAX_CANT);
104                 
105                 spin = new JSpinner(m.getSpinnerModel());
106                 spin.setEditor(new SpinnerEditor(spin));
107                 panel.add(spin,"growx");
108                 
109                 panel.add(new UnitSelector(m),"growx");
110                 panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT,FinSet.MAX_CANT)),
111                                 "w 100lp, wrap 40lp");
112                 
113                 
114                 
115                 ////  Position
116                 panel.add(new JLabel("Position relative to:"));
117
118                 combo = new JComboBox(
119                                 new EnumModel<RocketComponent.Position>(component, "RelativePosition",
120                                                 new RocketComponent.Position[] {
121                                                 RocketComponent.Position.TOP,
122                                                 RocketComponent.Position.MIDDLE,
123                                                 RocketComponent.Position.BOTTOM,
124                                                 RocketComponent.Position.ABSOLUTE
125                                 }));
126                 panel.add(combo,"spanx 3, growx, wrap");
127                                 
128                 panel.add(new JLabel("plus"),"right");
129
130                 m = new DoubleModel(component,"PositionValue",UnitGroup.UNITS_LENGTH);
131                 spin = new JSpinner(m.getSpinnerModel());
132                 spin.setEditor(new SpinnerEditor(spin));
133                 panel.add(spin,"growx");
134                 
135                 panel.add(new UnitSelector(m),"growx");
136                 panel.add(new BasicSlider(m.getSliderModel(
137                                 new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
138                                 new DoubleModel(component.getParent(), "Length"))),
139                                 "w 100lp, wrap");
140
141                 
142                 
143                 
144
145                 mainPanel.add(panel, "aligny 20%");
146                 mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
147                 
148                 
149                 panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
150                 
151                 
152                 
153                 
154                 ////  Cross section
155                 panel.add(new JLabel("Fin cross section:"),"span, split");
156                 combo = new JComboBox(
157                                 new EnumModel<FinSet.CrossSection>(component,"CrossSection"));
158                 panel.add(combo,"growx, wrap unrel");
159                 
160
161                 ////  Thickness
162                 panel.add(new JLabel("Thickness:"));
163                 
164                 m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
165                 
166                 spin = new JSpinner(m.getSpinnerModel());
167                 spin.setEditor(new SpinnerEditor(spin));
168                 panel.add(spin,"growx");
169                 
170                 panel.add(new UnitSelector(m),"growx");
171                 panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
172                 
173
174                 //// Material
175                 materialPanel(panel, Material.Type.BULK);
176                 
177                 
178                 
179                 mainPanel.add(panel, "aligny 20%");
180                 
181                 return mainPanel;
182         }
183         
184         
185         
186         private JPanel shapePane() {
187                 JPanel panel = new JPanel(new MigLayout("fill"));
188                 
189                 
190                 // Create the figure
191                 figure = new FinPointFigure(finset);
192                 ScaleScrollPane figurePane = new FinPointScrollPane();
193                 figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
194                 figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
195
196                 // Create the table
197                 tableModel = new FinPointTableModel();
198                 table = new JTable(tableModel);
199                 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
200                 for (int i=0; i < Columns.values().length; i++) {
201                         table.getColumnModel().getColumn(i).
202                                 setPreferredWidth(Columns.values()[i].getWidth());
203                 }
204                 JScrollPane tablePane = new JScrollPane(table);
205                 
206                 
207 //              panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
208 //              panel.add(new JLabel("    View:"), "wrap, aligny bottom");
209                 
210                 
211                 panel.add(tablePane,"growy, width :100lp:, height 100lp:250lp:");
212                 panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
213                 
214                 panel.add(new ResizeLabel("Double-click", -2), "alignx 50%");
215                 
216                 panel.add(new ScaleSelector(figurePane),"spany 2");
217                 panel.add(new ResizeLabel("Click+drag: Add and move points   " +
218                                 "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
219                 
220                 
221                 panel.add(new ResizeLabel("to edit", -2), "alignx 50%");
222                 
223                 return panel;
224         }
225         
226         
227         
228         
229         
230         @Override
231         public void updateFields() {
232                 super.updateFields();
233                 
234                 if (tableModel != null) {
235                         tableModel.fireTableDataChanged();
236                 }
237                 if (figure != null) {
238                         figure.updateFigure();
239                 }
240         }
241         
242         
243         
244         
245         private class FinPointScrollPane extends ScaleScrollPane {
246                 private static final int ANY_MASK = 
247                         (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK | 
248                                         MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK | 
249                                         MouseEvent.SHIFT_DOWN_MASK);
250                 
251                 private int dragIndex = -1;
252                 
253                 public FinPointScrollPane() {
254                         super(figure, false);   // Disallow fitting as it's buggy
255                 }
256
257                 @Override
258                 public void mousePressed(MouseEvent event) {
259                         int mods = event.getModifiersEx();
260                         
261                         if (event.getButton() != MouseEvent.BUTTON1 ||
262                                         (mods & ANY_MASK) != 0) {
263                                 super.mousePressed(event);
264                                 return;
265                         }
266                         
267                         int index = getPoint(event);
268                         if (index >= 0) {
269                                 dragIndex = index;
270                                 return;
271                         }
272                         index = getSegment(event);
273                         if (index >= 0) {
274                                 Point2D.Double point = getCoordinates(event);
275                                 finset.addPoint(index);
276                                 try {
277                                         finset.setPoint(index, point.x, point.y);
278                                 } catch (IllegalArgumentException ignore) { }
279                                 dragIndex = index;
280                                 
281                                 return;
282                         }
283                         
284                         super.mousePressed(event);
285                         return;
286                 }
287
288                 
289                 @Override
290                 public void mouseDragged(MouseEvent event) {
291                         int mods = event.getModifiersEx();
292                         if (dragIndex < 0 ||
293                                         (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != 
294                                                 MouseEvent.BUTTON1_DOWN_MASK) {
295                                 super.mouseDragged(event);
296                                 return;
297                         }
298                         Point2D.Double point = getCoordinates(event);
299                         
300                         try {
301                                 finset.setPoint(dragIndex, point.x, point.y);
302                         } catch (IllegalArgumentException ignore) {
303                                 System.out.println("IAE:"+ignore);
304                         }
305                 }
306                 
307                 
308                 @Override
309                 public void mouseReleased(MouseEvent event) {
310                         dragIndex = -1;
311                         super.mouseReleased(event);
312                 }
313                 
314                 @Override
315                 public void mouseClicked(MouseEvent event) {
316                         int mods = event.getModifiersEx();
317                         if (event.getButton() != MouseEvent.BUTTON1 ||
318                                         (mods & ANY_MASK) != MouseEvent.CTRL_DOWN_MASK) {
319                                 super.mouseClicked(event);
320                                 return;
321                         }
322                         
323                         int index = getPoint(event);
324                         if (index < 0) {
325                                 super.mouseClicked(event);
326                                 return;
327                         }
328
329                         try {
330                                 finset.removePoint(index);
331                         } catch (IllegalArgumentException ignore) {
332                         }
333                 }
334                 
335                 
336                 private int getPoint(MouseEvent event) {
337                         Point p0 = event.getPoint();
338                         Point p1 = this.getViewport().getViewPosition();
339                         int x = p0.x + p1.x;
340                         int y = p0.y + p1.y;
341                         
342                         return figure.getIndexByPoint(x, y);
343                 }
344                 
345                 private int getSegment(MouseEvent event) {
346                         Point p0 = event.getPoint();
347                         Point p1 = this.getViewport().getViewPosition();
348                         int x = p0.x + p1.x;
349                         int y = p0.y + p1.y;
350                         
351                         return figure.getSegmentByPoint(x, y);
352                 }
353                 
354                 private Point2D.Double getCoordinates(MouseEvent event) {
355                         Point p0 = event.getPoint();
356                         Point p1 = this.getViewport().getViewPosition();
357                         int x = p0.x + p1.x;
358                         int y = p0.y + p1.y;
359                         
360                         return figure.convertPoint(x, y);
361                 }
362                 
363                 
364         }
365         
366         
367
368         
369         
370         private enum Columns {
371 //              NUMBER {
372 //                      @Override
373 //                      public String toString() {
374 //                              return "#";
375 //                      }
376 //                      @Override
377 //                      public String getValue(FreeformFinSet finset, int row) {
378 //                              return "" + (row+1) + ".";
379 //                      }
380 //                      @Override
381 //                      public int getWidth() {
382 //                              return 10;
383 //                      }
384 //              }, 
385                 X {
386                         @Override
387                         public String toString() {
388                                 return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
389                         }
390                         @Override
391                         public String getValue(FreeformFinSet finset, int row) {
392                                 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
393                                         .toString(finset.getFinPoints()[row].x);
394                         }
395                 }, 
396                 Y {
397                         @Override
398                         public String toString() {
399                                 return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
400                         }
401                         @Override
402                         public String getValue(FreeformFinSet finset, int row) {
403                                 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
404                                         .toString(finset.getFinPoints()[row].y);
405                         }
406                 };
407                 
408                 public abstract String getValue(FreeformFinSet finset, int row);
409                 @Override
410                 public abstract String toString();
411                 public int getWidth() {
412                         return 20;
413                 }
414         }
415         
416         private class FinPointTableModel extends AbstractTableModel {
417                 
418                 @Override
419                 public int getColumnCount() {
420                         return Columns.values().length;
421                 }
422
423                 @Override
424                 public int getRowCount() {
425                         return finset.getPointCount();
426                 }
427
428                 @Override
429                 public Object getValueAt(int rowIndex, int columnIndex) {
430                         return Columns.values()[columnIndex].getValue(finset, rowIndex);
431                 }
432                 
433                 @Override
434                 public String getColumnName(int columnIndex) {
435                         return Columns.values()[columnIndex].toString();
436                 }
437                 
438                 @Override
439                 public boolean isCellEditable(int rowIndex, int columnIndex) {
440                         if (rowIndex == 0 || rowIndex == getRowCount()-1) {
441                                 return (columnIndex == Columns.X.ordinal());
442                         }
443                         
444                         return (columnIndex == Columns.X.ordinal() || columnIndex == Columns.Y.ordinal());
445                 }
446                 
447                 @Override
448                 public void setValueAt(Object o, int rowIndex, int columnIndex) {
449                         if (!(o instanceof String))
450                                 return;
451                         
452                         String str = (String)o;
453                         try {
454                                 
455                                 double value = UnitGroup.UNITS_LENGTH.fromString(str);
456                                 Coordinate c = finset.getFinPoints()[rowIndex];
457                                 if (columnIndex == Columns.X.ordinal())
458                                         c = c.setX(value);
459                                 else
460                                         c = c.setY(value);
461                                 
462                                 finset.setPoint(rowIndex, c.x, c.y);
463                         
464                         } catch (NumberFormatException ignore) {
465                         }
466                 }
467                 
468                 
469         }
470 }