87be5fb40592fd06546250371e255c7596b410f5
[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.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;
39
40 public class FreeformFinSetConfig extends FinSetConfig {
41
42         private final FreeformFinSet finset;
43         private JTable table = null;
44         private FinPointTableModel tableModel = null;
45         
46         private FinPointFigure figure = null;
47         private static final Translator trans = Application.getTranslator();
48
49         
50         public FreeformFinSetConfig(RocketComponent component) {
51                 super(component);
52                 this.finset = (FreeformFinSet)component;
53
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);
61                 
62                 addFinSetButtons();
63         }
64         
65         
66         
67         private JPanel generalPane() {
68
69                 DoubleModel m;
70                 JSpinner spin;
71                 JComboBox combo;
72                 
73                 JPanel mainPanel = new JPanel(new MigLayout("fill"));
74                 
75                 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel","[][65lp::][30lp::]",""));
76                 
77                 
78                 
79                 ////  Number of fins:
80                 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Numberoffins")));
81                 
82                 IntegerModel im = new IntegerModel(component,"FinCount",1,8);
83                 
84                 spin = new JSpinner(im.getSpinnerModel());
85                 spin.setEditor(new SpinnerEditor(spin));
86                 panel.add(spin,"growx, wrap");
87                 
88                 
89                 ////  Base rotation
90                 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Finrotation")));
91                 
92                 m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE,-Math.PI,Math.PI);
93                 
94                 spin = new JSpinner(m.getSpinnerModel());
95                 spin.setEditor(new SpinnerEditor(spin));
96                 panel.add(spin,"growx");
97                 
98                 panel.add(new UnitSelector(m),"growx");
99                 panel.add(new BasicSlider(m.getSliderModel(-Math.PI,Math.PI)),"w 100lp, wrap");
100                 
101                 
102
103                 ////  Fin cant
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"));
107                 panel.add(label);
108                 
109                 m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
110                                 -FinSet.MAX_CANT,FinSet.MAX_CANT);
111                 
112                 spin = new JSpinner(m.getSpinnerModel());
113                 spin.setEditor(new SpinnerEditor(spin));
114                 panel.add(spin,"growx");
115                 
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");
119                 
120                 
121                 
122                 ////  Position
123                 //// Position relative to:
124                 panel.add(new JLabel("FreeformFinSetCfg.lbl.Posrelativeto"));
125
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
133                                 }));
134                 panel.add(combo,"spanx 3, growx, wrap");
135                                 //// plus
136                 panel.add(new JLabel("FreeformFinSetCfg.lbl.plus"),"right");
137
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");
142                 
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"))),
147                                 "w 100lp, wrap");
148
149                 
150                 
151                 
152
153                 mainPanel.add(panel, "aligny 20%");
154                 mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
155                 
156                 
157                 panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
158                 
159                 
160                 
161                 
162                 ////  Cross section
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");
168                 
169
170                 ////  Thickness:
171                 panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Thickness")));
172                 
173                 m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
174                 
175                 spin = new JSpinner(m.getSpinnerModel());
176                 spin.setEditor(new SpinnerEditor(spin));
177                 panel.add(spin,"growx");
178                 
179                 panel.add(new UnitSelector(m),"growx");
180                 panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
181                 
182
183                 //// Material
184                 materialPanel(panel, Material.Type.BULK);
185                 
186                 
187                 
188                 mainPanel.add(panel, "aligny 20%");
189                 
190                 return mainPanel;
191         }
192         
193         
194         
195         private JPanel shapePane() {
196                 JPanel panel = new JPanel(new MigLayout("fill"));
197                 
198                 
199                 // Create the figure
200                 figure = new FinPointFigure(finset);
201                 ScaleScrollPane figurePane = new FinPointScrollPane();
202                 figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
203                 figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
204
205                 // Create the table
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());
212                 }
213                 JScrollPane tablePane = new JScrollPane(table);
214                 
215                 
216 //              panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
217 //              panel.add(new JLabel("    View:"), "wrap, aligny bottom");
218                 
219                 
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");
222                 
223                 panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
224                 
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");
228                 
229                 
230                 panel.add(new StyledLabel("to edit", -2), "alignx 50%");
231                 
232                 return panel;
233         }
234         
235         
236         
237         
238         
239         @Override
240         public void updateFields() {
241                 super.updateFields();
242                 
243                 if (tableModel != null) {
244                         tableModel.fireTableDataChanged();
245                 }
246                 if (figure != null) {
247                         figure.updateFigure();
248                 }
249         }
250         
251         
252         
253         
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);
259                 
260                 private int dragIndex = -1;
261                 
262                 public FinPointScrollPane() {
263                         super(figure, false);   // Disallow fitting as it's buggy
264                 }
265
266                 @Override
267                 public void mousePressed(MouseEvent event) {
268                         int mods = event.getModifiersEx();
269                         
270                         if (event.getButton() != MouseEvent.BUTTON1 ||
271                                         (mods & ANY_MASK) != 0) {
272                                 super.mousePressed(event);
273                                 return;
274                         }
275                         
276                         int index = getPoint(event);
277                         if (index >= 0) {
278                                 dragIndex = index;
279                                 return;
280                         }
281                         index = getSegment(event);
282                         if (index >= 0) {
283                                 Point2D.Double point = getCoordinates(event);
284                                 finset.addPoint(index);
285                                 try {
286                                         finset.setPoint(index, point.x, point.y);
287                                 } catch (IllegalFinPointException ignore) { }
288                                 dragIndex = index;
289                                 
290                                 return;
291                         }
292                         
293                         super.mousePressed(event);
294                         return;
295                 }
296
297                 
298                 @Override
299                 public void mouseDragged(MouseEvent event) {
300                         int mods = event.getModifiersEx();
301                         if (dragIndex < 0 ||
302                                         (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != 
303                                                 MouseEvent.BUTTON1_DOWN_MASK) {
304                                 super.mouseDragged(event);
305                                 return;
306                         }
307                         Point2D.Double point = getCoordinates(event);
308                         
309                         try {
310                                 finset.setPoint(dragIndex, point.x, point.y);
311                         } catch (IllegalFinPointException ignore) {
312                                 System.out.println("IAE:"+ignore);
313                         }
314                 }
315                 
316                 
317                 @Override
318                 public void mouseReleased(MouseEvent event) {
319                         dragIndex = -1;
320                         super.mouseReleased(event);
321                 }
322                 
323                 @Override
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);
329                                 return;
330                         }
331                         
332                         int index = getPoint(event);
333                         if (index < 0) {
334                                 super.mouseClicked(event);
335                                 return;
336                         }
337
338                         try {
339                                 finset.removePoint(index);
340                         } catch (IllegalFinPointException ignore) {
341                         }
342                 }
343                 
344                 
345                 private int getPoint(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.getIndexByPoint(x, y);
352                 }
353                 
354                 private int getSegment(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.getSegmentByPoint(x, y);
361                 }
362                 
363                 private Point2D.Double getCoordinates(MouseEvent event) {
364                         Point p0 = event.getPoint();
365                         Point p1 = this.getViewport().getViewPosition();
366                         int x = p0.x + p1.x;
367                         int y = p0.y + p1.y;
368                         
369                         return figure.convertPoint(x, y);
370                 }
371                 
372                 
373         }
374         
375         
376
377         
378         
379         private enum Columns {
380 //              NUMBER {
381 //                      @Override
382 //                      public String toString() {
383 //                              return "#";
384 //                      }
385 //                      @Override
386 //                      public String getValue(FreeformFinSet finset, int row) {
387 //                              return "" + (row+1) + ".";
388 //                      }
389 //                      @Override
390 //                      public int getWidth() {
391 //                              return 10;
392 //                      }
393 //              }, 
394                 X {
395                         @Override
396                         public String toString() {
397                                 return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
398                         }
399                         @Override
400                         public String getValue(FreeformFinSet finset, int row) {
401                                 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
402                                         .toString(finset.getFinPoints()[row].x);
403                         }
404                 }, 
405                 Y {
406                         @Override
407                         public String toString() {
408                                 return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
409                         }
410                         @Override
411                         public String getValue(FreeformFinSet finset, int row) {
412                                 return UnitGroup.UNITS_LENGTH.getDefaultUnit()
413                                         .toString(finset.getFinPoints()[row].y);
414                         }
415                 };
416                 
417                 public abstract String getValue(FreeformFinSet finset, int row);
418                 @Override
419                 public abstract String toString();
420                 public int getWidth() {
421                         return 20;
422                 }
423         }
424         
425         private class FinPointTableModel extends AbstractTableModel {
426                 
427                 @Override
428                 public int getColumnCount() {
429                         return Columns.values().length;
430                 }
431
432                 @Override
433                 public int getRowCount() {
434                         return finset.getPointCount();
435                 }
436
437                 @Override
438                 public Object getValueAt(int rowIndex, int columnIndex) {
439                         return Columns.values()[columnIndex].getValue(finset, rowIndex);
440                 }
441                 
442                 @Override
443                 public String getColumnName(int columnIndex) {
444                         return Columns.values()[columnIndex].toString();
445                 }
446                 
447                 @Override
448                 public boolean isCellEditable(int rowIndex, int columnIndex) {
449                         if (rowIndex == 0 || rowIndex == getRowCount()-1) {
450                                 return (columnIndex == Columns.X.ordinal());
451                         }
452                         
453                         return (columnIndex == Columns.X.ordinal() || columnIndex == Columns.Y.ordinal());
454                 }
455                 
456                 @Override
457                 public void setValueAt(Object o, int rowIndex, int columnIndex) {
458                         if (!(o instanceof String))
459                                 return;
460                         
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);
465                         }
466                         
467                         String str = (String)o;
468                         try {
469                                 
470                                 double value = UnitGroup.UNITS_LENGTH.fromString(str);
471                                 Coordinate c = finset.getFinPoints()[rowIndex];
472                                 if (columnIndex == Columns.X.ordinal())
473                                         c = c.setX(value);
474                                 else
475                                         c = c.setY(value);
476                                 
477                                 finset.setPoint(rowIndex, c.x, c.y);
478                         
479                         } catch (NumberFormatException ignore) {
480                         } catch (IllegalFinPointException ignore) {
481                         }
482                 }
483         }
484 }