KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > core > gui > base > DateChooser


1 //The contents of this file are subject to the Mozilla Public License Version 1.1
2
//(the "License"); you may not use this file except in compliance with the
3
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
4
//
5
//Software distributed under the License is distributed on an "AS IS" basis,
6
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
7
//for the specific language governing rights and
8
//limitations under the License.
9
//
10
//The Original Code is "The Columba Project"
11
//
12
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
13
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
14
//
15
//All Rights Reserved.
16
package org.columba.core.gui.base;
17
18 import java.awt.BorderLayout JavaDoc;
19 import java.awt.Color JavaDoc;
20 import java.awt.Dimension JavaDoc;
21 import java.awt.FontMetrics JavaDoc;
22 import java.awt.Graphics JavaDoc;
23 import java.awt.Insets JavaDoc;
24 import java.awt.event.ActionEvent JavaDoc;
25 import java.awt.event.ActionListener JavaDoc;
26 import java.awt.event.MouseAdapter JavaDoc;
27 import java.awt.event.MouseEvent JavaDoc;
28 import java.text.DateFormatSymbols JavaDoc;
29 import java.text.SimpleDateFormat JavaDoc;
30 import java.util.Calendar JavaDoc;
31
32 import javax.swing.BorderFactory JavaDoc;
33 import javax.swing.JButton JavaDoc;
34 import javax.swing.JComponent JavaDoc;
35 import javax.swing.JLabel JavaDoc;
36 import javax.swing.JPanel JavaDoc;
37 import javax.swing.UIManager JavaDoc;
38
39 /**
40  * taken some code from the Kiwi Toolkit: http://www.dystance.net/ping/kiwi/
41  * author: Mark Lindner
42  *
43  */

44
45 public class DateChooser extends JPanel JavaDoc implements ActionListener JavaDoc {
46     private static final int cellSize = 22;
47
48     private static final int[] daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31,
49             30, 31, 30, 31 };
50
51     private static final int[] daysInMonthLeap = { 31, 29, 31, 30, 31, 30, 31,
52             31, 30, 31, 30, 31 };
53
54     private static final Color JavaDoc weekendColor = Color.red.darker();
55
56     // private ActionSupport asupport;
57

58     /** <i>Date changed</i> event command. */
59     public static final String JavaDoc DATE_CHANGE_CMD = "dateChanged";
60
61     /** <i>Month changed</i> event command. */
62     public static final String JavaDoc MONTH_CHANGE_CMD = "monthChanged";
63
64     /** <i>Year changed</i> event command. */
65     public static final String JavaDoc YEAR_CHANGE_CMD = "yearChanged";
66
67     CalendarPane calendarPane;
68
69     // private JLabel l_date, l_year, l_month;
70
private JLabel JavaDoc l_date;
71
72     // private JLabel l_date, l_year, l_month;
73
private JLabel JavaDoc l_month;
74
75     // private JButton b_lyear, b_ryear, b_lmonth, b_rmonth;
76
private JButton JavaDoc b_lmonth;
77
78     // private JButton b_lyear, b_ryear, b_lmonth, b_rmonth;
79
private JButton JavaDoc b_rmonth;
80
81     private SimpleDateFormat JavaDoc datefmt = new SimpleDateFormat JavaDoc("E d MMM yyyy");
82
83     private Calendar JavaDoc selectedDate = null;
84
85     private Calendar JavaDoc minDate = null;
86
87     private Calendar JavaDoc maxDate = null;
88
89     private int selectedDay;
90
91     private int firstDay;
92
93     private int minDay = -1;
94
95     private int maxDay = -1;
96
97     private String JavaDoc[] months;
98
99     private String JavaDoc[] labels = new String JavaDoc[7];
100
101     private Color JavaDoc highlightColor;
102
103     private Color JavaDoc disabledColor;
104
105     private boolean clipMin = false;
106
107     private boolean clipMax = false;
108
109     private boolean clipAllMin = false;
110
111     private boolean clipAllMax = false;
112
113     private int[] weekendCols = { 0, 0 };
114
115     /**
116      * Construct a new <code>DateChooser</code>. The selection will be
117      * initialized to the current date.
118      */

119     public DateChooser() {
120         this(Calendar.getInstance());
121     }
122
123     /**
124      * Construct a new <code>DateChooser</code> with the specified selected
125      * date.
126      *
127      * @param <code>date</code> The date for the selection.
128      */

