Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/theotherbell/ui/DatePicker.java


1   /***********************************************************
2   
3    DatePicker.java
4    Copyright (C) 2003 Brenda Bell
5   
6    ***********************************************************/
7   
8   /***********************************************************
9   
10   This file is part of JavaDatePicker.
11  
12   JavaDatePicker is free software; you can redistribute it
13   and/or modify it under the terms of the GNU Lesser
14   General Public License as published by the Free Software
15   Foundation; either version 2 of the License, or (at your
16   option) any later version.
17  
18   JavaDatePicker is distributed in the hope that it will
19   be useful, but WITHOUT ANY WARRANTY; without even the
20   implied warranty of MERCHANTABILITY or FITNESS FOR A
21   PARTICULAR PURPOSE.  See the GNU Lesser General Public
22   License for more details.
23  
24   You should have received a copy of the GNU Lesser
25   General Public License along with JavaDatePicker; if
26   not, write to the Free Software Foundation, Inc., 59
27   Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  
29   ***********************************************************/
30  
31  package com.theotherbell.ui;
32  
33  import java.awt.*;
34  import java.awt.event.*;
35  import java.util.GregorianCalendar;
36  import java.util.Date;
37  import java.util.Calendar;
38  import java.text.DateFormat;
39  import java.text.FieldPosition;
40  
41  import javax.swing.*;
42  import javax.swing.plaf.BorderUIResource;
43  
44  import org.netbeans.lib.awtextra.AbsoluteConstraints;
45  import org.netbeans.lib.awtextra.AbsoluteLayout;
46  
47  /**
48   * GUI component that allows the user to choose a date from a calendar.
49   * Usage is illustrated in the sample code below:
50   * <PRE>
51   *  JDialog dlg = new JDialog(new Frame(), true);
52   *  DatePicker dp = new DatePicker();
53   *  dp.setHideOnSelect(false);
54   *  dlg.getContentPane().add(dp);
55   *  dlg.pack();
56   *  dlg.show();
57   *  System.out.println(dp.getDate().toString());
58   *  dlg.dispose();
59   *  System.exit(0);
60   * </PRE>
61   * @author B. Bell
62   * @version 1.1a
63   */
64  public final class DatePicker extends JPanel {
65  
66      /**
67       * X coordinate for upper left corner of week 1 day 1.
68       */
69      private static final int startX = 10;
70  
71      /**
72       * Y coordinate for upper left corner of week 1 day 1.
73       */
74      private static final int startY = 60;
75  
76      /**
77       * Small font.
78       */
79      private static final Font smallFont = new Font("Dialog", Font.PLAIN, 10);
80  
81      /**
82       * Large font.
83       */
84      private static final Font largeFont = new Font("Dialog", Font.PLAIN, 12);
85  
86      /**
87       * Insets for day components and small buttons.
88       */
89      private static final Insets insets = new Insets(2, 2, 2, 2);
90  
91      /**
92       * Highlighted color.
93       */
94      private static final Color highlight = new Color(255, 255, 204);
95  
96      /**
97       * Enabled color.
98       */
99      private static final Color white = new Color(255, 255, 255);
100 
101     /**
102      * Disabled color.
103      */
104     private static final Color gray = new Color(204, 204, 204);
105 
106     /**
107      * Most recently selected day component.
108      */
109     private Component selectedDay = null;
110 
111     /**
112      * Currently selected date.
113      */
114     private GregorianCalendar selectedDate = null;
115 
116     /**
117      * Tracks the original date set when the component was created.
118      */
119     private GregorianCalendar originalDate = null;
120 
121     /**
122      * When true, the panel will be hidden as soon as a day is
123      * selected by clicking the day or clicking the Today button.
124      */
125     private boolean hideOnSelect = true;
126 
127     /**
128      * When clicked, displays the previous month.
129      */
130     private final JButton backButton = new JButton();
131 
132     /**
133      * Displays the currently selected month and year.
134      */
135     private final JLabel monthAndYear = new JLabel();
136 
137     /**
138      * When clicked, displays the next month.
139      */
140     private final JButton forwardButton = new JButton();
141 
142     /**
143      * Column headings for the days of the week.
144      */
145     private final JTextField[] dayHeadings = new JTextField[]{
146         new JTextField("S"),
147         new JTextField("M"),
148         new JTextField("T"),
149         new JTextField("W"),
150         new JTextField("T"),
151         new JTextField("F"),
152         new JTextField("S")};
153 
154     /**
155      * 2-dimensional array for 6 weeks of 7 days each.
156      */
157     private final JTextField[][] daysInMonth = new JTextField[][]{
158         {new JTextField(),
159          new JTextField(),
160          new JTextField(),
161          new JTextField(),
162          new JTextField(),
163          new JTextField(),
164          new JTextField()},
165         {new JTextField(),
166          new JTextField(),
167          new JTextField(),
168          new JTextField(),
169          new JTextField(),
170          new JTextField(),
171          new JTextField()},
172         {new JTextField(),
173          new JTextField(),
174          new JTextField(),
175          new JTextField(),
176          new JTextField(),
177          new JTextField(),
178          new JTextField()},
179         {new JTextField(),
180          new JTextField(),
181          new JTextField(),
182          new JTextField(),
183          new JTextField(),
184          new JTextField(),
185          new JTextField()},
186         {new JTextField(),
187          new JTextField(),
188          new JTextField(),
189          new JTextField(),
190          new JTextField(),
191          new JTextField(),
192          new JTextField()},
193         {new JTextField(),
194          new JTextField(),
195          new JTextField(),
196          new JTextField(),
197          new JTextField(),
198          new JTextField(),
199          new JTextField()}
200     };
201 
202     /**
203      * When clicked, sets the selected day to the current date.
204      */
205     private final JButton todayButton = new JButton();
206 
207     /**
208      * When clicked, hides the calendar and sets the selected date to "empty".
209      */
210     private final JButton cancelButton = new JButton();
211 
212     /**
213      * Default constructor that sets the currently selected date to the
214      * current date.
215      */
216     public DatePicker() {
217         super();
218         selectedDate = getToday();
219         init();
220     }
221 
222     /**
223      * Alternate constructor that sets the currently selected date to the
224      * specified date if non-null.
225      * @param initialDate
226      */
227     public DatePicker(final Date initialDate) {
228         super();
229         if (null == initialDate)
230             selectedDate = getToday();
231         else
232             (selectedDate = new GregorianCalendar()).setTime(initialDate);
233         originalDate = new GregorianCalendar(
234                 selectedDate.get(Calendar.YEAR),
235                 selectedDate.get(Calendar.MONTH),
236                 selectedDate.get(Calendar.DATE));
237         init();
238     }
239 
240     /**
241      * Returns true if the panel will be made invisible after a day is
242      * selected.
243      * @return true or false
244      */
245     public boolean isHideOnSelect() {
246         return hideOnSelect;
247     }
248 
249     /**
250      * Controls whether the panel will be made invisible after a day is
251      * selected.
252      * @param hideOnSelect
253      */
254     public void setHideOnSelect(final boolean hideOnSelect) {
255         if (this.hideOnSelect != hideOnSelect) {
256             this.hideOnSelect = hideOnSelect;
257             initButtons(false);
258         }
259     }
260 
261     /**
262      * Returns the currently selected date.
263      * @return date
264      */
265     public Date getDate() {
266         if (null != selectedDate)
267             return selectedDate.getTime();
268         return null;
269     }
270 
271     /**
272      * Initializes the panel components according to the current value
273      * of selectedDate.
274      */
275     private void init() {
276         setLayout(new AbsoluteLayout());
277         this.setMinimumSize(new Dimension(161, 226));
278         this.setMaximumSize(getMinimumSize());
279         this.setPreferredSize(getMinimumSize());
280         this.setBorder(new BorderUIResource.EtchedBorderUIResource());
281 
282         backButton.setFont(smallFont);
283         backButton.setText("<");
284         backButton.setMargin(insets);
285         backButton.setDefaultCapable(false);
286         backButton.addActionListener(new ActionListener() {
287             public void actionPerformed(final ActionEvent evt) {
288                 onBackClicked(evt);
289             }
290         });
291         add(backButton, new AbsoluteConstraints(10, 10, 20, 20));
292 
293         monthAndYear.setFont(largeFont);
294         monthAndYear.setHorizontalAlignment(JTextField.CENTER);
295         monthAndYear.setText(formatDateText(selectedDate.getTime()));
296         add(monthAndYear, new AbsoluteConstraints(30, 10, 100, 20));
297 
298         forwardButton.setFont(smallFont);
299         forwardButton.setText(">");
300         forwardButton.setMargin(insets);
301         forwardButton.setDefaultCapable(false);
302         forwardButton.addActionListener(new ActionListener() {
303             public void actionPerformed(final ActionEvent evt) {
304                 onForwardClicked(evt);
305             }
306         });
307         add(forwardButton, new AbsoluteConstraints(130, 10, 20, 20));
308 
309         // layout the column headings for the days of the week
310         int x = startX;
311         for (int ii = 0; ii < dayHeadings.length; ii++) {
312             dayHeadings[ii].setBackground(gray);
313             dayHeadings[ii].setEditable(false);
314             dayHeadings[ii].setFont(smallFont);
315             dayHeadings[ii].setHorizontalAlignment(JTextField.CENTER);
316             dayHeadings[ii].setFocusable(false);
317             add(dayHeadings[ii], new AbsoluteConstraints(x, 40, 21, 21));
318             x += 20;
319         }
320 
321         // layout the days of the month
322         x = startX;
323         int y = startY;
324         for (int ii = 0; ii < daysInMonth.length; ii++) {
325             for (int jj = 0; jj < daysInMonth[ii].length; jj++) {
326                 daysInMonth[ii][jj].setBackground(gray);
327                 daysInMonth[ii][jj].setEditable(false);
328                 daysInMonth[ii][jj].setFont(smallFont);
329                 daysInMonth[ii][jj].setHorizontalAlignment(JTextField.RIGHT);
330                 daysInMonth[ii][jj].setText("");
331                 daysInMonth[ii][jj].setFocusable(false);
332                 daysInMonth[ii][jj].addMouseListener(new MouseAdapter() {
333                     public void mouseClicked(final MouseEvent evt) {
334                         onDayClicked(evt);
335                     }
336                 });
337                 add(daysInMonth[ii][jj], new AbsoluteConstraints(x, y, 21, 21));
338                 x += 20;
339             }
340             x = startX;
341             y += 20;
342         }
343 
344         initButtons(true);
345 
346         calculateCalendar();
347     }
348 
349     /**
350      * Initializes Today and Cancel buttons dependent on whether hideOnSelect
351      * is set; if the panel will stay open, the Cancel button is invisible.
352      * @param firstTime
353      */
354     private void initButtons(final boolean firstTime) {
355         if (firstTime) {
356             final Dimension buttonSize = new Dimension(68, 24);
357             todayButton.setFont(largeFont);
358             todayButton.setText("Today");
359             todayButton.setMargin(insets);
360             todayButton.setMaximumSize(buttonSize);
361             todayButton.setMinimumSize(buttonSize);
362             todayButton.setPreferredSize(buttonSize);
363             todayButton.setDefaultCapable(true);
364             todayButton.setSelected(true);
365             todayButton.addActionListener(new ActionListener() {
366                 public void actionPerformed(final ActionEvent evt) {
367                     onToday(evt);
368                 }
369             });
370 
371             cancelButton.setFont(largeFont);
372             cancelButton.setText("Cancel");
373             cancelButton.setMargin(insets);
374             cancelButton.setMaximumSize(buttonSize);
375             cancelButton.setMinimumSize(buttonSize);
376             cancelButton.setPreferredSize(buttonSize);
377             cancelButton.addActionListener(new ActionListener() {
378                 public void actionPerformed(final ActionEvent evt) {
379                     onCancel(evt);
380                 }
381             });
382         } else {
383             this.remove(todayButton);
384             this.remove(cancelButton);
385         }
386 
387         if (hideOnSelect) {
388             add(todayButton, new AbsoluteConstraints(25, 190, 52, -1));
389             add(cancelButton, new AbsoluteConstraints(87, 190, 52, -1));
390         } else {
391             add(todayButton, new AbsoluteConstraints(55, 190, 52, -1));
392         }
393     }
394 
395     /**
396      * Event handler for the Today button that sets the currently selected
397      * date to the current date.
398      * @param evt
399      */
400     private void onToday(final java.awt.event.ActionEvent evt) {
401         selectedDate = getToday();
402         setVisible(!hideOnSelect);
403         if (isVisible()) { // don't bother with calculation if not visible
404             monthAndYear.setText(formatDateText(selectedDate.getTime()));
405             calculateCalendar();
406         }
407     }
408 
409     /**
410      * Event handler for the Cancel button that unsets the currently selected date.
411      * @param evt
412      */
413     private void onCancel(final ActionEvent evt) {
414         selectedDate = originalDate;
415         setVisible(!hideOnSelect);
416     }
417 
418     /**
419      * Event handler for the forward button that increments the currently
420      * selected month.
421      * @param evt
422      */
423     private void onForwardClicked(final java.awt.event.ActionEvent evt) {
424         final int day = selectedDate.get(Calendar.DATE);
425         selectedDate.set(Calendar.DATE, 1);
426         selectedDate.add(Calendar.MONTH, 1);
427         selectedDate.set(Calendar.DATE,
428                 Math.min(day, calculateDaysInMonth(selectedDate)));
429         monthAndYear.setText(formatDateText(selectedDate.getTime()));
430         calculateCalendar();
431     }
432 
433     /**
434      * Event handler for the back button that decrements the currently selected
435      * month.
436      * @param evt
437      */
438     private void onBackClicked(final java.awt.event.ActionEvent evt) {
439         final int day = selectedDate.get(Calendar.DATE);
440         selectedDate.set(Calendar.DATE, 1);
441         selectedDate.add(Calendar.MONTH, -1);
442         selectedDate.set(Calendar.DATE,
443                 Math.min(day, calculateDaysInMonth(selectedDate)));
444         monthAndYear.setText(formatDateText(selectedDate.getTime()));
445         calculateCalendar();
446     }
447 
448     /**
449      * Event handler that sets the currently selected date to the clicked day.
450      * @param evt
451      */
452     private void onDayClicked(final java.awt.event.MouseEvent evt) {
453         final javax.swing.JTextField fld = (javax.swing.JTextField) evt.getSource();
454         if (!"".equals(fld.getText())) {
455             if (null != selectedDay) {
456                 selectedDay.setBackground(white);
457             }
458             fld.setBackground(highlight);
459             selectedDay = fld;
460             selectedDate.set(
461                     Calendar.DATE,
462                     Integer.parseInt(fld.getText()));
463             setVisible(!hideOnSelect);
464         }
465     }
466 
467     /**
468      * Returns the current date.
469      * @return date
470      */
471     private static GregorianCalendar getToday() {
472         final GregorianCalendar gc = new GregorianCalendar();
473         gc.set(Calendar.HOUR_OF_DAY, 0);
474         gc.set(Calendar.MINUTE, 0);
475         gc.set(Calendar.SECOND, 0);
476         gc.set(Calendar.MILLISECOND, 0);
477         return gc;
478     }
479 
480     /**
481      * Calculates the days of the month.
482      */
483     private void calculateCalendar() {
484         // clear the selected date
485         if (null != selectedDay) {
486             selectedDay.setBackground(white);
487             selectedDay = null;
488         }
489 
490         // get the first day of the selected year and month
491         final GregorianCalendar c = new GregorianCalendar(
492                 selectedDate.get(Calendar.YEAR),
493                 selectedDate.get(Calendar.MONTH),
494                 1);
495 
496         // figure out the maximum number of days in the month
497         final int maxDay = calculateDaysInMonth(c);
498 
499         // figure out the day that should be selected in this month
500         // based on the previously selected day and the maximum number
501         // of days in the month
502         final int selectedDay = Math.min(maxDay, selectedDate.get(
503                 Calendar.DATE));
504 
505         // clear the days up to the first day of the month
506         int dow = c.get(Calendar.DAY_OF_WEEK);
507         for (int dd = 0; dd < dow; dd++) {
508             daysInMonth[0][dd].setText("");
509             daysInMonth[0][dd].setBackground(gray);
510         }
511 
512         // construct the days in the selected month
513         int week;
514         do {
515             week = c.get(Calendar.WEEK_OF_MONTH);
516             dow = c.get(Calendar.DAY_OF_WEEK);
517             final JTextField fld = this.daysInMonth[week - 1][dow - 1];
518             fld.setText(Integer.toString(c.get(Calendar.DATE)));
519             if (selectedDay == c.get(Calendar.DATE)) {
520                 fld.setBackground(highlight);
521                 this.selectedDay = fld;
522             } else
523                 fld.setBackground(white);
524             if (c.get(Calendar.DATE) >= maxDay)
525                 break;
526             c.add(Calendar.DATE, 1);
527         } while (c.get(Calendar.DATE) <= maxDay);
528 
529         // clear all the days after the last day of the month
530         week--;
531         for (int ww = week; ww < daysInMonth.length; ww++) {
532             for (int dd = dow; dd < daysInMonth[ww].length; dd++) {
533                 daysInMonth[ww][dd].setText("");
534                 daysInMonth[ww][dd].setBackground(gray);
535             }
536             dow = 0;
537         }
538 
539         // set the currently selected date
540         c.set(Calendar.DATE, selectedDay);
541         selectedDate = c;
542     }
543 
544     /**
545      * Calculates the number of days in the specified month.
546      * @param c
547      * @return number of days in the month
548      */
549     private static int calculateDaysInMonth(final Calendar c) {
550         int daysInMonth = 0;
551         switch (c.get(Calendar.MONTH)) {
552             case 0:
553             case 2:
554             case 4:
555             case 6:
556             case 7:
557             case 9:
558             case 11:
559                 daysInMonth = 31;
560                 break;
561             case 3:
562             case 5:
563             case 8:
564             case 10:
565                 daysInMonth = 30;
566                 break;
567             case 1:
568                 final int year = c.get(Calendar.YEAR);
569                 daysInMonth =
570                         (0 == year % 1000) ? 29 :
571                         (0 == year % 100) ? 28 :
572                         (0 == year % 4) ? 29 : 28;
573                 break;
574         }
575         return daysInMonth;
576     }
577 
578     /**
579      * Returns a short string representation of the specified date (January, 2001).
580      * @param dt
581      * @return short string
582      */
583     private static String formatDateText(final Date dt) {
584         final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
585 
586         final StringBuffer mm = new StringBuffer();
587         final StringBuffer yy = new StringBuffer();
588         final FieldPosition mmfp = new FieldPosition(DateFormat.MONTH_FIELD);
589         final FieldPosition yyfp = new FieldPosition(DateFormat.YEAR_FIELD);
590         df.format(dt, mm, mmfp);
591         df.format(dt, yy, yyfp);
592         return (mm.toString().substring(mmfp.getBeginIndex(), mmfp.getEndIndex()) +
593                 " " + yy.toString().substring(yyfp.getBeginIndex(), yyfp.getEndIndex()));
594     }
595 
596 }