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 */
028
029package edu.wisc.ssec.mcidasv.data.dateChooser;
030
031import java.awt.BorderLayout;
032import java.awt.Color;
033import java.awt.Font;
034import java.beans.PropertyChangeEvent;
035import java.beans.PropertyChangeListener;
036import java.util.Calendar;
037import java.util.Date;
038import java.util.Locale;
039
040import javax.swing.BorderFactory;
041import javax.swing.JFrame;
042import javax.swing.JPanel;
043
044/**
045 * JCalendar is a bean for entering a date by choosing the year, month and day.
046 * 
047 * @author Kai Toedter
048 * @version $LastChangedRevision: 95 $
049 * @version $LastChangedDate: 2006-05-05 18:43:15 +0200 (Fr, 05 Mai 2006) $
050 */
051public class JCalendar extends JPanel implements PropertyChangeListener {
052        private static final long serialVersionUID = 8913369762644440133L;
053
054        private Calendar calendar;
055
056        /** the day chooser */
057        protected JDayChooser dayChooser;
058        private boolean initialized = false;
059
060        /** indicates if weeks of year shall be visible */
061        protected boolean weekOfYearVisible = true;
062
063        /** the locale */
064        protected Locale locale;
065
066        /** the month chooser */
067        protected JMonthChooser monthChooser;
068
069        private JPanel monthYearPanel;
070
071        /** the year chhoser */
072        protected JYearChooser yearChooser;
073
074        protected Date minSelectableDate;
075
076        protected Date maxSelectableDate;
077
078        /**
079         * Default JCalendar constructor.
080         */
081        public JCalendar() {
082                this(null, null, true, true);
083        }
084
085        /**
086         * JCalendar constructor which allows the initial date to be set.
087         * 
088         * @param date
089         *            the date
090         */
091        public JCalendar(Date date) {
092                this(date, null, true, true);
093        }
094
095        /**
096         * JCalendar constructor which allows the initial calendar to be set.
097         * 
098         * @param calendar
099         *            the calendar
100         */
101        public JCalendar(Calendar calendar) {
102                this(null, null, true, true);
103                setCalendar(calendar);
104        }
105
106        /**
107         * JCalendar constructor allowing the initial locale to be set.
108         * 
109         * @param locale
110         *            the new locale
111         */
112        public JCalendar(Locale locale) {
113                this(null, locale, true, true);
114        }
115
116        /**
117         * JCalendar constructor specifying both the initial date and locale.
118         * 
119         * @param date
120         *            the date
121         * @param locale
122         *            the new locale
123         */
124        public JCalendar(Date date, Locale locale) {
125                this(date, locale, true, true);
126        }
127
128        /**
129         * JCalendar constructor specifying both the initial date and the month
130         * spinner type.
131         * 
132         * @param date
133         *            the date
134         * @param monthSpinner
135         *            false, if no month spinner should be used
136         */
137        public JCalendar(Date date, boolean monthSpinner) {
138                this(date, null, monthSpinner, true);
139        }
140
141        /**
142         * JCalendar constructor specifying both the locale and the month spinner.
143         * 
144         * @param locale
145         *            the locale
146         * @param monthSpinner
147         *            false, if no month spinner should be used
148         */
149        public JCalendar(Locale locale, boolean monthSpinner) {
150                this(null, locale, monthSpinner, true);
151        }
152
153        /**
154         * JCalendar constructor specifying the month spinner type.
155         * 
156         * @param monthSpinner
157         *            false, if no month spinner should be used
158         */
159        public JCalendar(boolean monthSpinner) {
160                this(null, null, monthSpinner, true);
161        }
162
163        /**
164         * JCalendar constructor with month spinner parameter.
165         * 
166         * @param date
167         *            the date
168         * @param locale
169         *            the locale
170         * @param monthSpinner
171         *            false, if no month spinner should be used
172         * @param weekOfYearVisible
173         *            true, if weeks of year shall be visible
174         */
175        public JCalendar(Date date, Locale locale, boolean monthSpinner, boolean weekOfYearVisible) {
176
177                setName("JCalendar");
178
179                // needed for setFont() etc.
180                dayChooser = null;
181                monthChooser = null;
182                yearChooser = null;
183                this.weekOfYearVisible = weekOfYearVisible;
184
185                this.locale = locale;
186
187                if (locale == null) {
188                        this.locale = Locale.getDefault();
189                }
190
191                calendar = Calendar.getInstance();
192
193                setLayout(new BorderLayout());
194
195                monthYearPanel = new JPanel();
196                monthYearPanel.setLayout(new BorderLayout());
197
198                monthChooser = new JMonthChooser(monthSpinner);
199                yearChooser = new JYearChooser();
200                monthChooser.setYearChooser(yearChooser);
201                monthYearPanel.add(monthChooser, BorderLayout.WEST);
202                monthYearPanel.add(yearChooser, BorderLayout.CENTER);
203                monthYearPanel.setBorder(BorderFactory.createEmptyBorder());
204
205                dayChooser = new JDayChooser(weekOfYearVisible);
206                dayChooser.addPropertyChangeListener(this);
207                monthChooser.setDayChooser(dayChooser);
208                monthChooser.addPropertyChangeListener(this);
209                yearChooser.setDayChooser(dayChooser);
210                yearChooser.addPropertyChangeListener(this);
211                add(monthYearPanel, BorderLayout.NORTH);
212                add(dayChooser, BorderLayout.CENTER);
213
214                // Set the initialized flag before setting the calendar. This will
215                // cause the other components to be updated properly.
216                if (date != null) {
217                        calendar.setTime(date);
218                }
219
220                initialized = true;
221
222                setCalendar(calendar);
223        }
224
225        /**
226         * Creates a JFrame with a JCalendar inside and can be used for testing.
227         * 
228         * @param s
229         *            The command line arguments
230         */
231        public static void main(String[] s) {
232                JFrame frame = new JFrame("JCalendar");
233
234                JCalendar jcalendar = new JCalendar();
235                frame.getContentPane().add(jcalendar);
236                frame.pack();
237                frame.setVisible(true);
238        }
239
240        /**
241         * Returns the calendar property.
242         * 
243         * @return the value of the calendar property.
244         */
245        public Calendar getCalendar() {
246                return calendar;
247        }
248
249        /**
250         * Gets the dayChooser attribute of the JCalendar object
251         * 
252         * @return the dayChooser value
253         */
254        public JDayChooser getDayChooser() {
255                return dayChooser;
256        }
257
258        /**
259         * Returns the locale.
260         * 
261         * @return the value of the locale property.
262         * 
263         * @see #setLocale
264         */
265        public Locale getLocale() {
266                return locale;
267        }
268
269        /**
270         * Gets the monthChooser attribute of the JCalendar object
271         * 
272         * @return the monthChooser value
273         */
274        public JMonthChooser getMonthChooser() {
275                return monthChooser;
276        }
277
278        /**
279         * Gets the yearChooser attribute of the JCalendar object
280         * 
281         * @return the yearChooser value
282         */
283        public JYearChooser getYearChooser() {
284                return yearChooser;
285        }
286
287        /**
288         * Indicates if the weeks of year are visible..
289         * 
290         * @return boolean true, if weeks of year are visible
291         */
292        public boolean isWeekOfYearVisible() {
293                return dayChooser.isWeekOfYearVisible();
294        }
295
296        /**
297         * JCalendar is a PropertyChangeListener, for its day, month and year
298         * chooser.
299         * 
300         * @param evt
301         *            the property change event
302         */
303        public void propertyChange(PropertyChangeEvent evt) {
304                if (calendar != null) {
305                        Calendar c = (Calendar) calendar.clone();
306
307                        if (evt.getPropertyName().equals("day")) {
308                                c.set(Calendar.DAY_OF_MONTH, ((Integer) evt.getNewValue()).intValue());
309                                setCalendar(c, false);
310                        } else if (evt.getPropertyName().equals("month")) {
311                                c.set(Calendar.MONTH, ((Integer) evt.getNewValue()).intValue());
312                                setCalendar(c, false);
313                        } else if (evt.getPropertyName().equals("year")) {
314                                c.set(Calendar.YEAR, ((Integer) evt.getNewValue()).intValue());
315                                setCalendar(c, false);
316                        } else if (evt.getPropertyName().equals("date")) {
317                                c.setTime((Date) evt.getNewValue());
318                                setCalendar(c, true);
319                        }
320                }
321        }
322
323        /**
324         * Sets the background color.
325         * 
326         * @param bg
327         *            the new background
328         */
329        public void setBackground(Color bg) {
330                super.setBackground(bg);
331
332                if (dayChooser != null) {
333                        dayChooser.setBackground(bg);
334                }
335        }
336
337        /**
338         * Sets the calendar property. This is a bound property.
339         * 
340         * @param c
341         *            the new calendar
342         * @throws NullPointerException -
343         *             if c is null;
344         * @see #getCalendar
345         */
346        public void setCalendar(Calendar c) {
347                setCalendar(c, true);
348        }
349
350        /**
351         * Sets the calendar attribute of the JCalendar object
352         * 
353         * @param c
354         *            the new calendar value
355         * @param update
356         *            the new calendar value
357         * @throws NullPointerException -
358         *             if c is null;
359         */
360        private void setCalendar(Calendar c, boolean update) {
361                if (c == null) {
362                        setDate(null);
363                }
364                Calendar oldCalendar = calendar;
365                calendar = c;
366
367                if (update) {
368                        // Thanks to Jeff Ulmer for correcting a bug in the sequence :)
369                        yearChooser.setYear(c.get(Calendar.YEAR));
370                        monthChooser.setMonth(c.get(Calendar.MONTH));
371                        dayChooser.setDay(c.get(Calendar.DATE));
372                }
373
374                firePropertyChange("calendar", oldCalendar, calendar);
375        }
376
377        /**
378         * Enable or disable the JCalendar.
379         * 
380         * @param enabled
381         *            the new enabled value
382         */
383        public void setEnabled(boolean enabled) {
384                super.setEnabled(enabled);
385
386                if (dayChooser != null) {
387                        dayChooser.setEnabled(enabled);
388                        monthChooser.setEnabled(enabled);
389                        yearChooser.setEnabled(enabled);
390                }
391        }
392
393        /**
394         * Returns true, if enabled.
395         * 
396         * @return true, if enabled.
397         */
398        public boolean isEnabled() {
399                return super.isEnabled();
400        }
401
402        /**
403         * Sets the font property.
404         * 
405         * @param font
406         *            the new font
407         */
408        public void setFont(Font font) {
409                super.setFont(font);
410
411                if (dayChooser != null) {
412                        dayChooser.setFont(font);
413                        monthChooser.setFont(font);
414                        yearChooser.setFont(font);
415                }
416        }
417
418        /**
419         * Sets the foreground color.
420         * 
421         * @param fg
422         *            the new foreground
423         */
424        public void setForeground(Color fg) {
425                super.setForeground(fg);
426
427                if (dayChooser != null) {
428                        dayChooser.setForeground(fg);
429                        monthChooser.setForeground(fg);
430                        yearChooser.setForeground(fg);
431                }
432        }
433
434        /**
435         * Sets the locale property. This is a bound property.
436         * 
437         * @param l
438         *            the new locale value
439         * 
440         * @see #getLocale
441         */
442        public void setLocale(Locale l) {
443                if (!initialized) {
444                        super.setLocale(l);
445                } else {
446                        Locale oldLocale = locale;
447                        locale = l;
448                        dayChooser.setLocale(locale);
449                        monthChooser.setLocale(locale);
450                        firePropertyChange("locale", oldLocale, locale);
451                }
452        }
453
454        /**
455         * Sets the week of year visible.
456         * 
457         * @param weekOfYearVisible
458         *            true, if weeks of year shall be visible
459         */
460        public void setWeekOfYearVisible(boolean weekOfYearVisible) {
461                dayChooser.setWeekOfYearVisible(weekOfYearVisible);
462                setLocale(locale); // hack for doing complete new layout :)
463        }
464
465        /**
466         * Gets the visibility of the decoration background.
467         * 
468         * @return true, if the decoration background is visible.
469         */
470        public boolean isDecorationBackgroundVisible() {
471                return dayChooser.isDecorationBackgroundVisible();
472        }
473
474        /**
475         * Sets the decoration background visible.
476         * 
477         * @param decorationBackgroundVisible
478         *            true, if the decoration background should be visible.
479         */
480        public void setDecorationBackgroundVisible(boolean decorationBackgroundVisible) {
481                dayChooser.setDecorationBackgroundVisible(decorationBackgroundVisible);
482                setLocale(locale); // hack for doing complete new layout :)
483        }
484
485        /**
486         * Gets the visibility of the decoration border.
487         * 
488         * @return true, if the decoration border is visible.
489         */
490        public boolean isDecorationBordersVisible() {
491                return dayChooser.isDecorationBordersVisible();
492        }
493
494        /**
495         * Sets the decoration borders visible.
496         * 
497         * @param decorationBordersVisible
498         *            true, if the decoration borders should be visible.
499         */
500        public void setDecorationBordersVisible(boolean decorationBordersVisible) {
501                dayChooser.setDecorationBordersVisible(decorationBordersVisible);
502                setLocale(locale); // hack for doing complete new layout :)
503        }
504
505        /**
506         * Returns the color of the decoration (day names and weeks).
507         * 
508         * @return the color of the decoration (day names and weeks).
509         */
510        public Color getDecorationBackgroundColor() {
511                return dayChooser.getDecorationBackgroundColor();
512        }
513
514        /**
515         * Sets the background of days and weeks of year buttons.
516         * 
517         * @param decorationBackgroundColor
518         *            the background color
519         */
520        public void setDecorationBackgroundColor(Color decorationBackgroundColor) {
521                dayChooser.setDecorationBackgroundColor(decorationBackgroundColor);
522        }
523
524        /**
525         * Returns the Sunday foreground.
526         * 
527         * @return Color the Sunday foreground.
528         */
529        public Color getSundayForeground() {
530                return dayChooser.getSundayForeground();
531        }
532
533        /**
534         * Returns the weekday foreground.
535         * 
536         * @return Color the weekday foreground.
537         */
538        public Color getWeekdayForeground() {
539                return dayChooser.getWeekdayForeground();
540        }
541
542        /**
543         * Sets the Sunday foreground.
544         * 
545         * @param sundayForeground
546         *            the sundayForeground to set
547         */
548        public void setSundayForeground(Color sundayForeground) {
549                dayChooser.setSundayForeground(sundayForeground);
550        }
551
552        /**
553         * Sets the weekday foreground.
554         * 
555         * @param weekdayForeground
556         *            the weekdayForeground to set
557         */
558        public void setWeekdayForeground(Color weekdayForeground) {
559                dayChooser.setWeekdayForeground(weekdayForeground);
560        }
561
562        /**
563         * Returns a Date object.
564         * 
565         * @return a date object constructed from the calendar property.
566         */
567        public Date getDate() {
568                return new Date(calendar.getTimeInMillis());
569        }
570
571        /**
572         * Sets the date. Fires the property change "date".
573         * 
574         * @param date
575         *            the new date.
576         * @throws NullPointerException -
577         *             if tha date is null
578         */
579        public void setDate(Date date) {
580                Date oldDate = calendar.getTime();
581                calendar.setTime(date);
582                int year = calendar.get(Calendar.YEAR);
583                int month = calendar.get(Calendar.MONTH);
584                int day = calendar.get(Calendar.DAY_OF_MONTH);
585
586                yearChooser.setYear(year);
587                monthChooser.setMonth(month);
588                dayChooser.setCalendar(calendar);
589                dayChooser.setDay(day);
590
591                firePropertyChange("date", oldDate, date);
592        }
593
594        /**
595         * Sets a valid date range for selectable dates. If max is before
596         * min, the default range with no limitation is set.
597         * 
598         * @param min
599         *            the minimum selectable date or null (then the minimum date is
600         *            set to 01\01\0001)
601         * @param max
602         *            the maximum selectable date or null (then the maximum date is
603         *            set to 01\01\9999)
604         */
605        public void setSelectableDateRange(Date min, Date max) {
606                dayChooser.setSelectableDateRange(min, max);
607        };
608
609        /**
610         * Gets the minimum selectable date.
611         * 
612         * @return the minimum selectable date
613         */
614        public Date getMaxSelectableDate() {
615                return dayChooser.getMaxSelectableDate();
616        }
617
618        /**
619         * Gets the maximum selectable date.
620         * 
621         * @return the maximum selectable date
622         */
623        public Date getMinSelectableDate() {
624                return dayChooser.getMinSelectableDate();
625        }
626
627        /**
628         * Sets the maximum selectable date.
629         * 
630         * @param max maximum selectable date
631         */
632        public void setMaxSelectableDate(Date max) {
633                dayChooser.setMaxSelectableDate(max);
634        }
635
636        /**
637         * Sets the minimum selectable date.
638         * 
639         * @param min minimum selectable date
640         */
641        public void setMinSelectableDate(Date min) {
642                dayChooser.setMinSelectableDate(min);
643        }
644
645        /**
646         * Gets the maximum number of characters of a day name or 0. If 0 is
647         * returned, dateFormatSymbols.getShortWeekdays() will be used.
648         * 
649         * @return the maximum number of characters of a day name or 0.
650         */
651        public int getMaxDayCharacters() {
652                return dayChooser.getMaxDayCharacters();
653        }
654
655        /**
656         * Sets the maximum number of characters per day in the day bar. Valid
657         * values are 0-4. If set to 0, dateFormatSymbols.getShortWeekdays() will be
658         * used, otherwise theses strings will be reduced to the maximum number of
659         * characters.
660         * 
661         * @param maxDayCharacters
662         *            the maximum number of characters of a day name.
663         */
664        public void setMaxDayCharacters(int maxDayCharacters) {
665                dayChooser.setMaxDayCharacters(maxDayCharacters);
666        }
667}