001    /*
002     * $Id: JCalendar.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.Font;
036    import java.beans.PropertyChangeEvent;
037    import java.beans.PropertyChangeListener;
038    import java.util.Calendar;
039    import java.util.Date;
040    import java.util.Locale;
041    
042    import javax.swing.BorderFactory;
043    import javax.swing.JFrame;
044    import 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     */
053    public 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    }