129     public DateChooser(Calendar JavaDoc date) {
130         // asupport = new ActionSupport(this);
131
DateFormatSymbols JavaDoc sym = new DateFormatSymbols JavaDoc();
132
133         months = sym.getShortMonths();
134
135         String JavaDoc[] wkd = sym.getShortWeekdays();
136
137         for (int i = 0; i < 7; i++) {
138             int l = Math.min(wkd[i + 1].length(), 2);
139             labels[i] = wkd[i + 1].substring(0, l);
140         }
141
142         // Let's at least make a half-assed attempt at conforming to the Metal
143
// PLAF colors.
144
highlightColor = UIManager.getColor("List.selectionBackground");
145         disabledColor = Color.red;
146
147         setBorder(BorderFactory.createEtchedBorder());
148
149         setLayout(new BorderLayout JavaDoc(5, 5));
150
151         JPanel JavaDoc top = new JPanel JavaDoc();
152         top.setLayout(new BorderLayout JavaDoc(0, 0));
153         top.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
154
155         // top.setBorder( BorderFactory.createEtchedBorder() );
156
JPanel JavaDoc p1 = new JPanel JavaDoc();
157         p1.setLayout(new BorderLayout JavaDoc());
158         top.add(p1, BorderLayout.CENTER);
159
160         b_lmonth = new JButton JavaDoc("<");
161         b_lmonth.addActionListener(this);
162         b_lmonth.setMargin(new Insets JavaDoc(0, 0, 0, 0));
163
164         // b_lmonth.setFocusPainted(false);
165
// b_lmonth.setOpaque(false);
166
// b_lmonth.addActionListener(this);
167
p1.add(b_lmonth, BorderLayout.WEST);
168
169         l_month = new JLabel JavaDoc();
170
171         // p1.add(l_month);
172
/*
173          * l_year = new JLabel(); p1.add(l_year);
174          */

175         l_date = new JLabel JavaDoc("Date");
176         l_date.setAlignmentX(0);
177         p1.add(l_date, BorderLayout.CENTER);
178
179         b_rmonth = new JButton JavaDoc(">");
180         b_rmonth.addActionListener(this);
181         b_rmonth.setMargin(new Insets JavaDoc(0, 0, 0, 0));
182
183         // b_rmonth.setFocusPainted(false);
184
// b_rmonth.setOpaque(false);
185
// b_rmonth.addActionListener(this);
186
p1.add(b_rmonth, BorderLayout.EAST);
187
188         /*
189          * JPanel p2 = new JPanel(); p2.setLayout(new
190          * FlowLayout(FlowLayout.LEFT)); top.add("East", p2);
191          *
192          * b_lyear = new JButton("<"); b_lyear.addActionListener( this );
193          * //b_lyear.setMargin(KiwiUtils.emptyInsets);
194          * b_lyear.setFocusPainted(false); b_lyear.setOpaque(false);
195          * //b_lyear.addActionListener(this); p2.add(b_lyear);
196          *
197          * l_year = new JLabel(); p2.add(l_year);
198          *
199          * b_ryear = new JButton(">"); b_ryear.addActionListener( this );
200          * //b_ryear.setMargin(KiwiUtils.emptyInsets);
201          * b_ryear.setFocusPainted(false); b_ryear.setOpaque(false);
202          * //b_ryear.addActionListener(this); p2.add(b_ryear);
203          */

204         add("North", top);
205
206         calendarPane = new CalendarPane();
207         calendarPane.setOpaque(false);
208         add("Center", calendarPane);
209
210         /*
211          * Font f = getFont(); setFont(new Font(f.getName(), Font.BOLD,
212          * f.getSize()));
213          */

214         int fd = date.getFirstDayOfWeek();
215         weekendCols[0] = (Calendar.SUNDAY - fd + 7) % 7;
216         weekendCols[1] = (Calendar.SATURDAY - fd + 7) % 7;
217
218         setSelectedDate(date);
219     }
220
221     public static boolean isLeapYear(int year) {
222         return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
223     }
224
225     /* Copy the relevant portions of a date. */
226     private Calendar JavaDoc copyDate(Calendar JavaDoc source, Calendar JavaDoc dest) {
227         if (dest == null) {
228             dest = Calendar.getInstance();
229         }
230
231         dest.set(Calendar.YEAR, source.get(Calendar.YEAR));
232         dest.set(Calendar.MONTH, source.get(Calendar.MONTH));
233         dest.set(Calendar.DATE, source.get(Calendar.DATE));
234
235         return (dest);
236     }
237
238     /**
239      * Add a <code>ActionListener</code> to this component's list of
240      * listeners.
241      *
242      * @param listener
243      * The listener to add.
244      */

245     public void addActionListener(ActionListener JavaDoc listener) {
246         // asupport.addActionListener(listener);
247
}
248
249     /**
250      * Remove a <code>ActionListener</code> from this component's list of
251      * listeners.
252      *
253      * @param listener
254      * The listener to remove.
255      */

