1 package net.sf.openrocket.gui.components;
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;
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;
26 import net.sf.openrocket.gui.adaptors.DoubleModel;
27 import net.sf.openrocket.unit.Unit;
28 import net.sf.openrocket.unit.UnitGroup;
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.
36 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
39 public class UnitSelector extends ResizeLabel implements ChangeListener, MouseListener,
42 private final DoubleModel model;
43 private final Action[] extraActions;
45 private UnitGroup unitGroup;
46 private Unit currentUnit;
48 private final boolean showValue;
50 private final Border normalBorder;
51 private final Border withinBorder;
54 private final List<ItemListener> itemListeners = new ArrayList<ItemListener>();
58 * Common private constructor that sets the values and sets up the borders.
59 * Either model or group must be null.
66 private UnitSelector(DoubleModel model, boolean showValue, UnitGroup group,
71 this.showValue = showValue;
74 this.unitGroup = model.getUnitGroup();
75 this.currentUnit = model.getCurrentUnit();
77 this.unitGroup = group;
78 this.currentUnit = group.getDefaultUnit();
81 this.extraActions = actions;
83 addMouseListener(this);
85 // Define borders to use:
87 normalBorder = new CompoundBorder(
88 new LineBorder(new Color(0f, 0f, 0f, 0.08f), 1), new EmptyBorder(1, 1, 1,
90 withinBorder = new CompoundBorder(new LineBorder(new Color(0f, 0f, 0f, 0.6f)),
91 new EmptyBorder(1, 1, 1, 1));
93 setBorder(normalBorder);
99 public UnitSelector(DoubleModel model, Action... actions) {
100 this(model, false, actions);
103 public UnitSelector(DoubleModel model, boolean showValue, Action... actions) {
104 this(model, showValue, null, actions);
106 // Add model listener
107 this.model.addChangeListener(this);
111 public UnitSelector(UnitGroup group, Action... actions) {
112 this(null, false, group, actions);
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>.
122 * @return the DoubleModel being used, or <code>null</code>.
124 public DoubleModel getModel() {
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>.
133 * @return the UnitGroup being used, or <code>null</code>.
135 public UnitGroup getUnitGroup() {
140 public void setUnitGroup(UnitGroup group) {
142 throw new IllegalStateException(
143 "UnitGroup cannot be set when backed up with model.");
146 if (this.unitGroup == group)
149 this.unitGroup = group;
150 this.currentUnit = group.getDefaultUnit();
156 * Return the currently selected unit. Works both when backup up with a DoubleModel
159 * @return the currently selected unit.
161 public Unit getSelectedUnit() {
167 * Set the currently selected unit. Sets it to the DoubleModel if it is backed up
170 * @param unit the unit to select.
172 public void setSelectedUnit(Unit unit) {
173 if (!unitGroup.contains(unit)) {
174 throw new IllegalArgumentException("unit " + unit
175 + " not contained in group " + unitGroup);
178 this.currentUnit = unit;
180 model.setCurrentUnit(unit);
189 * Updates the text of the label
191 private void updateText() {
194 Unit unit = model.getCurrentUnit();
196 setText(unit.toStringUnit(model.getValue()));
198 setText(unit.getUnit());
201 } else if (unitGroup != null) {
203 setText(currentUnit.getUnit());
206 throw new IllegalStateException("Both model and unitGroup are null.");
212 * Update the component when the DoubleModel changes.
214 public void stateChanged(ChangeEvent e) {
220 //////// ItemListener handling ////////
222 public void addItemListener(ItemListener listener) {
223 itemListeners.add(listener);
226 public void removeItemListener(ItemListener listener) {
227 itemListeners.remove(listener);
230 protected void fireItemEvent() {
231 ItemEvent event = null;
232 ItemListener[] listeners = itemListeners.toArray(new ItemListener[0]);
233 for (ItemListener l: listeners) {
235 event = new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, getSelectedUnit(),
238 l.itemStateChanged(event);
244 //////// Popup ////////
246 private void popup() {
247 JPopupMenu popup = new JPopupMenu();
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));
256 for (int i = 0; i < extraActions.length; i++) {
257 if (extraActions[i] == null && i < extraActions.length - 1) {
258 popup.addSeparator();
260 popup.add(new JMenuItem(extraActions[i]));
264 Dimension d = getSize();
265 popup.show(this, 0, d.height);
270 * ActionListener class that sets the currently selected unit.
272 private class UnitSelectorItem implements ActionListener {
273 private final Unit unit;
275 public UnitSelectorItem(Unit u) {
279 public void actionPerformed(ActionEvent e) {
280 setSelectedUnit(unit);
286 public Object[] getSelectedObjects() {
287 return new Object[]{ getSelectedUnit() };
292 //////// Mouse handling ////////
294 public void mouseClicked(MouseEvent e) {
295 if (unitGroup.getUnitCount() > 1)
299 public void mouseEntered(MouseEvent e) {
300 if (unitGroup.getUnitCount() > 1)
301 setBorder(withinBorder);
304 public void mouseExited(MouseEvent e) {
305 setBorder(normalBorder);
308 public void mousePressed(MouseEvent e) {
311 public void mouseReleased(MouseEvent e) {