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