Create the new trunk
[debian/openrocket] / src / net / sf / openrocket / gui / UnitSelector.java
1 package net.sf.openrocket.gui;
2
3
4 import java.awt.Color;
5 import java.awt.Dimension;
6 import java.awt.ItemSelectable;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.ItemEvent;
10 import java.awt.event.ItemListener;
11 import java.awt.event.MouseEvent;
12 import java.awt.event.MouseListener;
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import javax.swing.Action;
17 import javax.swing.JMenuItem;
18 import javax.swing.JPopupMenu;
19 import javax.swing.border.Border;
20 import javax.swing.border.CompoundBorder;
21 import javax.swing.border.EmptyBorder;
22 import javax.swing.border.LineBorder;
23 import javax.swing.event.ChangeEvent;
24 import javax.swing.event.ChangeListener;
25
26 import net.sf.openrocket.gui.adaptors.DoubleModel;
27 import net.sf.openrocket.unit.Unit;
28 import net.sf.openrocket.unit.UnitGroup;
29
30
31 /**
32  * A Swing component that allows one to choose a unit from a UnitGroup within
33  * a DoubleModel model.  The current unit of the model is shown as a JLabel, and
34  * the unit can be changed by clicking on the label.
35  * 
36  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
37  */
38
39 public class UnitSelector extends ResizeLabel implements ChangeListener, MouseListener,
40                 ItemSelectable {
41
42         private final DoubleModel model;
43         private final Action[] extraActions;
44
45         private UnitGroup unitGroup;
46         private Unit currentUnit;
47
48         private final boolean showValue;
49
50         private final Border normalBorder;
51         private final Border withinBorder;
52
53
54         private final List<ItemListener> itemListeners = new ArrayList<ItemListener>();
55
56
57         /**
58          * Common private constructor that sets the values and sets up the borders.
59          * Either model or group must be null.
60          * 
61          * @param model
62          * @param showValue
63          * @param group
64          * @param actions
65          */
66         private UnitSelector(DoubleModel model, boolean showValue, UnitGroup group,
67                         Action[] actions) {
68                 super();
69
70                 this.model = model;
71                 this.showValue = showValue;
72
73                 if (model != null) {
74                         this.unitGroup = model.getUnitGroup();
75                         this.currentUnit = model.getCurrentUnit();
76                 } else {
77                         this.unitGroup = group;
78                         this.currentUnit = group.getDefaultUnit();
79                 }
80
81                 this.extraActions = actions;
82
83                 addMouseListener(this);
84
85                 // Define borders to use:
86
87                 normalBorder = new CompoundBorder(
88                                 new LineBorder(new Color(0f, 0f, 0f, 0.08f), 1), new EmptyBorder(1, 1, 1,
89                                                 1));
90                 withinBorder = new CompoundBorder(new LineBorder(new Color(0f, 0f, 0f, 0.6f)),
91                                 new EmptyBorder(1, 1, 1, 1));
92
93                 setBorder(normalBorder);
94                 updateText();
95         }
96
97
98
99         public UnitSelector(DoubleModel model, Action... actions) {
100                 this(model, false, actions);
101         }
102
103         public UnitSelector(DoubleModel model, boolean showValue, Action... actions) {
104                 this(model, showValue, null, actions);
105
106                 // Add model listener
107                 this.model.addChangeListener(this);
108         }
109
110
111         public UnitSelector(UnitGroup group, Action... actions) {
112                 this(null, false, group, actions);
113         }
114
115
116
117
118         /**
119          * Return the DoubleModel that is backing this selector up, or <code>null</code>.
120          * Either this method or {@link #getUnitGroup()} always returns <code>null</code>.
121          * 
122          * @return              the DoubleModel being used, or <code>null</code>.
123          */
124         public DoubleModel getModel() {
125                 return model;
126         }
127
128
129         /**
130          * Return the unit group that is being shown, or <code>null</code>.  Either this method
131          * or {@link #getModel()} always returns <code>null</code>.
132          * 
133          * @return              the UnitGroup being used, or <code>null</code>.
134          */
135         public UnitGroup getUnitGroup() {
136                 return unitGroup;
137         }
138
139
140         public void setUnitGroup(UnitGroup group) {
141                 if (model != null) {
142                         throw new IllegalStateException(
143                                         "UnitGroup cannot be set when backed up with model.");
144                 }
145
146                 if (this.unitGroup == group)
147                         return;
148
149                 this.unitGroup = group;
150                 this.currentUnit = group.getDefaultUnit();
151                 updateText();
152         }
153
154
155         /**
156          * Return the currently selected unit.  Works both when backup up with a DoubleModel
157          * and UnitGroup.
158          * 
159          * @return              the currently selected unit.
160          */
161         public Unit getSelectedUnit() {
162                 return currentUnit;
163         }
164
165
166         /**
167          * Set the currently selected unit.  Sets it to the DoubleModel if it is backed up
168          * by it.
169          * 
170          * @param unit          the unit to select.
171          */
172         public void setSelectedUnit(Unit unit) {
173                 if (!unitGroup.contains(unit)) {
174                         throw new IllegalArgumentException("unit " + unit
175                                         + " not contained in group " + unitGroup);
176                 }
177
178                 this.currentUnit = unit;
179                 if (model != null) {
180                         model.setCurrentUnit(unit);
181                 }
182                 updateText();
183                 fireItemEvent();
184         }
185
186
187
188         /**
189          * Updates the text of the label
190          */
191         private void updateText() {
192                 if (model != null) {
193
194                         Unit unit = model.getCurrentUnit();
195                         if (showValue) {
196                                 setText(unit.toStringUnit(model.getValue()));
197                         } else {
198                                 setText(unit.getUnit());
199                         }
200
201                 } else if (unitGroup != null) {
202
203                         setText(currentUnit.getUnit());
204
205                 } else {
206                         throw new IllegalStateException("Both model and unitGroup are null.");
207                 }
208         }
209
210
211         /**
212          * Update the component when the DoubleModel changes.
213          */
214         public void stateChanged(ChangeEvent e) {
215                 updateText();
216         }
217
218
219
220         ////////  ItemListener handling  ////////
221
222         public void addItemListener(ItemListener listener) {
223                 itemListeners.add(listener);
224         }
225
226         public void removeItemListener(ItemListener listener) {
227                 itemListeners.remove(listener);
228         }
229
230         protected void fireItemEvent() {
231                 ItemEvent event = null;
232                 ItemListener[] listeners = itemListeners.toArray(new ItemListener[0]);
233                 for (ItemListener l: listeners) {
234                         if (event == null) {
235                                 event = new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, getSelectedUnit(),
236                                                 ItemEvent.SELECTED);
237                         }
238                         l.itemStateChanged(event);
239                 }
240         }
241
242
243
244         ////////  Popup  ////////
245
246         private void popup() {
247                 JPopupMenu popup = new JPopupMenu();
248
249                 for (int i = 0; i < unitGroup.getUnitCount(); i++) {
250                         Unit unit = unitGroup.getUnit(i);
251                         JMenuItem item = new JMenuItem(unit.getUnit());
252                         item.addActionListener(new UnitSelectorItem(unit));
253                         popup.add(item);
254                 }
255
256                 for (int i = 0; i < extraActions.length; i++) {
257                         if (extraActions[i] == null && i < extraActions.length - 1) {
258                                 popup.addSeparator();
259                         } else {
260                                 popup.add(new JMenuItem(extraActions[i]));
261                         }
262                 }
263
264                 Dimension d = getSize();
265                 popup.show(this, 0, d.height);
266         }
267
268
269         /**
270          * ActionListener class that sets the currently selected unit.
271          */
272         private class UnitSelectorItem implements ActionListener {
273                 private final Unit unit;
274
275                 public UnitSelectorItem(Unit u) {
276                         unit = u;
277                 }
278
279                 public void actionPerformed(ActionEvent e) {
280                         setSelectedUnit(unit);
281                 }
282         }
283
284
285         @Override
286         public Object[] getSelectedObjects() {
287                 return new Object[]{ getSelectedUnit() };
288         }
289
290
291
292         ////////  Mouse handling ////////
293
294         public void mouseClicked(MouseEvent e) {
295                 if (unitGroup.getUnitCount() > 1)
296                         popup();
297         }
298
299         public void mouseEntered(MouseEvent e) {
300                 if (unitGroup.getUnitCount() > 1)
301                         setBorder(withinBorder);
302         }
303
304         public void mouseExited(MouseEvent e) {
305                 setBorder(normalBorder);
306         }
307
308         public void mousePressed(MouseEvent e) {
309         } // Ignore
310
311         public void mouseReleased(MouseEvent e) {
312         } // Ignore
313
314 }