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    }