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