package uk.ac.rhul.cs.cl1.ui; import javax.swing.AbstractSpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * Spinner number model with an extra item. * * This spinner model works like the <code>SpinnerNumberModel</code>, * but getting the previous value of the lowest number of the model * yields a special item that can be specified at construction * time or with the <code>setExtraItem()</code> method. * * @author tamas * */ public class ExtendedSpinnerNumberModel extends AbstractSpinnerModel { class MyChangeListener implements ChangeListener { public void stateChanged(ChangeEvent event) { fireStateChanged(); } } /** * The extra item in the spinner model. */ private Object extraItem = null; /** * The linked number model. */ private SpinnerNumberModel model; /** * Whether the extra item is the selected one at the moment. */ private boolean showingExtraItem = false; public ExtendedSpinnerNumberModel() { model = new SpinnerNumberModel(); model.addChangeListener(new MyChangeListener()); } public ExtendedSpinnerNumberModel(Object value, double minimum, double maximum, double stepSize, Object extraItem) { model = new SpinnerNumberModel(minimum, minimum, maximum, stepSize); model.addChangeListener(new MyChangeListener()); this.setExtraItem(extraItem); this.setValue(value); } public ExtendedSpinnerNumberModel(int value, int minimum, int maximum, int stepSize, Object extraItem) { model = new SpinnerNumberModel(value, minimum, maximum, stepSize); model.addChangeListener(new MyChangeListener()); this.setExtraItem(extraItem); } @SuppressWarnings("rawtypes") public ExtendedSpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize, Object extraItem) { model = new SpinnerNumberModel(value, minimum, maximum, stepSize); model.addChangeListener(new MyChangeListener()); this.setExtraItem(extraItem); } /** * Returns the extra item of the spinner model. */ public Object getExtraItem() { return this.extraItem; } public Object getNextValue() { Object result; if (showingExtraItem) { // We are at the extra item, which means that the superclass is // at the first item. Simply return the current value of the // superclass and clear the flag. showingExtraItem = false; result = model.getValue(); fireStateChanged(); } else { result = model.getNextValue(); } return result; } public Object getPreviousValue() { if (showingExtraItem) { // We are at the extra item and there is no previous one, so just // return null. return null; } Object result = model.getPreviousValue(); if (result == null && !showingExtraItem) { // We have to show the extra item now showingExtraItem = true; fireStateChanged(); return extraItem; } return result; } public Object getValue() { return showingExtraItem ? extraItem : model.getValue(); } /** * Sets the extra item of the spinner model. */ public void setExtraItem(Object extraItem) { this.extraItem = extraItem; if (showingExtraItem) { this.setValue(extraItem == null ? model.getValue() : extraItem); } } public void setValue(Object value) { showingExtraItem = (value == extraItem); if (showingExtraItem) { model.setValue(model.getMinimum()); } else { model.setValue(value); } } }