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    package edu.wisc.ssec.mcidasv.data.dateChooser;
029    
030    import java.awt.BorderLayout;
031    import java.awt.Color;
032    import java.awt.Font;
033    import java.awt.Graphics;
034    import java.awt.GridLayout;
035    import java.awt.Insets;
036    import java.awt.event.ActionEvent;
037    import java.awt.event.ActionListener;
038    import java.awt.event.FocusEvent;
039    import java.awt.event.FocusListener;
040    import java.awt.event.KeyEvent;
041    import java.awt.event.KeyListener;
042    import java.awt.event.MouseListener;
043    
044    import java.text.DateFormatSymbols;
045    
046    import java.util.Calendar;
047    import java.util.Date;
048    import java.util.Locale;
049    
050    import javax.swing.JButton;
051    import javax.swing.JFrame;
052    import javax.swing.JPanel;
053    import javax.swing.UIManager;
054    
055    /**
056     * JDayChooser is a bean for choosing a day.
057     * 
058     * @author Kai Toedter
059     * @version $LastChangedRevision: 107 $
060     * @version $LastChangedDate: 2009-05-01 15:48:00 +0200 (Fr, 01 Mai 2009) $
061     */
062    public class JDayChooser extends JPanel implements ActionListener, KeyListener,
063                    FocusListener {
064            private static final long serialVersionUID = 5876398337018781820L;
065    
066            protected JButton[] days;
067    
068            protected JButton[] weeks;
069    
070            protected JButton selectedDay;
071    
072            protected JPanel weekPanel;
073    
074            protected JPanel dayPanel;
075    
076            protected int day;
077    
078            protected Color oldDayBackgroundColor;
079    
080            protected Color selectedColor;
081    
082            protected Color sundayForeground;
083    
084            protected Color weekdayForeground;
085    
086            protected Color decorationBackgroundColor;
087    
088            protected String[] dayNames;
089    
090            protected Calendar calendar;
091    
092            protected Calendar today;
093    
094            protected Locale locale;
095    
096            protected boolean initialized;
097    
098            protected boolean weekOfYearVisible;
099    
100            protected boolean decorationBackgroundVisible = true;
101    
102            protected boolean decorationBordersVisible;
103    
104            protected boolean dayBordersVisible;
105    
106            private boolean alwaysFireDayProperty;
107    
108            protected Date minSelectableDate;
109    
110            protected Date maxSelectableDate;
111    
112            protected Date defaultMinSelectableDate;
113    
114            protected Date defaultMaxSelectableDate;
115    
116            protected int maxDayCharacters;
117    
118            /**
119             * Default JDayChooser constructor.
120             */
121            public JDayChooser() {
122                    this(false);
123            }
124    
125            /**
126             * JDayChooser constructor.
127             * 
128             * @param weekOfYearVisible
129             *            true, if the weeks of a year shall be shown
130             */
131            public JDayChooser(boolean weekOfYearVisible) {
132                    setName("JDayChooser");
133                    setBackground(Color.blue);
134                    this.weekOfYearVisible = weekOfYearVisible;
135                    locale = Locale.getDefault();
136                    days = new JButton[49];
137                    selectedDay = null;
138                    calendar = Calendar.getInstance(locale);
139                    today = (Calendar) calendar.clone();
140    
141                    setLayout(new BorderLayout());
142    
143                    dayPanel = new JPanel();
144                    dayPanel.setLayout(new GridLayout(7, 7));
145    
146                    sundayForeground = new Color(164, 0, 0);
147                    weekdayForeground = new Color(0, 90, 164);
148    
149                    // decorationBackgroundColor = new Color(194, 211, 252);
150                    // decorationBackgroundColor = new Color(206, 219, 246);
151                    decorationBackgroundColor = new Color(210, 228, 238);
152    
153                    for (int y = 0; y < 7; y++) {
154                            for (int x = 0; x < 7; x++) {
155                                    int index = x + (7 * y);
156    
157                                    if (y == 0) {
158                                            // Create a button that doesn't react on clicks or focus
159                                            // changes.
160                                            // Thanks to Thomas Schaefer for the focus hint :)
161                                            days[index] = new DecoratorButton();
162                                    } else {
163                                            days[index] = new JButton("x") {
164                                                    private static final long serialVersionUID = -7433645992591669725L;
165    
166                                                    public void paint(Graphics g) {
167                                                            if ("Windows".equals(UIManager.getLookAndFeel()
168                                                                            .getID())) {
169                                                                    // this is a hack to get the background painted
170                                                                    // when using Windows Look & Feel
171                                                                    if (selectedDay == this) {
172                                                                            g.setColor(selectedColor);
173                                                                            g.fillRect(0, 0, getWidth(), getHeight());
174                                                                    }
175                                                            }
176                                                            super.paint(g);
177                                                    }
178    
179                                            };
180                                            days[index].addActionListener(this);
181                                            days[index].addKeyListener(this);
182                                            days[index].addFocusListener(this);
183                                    }
184    
185                                    days[index].setMargin(new Insets(0, 0, 0, 0));
186                                    days[index].setFocusPainted(false);
187                                    dayPanel.add(days[index]);
188                            }
189                    }
190    
191                    weekPanel = new JPanel();
192                    weekPanel.setLayout(new GridLayout(7, 1));
193                    weeks = new JButton[7];
194    
195                    for (int i = 0; i < 7; i++) {
196                            weeks[i] = new DecoratorButton();
197                            weeks[i].setMargin(new Insets(0, 0, 0, 0));
198                            weeks[i].setFocusPainted(false);
199                            weeks[i].setForeground(new Color(100, 100, 100));
200    
201                            if (i != 0) {
202                                    weeks[i].setText("0" + (i + 1));
203                            }
204    
205                            weekPanel.add(weeks[i]);
206                    }
207    
208                    Calendar tmpCalendar = Calendar.getInstance();
209                    tmpCalendar.set(1, 0, 1, 1, 1);
210                    defaultMinSelectableDate = tmpCalendar.getTime();
211                    minSelectableDate = defaultMinSelectableDate;
212                    tmpCalendar.set(9999, 0, 1, 1, 1);
213                    defaultMaxSelectableDate = tmpCalendar.getTime();
214                    maxSelectableDate = defaultMaxSelectableDate;
215    
216                    init();
217    
218                    setDay(Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
219                    add(dayPanel, BorderLayout.CENTER);
220    
221                    if (weekOfYearVisible) {
222                            add(weekPanel, BorderLayout.WEST);
223                    }
224                    initialized = true;
225                    updateUI();
226            }
227    
228            /**
229             * Initilizes the locale specific names for the days of the week.
230             */
231            protected void init() {
232                    JButton testButton = new JButton();
233                    oldDayBackgroundColor = testButton.getBackground();
234                    selectedColor = new Color(160, 160, 160);
235    
236                    Date date = calendar.getTime();
237                    calendar = Calendar.getInstance(locale);
238                    calendar.setTime(date);
239    
240                    drawDayNames();
241                    drawDays();
242            }
243    
244            /**
245             * Draws the day names of the day columnes.
246             */
247            private void drawDayNames() {
248                    int firstDayOfWeek = calendar.getFirstDayOfWeek();
249                    DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(locale);
250                    dayNames = dateFormatSymbols.getShortWeekdays();
251    
252                    int day = firstDayOfWeek;
253    
254                    for (int i = 0; i < 7; i++) {
255                            if (maxDayCharacters > 0 && maxDayCharacters < 5) {
256                                    if (dayNames[day].length() >= maxDayCharacters) {
257                                            dayNames[day] = dayNames[day]
258                                                            .substring(0, maxDayCharacters);
259                                    }
260                            }
261    
262                            days[i].setText(dayNames[day]);
263    
264                            if (day == 1) {
265                                    days[i].setForeground(sundayForeground);
266                            } else {
267                                    days[i].setForeground(weekdayForeground);
268                            }
269    
270                            if (day < 7) {
271                                    day++;
272                            } else {
273                                    day -= 6;
274                            }
275                    }
276            }
277    
278            /**
279             * Initializes both day names and weeks of the year.
280             */
281            protected void initDecorations() {
282                    for (int x = 0; x < 7; x++) {
283                            days[x].setContentAreaFilled(decorationBackgroundVisible);
284                            days[x].setBorderPainted(decorationBordersVisible);
285                            days[x].invalidate();
286                            days[x].repaint();
287                            weeks[x].setContentAreaFilled(decorationBackgroundVisible);
288                            weeks[x].setBorderPainted(decorationBordersVisible);
289                            weeks[x].invalidate();
290                            weeks[x].repaint();
291                    }
292            }
293    
294            /**
295             * Hides and shows the week buttons.
296             */
297            protected void drawWeeks() {
298                    Calendar tmpCalendar = (Calendar) calendar.clone();
299    
300                    for (int i = 1; i < 7; i++) {
301                            tmpCalendar.set(Calendar.DAY_OF_MONTH, (i * 7) - 6);
302    
303                            int week = tmpCalendar.get(Calendar.WEEK_OF_YEAR);
304                            String buttonText = Integer.toString(week);
305    
306                            if (week < 10) {
307                                    buttonText = "0" + buttonText;
308                            }
309    
310                            weeks[i].setText(buttonText);
311    
312                            if ((i == 5) || (i == 6)) {
313                                    weeks[i].setVisible(days[i * 7].isVisible());
314                            }
315                    }
316            }
317    
318            /**
319             * Hides and shows the day buttons.
320             */
321            protected void drawDays() {
322                    Calendar tmpCalendar = (Calendar) calendar.clone();
323                    tmpCalendar.set(Calendar.HOUR_OF_DAY, 0);
324                    tmpCalendar.set(Calendar.MINUTE, 0);
325                    tmpCalendar.set(Calendar.SECOND, 0);
326                    tmpCalendar.set(Calendar.MILLISECOND, 0);
327    
328                    Calendar minCal = Calendar.getInstance();
329                    minCal.setTime(minSelectableDate);
330                    minCal.set(Calendar.HOUR_OF_DAY, 0);
331                    minCal.set(Calendar.MINUTE, 0);
332                    minCal.set(Calendar.SECOND, 0);
333                    minCal.set(Calendar.MILLISECOND, 0);
334    
335                    Calendar maxCal = Calendar.getInstance();
336                    maxCal.setTime(maxSelectableDate);
337                    maxCal.set(Calendar.HOUR_OF_DAY, 0);
338                    maxCal.set(Calendar.MINUTE, 0);
339                    maxCal.set(Calendar.SECOND, 0);
340                    maxCal.set(Calendar.MILLISECOND, 0);
341    
342                    int firstDayOfWeek = tmpCalendar.getFirstDayOfWeek();
343                    tmpCalendar.set(Calendar.DAY_OF_MONTH, 1);
344    
345                    int firstDay = tmpCalendar.get(Calendar.DAY_OF_WEEK) - firstDayOfWeek;
346    
347                    if (firstDay < 0) {
348                            firstDay += 7;
349                    }
350    
351                    int i;
352    
353                    for (i = 0; i < firstDay; i++) {
354                            days[i + 7].setVisible(false);
355                            days[i + 7].setText("");
356                    }
357    
358                    tmpCalendar.add(Calendar.MONTH, 1);
359    
360                    Date firstDayInNextMonth = tmpCalendar.getTime();
361                    tmpCalendar.add(Calendar.MONTH, -1);
362    
363                    Date day = tmpCalendar.getTime();
364                    int n = 0;
365                    Color foregroundColor = getForeground();
366    
367                    while (day.before(firstDayInNextMonth)) {
368                            days[i + n + 7].setText(Integer.toString(n + 1));
369                            days[i + n + 7].setVisible(true);
370    
371                            if ((tmpCalendar.get(Calendar.DAY_OF_YEAR) == today
372                                            .get(Calendar.DAY_OF_YEAR))
373                                            && (tmpCalendar.get(Calendar.YEAR) == today
374                                                            .get(Calendar.YEAR))) {
375                                    days[i + n + 7].setForeground(sundayForeground);
376                            } else {
377                                    days[i + n + 7].setForeground(foregroundColor);
378                            }
379    
380                            if ((n + 1) == this.day) {
381                                    days[i + n + 7].setBackground(selectedColor);
382                                    selectedDay = days[i + n + 7];
383                            } else {
384                                    days[i + n + 7].setBackground(oldDayBackgroundColor);
385                            }
386    
387                            if (tmpCalendar.before(minCal) || tmpCalendar.after(maxCal)) {
388                                    days[i + n + 7].setEnabled(false);
389                            } else {
390                                    days[i + n + 7].setEnabled(true);
391                            }
392    
393                            n++;
394                            tmpCalendar.add(Calendar.DATE, 1);
395                            day = tmpCalendar.getTime();
396                    }
397    
398                    for (int k = n + i + 7; k < 49; k++) {
399                            days[k].setVisible(false);
400                            days[k].setText("");
401                    }
402    
403                    drawWeeks();
404            }
405    
406            /**
407             * Returns the locale.
408             * 
409             * @return the locale value
410             * 
411             * @see #setLocale
412             */
413            public Locale getLocale() {
414                    return locale;
415            }
416    
417            /**
418             * Sets the locale.
419             * 
420             * @param locale
421             *            the new locale value
422             * 
423             * @see #getLocale
424             */
425            public void setLocale(Locale locale) {
426                    if (!initialized) {
427                            super.setLocale(locale);
428                    } else {
429                            this.locale = locale;
430                            super.setLocale(locale);
431                            init();
432                    }
433            }
434    
435            /**
436             * Sets the day. This is a bound property.
437             * 
438             * @param d
439             *            the day
440             * 
441             * @see #getDay
442             */
443            public void setDay(int d) {
444                    if (d < 1) {
445                            d = 1;
446                    }
447                    Calendar tmpCalendar = (Calendar) calendar.clone();
448                    tmpCalendar.set(Calendar.DAY_OF_MONTH, 1);
449                    tmpCalendar.add(Calendar.MONTH, 1);
450                    tmpCalendar.add(Calendar.DATE, -1);
451    
452                    int maxDaysInMonth = tmpCalendar.get(Calendar.DATE);
453    
454                    if (d > maxDaysInMonth) {
455                            d = maxDaysInMonth;
456                    }
457    
458                    int oldDay = day;
459                    day = d;
460    
461                    if (selectedDay != null) {
462                            selectedDay.setBackground(oldDayBackgroundColor);
463                            selectedDay.repaint();
464                    }
465    
466                    for (int i = 7; i < 49; i++) {
467                            if (days[i].getText().equals(Integer.toString(day))) {
468                                    selectedDay = days[i];
469                                    selectedDay.setBackground(selectedColor);
470                                    break;
471                            }
472                    }
473    
474                    if (alwaysFireDayProperty) {
475                            firePropertyChange("day", 0, day);
476                    } else {
477                            firePropertyChange("day", oldDay, day);
478                    }
479            }
480    
481            /**
482             * this is needed for JDateChooser.
483             * 
484             * @param alwaysFire
485             *            true, if day property shall be fired every time a day is
486             *            chosen.
487             */
488            public void setAlwaysFireDayProperty(boolean alwaysFire) {
489                    alwaysFireDayProperty = alwaysFire;
490            }
491    
492            /**
493             * Returns the selected day.
494             * 
495             * @return the day value
496             * 
497             * @see #setDay
498             */
499            public int getDay() {
500                    return day;
501            }
502    
503            /**
504             * Sets a specific month. This is needed for correct graphical
505             * representation of the days.
506             * 
507             * @param month
508             *            the new month
509             */
510            public void setMonth(int month) {
511                    calendar.set(Calendar.MONTH, month);
512                    int maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
513                    
514                    int adjustedDay = day;
515                    if (day > maxDays) {
516                            adjustedDay = maxDays;
517                            setDay(adjustedDay);
518                    }
519    
520                    drawDays();
521            }
522    
523            /**
524             * Sets a specific year. This is needed for correct graphical representation
525             * of the days.
526             * 
527             * @param year
528             *            the new year
529             */
530            public void setYear(int year) {
531                    calendar.set(Calendar.YEAR, year);
532                    drawDays();
533            }
534    
535            /**
536             * Sets a specific calendar. This is needed for correct graphical
537             * representation of the days.
538             * 
539             * @param calendar
540             *            the new calendar
541             */
542            public void setCalendar(Calendar calendar) {
543                    this.calendar = calendar;
544                    drawDays();
545            }
546    
547            /**
548             * Sets the font property.
549             * 
550             * @param font
551             *            the new font
552             */
553            public void setFont(Font font) {
554                    if (days != null) {
555                            for (int i = 0; i < 49; i++) {
556                                    days[i].setFont(font);
557                            }
558                    }
559                    if (weeks != null) {
560                            for (int i = 0; i < 7; i++) {
561                                    weeks[i].setFont(font);
562                            }
563                    }
564            }
565    
566            /**
567             * Sets the foregroundColor color.
568             * 
569             * @param foreground
570             *            the new foregroundColor
571             */
572            public void setForeground(Color foreground) {
573                    super.setForeground(foreground);
574    
575                    if (days != null) {
576                            for (int i = 7; i < 49; i++) {
577                                    days[i].setForeground(foreground);
578                            }
579    
580                            drawDays();
581                    }
582            }
583    
584            /**
585             * JDayChooser is the ActionListener for all day buttons.
586             * 
587             * @param e
588             *            the ActionEvent
589             */
590            public void actionPerformed(ActionEvent e) {
591                    JButton button = (JButton) e.getSource();
592                    String buttonText = button.getText();
593                    int day = new Integer(buttonText).intValue();
594                    setDay(day);
595            }
596    
597            /**
598             * JDayChooser is the FocusListener for all day buttons. (Added by Thomas
599             * Schaefer)
600             * 
601             * @param e
602             *            the FocusEvent
603             */
604            /*
605             * Code below commented out by Mark Brown on 24 Aug 2004. This code breaks
606             * the JDateChooser code by triggering the actionPerformed method on the
607             * next day button. This causes the date chosen to always be incremented by
608             * one day.
609             */
610            public void focusGained(FocusEvent e) {
611                    // JButton button = (JButton) e.getSource();
612                    // String buttonText = button.getText();
613                    //
614                    // if ((buttonText != null) && !buttonText.equals("") &&
615                    // !e.isTemporary()) {
616                    // actionPerformed(new ActionEvent(e.getSource(), 0, null));
617                    // }
618            }
619    
620            /**
621             * Does nothing.
622             * 
623             * @param e
624             *            the FocusEvent
625             */
626            public void focusLost(FocusEvent e) {
627            }
628    
629            /**
630             * JDayChooser is the KeyListener for all day buttons. (Added by Thomas
631             * Schaefer and modified by Austin Moore)
632             * 
633             * @param e
634             *            the KeyEvent
635             */
636            public void keyPressed(KeyEvent e) {
637                    int offset = (e.getKeyCode() == KeyEvent.VK_UP) ? (-7) : ((e
638                                    .getKeyCode() == KeyEvent.VK_DOWN) ? (+7)
639                                    : ((e.getKeyCode() == KeyEvent.VK_LEFT) ? (-1) : ((e
640                                                    .getKeyCode() == KeyEvent.VK_RIGHT) ? (+1) : 0)));
641    
642                    int newDay = getDay() + offset;
643    
644                    if ((newDay >= 1)
645                                    && (newDay <= calendar.getMaximum(Calendar.DAY_OF_MONTH))) {
646                            setDay(newDay);
647                    }
648            }
649    
650            /**
651             * Does nothing.
652             * 
653             * @param e
654             *            the KeyEvent
655             */
656            public void keyTyped(KeyEvent e) {
657            }
658    
659            /**
660             * Does nothing.
661             * 
662             * @param e
663             *            the KeyEvent
664             */
665            public void keyReleased(KeyEvent e) {
666            }
667    
668            /**
669             * Enable or disable the JDayChooser.
670             * 
671             * @param enabled
672             *            The new enabled value
673             */
674            public void setEnabled(boolean enabled) {
675                    super.setEnabled(enabled);
676    
677                    for (short i = 0; i < days.length; i++) {
678                            if (days[i] != null) {
679                                    days[i].setEnabled(enabled);
680                            }
681                    }
682    
683                    for (short i = 0; i < weeks.length; i++) {
684                            if (weeks[i] != null) {
685                                    weeks[i].setEnabled(enabled);
686                            }
687                    }
688            }
689    
690            /**
691             * In some Countries it is often usefull to know in which week of the year a
692             * date is.
693             * 
694             * @return boolean true, if the weeks of the year is shown
695             */
696            public boolean isWeekOfYearVisible() {
697                    return weekOfYearVisible;
698            }
699    
700            /**
701             * In some Countries it is often usefull to know in which week of the year a
702             * date is.
703             * 
704             * @param weekOfYearVisible
705             *            true, if the weeks of the year shall be shown
706             */
707            public void setWeekOfYearVisible(boolean weekOfYearVisible) {
708                    if (weekOfYearVisible == this.weekOfYearVisible) {
709                            return;
710                    } else if (weekOfYearVisible) {
711                            add(weekPanel, BorderLayout.WEST);
712                    } else {
713                            remove(weekPanel);
714                    }
715    
716                    this.weekOfYearVisible = weekOfYearVisible;
717                    validate();
718                    dayPanel.validate();
719            }
720    
721            /**
722             * Returns the day panel.
723             * 
724             * @return the day panel
725             */
726            public JPanel getDayPanel() {
727                    return dayPanel;
728            }
729    
730            /**
731             * Returns the color of the decoration (day names and weeks).
732             * 
733             * @return the color of the decoration (day names and weeks).
734             */
735            public Color getDecorationBackgroundColor() {
736                    return decorationBackgroundColor;
737            }
738    
739            /**
740             * Sets the background of days and weeks of year buttons.
741             * 
742             * @param decorationBackgroundColor
743             *            The background to set
744             */
745            public void setDecorationBackgroundColor(Color decorationBackgroundColor) {
746                    this.decorationBackgroundColor = decorationBackgroundColor;
747    
748                    if (days != null) {
749                            for (int i = 0; i < 7; i++) {
750                                    days[i].setBackground(decorationBackgroundColor);
751                            }
752                    }
753    
754                    if (weeks != null) {
755                            for (int i = 0; i < 7; i++) {
756                                    weeks[i].setBackground(decorationBackgroundColor);
757                            }
758                    }
759            }
760    
761            /**
762             * Returns the Sunday foreground.
763             * 
764             * @return Color the Sunday foreground.
765             */
766            public Color getSundayForeground() {
767                    return sundayForeground;
768            }
769    
770            /**
771             * Returns the weekday foreground.
772             * 
773             * @return Color the weekday foreground.
774             */
775            public Color getWeekdayForeground() {
776                    return weekdayForeground;
777            }
778    
779            /**
780             * Sets the Sunday foreground.
781             * 
782             * @param sundayForeground
783             *            The sundayForeground to set
784             */
785            public void setSundayForeground(Color sundayForeground) {
786                    this.sundayForeground = sundayForeground;
787                    drawDayNames();
788                    drawDays();
789            }
790    
791            /**
792             * Sets the weekday foreground.
793             * 
794             * @param weekdayForeground
795             *            The weekdayForeground to set
796             */
797            public void setWeekdayForeground(Color weekdayForeground) {
798                    this.weekdayForeground = weekdayForeground;
799                    drawDayNames();
800                    drawDays();
801            }
802    
803            /**
804             * Requests that the selected day also have the focus.
805             */
806            public void setFocus() {
807                    if (selectedDay != null) {
808                            this.selectedDay.requestFocus();
809                    }
810            }
811    
812            /**
813             * The decoration background is the background color of the day titles and
814             * the weeks of the year.
815             * 
816             * @return Returns true, if the decoration background is painted.
817             */
818            public boolean isDecorationBackgroundVisible() {
819                    return decorationBackgroundVisible;
820            }
821    
822            /**
823             * The decoration background is the background color of the day titles and
824             * the weeks of the year.
825             * 
826             * @param decorationBackgroundVisible
827             *            true, if the decoration background shall be painted.
828             */
829            public void setDecorationBackgroundVisible(
830                            boolean decorationBackgroundVisible) {
831                    this.decorationBackgroundVisible = decorationBackgroundVisible;
832                    initDecorations();
833            }
834    
835            /**
836             * The decoration border is the button border of the day titles and the
837             * weeks of the year.
838             * 
839             * @return Returns true, if the decoration border is painted.
840             */
841            public boolean isDecorationBordersVisible() {
842                    return decorationBordersVisible;
843            }
844    
845            public boolean isDayBordersVisible() {
846                    return dayBordersVisible;
847            }
848    
849            /**
850             * The decoration border is the button border of the day titles and the
851             * weeks of the year.
852             * 
853             * @param decorationBordersVisible
854             *            true, if the decoration border shall be painted.
855             */
856            public void setDecorationBordersVisible(boolean decorationBordersVisible) {
857                    this.decorationBordersVisible = decorationBordersVisible;
858                    initDecorations();
859            }
860    
861            public void setDayBordersVisible(boolean dayBordersVisible) {
862                    this.dayBordersVisible = dayBordersVisible;
863                    if (initialized) {
864                            for (int x = 7; x < 49; x++) {
865                                    if ("Windows".equals(UIManager.getLookAndFeel().getID())) {
866                                            days[x].setContentAreaFilled(dayBordersVisible);
867                                    } else {
868                                            days[x].setContentAreaFilled(true);
869                                    }
870                                    days[x].setBorderPainted(dayBordersVisible);
871                            }
872                    }
873            }
874    
875            /**
876             * Updates the UI and sets the day button preferences.
877             */
878            public void updateUI() {
879                    super.updateUI();
880                    setFont(Font.decode("Dialog Plain 11"));
881    
882                    if (weekPanel != null) {
883                            weekPanel.updateUI();
884                    }
885                    if (initialized) {
886                            if ("Windows".equals(UIManager.getLookAndFeel().getID())) {
887                                    setDayBordersVisible(false);
888                                    setDecorationBackgroundVisible(true);
889                                    setDecorationBordersVisible(false);
890                            } else {
891                                    setDayBordersVisible(true);
892                                    setDecorationBackgroundVisible(decorationBackgroundVisible);
893                                    setDecorationBordersVisible(decorationBordersVisible);
894                            }
895                    }
896            }
897    
898            /**
899             * Sets a valid date range for selectable dates. If max is before min, the
900             * default range with no limitation is set.
901             * 
902             * @param min
903             *            the minimum selectable date or null (then the minimum date is
904             *            set to 01\01\0001)
905             * @param max
906             *            the maximum selectable date or null (then the maximum date is
907             *            set to 01\01\9999)
908             */
909            public void setSelectableDateRange(Date min, Date max) {
910                    if (min == null) {
911                            minSelectableDate = defaultMinSelectableDate;
912                    } else {
913                            minSelectableDate = min;
914                    }
915                    if (max == null) {
916                            maxSelectableDate = defaultMaxSelectableDate;
917                    } else {
918                            maxSelectableDate = max;
919                    }
920                    if (maxSelectableDate.before(minSelectableDate)) {
921                            minSelectableDate = defaultMinSelectableDate;
922                            maxSelectableDate = defaultMaxSelectableDate;
923                    }
924                    drawDays();
925            }
926    
927            /**
928             * Sets the maximum selectable date. If null, the date 01\01\9999 will be
929             * set instead.
930             * 
931             * @param max
932             *            the maximum selectable date
933             * 
934             * @return the maximum selectable date
935             */
936            public Date setMaxSelectableDate(Date max) {
937                    if (max == null) {
938                            maxSelectableDate = defaultMaxSelectableDate;
939                    } else {
940                            maxSelectableDate = max;
941                    }
942                    drawDays();
943                    return maxSelectableDate;
944            }
945    
946            /**
947             * Sets the minimum selectable date. If null, the date 01\01\0001 will be
948             * set instead.
949             * 
950             * @param min
951             *            the minimum selectable date
952             * 
953             * @return the minimum selectable date
954             */
955            public Date setMinSelectableDate(Date min) {
956                    if (min == null) {
957                            minSelectableDate = defaultMinSelectableDate;
958                    } else {
959                            minSelectableDate = min;
960                    }
961                    drawDays();
962                    return minSelectableDate;
963            }
964    
965            /**
966             * Gets the maximum selectable date.
967             * 
968             * @return the maximum selectable date
969             */
970            public Date getMaxSelectableDate() {
971                    return maxSelectableDate;
972            }
973    
974            /**
975             * Gets the minimum selectable date.
976             * 
977             * @return the minimum selectable date
978             */
979            public Date getMinSelectableDate() {
980                    return minSelectableDate;
981            }
982    
983            /**
984             * Gets the maximum number of characters of a day name or 0. If 0 is
985             * returned, dateFormatSymbols.getShortWeekdays() will be used.
986             * 
987             * @return the maximum number of characters of a day name or 0.
988             */
989            public int getMaxDayCharacters() {
990                    return maxDayCharacters;
991            }
992    
993            /**
994             * Sets the maximum number of characters per day in the day bar. Valid
995             * values are 0-4. If set to 0, dateFormatSymbols.getShortWeekdays() will be
996             * used, otherwise theses strings will be reduced to the maximum number of
997             * characters.
998             * 
999             * @param maxDayCharacters
1000             *            the maximum number of characters of a day name.
1001             */
1002            public void setMaxDayCharacters(int maxDayCharacters) {
1003                    if (maxDayCharacters == this.maxDayCharacters) {
1004                            return;
1005                    }
1006    
1007                    if (maxDayCharacters < 0 || maxDayCharacters > 4) {
1008                            this.maxDayCharacters = 0;
1009                    } else {
1010                            this.maxDayCharacters = maxDayCharacters;
1011                    }
1012                    drawDayNames();
1013                    drawDays();
1014                    invalidate();
1015            }
1016    
1017            /**
1018             * Creates a JFrame with a JDayChooser inside and can be used for testing.
1019             * 
1020             * @param s
1021             *            The command line arguments
1022             */
1023            public static void main(String[] s) {
1024                    JFrame frame = new JFrame("JDayChooser");
1025                    frame.getContentPane().add(new JDayChooser());
1026                    frame.pack();
1027                    frame.setVisible(true);
1028            }
1029    
1030            class DecoratorButton extends JButton {
1031                    private static final long serialVersionUID = -5306477668406547496L;
1032    
1033                    public DecoratorButton() {
1034                            setBackground(decorationBackgroundColor);
1035                            setContentAreaFilled(decorationBackgroundVisible);
1036                            setBorderPainted(decorationBordersVisible);
1037                    }
1038    
1039                    public void addMouseListener(MouseListener l) {
1040                    }
1041    
1042                    public boolean isFocusable() {
1043                            return false;
1044                    }
1045    
1046                    public void paint(Graphics g) {
1047                            if ("Windows".equals(UIManager.getLookAndFeel().getID())) {
1048                                    // this is a hack to get the background painted
1049                                    // when using Windows Look & Feel
1050                                    if (decorationBackgroundVisible) {
1051                                            g.setColor(decorationBackgroundColor);
1052                                    } else {
1053                                            g.setColor(days[7].getBackground());
1054                                    }
1055                                    g.fillRect(0, 0, getWidth(), getHeight());
1056                                    if (isBorderPainted()) {
1057                                            setContentAreaFilled(true);
1058                                    } else {
1059                                            setContentAreaFilled(false);
1060                                    }
1061                            }
1062                            super.paint(g);
1063                    }
1064            };
1065    }