001 /*
002 * $Id: JSpinField.java,v 1.3 2012/02/19 17:35:46 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.data.dateChooser;
032
033 import java.awt.BorderLayout;
034 import java.awt.Color;
035 import java.awt.Component;
036 import java.awt.Dimension;
037 import java.awt.Font;
038 import java.awt.event.ActionEvent;
039 import java.awt.event.ActionListener;
040 import java.awt.event.FocusEvent;
041 import java.awt.event.FocusListener;
042
043 import javax.swing.BorderFactory;
044 import javax.swing.JFrame;
045 import javax.swing.JPanel;
046 import javax.swing.JSpinner;
047 import javax.swing.JTextField;
048 import javax.swing.SpinnerNumberModel;
049 import javax.swing.SwingConstants;
050 import javax.swing.UIManager;
051 import javax.swing.event.CaretEvent;
052 import javax.swing.event.CaretListener;
053 import javax.swing.event.ChangeEvent;
054 import javax.swing.event.ChangeListener;
055
056 /**
057 * JSpinField is a numeric field with 2 spin buttons to increase or decrease the
058 * value. It has the same interface as the "old" JSpinField but uses a JSpinner
059 * internally (since J2SE SDK 1.4) rather than a scrollbar for emulating the
060 * spin buttons.
061 *
062 * @author Kai Toedter
063 * @version $LastChangedRevision: 85 $
064 * @version $LastChangedDate: 2006-04-28 13:50:52 +0200 (Fr, 28 Apr 2006) $
065 */
066 public class JSpinField extends JPanel implements ChangeListener, CaretListener, ActionListener,
067 FocusListener {
068 private static final long serialVersionUID = 1694904792717740650L;
069
070 protected JSpinner spinner;
071
072 /** the text (number) field */
073 protected JTextField textField;
074 protected int min;
075 protected int max;
076 protected int value;
077 protected Color darkGreen;
078
079 /**
080 * Default JSpinField constructor. The valid value range is between
081 * Integer.MIN_VALUE and Integer.MAX_VALUE. The initial value is 0.
082 */
083 public JSpinField() {
084 this(Integer.MIN_VALUE, Integer.MAX_VALUE);
085 }
086
087 /**
088 * JSpinField constructor with given minimum and maximum vaues and initial
089 * value 0.
090 */
091 public JSpinField(int min, int max) {
092 super();
093 setName("JSpinField");
094 this.min = min;
095 if (max < min)
096 max = min;
097 this.max = max;
098 value = 0;
099 if (value < min)
100 value = min;
101 if (value > max)
102 value = max;
103
104 darkGreen = new Color(0, 150, 0);
105 setLayout(new BorderLayout());
106 textField = new JTextField();
107 textField.addCaretListener(this);
108 textField.addActionListener(this);
109 textField.setHorizontalAlignment(SwingConstants.RIGHT);
110 textField.setBorder(BorderFactory.createEmptyBorder());
111 textField.setText(Integer.toString(value));
112 textField.addFocusListener(this);
113 spinner = new JSpinner() {
114 private static final long serialVersionUID = -6287709243342021172L;
115 private JTextField textField = new JTextField();
116
117 public Dimension getPreferredSize() {
118 Dimension size = super.getPreferredSize();
119 return new Dimension(size.width, textField.getPreferredSize().height);
120 }
121 };
122 spinner.setEditor(textField);
123 spinner.addChangeListener(this);
124 // spinner.setSize(spinner.getWidth(), textField.getHeight());
125 add(spinner, BorderLayout.CENTER);
126 }
127
128 public void adjustWidthToMaximumValue() {
129 JTextField testTextField = new JTextField(Integer.toString(max));
130 int width = testTextField.getPreferredSize().width;
131 int height = testTextField.getPreferredSize().height;
132 textField.setPreferredSize(new Dimension(width, height));
133 textField.revalidate();
134 }
135
136 /**
137 * Is invoked when the spinner model changes
138 *
139 * @param e
140 * the ChangeEvent
141 */
142 public void stateChanged(ChangeEvent e) {
143 SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel();
144 int value = model.getNumber().intValue();
145 setValue(value);
146 }
147
148 /**
149 * Sets the value attribute of the JSpinField object.
150 *
151 * @param newValue
152 * The new value
153 * @param updateTextField
154 * true if text field should be updated
155 */
156 protected void setValue(int newValue, boolean updateTextField, boolean firePropertyChange) {
157 int oldValue = value;
158 if (newValue < min) {
159 value = min;
160 } else if (newValue > max) {
161 value = max;
162 } else {
163 value = newValue;
164 }
165
166 if (updateTextField) {
167 textField.setText(Integer.toString(value));
168 textField.setForeground(Color.black);
169 }
170
171 if (firePropertyChange) {
172 firePropertyChange("value", oldValue, value);
173 }
174 }
175
176 /**
177 * Sets the value. This is a bound property.
178 *
179 * @param newValue
180 * the new value
181 *
182 * @see #getValue
183 */
184 public void setValue(int newValue) {
185 setValue(newValue, true, true);
186 spinner.setValue(new Integer(value));
187 }
188
189 /**
190 * Returns the value.
191 *
192 * @return the value value
193 */
194 public int getValue() {
195 return value;
196 }
197
198 /**
199 * Sets the minimum value.
200 *
201 * @param newMinimum
202 * the new minimum value
203 *
204 * @see #getMinimum
205 */
206 public void setMinimum(int newMinimum) {
207 min = newMinimum;
208 }
209
210 /**
211 * Returns the minimum value.
212 *
213 * @return the minimum value
214 */
215 public int getMinimum() {
216 return min;
217 }
218
219 /**
220 * Sets the maximum value and adjusts the preferred width.
221 *
222 * @param newMaximum
223 * the new maximum value
224 *
225 * @see #getMaximum
226 */
227 public void setMaximum(int newMaximum) {
228 max = newMaximum;
229 }
230
231 /**
232 * Sets the horizontal alignment of the displayed value.
233 *
234 * @param alignment
235 * the horizontal alignment
236 */
237 public void setHorizontalAlignment(int alignment) {
238 textField.setHorizontalAlignment(alignment);
239 }
240
241 /**
242 * Returns the maximum value.
243 *
244 * @return the maximum value
245 */
246 public int getMaximum() {
247 return max;
248 }
249
250 /**
251 * Sets the font property.
252 *
253 * @param font
254 * the new font
255 */
256 public void setFont(Font font) {
257 if (textField != null) {
258 textField.setFont(font);
259 }
260 }
261
262 /**
263 * Sets the foreground
264 *
265 * @param fg
266 * the foreground
267 */
268 public void setForeground(Color fg) {
269 if (textField != null) {
270 textField.setForeground(fg);
271 }
272 }
273
274 /**
275 * After any user input, the value of the textfield is proofed. Depending on
276 * being an integer, the value is colored green or red.
277 *
278 * @param e
279 * the caret event
280 */
281 public void caretUpdate(CaretEvent e) {
282 try {
283 int testValue = Integer.valueOf(textField.getText()).intValue();
284
285 if ((testValue >= min) && (testValue <= max)) {
286 textField.setForeground(darkGreen);
287 setValue(testValue, false, true);
288 } else {
289 textField.setForeground(Color.red);
290 }
291 } catch (Exception ex) {
292 if (ex instanceof NumberFormatException) {
293 textField.setForeground(Color.red);
294 }
295
296 // Ignore all other exceptions, e.g. illegal state exception
297 }
298
299 textField.repaint();
300 }
301
302 /**
303 * After any user input, the value of the textfield is proofed. Depending on
304 * being an integer, the value is colored green or red. If the textfield is
305 * green, the enter key is accepted and the new value is set.
306 *
307 * @param e
308 * Description of the Parameter
309 */
310 public void actionPerformed(ActionEvent e) {
311 if (textField.getForeground().equals(darkGreen)) {
312 setValue(Integer.valueOf(textField.getText()).intValue());
313 }
314 }
315
316 /**
317 * Enable or disable the JSpinField.
318 *
319 * @param enabled
320 * The new enabled value
321 */
322 public void setEnabled(boolean enabled) {
323 super.setEnabled(enabled);
324 spinner.setEnabled(enabled);
325 textField.setEnabled(enabled);
326 /*
327 * Fixes the background bug
328 * 4991597 and sets the background explicitely to a
329 * TextField.inactiveBackground.
330 */
331 if (!enabled) {
332 textField.setBackground(UIManager.getColor("TextField.inactiveBackground"));
333 }
334 }
335
336 /**
337 * Returns the year chooser's spinner (which allow the focus to be set to
338 * it).
339 *
340 * @return Component the spinner or null, if the month chooser has no
341 * spinner
342 */
343 public Component getSpinner() {
344 return spinner;
345 }
346
347 /**
348 * Creates a JFrame with a JSpinField inside and can be used for testing.
349 *
350 * @param s
351 * The command line arguments
352 */
353 public static void main(String[] s) {
354 JFrame frame = new JFrame("JSpinField");
355 frame.getContentPane().add(new JSpinField());
356 frame.pack();
357 frame.setVisible(true);
358 }
359
360 /*
361 * (non-Javadoc)
362 *
363 * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
364 */
365 public void focusGained(FocusEvent e) {
366 }
367
368 /**
369 * The value of the text field is checked against a valid (green) value. If
370 * valid, the value is set and a property change is fired.
371 */
372 public void focusLost(FocusEvent e) {
373 actionPerformed(null);
374 }
375 }