256     public void removeActionListener(ActionListener JavaDoc listener) {
257         // asupport.removeActionListener(listener);
258
}
259
260     /**
261      * Set the highlight color for this component.
262      *
263      * @param color
264      * The new highlight color.
265      */

266     public void setHighlightColor(Color JavaDoc color) {
267         highlightColor = color;
268     }
269
270     /**
271      * Get the highlight color for this component.
272      *
273      * @return The current highlight color.
274      */

275     public Color JavaDoc getHighlightColor() {
276         return (highlightColor);
277     }
278
279     /**
280      * Get a copy of the <code>Calendar</code> object that represents the
281      * currently selected date.
282      *
283      * @return The currently selected date.
284      */

285     public Calendar JavaDoc getSelectedDate() {
286         return ((Calendar JavaDoc) selectedDate.clone());
287     }
288
289     /**
290      * Set the selected date for the chooser.
291      *
292      * @param date
293      * The date to select.
294      */

295     public void setSelectedDate(Calendar JavaDoc date) {
296         selectedDate = copyDate(date, selectedDate);
297         selectedDay = selectedDate.get(Calendar.DAY_OF_MONTH);
298
299         _refresh();
300     }
301
302     /**
303      * Set the earliest selectable date for the chooser.
304      *
305      * @param date
306      * The (possibly <code>null</code>) minimum selectable date.
307      */

308     public void setMinimumDate(Calendar JavaDoc date) {
309         minDate = ((date == null) ? null : copyDate(date, minDate));
310         minDay = ((date == null) ? (-1) : minDate.get(Calendar.DATE));
311
312         _refresh();
313     }
314
315     /**
316      * Get the earliest selectable date for the chooser.
317      *
318      * @return The minimum selectable date, or <code>null</code> if there is
319      * no minimum date currently set.
320      */

321     public Calendar JavaDoc getMinimumDate() {
322         return (minDate);
323     }
324
325     /**
326      * Set the latest selectable date for the chooser.
327      *
328      * @param date
329      * The (possibly <code>null</code>) maximum selectable date.
330      */

331     public void setMaximumDate(Calendar JavaDoc date) {
332         maxDate = ((date == null) ? null : copyDate(date, maxDate));
333         maxDay = ((date == null) ? (-1) : maxDate.get(Calendar.DATE));
334
335         _refresh();
336     }
337
338     /**
339      * Get the latest selectable date for the chooser.
340      *
341      * @return The maximum selectable date, or <code>null</code> if there is
342      * no maximum date currently set.
343      */

344     public Calendar JavaDoc getMaximumDate() {
345         return (maxDate);
346     }
347
348     /**
349      * Set the format for the textual date display at the bottom of the
350      * component.
351      *
352      * @param <code>format</code> The new date format to use.
353      */

354     public void setDateFormat(SimpleDateFormat JavaDoc format) {
355         datefmt = format;
356
357         _refresh();
358     }
359
360     /** Handle events. This method is public as an implementation side-effect. */
361     public void actionPerformed(ActionEvent JavaDoc evt) {
362         Object JavaDoc o = evt.getSource();
363
364         if (o == b_lmonth) {
365             selectedDate.add(Calendar.MONTH, -1);
366         } else if (o == b_rmonth) {
367             selectedDate.add(Calendar.MONTH, 1);
368         }
369
370         /*
371          * else if (o == b_lyear) { selectedDate.add(Calendar.YEAR, -1); if
372          * (minDate != null) { int m = minDate.get(Calendar.MONTH); if
373          * (selectedDate.get(Calendar.MONTH) < m)
374          * selectedDate.set(Calendar.MONTH, m); } }
375          *
376          * else if (o == b_ryear) { selectedDate.add(Calendar.YEAR, 1); if
377          * (maxDate != null) { int m = maxDate.get(Calendar.MONTH); if
378          * (selectedDate.get(Calendar.MONTH) > m)
379          * selectedDate.set(Calendar.MONTH, m); } }
380          */

381         selectedDay = 1;
382         selectedDate.set(Calendar.DATE, selectedDay);
383
384         _refresh();
385
386         /*
387          * asupport.fireActionEvent(((o == b_lmonth) || (o == b_rmonth)) ?
388          * MONTH_CHANGE_CMD : YEAR_CHANGE_CMD);
389          */

390     }
391
392     /*
393      * Determine what day of week the first day of the month falls on. It's too
394      * bad we have to resort to this hack; the Java API provides no means of
395      * doing this any other way.
396      */

