001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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 http://www.gnu.org/licenses.
027     */
028    
029    package edu.wisc.ssec.mcidasv.data.dateChooser;
030    
031    import java.awt.BorderLayout;
032    import java.awt.Color;
033    import java.awt.Font;
034    import java.beans.PropertyChangeEvent;
035    import java.beans.PropertyChangeListener;
036    import java.util.Calendar;
037    import java.util.Date;
038    import java.util.Locale;
039    
040    import javax.swing.BorderFactory;
041    import javax.swing.JFrame;
042    import 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     */
051    public 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    }