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 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();
76 } else if (group != null) {
77 this.unitGroup = group;
78 this.currentUnit = group.getDefaultUnit();
80 this.unitGroup = UnitGroup.UNITS_NONE;
81 this.currentUnit = UnitGroup.UNITS_NONE.getDefaultUnit();
84 this.extraActions = actions;
86 addMouseListener(this);
88 // Define borders to use:
90 normalBorder = new CompoundBorder(
91 new LineBorder(new Color(0f, 0f, 0f, 0.08f), 1), new EmptyBorder(1, 1, 1,
93 withinBorder = new CompoundBorder(new LineBorder(new Color(0f, 0f, 0f, 0.6f)),
94 new EmptyBorder(1, 1, 1, 1));
96 // Add model listener if showing value
98 this.model.addChangeListener(this);
100 setBorder(normalBorder);
106 public UnitSelector(DoubleModel model, Action... actions) {
107 this(model, false, actions);
110 public UnitSelector(DoubleModel model, boolean showValue, Action... actions) {
111 this(model, showValue, null, actions);
115 public UnitSelector(UnitGroup group, Action... actions) {
116 this(null, false, group, actions);
123 * Return the DoubleModel that is backing this selector up, or <code>null</code>.
124 * Either this method or {@link #getUnitGroup()} always returns <code>null</code>.
126 * @return the DoubleModel being used, or <code>null</code>.
128 public DoubleModel getModel() {
134 * Set the current double model.
136 * @param model the model to set, <code>null</code> is NOT allowed.
138 public void setModel(DoubleModel model) {
139 if (this.model != null && showValue) {
140 this.model.removeChangeListener(this);
143 this.unitGroup = model.getUnitGroup();
144 this.currentUnit = model.getCurrentUnit();
146 this.model.addChangeListener(this);
154 * Return the unit group that is being shown, or <code>null</code>. Either this method
155 * or {@link #getModel()} always returns <code>null</code>.
157 * @return the UnitGroup being used, or <code>null</code>.
159 public UnitGroup getUnitGroup() {
164 public void setUnitGroup(UnitGroup group) {
166 throw new IllegalStateException(
167 "UnitGroup cannot be set when backed up with model.");
170 if (this.unitGroup == group)
173 this.unitGroup = group;
174 this.currentUnit = group.getDefaultUnit();
180 * Return the currently selected unit. Works both when backup up with a DoubleModel
183 * @return the currently selected unit.
185 public Unit getSelectedUnit() {
191 * Set the currently selected unit. Sets it to the DoubleModel if it is backed up
194 * @param unit the unit to select.
196 public void setSelectedUnit(Unit unit) {
197 if (!unitGroup.contains(unit)) {
198 throw new IllegalArgumentException("unit " + unit
199 + " not contained in group " + unitGroup);
202 this.currentUnit = unit;
204 model.setCurrentUnit(unit);
213 * Updates the text of the label
215 private void updateText() {
218 Unit unit = model.getCurrentUnit();
220 setText(unit.toStringUnit(model.getValue()));
222 setText(unit.getUnit());
225 } else if (unitGroup != null) {
227 setText(currentUnit.getUnit());
230 throw new IllegalStateException("Both model and unitGroup are null.");
236 * Update the component when the DoubleModel changes.
238 public void stateChanged(ChangeEvent e) {
244 //////// ItemListener handling ////////
246 public void addItemListener(ItemListener listener) {
247 itemListeners.add(listener);
250 public void removeItemListener(ItemListener listener) {
251 itemListeners.remove(listener);
254 protected void fireItemEvent() {
255 ItemEvent event = null;
256 ItemListener[] listeners = itemListeners.toArray(new ItemListener[0]);
257 for (ItemListener l: listeners) {
259 event = new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, getSelectedUnit(),
262 l.itemStateChanged(event);
268 //////// Popup ////////
270 private void popup() {
271 JPopupMenu popup = new JPopupMenu();
273 for (int i = 0; i < unitGroup.getUnitCount(); i++) {
274 Unit unit = unitGroup.getUnit(i);
275 JMenuItem item = new JMenuItem(unit.getUnit());
276 item.addActionListener(new UnitSelectorItem(unit));
280 for (int i = 0; i < extraActions.length; i++) {
281 if (extraActions[i] == null && i < extraActions.length - 1) {
282 popup.addSeparator();
284 popup.add(new JMenuItem(extraActions[i]));
288 Dimension d = getSize();
289 popup.show(this, 0, d.height);
294 * ActionListener class that sets the currently selected unit.
296 private class UnitSelectorItem implements ActionListener {
297 private final Unit unit;
299 public UnitSelectorItem(Unit u) {
303 public void actionPerformed(ActionEvent e) {
304 setSelectedUnit(unit);
310 public Object[] getSelectedObjects() {
311 return new Object[]{ getSelectedUnit() };
316 //////// Mouse handling ////////
318 public void mouseClicked(MouseEvent e) {
319 if (unitGroup.getUnitCount() > 1)
323 public void mouseEntered(MouseEvent e) {
324 if (unitGroup.getUnitCount() > 1)
325 setBorder(withinBorder);
328 public void mouseExited(MouseEvent e) {
329 setBorder(normalBorder);
332 public void mousePressed(MouseEvent e) {
335 public void mouseReleased(MouseEvent e) {