View Javadoc
1   /*******************************************************************************
2    * JDateButton: Java Swing Date Button
3    * Copyright 2012,2014 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   * ------------------------------------------------------------
17   * SubVersion Revision Information:
18   * $URL: https://svn.code.sf.net/p/jdatebutton/code/tags/v2.1.0-b3/jdatebutton-swing/src/main/java/net/sourceforge/jdatebutton/swing/JDateDialog.java $
19   * $Revision: 34 $
20   * $Author: tonywasher $
21   * $Date: 2015-12-01 16:21:13 +0000 (Tue, 01 Dec 2015) $
22   ******************************************************************************/
23  package net.sourceforge.jdatebutton.swing;
24  
25  import java.awt.BorderLayout;
26  import java.awt.Color;
27  import java.awt.Component;
28  import java.awt.Font;
29  import java.awt.GridLayout;
30  import java.awt.Insets;
31  import java.awt.Window;
32  import java.awt.event.ActionEvent;
33  import java.awt.event.ActionListener;
34  import java.awt.event.KeyEvent;
35  import java.awt.event.MouseAdapter;
36  import java.awt.event.MouseEvent;
37  import java.awt.event.WindowAdapter;
38  import java.awt.event.WindowEvent;
39  import java.time.DayOfWeek;
40  import java.time.LocalDate;
41  import java.time.format.TextStyle;
42  import java.util.Calendar;
43  import java.util.Locale;
44  import java.util.ResourceBundle;
45  
46  import javax.swing.AbstractAction;
47  import javax.swing.ActionMap;
48  import javax.swing.BorderFactory;
49  import javax.swing.Box;
50  import javax.swing.BoxLayout;
51  import javax.swing.InputMap;
52  import javax.swing.JButton;
53  import javax.swing.JComponent;
54  import javax.swing.JDialog;
55  import javax.swing.JLabel;
56  import javax.swing.JOptionPane;
57  import javax.swing.JPanel;
58  import javax.swing.KeyStroke;
59  import javax.swing.SwingConstants;
60  import javax.swing.border.Border;
61  
62  /**
63   * Dialog class providing a dialog allowing selection of a date.
64   * <p>
65   * It will rebuild the dialog according to the currently set locale and date format whenever it is
66   * made visible
67   */
68  public class JDateDialog
69          extends JDialog {
70      /**
71       * Serial Id.
72       */
73      private static final long serialVersionUID = 2518033527621472786L;
74  
75      /**
76       * Standard font name.
77       */
78      private static final String FONT_NAME = "Courier";
79  
80      /**
81       * Standard font size.
82       */
83      private static final int FONT_SIZE = 10;
84  
85      /**
86       * Standard font.
87       */
88      private static final Font FONT_STANDARD = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
89  
90      /**
91       * Inactive font.
92       */
93      private static final Font FONT_INACTIVE = new Font(FONT_NAME, Font.ITALIC, FONT_SIZE);
94  
95      /**
96       * Selected font.
97       */
98      private static final Font FONT_SELECTED = new Font(FONT_NAME, Font.BOLD, FONT_SIZE);
99  
100     /**
101      * Resource Bundle.
102      */
103     private static final ResourceBundle NLS_BUNDLE = ResourceBundle.getBundle(JDateDialog.class.getName());
104 
105     /**
106      * ToolTip for Current Day.
107      */
108     public static final String NLS_CURRENTDAY = NLS_BUNDLE.getString("CurrentDay");
109 
110     /**
111      * ToolTip for Selected Day.
112      */
113     public static final String NLS_SELECTEDDAY = NLS_BUNDLE.getString("SelectedDay");
114 
115     /**
116      * ToolTip for Next Month.
117      */
118     public static final String NLS_NEXTMONTH = NLS_BUNDLE.getString("NextMonth");
119 
120     /**
121      * ToolTip for Previous Month.
122      */
123     public static final String NLS_PREVMONTH = NLS_BUNDLE.getString("PreviousMonth");
124 
125     /**
126      * ToolTip for Next Year.
127      */
128     public static final String NLS_NEXTYEAR = NLS_BUNDLE.getString("NextYear");
129 
130     /**
131      * ToolTip for Previous Year.
132      */
133     public static final String NLS_PREVYEAR = NLS_BUNDLE.getString("PreviousYear");
134 
135     /**
136      * Null Date selection text.
137      */
138     public static final String NLS_NULLSELECT = NLS_BUNDLE.getString("NullSelect");
139 
140     /**
141      * Escape action text.
142      */
143     private static final String ACTION_ESCAPE = "Escape";
144 
145     /**
146      * The month array.
147      */
148     private final PanelMonth theDaysPanel;
149 
150     /**
151      * The navigation.
152      */
153     private final PanelNavigation theNavigation;
154 
155     /**
156      * The Null Select.
157      */
158     private final JButton theNullButton;
159 
160     /**
161      * Is the Null button active.
162      */
163     private boolean isNullActive = false;
164 
165     /**
166      * The Date Configuration.
167      */
168     private final transient JDateConfig theConfig;
169 
170     /**
171      * Should we build names?
172      */
173     private boolean doBuildNames = true;
174 
175     /**
176      * The container panel.
177      */
178     private final JPanel theContainer;
179 
180     /**
181      * Have we selected a date?
182      */
183     private boolean haveSelected = false;
184 
185     /**
186      * Constructor.
187      * @param pParent the parent component to which this dialog is attached
188      * @param pConfig the configuration for the dialog
189      */
190     protected JDateDialog(final Component pParent,
191                           final JDateConfig pConfig) {
192         /* Initialise the dialog */
193         super(JOptionPane.getFrameForComponent(pParent), false);
194 
195         /* Set as undecorated */
196         setUndecorated(true);
197 
198         /* Store the DateConfig */
199         theConfig = pConfig;
200 
201         /* Build the panels */
202         theDaysPanel = new PanelMonth(this);
203         theNavigation = new PanelNavigation(this);
204 
205         /* Build the Null Select */
206         theNullButton = new JButton(NLS_NULLSELECT);
207         theNullButton.addActionListener(new ButtonHandler());
208 
209         /* Set this to be the main panel */
210         theContainer = new JPanel(new BorderLayout());
211         theContainer.setBorder(BorderFactory.createLineBorder(Color.black));
212         theContainer.add(theNavigation, BorderLayout.NORTH);
213         theContainer.add(theDaysPanel, BorderLayout.CENTER);
214         setContentPane(theContainer);
215         pack();
216 
217         /* Initialise the month */
218         initialiseMonth();
219 
220         /* Handle Escape Key */
221         handleEscapeKey(theContainer);
222 
223         /* Create focus listener */
224         addWindowFocusListener(new CalendarFocus());
225 
226         /* Set the relative location */
227         setLocationRelativeTo(pParent);
228     }
229 
230     /**
231      * Request a rebuild of panel names.
232      */
233     protected void doBuildNames() {
234         doBuildNames = true;
235     }
236 
237     /**
238      * Have we selected a date?
239      * @return true/false
240      */
241     public boolean haveSelected() {
242         return haveSelected;
243     }
244 
245     /**
246      * Obtain Date Configuration.
247      * @return the date configuration
248      */
249     public JDateConfig getDateConfig() {
250         return theConfig;
251     }
252 
253     /**
254      * Build the month.
255      */
256     private void buildMonth() {
257         /* Build the month */
258         theNavigation.buildMonth();
259         theDaysPanel.buildMonth();
260     }
261 
262     /**
263      * Set Selected Date.
264      * @param pDay the Selected day
265      */
266     private void setSelected(final int pDay) {
267         /* Set the selected day */
268         theConfig.setSelectedDay(pDay);
269 
270         /* Note that we have selected */
271         haveSelected = true;
272 
273         /* Close the dialog */
274         setVisible(false);
275     }
276 
277     /**
278      * Resize the dialog.
279      */
280     private void reSizeDialog() {
281         pack();
282     }
283 
284     /**
285      * InitialiseMonth.
286      */
287     private void initialiseMonth() {
288         /* Initialise the current month */
289         theConfig.initialiseCurrent();
290 
291         /* Build the day names if required */
292         if (doBuildNames) {
293             theDaysPanel.buildDayNames();
294         }
295         doBuildNames = false;
296 
297         /* Build detail */
298         buildMonth();
299 
300         /* If we need to change the visibility of the null button */
301         if (isNullActive != theConfig.allowNullDateSelection()) {
302             /* Add/Remove the button */
303             if (!isNullActive) {
304                 theContainer.add(theNullButton, BorderLayout.SOUTH);
305             } else {
306                 theContainer.remove(theNullButton);
307             }
308 
309             /* Record status and resize the dialog */
310             isNullActive = theConfig.allowNullDateSelection();
311             reSizeDialog();
312         }
313     }
314 
315     @Override
316     public void setVisible(final boolean bVisible) {
317         /* If we are becoming visible */
318         if (bVisible) {
319             /* Note that we have not selected */
320             haveSelected = false;
321 
322             /* Initialise the current month */
323             initialiseMonth();
324 
325             /* Set visibility and focus */
326             super.setVisible(true);
327             requestFocus();
328 
329             /* Pass the call on */
330         } else {
331             super.setVisible(bVisible);
332         }
333     }
334 
335     /**
336      * Handle Escape to close dialog.
337      * @param pPane the panel to handle keys for
338      */
339     private void handleEscapeKey(final JPanel pPane) {
340         /* Access Maps */
341         ActionMap myAction = pPane.getActionMap();
342         InputMap myInput = pPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
343 
344         /* Build the maps */
345         myInput.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ACTION_ESCAPE);
346         myAction.put(ACTION_ESCAPE, new CalendarAction());
347     }
348 
349     /**
350      * Close non-Modal.
351      */
352     private void closeNonModal() {
353         setVisible(false);
354     }
355 
356     /**
357      * CalendarAction Handle escape action.
358      */
359     private class CalendarAction
360             extends AbstractAction {
361         /**
362          * SerialId.
363          */
364         private static final long serialVersionUID = 5464442251457102478L;
365 
366         @Override
367         public void actionPerformed(final ActionEvent e) {
368             closeNonModal();
369         }
370     }
371 
372     /**
373      * CalendarFocus Handle loss of focus.
374      */
375     private class CalendarFocus
376             extends WindowAdapter {
377         @Override
378         public void windowLostFocus(final WindowEvent e) {
379             Window myOppo = e.getOppositeWindow();
380 
381             /*
382              * Ignore loss of focus to unidentified window. This is to bypass a problem in browser
383              * applets where a temporary loss of focus occurs immediately upon display of a dialog.
384              */
385             if (myOppo != null) {
386                 closeNonModal();
387             }
388         }
389     }
390 
391     /**
392      * ButtonHandler Handle button presses.
393      */
394     private class ButtonHandler
395             implements ActionListener {
396         @Override
397         public void actionPerformed(final ActionEvent e) {
398             /* Access the event source */
399             Object src = e.getSource();
400 
401             /* If the button is the Null button */
402             if (theNullButton.equals(src)) {
403                 /* Set the null date */
404                 setSelected(-1);
405             }
406         }
407     }
408 
409     /**
410      * PanelNavigation class allowing navigation between months.
411      */
412     private static final class PanelNavigation
413             extends JPanel {
414         /**
415          * Serial Id.
416          */
417         private static final long serialVersionUID = 4399207012690467687L;
418 
419         /**
420          * The owning dialog.
421          */
422         private final JDateDialog theDialog;
423 
424         /**
425          * The Date Configuration.
426          */
427         private final transient JDateConfig theConfig;
428 
429         /**
430          * The Date Label.
431          */
432         private final JLabel theDateLabel;
433 
434         /**
435          * The Previous Month Button.
436          */
437         private final JButton thePrevMonthButton;
438 
439         /**
440          * The Next Month Button.
441          */
442         private final JButton theNextMonthButton;
443 
444         /**
445          * The Previous Year Button.
446          */
447         private final JButton thePrevYearButton;
448 
449         /**
450          * The Next Year Button.
451          */
452         private final JButton theNextYearButton;
453 
454         /**
455          * Constructor.
456          * @param pDialog the owning dialog
457          */
458         private PanelNavigation(final JDateDialog pDialog) {
459             /* Record the dialog */
460             theDialog = pDialog;
461 
462             /* Store the Date Configuration */
463             theConfig = pDialog.getDateConfig();
464 
465             /* Create the label */
466             theDateLabel = new JLabel();
467 
468             /* Create the buttons */
469             thePrevMonthButton = new JButton(ArrowIcon.LEFT);
470             theNextMonthButton = new JButton(ArrowIcon.RIGHT);
471             thePrevYearButton = new JButton(ArrowIcon.DOUBLELEFT);
472             theNextYearButton = new JButton(ArrowIcon.DOUBLERIGHT);
473 
474             /* Add ToopTips */
475             theNextMonthButton.setToolTipText(NLS_NEXTMONTH);
476             thePrevMonthButton.setToolTipText(NLS_PREVMONTH);
477             theNextYearButton.setToolTipText(NLS_NEXTYEAR);
478             thePrevYearButton.setToolTipText(NLS_PREVYEAR);
479 
480             /* Listen for button events */
481             NavigateListener myListener = new NavigateListener();
482             thePrevMonthButton.addActionListener(myListener);
483             theNextMonthButton.addActionListener(myListener);
484             thePrevYearButton.addActionListener(myListener);
485             theNextYearButton.addActionListener(myListener);
486 
487             /* Restrict the margins */
488             thePrevMonthButton.setMargin(new Insets(1, 1, 1, 1));
489             theNextMonthButton.setMargin(new Insets(1, 1, 1, 1));
490             thePrevYearButton.setMargin(new Insets(1, 1, 1, 1));
491             theNextYearButton.setMargin(new Insets(1, 1, 1, 1));
492 
493             /* Add these elements into a box */
494             setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
495             add(thePrevYearButton);
496             add(Box.createHorizontalStrut(1));
497             add(thePrevMonthButton);
498             add(Box.createHorizontalGlue());
499             add(theDateLabel);
500             add(Box.createHorizontalGlue());
501             add(theNextMonthButton);
502             add(Box.createHorizontalStrut(1));
503             add(theNextYearButton);
504         }
505 
506         /**
507          * Build month details.
508          */
509         private void buildMonth() {
510             /* Store the active month */
511             LocalDate myBase = theConfig.getCurrentMonth();
512             Locale myLocale = theConfig.getLocale();
513 
514             /* Determine the display for the label */
515             String myMonth = myBase.getMonth().getDisplayName(TextStyle.FULL, myLocale);
516             String myYear = Integer.toString(myBase.getYear());
517 
518             /* If we are showing pretty month capitalise the name */
519             if (theConfig.doPretty()) {
520                 /* UpperCase the first character */
521                 myMonth = theConfig.capitaliseString(myMonth);
522             }
523 
524             /* Set the label */
525             theDateLabel.setText(myMonth
526                                  + ", "
527                                  + myYear);
528 
529             /* Access boundary dates */
530             LocalDate myEarliest = theConfig.getEarliestDate();
531             LocalDate myLatest = theConfig.getLatestDate();
532 
533             /* Enable/Disable buttons as required */
534             thePrevMonthButton.setEnabled(!JDateConfig.isSameMonth(myEarliest, myBase));
535             thePrevYearButton.setEnabled(!JDateConfig.isSameYear(myEarliest, myBase));
536             theNextMonthButton.setEnabled(!JDateConfig.isSameMonth(myLatest, myBase));
537             theNextYearButton.setEnabled(!JDateConfig.isSameYear(myLatest, myBase));
538         }
539 
540         /**
541          * Action Listener for buttons.
542          */
543         private class NavigateListener
544                 implements ActionListener {
545             @Override
546             public void actionPerformed(final ActionEvent e) {
547                 /* Access the event source */
548                 Object src = e.getSource();
549 
550                 /* If the button is previous month */
551                 if (thePrevMonthButton.equals(src)) {
552                     /* Adjust the month */
553                     theConfig.previousMonth();
554                     theDialog.buildMonth();
555                 } else if (theNextMonthButton.equals(src)) {
556                     /* Adjust the month */
557                     theConfig.nextMonth();
558                     theDialog.buildMonth();
559                 } else if (thePrevYearButton.equals(src)) {
560                     /* Adjust the month */
561                     theConfig.previousYear();
562                     theDialog.buildMonth();
563                 } else if (theNextYearButton.equals(src)) {
564                     /* Adjust the month */
565                     theConfig.nextYear();
566                     theDialog.buildMonth();
567                 }
568             }
569         }
570     }
571 
572     /**
573      * PanelMonth class representing the set of PanelDay labels in a month.
574      */
575     private static final class PanelMonth
576             extends JPanel {
577         /**
578          * Serial Id.
579          */
580         private static final long serialVersionUID = 4003892701953050327L;
581 
582         /**
583          * Number of days in week.
584          */
585         private static final int DAYS_IN_WEEK = 7;
586 
587         /**
588          * Maximum # of weeks in month.
589          */
590         private static final int MAX_WEEKS_IN_MONTH = 6;
591 
592         /**
593          * The array of days of week (in column order).
594          */
595         private final DayOfWeek[] theDaysOfWk = new DayOfWeek[DAYS_IN_WEEK];
596 
597         /**
598          * The Array of Day Names.
599          */
600         private final JLabel[] theHdrs = new JLabel[DAYS_IN_WEEK];
601 
602         /**
603          * The Array of Day Labels.
604          */
605         private final PanelDay[][] theDays = new PanelDay[MAX_WEEKS_IN_MONTH][DAYS_IN_WEEK];
606 
607         /**
608          * The Dialog.
609          */
610         private final JDateDialog theDialog;
611 
612         /**
613          * The Date Configuration.
614          */
615         private final transient JDateConfig theConfig;
616 
617         /**
618          * The number of currently visible rows.
619          */
620         private int theNumRows = MAX_WEEKS_IN_MONTH;
621 
622         /**
623          * Constructor.
624          * @param pDialog the owning dialog
625          */
626         private PanelMonth(final JDateDialog pDialog) {
627             /* Store the dialog */
628             theDialog = pDialog;
629 
630             /* Store the Date Configuration */
631             theConfig = pDialog.getDateConfig();
632 
633             /* Set this as a 7x7 GridLayout */
634             GridLayout myLayout = new GridLayout();
635             myLayout.setColumns(DAYS_IN_WEEK);
636             myLayout.setRows(0);
637             setLayout(myLayout);
638 
639             /* Loop through the labels */
640             for (int iCol = 0; iCol < DAYS_IN_WEEK; iCol++) {
641                 /* Access the label */
642                 JLabel myLabel = new JLabel();
643                 theHdrs[iCol] = myLabel;
644 
645                 /* Set colour */
646                 myLabel.setHorizontalAlignment(SwingConstants.CENTER);
647                 myLabel.setBackground(Color.lightGray);
648                 myLabel.setOpaque(true);
649 
650                 /* Add to the grid */
651                 add(myLabel);
652             }
653 
654             /* Add the Days to the layout */
655             for (int iRow = 0; iRow < MAX_WEEKS_IN_MONTH; iRow++) {
656                 for (int iCol = 0; iCol < DAYS_IN_WEEK; iCol++) {
657                     PanelDay myDay = new PanelDay(pDialog);
658                     theDays[iRow][iCol] = myDay;
659                     add(myDay);
660                 }
661             }
662         }
663 
664         /**
665          * ReSize the number of visible rows.
666          * @param iNumRows number of visible rows
667          */
668         private void reSizeRows(final int iNumRows) {
669             /* Hide any visible rows that should now be hidden */
670             while (iNumRows < theNumRows) {
671                 /* Decrement number of rows */
672                 theNumRows--;
673 
674                 /* Loop through remaining rows */
675                 for (PanelDay day : theDays[theNumRows]) {
676                     /* Remove from panel */
677                     remove(day);
678                 }
679             }
680 
681             /* Show any hidden rows that should now be visible */
682             while (iNumRows > theNumRows) {
683                 /* Loop through remaining rows */
684                 for (PanelDay day : theDays[theNumRows]) {
685                     /* Add to panel */
686                     add(day);
687                 }
688 
689                 /* Increment number of rows */
690                 theNumRows++;
691             }
692 
693             /* RePack the Dialog */
694             theDialog.reSizeDialog();
695         }
696 
697         /**
698          * Is the DayOfWeek a Weekend day.
699          * @param pDoW the day of the week
700          * @return true/false
701          */
702         private static boolean isWeekend(final DayOfWeek pDoW) {
703             switch (pDoW) {
704                 case SATURDAY:
705                 case SUNDAY:
706                     return true;
707                 default:
708                     return false;
709             }
710         }
711 
712         /**
713          * obtain column number for DayOfWeek.
714          * @param pDoW the day of the week
715          * @return the column number
716          */
717         private int getDayColumn(final DayOfWeek pDoW) {
718             for (int i = 0; i < DAYS_IN_WEEK; i++) {
719                 if (theDaysOfWk[i] == pDoW) {
720                     return i;
721                 }
722             }
723             return -1;
724         }
725 
726         /**
727          * build the month display for the requested month.
728          */
729         private void buildMonth() {
730             int iRow = 0;
731             int iCol = 0;
732 
733             /* Access the current month */
734             LocalDate myCurr = theConfig.getCurrentMonth();
735             int iMonth = myCurr.getMonthValue();
736 
737             /* Access the Weekday of the 1st of the month */
738             DayOfWeek myWeekDay = myCurr.getDayOfWeek();
739             int iFirstCol = getDayColumn(myWeekDay);
740 
741             /* Access the interesting days of the month */
742             int iCurrent = theConfig.getCurrentDay();
743             int iSelected = theConfig.getSelectedDay();
744             int iEarliest = theConfig.getEarliestDay();
745             int iLatest = theConfig.getLatestDay();
746 
747             /* Adjust the day to beginning of week if required */
748             if (iFirstCol > 0) {
749                 myCurr = myCurr.minusDays(iFirstCol);
750             }
751 
752             /* Loop through initial columns */
753             for (int iDay = myCurr.getDayOfMonth(); iCol < iFirstCol; iCol++, iDay++, myCurr = myCurr.plusDays(1)) {
754                 /* Access the label */
755                 PanelDay myLabel = theDays[0][iCol];
756 
757                 /* Reset the day and set no day */
758                 myLabel.resetDay(false);
759                 myLabel.setDay(iDay, false);
760             }
761 
762             /* Loop through the days of the month */
763             for (int iDay = 1; iMonth == myCurr.getMonthValue(); iCol++, iDay++, myCurr = myCurr.plusDays(1)) {
764                 /* Reset column if necessary */
765                 if (iCol > MAX_WEEKS_IN_MONTH) {
766                     iRow++;
767                     iCol = 0;
768                 }
769 
770                 /* Access the label */
771                 PanelDay myLabel = theDays[iRow][iCol];
772 
773                 /* Set initial parts of the day */
774                 myLabel.resetDay(true);
775                 if (isWeekend(myCurr.getDayOfWeek())) {
776                     myLabel.setWeekend();
777                 }
778                 if (iCurrent == iDay) {
779                     myLabel.setCurrent();
780                 }
781                 if (iSelected == iDay) {
782                     myLabel.setSelected();
783                 }
784 
785                 /* Determine whether the day is select-able */
786                 boolean isSelectable = true;
787                 if ((iEarliest > 0)
788                     && (iDay < iEarliest)) {
789                     isSelectable = false;
790                 } else if ((iLatest > 0)
791                            && (iDay > iLatest)) {
792                     isSelectable = false;
793                 }
794 
795                 /* Set the day */
796                 myLabel.setDay(iDay, isSelectable);
797             }
798 
799             /* Loop through remaining columns */
800             for (int iDay = 1; iCol < DAYS_IN_WEEK; iCol++, iDay++) {
801                 /* Access the label */
802                 PanelDay myLabel = theDays[iRow][iCol];
803 
804                 /* Reset the day and set no day */
805                 myLabel.resetDay(false);
806                 myLabel.setDay(iDay, false);
807             }
808 
809             /* Ensure correct number of rows are visible */
810             reSizeRows(iRow + 1);
811         }
812 
813         /**
814          * build Day names.
815          */
816         private void buildDayNames() {
817             /* Get todays date */
818             Locale myLocale = theConfig.getLocale();
819             Calendar myDate = Calendar.getInstance(myLocale);
820             int myStart = myDate.getFirstDayOfWeek();
821             if (myStart == Calendar.SUNDAY) {
822                 myStart += DAYS_IN_WEEK;
823             }
824 
825             /* Build the array of the days of the week */
826             DayOfWeek myDoW = DayOfWeek.of(myStart - 1);
827             for (int iDay = 0; iDay < DAYS_IN_WEEK; iDay++, myDoW = myDoW.plus(1)) {
828                 /* Store the day into the array */
829                 theDaysOfWk[iDay] = myDoW;
830             }
831 
832             /* Loop through the labels */
833             for (int iCol = 0; iCol < DAYS_IN_WEEK; iCol++) {
834                 /* Access the label */
835                 JLabel myLabel = theHdrs[iCol];
836 
837                 /* Access the required name */
838                 myDoW = theDaysOfWk[iCol];
839                 String myName = myDoW.getDisplayName(TextStyle.SHORT, myLocale);
840 
841                 /* If the name is too long */
842                 int iMaxDayLen = theConfig.getMaxDayLen();
843                 if (myName.length() > iMaxDayLen) {
844                     /* Shrink the name */
845                     myName = theConfig.doShrinkFromRight()
846                                                            ? myName.substring(0, iMaxDayLen)
847                                                            : myName.substring(myName.length()
848                                                                               - iMaxDayLen);
849                 }
850 
851                 /* If we are showing pretty weekdays capitalise the character */
852                 if (theConfig.doPretty()) {
853                     /* UpperCase the first character */
854                     myName = theConfig.capitaliseString(myName);
855                 }
856 
857                 /* Set the name */
858                 myLabel.setText(myName);
859 
860                 /* Set colour */
861                 myLabel.setForeground(isWeekend(myDoW)
862                                                        ? Color.red
863                                                        : Color.black);
864             }
865         }
866     }
867 
868     /**
869      * Panel class representing a single day in the panel.
870      */
871     private static final class PanelDay
872             extends JLabel {
873         /**
874          * Serial Id.
875          */
876         private static final long serialVersionUID = -5636278095729007866L;
877 
878         /**
879          * Owning dialog.
880          */
881         private final JDateDialog theDialog;
882 
883         /**
884          * The standard border.
885          */
886         private static final Border BORDER_STD = BorderFactory.createEmptyBorder();
887 
888         /**
889          * The selected border.
890          */
891         private static final Border BORDER_SEL = BorderFactory.createLineBorder(Color.green.darker());
892 
893         /**
894          * The highlighted border.
895          */
896         private static final Border BORDER_HLT = BorderFactory.createLineBorder(Color.orange);
897 
898         /**
899          * The Day that this Label represents.
900          */
901         private int theDay = -1;
902 
903         /**
904          * Is the day select-able.
905          */
906         private boolean isSelectable = false;
907 
908         /**
909          * The font.
910          */
911         private Font theFont = null;
912 
913         /**
914          * The foreground colour.
915          */
916         private Color theForeGround = null;
917 
918         /**
919          * The background colour.
920          */
921         private Color theBackGround = null;
922 
923         /**
924          * The border.
925          */
926         private Border theBorder = null;
927 
928         /**
929          * The toolTip.
930          */
931         private String theToolTip = null;
932 
933         /**
934          * Constructor.
935          * @param pDialog the owning dialog
936          */
937         private PanelDay(final JDateDialog pDialog) {
938             /* Store the parameter */
939             theDialog = pDialog;
940 
941             /* Initialise values */
942             setHorizontalAlignment(SwingConstants.CENTER);
943             setOpaque(true);
944             addMouseListener(new CalendarMouse(this));
945         }
946 
947         /**
948          * Set day for label.
949          * @param pDay the Day number
950          * @param pSelectable is the day select-able
951          */
952         private void setDay(final int pDay,
953                             final boolean pSelectable) {
954             /* Record the day */
955             theDay = pDay;
956             isSelectable = pSelectable;
957 
958             /* Set the text for the item */
959             if (pDay > 0) {
960                 setText(Integer.toString(theDay));
961             } else {
962                 setText("");
963             }
964 
965             /* Set Characteristics */
966             setFont(theFont);
967             setForeground(theForeGround);
968             setBackground(theBackGround);
969             setBorder(theBorder);
970             setToolTipText(theToolTip);
971 
972             /* Enable/Disable the label */
973             setEnabled(isSelectable);
974         }
975 
976         /**
977          * Reset a Day Label.
978          * @param isActive true/false
979          */
980         private void resetDay(final boolean isActive) {
981             /* Record detail */
982             theFont = isActive
983                                ? FONT_STANDARD
984                                : FONT_INACTIVE;
985             theForeGround = Color.black;
986             theBackGround = Color.white;
987             theBorder = BORDER_STD;
988             theToolTip = null;
989             isSelectable = isActive;
990         }
991 
992         /**
993          * Set a day as a Weekend.
994          */
995         private void setWeekend() {
996             /* Record detail */
997             theForeGround = Color.red;
998         }
999 
1000         /**
1001          * Set a day as Current Day.
1002          */
1003         private void setCurrent() {
1004             /* Record detail */
1005             theForeGround = Color.blue;
1006             theBackGround = Color.gray;
1007             theToolTip = NLS_CURRENTDAY;
1008         }
1009 
1010         /**
1011          * Set a day as Selected Day.
1012          */
1013         private void setSelected() {
1014             /* Record detail */
1015             theFont = FONT_SELECTED;
1016             theForeGround = Color.green.darker();
1017             theBackGround = Color.green.brighter();
1018             theBorder = BORDER_SEL;
1019             theToolTip = NLS_SELECTEDDAY;
1020         }
1021 
1022         /**
1023          * CalendarMouse.
1024          */
1025         private final class CalendarMouse
1026                 extends MouseAdapter {
1027             /**
1028              * The Day Panel.
1029              */
1030             private final PanelDay theOwner;
1031 
1032             /**
1033              * Constructor.
1034              * @param pOwner the owning panel
1035              */
1036             private CalendarMouse(final PanelDay pOwner) {
1037                 /* Store parameters */
1038                 theOwner = pOwner;
1039             }
1040 
1041             @Override
1042             public void mouseClicked(final MouseEvent e) {
1043                 /* If item is select-able */
1044                 if (isSelectable) {
1045                     theDialog.setSelected(theDay);
1046                 }
1047             }
1048 
1049             @Override
1050             public void mouseEntered(final MouseEvent e) {
1051                 /* Highlight the border of a select-able item */
1052                 if (isSelectable) {
1053                     theOwner.setBorder(BORDER_HLT);
1054                 }
1055             }
1056 
1057             @Override
1058             public void mouseExited(final MouseEvent e) {
1059                 /* Reset border to standard for label that has changed */
1060                 if (isSelectable) {
1061                     theOwner.setBorder(theBorder);
1062                 }
1063             }
1064         }
1065     }
1066 }