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    }