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