added svn:ignores
[debian/openrocket] / src / net / sf / openrocket / gui / configdialog / InnerTubeConfig.java
1 package net.sf.openrocket.gui.configdialog;
2
3
4
5 import java.awt.Color;
6 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.Graphics2D;
9 import java.awt.Rectangle;
10 import java.awt.RenderingHints;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.MouseEvent;
14 import java.awt.event.MouseListener;
15 import java.awt.geom.Ellipse2D;
16 import java.util.List;
17
18 import javax.swing.BorderFactory;
19 import javax.swing.JButton;
20 import javax.swing.JComponent;
21 import javax.swing.JLabel;
22 import javax.swing.JPanel;
23 import javax.swing.JSpinner;
24 import javax.swing.SwingUtilities;
25 import javax.swing.border.BevelBorder;
26 import javax.swing.event.ChangeEvent;
27 import javax.swing.event.ChangeListener;
28
29 import net.miginfocom.swing.MigLayout;
30 import net.sf.openrocket.gui.Resettable;
31 import net.sf.openrocket.gui.SpinnerEditor;
32 import net.sf.openrocket.gui.adaptors.DoubleModel;
33 import net.sf.openrocket.gui.components.BasicSlider;
34 import net.sf.openrocket.gui.components.UnitSelector;
35 import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
36 import net.sf.openrocket.rocketcomponent.Clusterable;
37 import net.sf.openrocket.rocketcomponent.InnerTube;
38 import net.sf.openrocket.rocketcomponent.MotorMount;
39 import net.sf.openrocket.rocketcomponent.RocketComponent;
40 import net.sf.openrocket.unit.UnitGroup;
41 import net.sf.openrocket.util.BugException;
42 import net.sf.openrocket.util.Coordinate;
43
44
45 public class InnerTubeConfig extends ThicknessRingComponentConfig {
46         
47
48         public InnerTubeConfig(RocketComponent c) {
49                 super(c);
50                 
51                 JPanel tab;
52                 
53                 tab = new MotorConfig((MotorMount) c);
54                 tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 1);
55                 
56                 tab = clusterTab();
57                 tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 2);
58                 
59                 tab = positionTab();
60                 tabbedPane.insertTab("Radial position", null, tab, "Radial position", 3);
61                 
62                 tabbedPane.setSelectedIndex(0);
63         }
64         
65         
66         private JPanel clusterTab() {
67                 JPanel panel = new JPanel(new MigLayout());
68                 
69                 JPanel subPanel = new JPanel(new MigLayout());
70                 
71                 // Cluster type selection
72                 subPanel.add(new JLabel("Select cluster configuration:"), "spanx, wrap");
73                 subPanel.add(new ClusterSelectionPanel((InnerTube) component), "spanx, wrap");
74                 //              JPanel clusterSelection = new ClusterSelectionPanel((InnerTube)component);
75                 //              clusterSelection.setBackground(Color.blue);
76                 //              subPanel.add(clusterSelection);
77                 
78                 panel.add(subPanel);
79                 
80
81                 subPanel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]"));
82                 
83                 // Tube separation scale
84                 JLabel l = new JLabel("Tube separation:");
85                 l.setToolTipText("The separation of the tubes, 1.0 = touching each other");
86                 subPanel.add(l);
87                 DoubleModel dm = new DoubleModel(component, "ClusterScale", 1, UnitGroup.UNITS_NONE, 0);
88                 
89                 JSpinner spin = new JSpinner(dm.getSpinnerModel());
90                 spin.setEditor(new SpinnerEditor(spin));
91                 spin.setToolTipText("The separation of the tubes, 1.0 = touching each other");
92                 subPanel.add(spin, "growx");
93                 
94                 BasicSlider bs = new BasicSlider(dm.getSliderModel(0, 1, 4));
95                 bs.setToolTipText("The separation of the tubes, 1.0 = touching each other");
96                 subPanel.add(bs, "skip,w 100lp, wrap");
97                 
98                 // Rotation
99                 l = new JLabel("Rotation:");
100                 l.setToolTipText("Rotation angle of the cluster configuration");
101                 subPanel.add(l);
102                 dm = new DoubleModel(component, "ClusterRotation", 1, UnitGroup.UNITS_ANGLE,
103                                 -Math.PI, Math.PI);
104                 
105                 spin = new JSpinner(dm.getSpinnerModel());
106                 spin.setEditor(new SpinnerEditor(spin));
107                 spin.setToolTipText("Rotation angle of the cluster configuration");
108                 subPanel.add(spin, "growx");
109                 
110                 subPanel.add(new UnitSelector(dm), "growx");
111                 bs = new BasicSlider(dm.getSliderModel(-Math.PI, 0, Math.PI));
112                 bs.setToolTipText("Rotation angle of the cluster configuration");
113                 subPanel.add(bs, "w 100lp, wrap para");
114                 
115
116
117                 // Split button
118                 JButton split = new JButton("Split cluster");
119                 split.setToolTipText("<html>Split the cluster into separate components.<br>" +
120                                 "This also duplicates all components attached to this inner tube.");
121                 split.addActionListener(new ActionListener() {
122                         @Override
123                         public void actionPerformed(ActionEvent e) {
124                                 // Do change in future for overall safety
125                                 SwingUtilities.invokeLater(new Runnable() {
126                                         @Override
127                                         public void run() {
128                                                 RocketComponent parent = component.getParent();
129                                                 int index = parent.getChildPosition(component);
130                                                 if (index < 0) {
131                                                         throw new BugException("Inconsistent state: component=" + component +
132                                                                         " parent=" + parent + " parent.children=" + parent.getChildren());
133                                                 }
134                                                 
135                                                 InnerTube tube = (InnerTube) component;
136                                                 if (tube.getClusterCount() <= 1)
137                                                         return;
138                                                 
139                                                 ComponentConfigDialog.addUndoPosition("Split cluster");
140                                                 
141                                                 Coordinate[] coords = { Coordinate.NUL };
142                                                 coords = component.shiftCoordinates(coords);
143                                                 parent.removeChild(index);
144                                                 for (int i = 0; i < coords.length; i++) {
145                                                         InnerTube copy = (InnerTube) component.copy();
146                                                         copy.setClusterConfiguration(ClusterConfiguration.SINGLE);
147                                                         copy.setClusterRotation(0.0);
148                                                         copy.setClusterScale(1.0);
149                                                         copy.setRadialShift(coords[i].y, coords[i].z);
150                                                         copy.setName(copy.getName() + " #" + (i + 1));
151                                                         
152                                                         parent.addChild(copy, index + i);
153                                                 }
154                                         }
155                                 });
156                         }
157                 });
158                 subPanel.add(split, "spanx, split 2, gapright para, sizegroup buttons, right");
159                 
160
161                 // Reset button
162                 JButton reset = new JButton("Reset settings");
163                 reset.setToolTipText("Reset the separation and rotation to the default values");
164                 reset.addActionListener(new ActionListener() {
165                         @Override
166                         public void actionPerformed(ActionEvent arg0) {
167                                 ((InnerTube) component).setClusterScale(1.0);
168                                 ((InnerTube) component).setClusterRotation(0.0);
169                         }
170                 });
171                 subPanel.add(reset, "sizegroup buttons, right");
172                 
173                 panel.add(subPanel, "grow");
174                 
175
176                 return panel;
177         }
178 }
179
180
181 class ClusterSelectionPanel extends JPanel {
182         private static final int BUTTON_SIZE = 50;
183         private static final int MOTOR_DIAMETER = 10;
184         
185         private static final Color SELECTED_COLOR = Color.RED;
186         private static final Color UNSELECTED_COLOR = Color.WHITE;
187         private static final Color MOTOR_FILL_COLOR = Color.GREEN;
188         private static final Color MOTOR_BORDER_COLOR = Color.BLACK;
189         
190         public ClusterSelectionPanel(Clusterable component) {
191                 super(new MigLayout("gap 0 0",
192                                 "[" + BUTTON_SIZE + "!][" + BUTTON_SIZE + "!][" + BUTTON_SIZE + "!][" + BUTTON_SIZE + "!]",
193                                 "[" + BUTTON_SIZE + "!][" + BUTTON_SIZE + "!][" + BUTTON_SIZE + "!]"));
194                 
195                 for (int i = 0; i < ClusterConfiguration.CONFIGURATIONS.length; i++) {
196                         ClusterConfiguration config = ClusterConfiguration.CONFIGURATIONS[i];
197                         
198                         JComponent button = new ClusterButton(component, config);
199                         if (i % 4 == 3)
200                                 add(button, "wrap");
201                         else
202                                 add(button);
203                 }
204                 
205         }
206         
207         
208         private class ClusterButton extends JPanel implements ChangeListener, MouseListener,
209                                                                                                                         Resettable {
210                 private Clusterable component;
211                 private ClusterConfiguration config;
212                 
213                 public ClusterButton(Clusterable c, ClusterConfiguration config) {
214                         component = c;
215                         this.config = config;
216                         setMinimumSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
217                         setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
218                         setMaximumSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
219                         setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
220                         //                      setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
221                         component.addChangeListener(this);
222                         addMouseListener(this);
223                 }
224                 
225                 
226                 @Override
227                 public void paintComponent(Graphics g) {
228                         super.paintComponent(g);
229                         Graphics2D g2 = (Graphics2D) g;
230                         Rectangle area = g2.getClipBounds();
231                         
232                         if (component.getClusterConfiguration() == config)
233                                 g2.setColor(SELECTED_COLOR);
234                         else
235                                 g2.setColor(UNSELECTED_COLOR);
236                         
237                         g2.fillRect(area.x, area.y, area.width, area.height);
238                         
239                         g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
240                                         RenderingHints.VALUE_STROKE_NORMALIZE);
241                         g2.setRenderingHint(RenderingHints.KEY_RENDERING,
242                                         RenderingHints.VALUE_RENDER_QUALITY);
243                         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
244                                         RenderingHints.VALUE_ANTIALIAS_ON);
245                         
246                         List<Double> points = config.getPoints();
247                         Ellipse2D.Float circle = new Ellipse2D.Float();
248                         for (int i = 0; i < points.size() / 2; i++) {
249                                 double x = points.get(i * 2);
250                                 double y = points.get(i * 2 + 1);
251                                 
252                                 double px = BUTTON_SIZE / 2 + x * MOTOR_DIAMETER;
253                                 double py = BUTTON_SIZE / 2 - y * MOTOR_DIAMETER;
254                                 circle.setFrameFromCenter(px, py, px + MOTOR_DIAMETER / 2, py + MOTOR_DIAMETER / 2);
255                                 
256                                 g2.setColor(MOTOR_FILL_COLOR);
257                                 g2.fill(circle);
258                                 g2.setColor(MOTOR_BORDER_COLOR);
259                                 g2.draw(circle);
260                         }
261                 }
262                 
263                 
264                 @Override
265                 public void stateChanged(ChangeEvent e) {
266                         repaint();
267                 }
268                 
269                 
270                 @Override
271                 public void mouseClicked(MouseEvent e) {
272                         if (e.getButton() == MouseEvent.BUTTON1) {
273                                 component.setClusterConfiguration(config);
274                         }
275                 }
276                 
277                 @Override
278                 public void mouseEntered(MouseEvent e) {
279                 }
280                 
281                 @Override
282                 public void mouseExited(MouseEvent e) {
283                 }
284                 
285                 @Override
286                 public void mousePressed(MouseEvent e) {
287                 }
288                 
289                 @Override
290                 public void mouseReleased(MouseEvent e) {
291                 }
292                 
293                 
294                 @Override
295                 public void resetModel() {
296                         component.removeChangeListener(this);
297                         removeMouseListener(this);
298                 }
299         }
300         
301 }