001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://www.gnu.org/licenses/.
027 */
028package edu.wisc.ssec.mcidasv.data.dateChooser;
029
030import java.awt.BorderLayout;
031import java.awt.Component;
032import java.awt.Dimension;
033import java.awt.Font;
034import java.awt.event.ItemEvent;
035import java.awt.event.ItemListener;
036import java.text.DateFormatSymbols;
037import java.util.Calendar;
038import java.util.Locale;
039
040import javax.swing.JComboBox;
041import javax.swing.JFrame;
042import javax.swing.JPanel;
043import javax.swing.JSpinner;
044import javax.swing.JTextField;
045import javax.swing.SpinnerNumberModel;
046import javax.swing.UIManager;
047import javax.swing.border.EmptyBorder;
048import javax.swing.event.ChangeEvent;
049import javax.swing.event.ChangeListener;
050
051/**
052 * JMonthChooser is a bean for choosing a month.
053 * 
054 * @author Kai Toedter
055 * @version $LastChangedRevision: 100 $
056 * @version $LastChangedDate: 2006-06-04 14:36:06 +0200 (So, 04 Jun 2006) $
057 */
058public class JMonthChooser extends JPanel implements ItemListener,
059                ChangeListener {
060        private static final long serialVersionUID = -2028361332231218527L;
061
062        /** true, if the month chooser has a spinner component */
063        protected boolean hasSpinner;
064
065        private Locale locale;
066
067        private int month;
068
069        private int oldSpinnerValue = 0;
070
071        // needed for comparison
072        private JDayChooser dayChooser;
073
074        private JYearChooser yearChooser;
075
076        private JComboBox comboBox;
077
078        private JSpinner spinner;
079
080        private boolean initialized;
081
082        private boolean localInitialize;
083
084        /**
085         * Default JMonthChooser constructor.
086         */
087        public JMonthChooser() {
088                this(true);
089        }
090
091        /**
092         * JMonthChooser constructor with month spinner parameter.
093         * 
094         * @param hasSpinner
095         *            true, if the month chooser should have a spinner component
096         */
097        public JMonthChooser(boolean hasSpinner) {
098                super();
099                setName("JMonthChooser");
100                this.hasSpinner = hasSpinner;
101
102                setLayout(new BorderLayout());
103
104                comboBox = new JComboBox();
105                comboBox.addItemListener(this);
106
107                // comboBox.addPopupMenuListener(this);
108                locale = Locale.getDefault();
109                initNames();
110
111                if (hasSpinner) {
112                        spinner = new JSpinner() {
113                                private static final long serialVersionUID = 1L;
114
115                                private JTextField textField = new JTextField();
116
117                                public Dimension getPreferredSize() {
118                                        Dimension size = super.getPreferredSize();
119                                        return new Dimension(size.width, textField
120                                                        .getPreferredSize().height);
121                                }
122                        };
123                        spinner.addChangeListener(this);
124                        spinner.setEditor(comboBox);
125                        comboBox.setBorder(new EmptyBorder(0, 0, 0, 0));
126                        updateUI();
127
128                        add(spinner, BorderLayout.WEST);
129                } else {
130                        add(comboBox, BorderLayout.WEST);
131                }
132
133                initialized = true;
134                setMonth(Calendar.getInstance().get(Calendar.MONTH));
135        }
136
137        /**
138         * Initializes the locale specific month names.
139         */
140        public void initNames() {
141                localInitialize = true;
142
143                DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(locale);
144                String[] monthNames = dateFormatSymbols.getMonths();
145
146                if (comboBox.getItemCount() == 12) {
147                        comboBox.removeAllItems();
148                }
149
150                for (int i = 0; i < 12; i++) {
151                        comboBox.addItem(monthNames[i]);
152                }
153
154                localInitialize = false;
155                comboBox.setSelectedIndex(month);
156        }
157
158        /**
159         * Is invoked if the state of the spnner changes.
160         * 
161         * @param e
162         *            the change event.
163         */
164        public void stateChanged(ChangeEvent e) {
165                SpinnerNumberModel model = (SpinnerNumberModel) ((JSpinner) e
166                                .getSource()).getModel();
167                int value = model.getNumber().intValue();
168                boolean increase = (value > oldSpinnerValue) ? true : false;
169                oldSpinnerValue = value;
170
171                int month = getMonth();
172
173                if (increase) {
174                        month += 1;
175
176                        if (month == 12) {
177                                month = 0;
178
179                                if (yearChooser != null) {
180                                        int year = yearChooser.getYear();
181                                        year += 1;
182                                        yearChooser.setYear(year);
183                                }
184                        }
185                } else {
186                        month -= 1;
187
188                        if (month == -1) {
189                                month = 11;
190
191                                if (yearChooser != null) {
192                                        int year = yearChooser.getYear();
193                                        year -= 1;
194                                        yearChooser.setYear(year);
195                                }
196                        }
197                }
198
199                setMonth(month);
200        }
201
202        /**
203         * The ItemListener for the months.
204         * 
205         * @param e
206         *            the item event
207         */
208        public void itemStateChanged(ItemEvent e) {
209                if (e.getStateChange() == ItemEvent.SELECTED) {
210                        int index = comboBox.getSelectedIndex();
211
212                        if ((index >= 0) && (index != month)) {
213                                setMonth(index, false);
214                        }
215                }
216        }
217
218        /**
219         * Sets the month attribute of the JMonthChooser object. Fires a property
220         * change "month".
221         * 
222         * @param newMonth
223         *            the new month value
224         * @param select
225         *            true, if the month should be selcted in the combo box.
226         */
227        private void setMonth(int newMonth, boolean select) {
228                if (!initialized || localInitialize) {
229                        return;
230                }
231
232                int oldMonth = month;
233                month = newMonth;
234
235                if (select) {
236                        comboBox.setSelectedIndex(month);
237                }
238
239                if (dayChooser != null) {
240                        dayChooser.setMonth(month);
241                }
242
243                firePropertyChange("month", oldMonth, month);
244        }
245
246        /**
247         * Sets the month. This is a bound property. Values are valid between 0
248         * (January) and 11 (December). A value &lt; 0 will be treated as 0, a
249         * value &gt; 11 will be treated as 11.
250         * 
251         * @param newMonth
252         *            the new month value
253         * 
254         * @see #getMonth
255         */
256        public void setMonth(int newMonth) {
257                if (newMonth < 0 || newMonth == Integer.MIN_VALUE) {
258                        setMonth(0, true);
259                } else if (newMonth > 11) {
260                        setMonth(11, true);
261                } else {
262                        setMonth(newMonth, true);
263                }
264        }
265
266        /**
267         * Returns the month.
268         * 
269         * @return the month value
270         */
271        public int getMonth() {
272                return month;
273        }
274
275        /**
276         * Convenience method set a day chooser.
277         * 
278         * @param dayChooser
279         *            the day chooser
280         */
281        public void setDayChooser(JDayChooser dayChooser) {
282                this.dayChooser = dayChooser;
283        }
284
285        /**
286         * Convenience method set a year chooser. If set, the spin for the month
287         * buttons will spin the year as well
288         * 
289         * @param yearChooser
290         *            the new yearChooser value
291         */
292        public void setYearChooser(JYearChooser yearChooser) {
293                this.yearChooser = yearChooser;
294        }
295
296        /**
297         * Returns the locale.
298         * 
299         * @return the locale value
300         * 
301         * @see #setLocale
302         */
303        public Locale getLocale() {
304                return locale;
305        }
306
307        /**
308         * Set the locale and initializes the new month names.
309         * 
310         * @param l
311         *            the new locale value
312         * 
313         * @see #getLocale
314         */
315        public void setLocale(Locale l) {
316                if (!initialized) {
317                        super.setLocale(l);
318                } else {
319                        locale = l;
320                        initNames();
321                }
322        }
323
324        /**
325         * Enable or disable the JMonthChooser.
326         * 
327         * @param enabled
328         *            the new enabled value
329         */
330        public void setEnabled(boolean enabled) {
331                super.setEnabled(enabled);
332                comboBox.setEnabled(enabled);
333
334                if (spinner != null) {
335                        spinner.setEnabled(enabled);
336                }
337        }
338
339        /**
340         * Returns the month chooser's comboBox text area (which allow the focus to
341         * be set to it).
342         * 
343         * @return the combo box
344         */
345        public Component getComboBox() {
346                return this.comboBox;
347        }
348
349        /**
350         * Returns the month chooser's comboBox bar (which allow the focus to be set
351         * to it).
352         * 
353         * @return Component the spinner or null, if the month chooser has no
354         *         spinner
355         */
356        public Component getSpinner() {
357                // Returns <null> if there is no spinner.
358                return spinner;
359        }
360
361        /**
362         * Returns the type of spinner the month chooser is using.
363         * 
364         * @return true, if the month chooser has a spinner
365         */
366        public boolean hasSpinner() {
367                return hasSpinner;
368        }
369
370    /**
371     * Sets the font for this component.
372     *
373     * @param font the desired {@code Font} for this component
374     */
375        public void setFont(Font font) {
376                if (comboBox != null) {
377                        comboBox.setFont(font);
378                }
379                super.setFont(font);
380        }
381
382        /**
383         * Updates the UI.
384         * 
385         * @see javax.swing.JPanel#updateUI()
386         */
387        public void updateUI() {
388                final JSpinner testSpinner = new JSpinner();
389                if (spinner != null) {
390                        if ("Windows".equals(UIManager.getLookAndFeel().getID())) {
391                                spinner.setBorder(testSpinner.getBorder());
392                        } else {
393                                spinner.setBorder(new EmptyBorder(0, 0, 0, 0));
394                        }
395                }
396        }
397
398        /**
399         * Creates a JFrame with a JMonthChooser inside and can be used for testing.
400         * 
401         * @param s
402         *            The command line arguments
403         */
404        public static void main(String[] s) {
405                JFrame frame = new JFrame("MonthChooser");
406                frame.getContentPane().add(new JMonthChooser());
407                frame.pack();
408                frame.setVisible(true);
409        }
410}