397     private void _computeFirstDay() {
398         int d = selectedDate.get(Calendar.DAY_OF_MONTH);
399         selectedDate.set(Calendar.DAY_OF_MONTH, 1);
400         firstDay = selectedDate.get(Calendar.DAY_OF_WEEK);
401         selectedDate.set(Calendar.DAY_OF_MONTH, d);
402     }
403
404     /*
405      * This method is called whenever the month or year changes. It's job is to
406      * repaint the labels and determine whether any selection range limits have
407      * been reached.
408      */

409     private void _refresh() {
410         l_date.setText(datefmt.format(selectedDate.getTime()));
411
412         // l_year.setText(String.valueOf(selectedDate.get(Calendar.YEAR)));
413
l_month.setText(months[selectedDate.get(Calendar.MONTH)]);
414
415         _computeFirstDay();
416         clipMin = clipMax = clipAllMin = clipAllMax = false;
417
418         // b_lyear.setEnabled(true);
419
// b_ryear.setEnabled(true);
420
b_lmonth.setEnabled(true);
421         b_rmonth.setEnabled(true);
422
423         // Disable anything that would cause the date to go out of range. This
424
// logic is extremely sensitive so be very careful when making changes.
425
// Every condition test in here is necessary, so don't remove anything.
426
if (minDate != null) {
427             int y = selectedDate.get(Calendar.YEAR);
428             int y0 = minDate.get(Calendar.YEAR);
429             int m = selectedDate.get(Calendar.MONTH);
430             int m0 = minDate.get(Calendar.MONTH);
431
432             // b_lyear.setEnabled(y > y0);
433
if (y == y0) {
434                 b_lmonth.setEnabled(m > m0);
435
436                 if (m == m0) {
437                     clipMin = true;
438
439                     int d0 = minDate.get(Calendar.DATE);
440
441                     if (selectedDay < d0) {
442                         selectedDate.set(Calendar.DATE, selectedDay = d0);
443                     }
444
445                     // allow out-of-range selection
446
// selectedDate.set(Calendar.DATE, selectedDay);
447
}
448             }
449
450             clipAllMin = ((m < m0) || (y < y0));
451         }
452
453         if (maxDate != null) {
454             int y = selectedDate.get(Calendar.YEAR);
455             int y1 = maxDate.get(Calendar.YEAR);
456             int m = selectedDate.get(Calendar.MONTH);
457             int m1 = maxDate.get(Calendar.MONTH);
458
459             // b_ryear.setEnabled(y < y1);
460
if (y == y1) {
461                 b_rmonth.setEnabled(m < m1);
462
463                 if (m == m1) {
464                     clipMax = true;
465
466                     int d1 = maxDate.get(Calendar.DATE);
467
468                     if (selectedDay > d1) {
469                         selectedDate.set(Calendar.DATE, selectedDay = d1);
470                     }
471
472                     // allow out-of-range selection
473
// selectedDate.set(Calendar.DATE, selectedDay);
474
}
475             }
476
477             clipAllMax = ((m > m1) || (y > y1));
478         }
479
480         // repaint the calendar pane
481
calendarPane.repaint();
482     }
483
484     
485     private class CalendarPane extends JComponent JavaDoc {
486
487         private int dp = 0;
488
489         private int x0 = 0;
490
491         private int y0 = 0;
492
493         /** Construct a new <code>CalendarView</code>. */
494         CalendarPane() {
495             addMouseListener(new _MouseListener2());
496         }
497
498         /** Paint the component. */
499         public void paint(Graphics JavaDoc gc) {
500             gc.setFont(UIManager.getFont("Label.font"));
501
502             FontMetrics JavaDoc fm = gc.getFontMetrics();
503             Insets JavaDoc ins = getInsets();
504             int h = fm.getMaxAscent();
505
506             gc.setColor(Color.white);
507             gc.fillRect(0, 0, getSize().width, getSize().height);
508
509             // figure out how many blank spaces there are before first day of
510
// month,
511
// and calculate coordinates of first drawn cell
512
dp = ((firstDay - selectedDate.getFirstDayOfWeek() + 7) % 7);
513
514             int x = dp;
515             int y = 0;
516             y0 = ((getSize().height - getPreferredSize().height) / 2);
517
518             int yp = y0;
519             x0 = ((getSize().width - getPreferredSize().width) / 2);
520
521             int xp = x0;
522
523             // paint the border
524
paintBorder(gc);
525
526             // set the clip rect to exclude the border & insets
527
gc.setColor(Color.black);
528             gc.clipRect(ins.left, ins.top,
529                     (getSize().width - ins.left - ins.right), (getSize().height
530                             - ins.top - ins.bottom));
531             gc.translate(ins.left, ins.top);
532
533             // draw the weekday headings
534
for (int i = 0, ii = selectedDate.getFirstDayOfWeek() - 1; i < 7; i++) {
535                 gc
536                         .drawString(labels[ii], xp + 5 + (i * (cellSize + 2)),
537                                 yp + h);
538
539                 if (++ii == 7) {
540                     ii = 0;
541                 }
542             }
543
544             yp += 20;
545             xp += (dp * (cellSize + 2));
546
547             // find out how many days there are in the current month
548
int month = DateChooser.this.selectedDate.get(Calendar.MONTH);
549             int dmax = (isLeapYear(DateChooser.this.selectedDate
550                     .get(Calendar.YEAR)) ? daysInMonthLeap[month]
551                     : daysInMonth[month]);
552
553             // draw all the day cells
554
for (int d = 1; d <= dmax; d++) {
555                 // draw the outline of the cell
556
// gc.setColor(MetalLookAndFeel.getPrimaryControlShadow());
557
gc.setColor(Color.gray);
558
559                 // gc.draw3DRect(xp, yp, cellSize, cellSize, true);
560
// if the cell is selected, fill it with the highlight color
561
if (d == selectedDay) {
562                     gc.setColor(highlightColor);
563                     gc.fillRect(xp + 1, yp + 1, cellSize - 2, cellSize - 2);
564                 }
565
566                 // set the pen color depending on weekday or weekend, and paint
567
// the
568
// day number in the cell
569
if ((clipMin && (d < minDay)) || (clipMax && (d > maxDay))
570                         || clipAllMin || clipAllMax) {
571                     gc.setColor(disabledColor);
572                 } else {
573                     gc
574                             .setColor(((weekendCols[0] == x) || (weekendCols[1] == x)) ? weekendColor
575                                     : Color.black);
576                 }
577
578                 String JavaDoc ss = String.valueOf(d);
579                 int sw = fm.stringWidth(ss);
580
581                 if (d == selectedDay) {
582                     gc.setColor(UIManager.getColor("List.selectionForeground"));
583                 }
584
585                 gc.drawString(ss, xp - 3 + (cellSize - sw), yp + 3 + h);
586
587                 // advance to the next cell position
588
if (++x == 7) {
589                     x = 0;
590                     xp = x0;
591                     y++;
592                     yp += (cellSize + 2);
593                 } else {
594                     xp += (cellSize + 2);
595                 }
596             }
597         }
598
599         /* Get the preferred size of the component. */
600         public Dimension JavaDoc getPreferredSize() {
601             Insets JavaDoc ins = getInsets();
602
603             return (new Dimension JavaDoc(
604                     (((cellSize + 2) * 7) + ins.left + ins.right),
605                     (((cellSize + 2) * 6) + 20) + ins.top + ins.bottom));
606         }
607
608         /* Get the minimum size of the component. */
609         public Dimension JavaDoc getMinimumSize() {
610             return (getPreferredSize());
611         }
612
613         /* Figure out which day the mouse click is on. */
614         private int getDay(MouseEvent JavaDoc evt) {
615             Insets JavaDoc ins = getInsets();
616
617             int x = evt.getX() - ins.left - x0;
618             int y = evt.getY() - ins.top - 20 - y0;
619             int maxw = (cellSize + 2) * 7;
620             int maxh = (cellSize + 2) * 6;
621
622             // check if totally out of range.
623
if ((x < 0) || (x > maxw) || (y < 0) || (y > maxh)) {
624                 return (-1);
625             }
626
627             y /= (cellSize + 2);
628             x /= (cellSize + 2);
629
630             int d = ((7 * y) + x) - (dp - 1);
631
632             if ((d < 1) || (d > selectedDate.getMaximum(Calendar.DAY_OF_MONTH))) {
633                 return (-1);
634             }
635
636             if ((clipMin && (d < minDay)) || (clipMax && (d > maxDay))) {
637                 return (-1);
638             }
639
640             return (d);
641         }
642
643         /* mouse listener */
644         private class _MouseListener2 extends MouseAdapter JavaDoc {
645             public void mouseReleased(MouseEvent JavaDoc evt) {
646                 int d = getDay(evt);
647
648                 if (d < 0) {
649                     return;
650                 }
651
652                 selectedDay = d;
653
654                 selectedDate.set(Calendar.DAY_OF_MONTH, selectedDay);
655                 _refresh();
656
657                 // asupport.fireActionEvent(DATE_CHANGE_CMD);
658
}
659         }
660     }
661 }
662
Popular Tags