001: package snow.utils.gui;
002:
003: import javax.swing.event.*;
004: import snow.utils.DateUtils;
005: import java.awt.*;
006: import java.awt.event.*;
007: import javax.swing.*;
008: import javax.swing.border.*;
009: import java.util.*;
010: import java.text.*;
011:
012: /** Dialog panel to select a date.
013: */
014: public class CalendarDateSelectorPanel extends JPanel {
015: protected Calendar selectedDate = Calendar.getInstance();
016: // limits the UI
017: long minValidDate = 0;
018: long maxValidDate = Long.MAX_VALUE;
019: // no tooltip if null.
020: String tooltipBelowMin = "too old date, not allowed";
021: String tooltipAboveMax = "not allowed"; // maybe something like "dates in the future are not allowed"
022:
023: final int fontSize = UIManager.getFont("Label.font").getSize();
024: final protected DaysPanel daysPanel;
025: final protected MonthPanel monthPanel;
026: final YearPanel yearPanel;
027:
028: protected Font boldFont;
029: protected Font smallFont;
030:
031: // if no null, closed when a day is clicked
032: protected JComponent componentToCloseWhenDayClicked = null;
033:
034: /**
035: * Uses DateUtils.getMidnightTodayMorning for today
036: */
037: public CalendarDateSelectorPanel() {
038: this (DateUtils.getMidnightTodayMorning());
039: }
040:
041: /**
042: */
043: public CalendarDateSelectorPanel(long time) {
044: super (new BorderLayout());
045:
046: smallFont = new Font("Dialog", Font.PLAIN, Math.max(
047: fontSize - 2, 9));
048: boldFont = new Font("Dialog", Font.BOLD, Math.min(fontSize + 2,
049: 12));
050:
051: selectedDate.setTimeInMillis(time);
052: selectedDate.setLenient(true); // 32 = 1 of next month, ...
053:
054: monthPanel = new MonthPanel();
055: daysPanel = new DaysPanel();
056: yearPanel = new YearPanel();
057:
058: JPanel contentPanel = new JPanel(new BorderLayout());
059: add(contentPanel, BorderLayout.CENTER);
060: contentPanel.add(monthPanel, BorderLayout.NORTH);
061: contentPanel.add(daysPanel, BorderLayout.CENTER);
062: contentPanel.add(yearPanel, BorderLayout.SOUTH);
063:
064: updateViews();
065:
066: } // Constructor
067:
068: /** useful with JPopupMenu
069: */
070: public void setComponentToCloseWhenDayClicked(JComponent c) {
071: componentToCloseWhenDayClicked = c;
072: }
073:
074: /** limits the UI.
075: */
076: public void setValidDateRange(long min, long max) {
077: if (min >= 0) {
078: minValidDate = min;
079: } else {
080: minValidDate = 0;
081: }
082:
083: if (max > 0 && max > min) {
084: maxValidDate = max;
085: } else {
086: maxValidDate = Long.MAX_VALUE;
087: }
088:
089: updateViews();
090: }
091:
092: private void updateViews() {
093: monthPanel.updateLabel();
094: yearPanel.updateLabel();
095: daysPanel.update();
096: }
097:
098: public Date getSelectedDate() {
099: return this .selectedDate.getTime();
100: }
101:
102: /** uses the selectedDate calendar but don't modifies it !!
103: */
104: private long getTimeForDay(int day) {
105: long back = selectedDate.getTimeInMillis();
106: selectedDate.set(Calendar.DAY_OF_MONTH, day);
107:
108: long res = selectedDate.getTimeInMillis();
109: // don't modifies it !!
110: selectedDate.setTimeInMillis(back);
111: return res;
112: }
113:
114: private void showPopup(Component comp, MouseEvent me) {
115: JPopupMenu pop = new JPopupMenu();
116: final long today = DateUtils.getMidnightTodayMorning();
117: if (today >= minValidDate && today <= maxValidDate) {
118: JMenuItem miToday = new JMenuItem("Set today");
119: pop.add(miToday);
120: miToday.addActionListener(new ActionListener() {
121: public void actionPerformed(ActionEvent ae) {
122: selectedDate.setTimeInMillis(today);
123: updateViews();
124: }
125: });
126: }
127: pop.show(comp, me.getX(), me.getY());
128: }
129:
130: class DaysPanel extends JPanel {
131: String[] days = new String[] { "Su", "Mo", "Tu", "We", "Th",
132: "Fr", "Sa", "Su" };
133:
134: public DaysPanel() {
135: super (new GridLayout(7, 7));
136:
137: addMouseListener(new MouseAdapter() {
138: @Override
139: public void mousePressed(MouseEvent me) {
140: if (me.isPopupTrigger())
141: showPopup(DaysPanel.this , me);
142: }
143:
144: @Override
145: public void mouseReleased(MouseEvent me) {
146: if (me.isPopupTrigger())
147: showPopup(DaysPanel.this , me);
148: }
149: });
150:
151: }
152:
153: private void setColor(int dayInWeek, JComponent c) {
154: c.setOpaque(true);
155: if (dayInWeek == 0 || dayInWeek == 6) {
156: c.setBackground(new Color(250, 220, 220, 255));
157: } else {
158: c.setBackground(new Color(196, 250, 191, 255));
159: }
160: }
161:
162: public void update() {
163: this .removeAll();
164: for (int i = 0; i < 7; i++) {
165: JLabel dayLabel = new JLabel(days[i]);
166: add(dayLabel);
167: dayLabel.setFont(smallFont);
168: dayLabel.setHorizontalAlignment(JLabel.CENTER);
169: dayLabel.setBorder(null);
170: }
171:
172: // compute the first day of the month
173: int selectedDayOfWeek = selectedDate
174: .get(Calendar.DAY_OF_WEEK) - 1; // 0=sunday
175: int selectedDay = selectedDate.get(Calendar.DAY_OF_MONTH); //1,2,...,31
176:
177: //System.out.println("day="+selectedDay+" in week="+selectedDayOfWeek);
178:
179: int weekDayOfMonthFirst = (7 + selectedDayOfWeek - (selectedDay - 1) % 7) % 7; // <7
180:
181: int numberOfDaysInMonth = selectedDate
182: .getActualMaximum(Calendar.DAY_OF_MONTH);
183: //System.out.println(""+weekDayOfMonthFirst);
184:
185: int dayInWeek = 0;
186: for (int i = 0; i < weekDayOfMonthFirst; i++) {
187: JLabel lab = new JLabel("");
188: setColor(dayInWeek % 7, lab);
189: dayInWeek++;
190: add(lab);
191: }
192:
193: for (int i = 1; i <= numberOfDaysInMonth; i++) {
194: final int ii = i;
195: JButton jb = new JButton("" + (i));
196: jb.addActionListener(new ActionListener() {
197: public void actionPerformed(ActionEvent e) {
198: selectedDate.set(Calendar.DAY_OF_MONTH, ii);
199: update();
200:
201: if (componentToCloseWhenDayClicked != null) {
202: componentToCloseWhenDayClicked
203: .setVisible(false);
204: }
205: }
206: });
207:
208: long td = getTimeForDay(ii);
209: if (td < minValidDate) {
210: jb.setEnabled(false);
211: if (tooltipBelowMin != null) {
212: jb.setToolTipText(tooltipBelowMin);
213: }
214: } else if (td > maxValidDate) {
215: jb.setEnabled(false);
216: if (tooltipAboveMax != null) {
217: jb.setToolTipText(tooltipAboveMax);
218: }
219: }
220:
221: if (jb.getToolTipText() == null) {
222: long dt = td - DateUtils.getMidnightTodayMorning();
223: if (dt >= 0) {
224: if (dt < 1000L * 24 * 3600) {
225: jb.setToolTipText("Today");
226: } else if (dt < 1000L * 24 * 3600 * 2) {
227: jb.setToolTipText("Tomorrow");
228: } else {
229: jb.setToolTipText("In "
230: + DateUtils
231: .formatTimeDifference(dt));
232: }
233: } else {
234: dt = -dt;
235: if (dt < 1000L * 24 * 3600 * 2) {
236: jb.setToolTipText("Yesterday");
237: } else {
238: jb.setToolTipText("For "
239: + DateUtils
240: .formatTimeDifference(dt));
241: }
242:
243: }
244: }
245:
246: jb.setBorder(null);
247: jb.setFocusPainted(false);
248: if (i == selectedDay) {
249: jb.setFont(boldFont);
250: jb.setBorder(new LineBorder(Color.black, 2));
251: } else {
252: jb.setFont(smallFont);
253: }
254: add(jb);
255: setColor(dayInWeek % 7, jb);
256: dayInWeek++;
257:
258: if (DateUtils.isToday(td)) {
259: jb.setBackground(Color.lightGray);
260: jb.setToolTipText("Today");
261: }
262: }
263:
264: int remains = 42 - numberOfDaysInMonth
265: - weekDayOfMonthFirst;
266: for (int i = 0; i < remains; i++) {
267: JLabel lab = new JLabel("");
268: setColor(dayInWeek % 7, lab);
269: dayInWeek++;
270: add(lab);
271: }
272: }
273: }
274:
275: class MonthPanel extends JPanel {
276: JButton shiftUpMonthBT = new JButton(" > ");
277: JButton shiftDownMonthBT = new JButton(" < ");
278: JLabel monthLabel = new JLabel();
279: SimpleDateFormat monthFormat = new SimpleDateFormat(
280: "MMMMMMMMMM");
281:
282: public MonthPanel() {
283: super (new BorderLayout());
284: monthLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
285: monthLabel.setHorizontalAlignment(JLabel.CENTER);
286: monthLabel.setFont(boldFont);
287:
288: add(shiftDownMonthBT, BorderLayout.WEST);
289: add(monthLabel, BorderLayout.CENTER);
290: add(shiftUpMonthBT, BorderLayout.EAST);
291:
292: shiftUpMonthBT.setBorder(null);
293: shiftDownMonthBT.setBorder(null);
294:
295: shiftUpMonthBT.addActionListener(new ActionListener() {
296: public void actionPerformed(ActionEvent e) {
297: selectedDate.set(Calendar.MONTH, selectedDate
298: .get(Calendar.MONTH) + 1);
299:
300: if (selectedDate.getTimeInMillis() > maxValidDate) {
301: // refuse !
302: selectedDate.setTimeInMillis(maxValidDate);
303:
304: }
305:
306: updateLabel();
307: yearPanel.updateLabel();
308: daysPanel.update();
309: }
310: });
311:
312: shiftDownMonthBT.addActionListener(new ActionListener() {
313: public void actionPerformed(ActionEvent e) {
314: selectedDate.set(Calendar.MONTH, selectedDate
315: .get(Calendar.MONTH) - 1);
316:
317: if (selectedDate.getTimeInMillis() < minValidDate) {
318: // refuse !
319: selectedDate.setTimeInMillis(minValidDate);
320:
321: }
322:
323: updateLabel();
324: yearPanel.updateLabel();
325: daysPanel.update();
326: }
327: });
328:
329: //updateLabel();
330: }
331:
332: public void updateLabel() {
333: monthLabel.setText(monthFormat.format(selectedDate
334: .getTime()));
335: }
336: }
337:
338: class YearPanel extends JPanel {
339: JButton shiftUpMonthBT = new JButton(" > ");
340: JButton shiftDownMonthBT = new JButton(" < ");
341: JLabel monthLabel = new JLabel();
342: SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
343:
344: public YearPanel() {
345: super (new BorderLayout());
346: monthLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
347: monthLabel.setHorizontalAlignment(JLabel.CENTER);
348: monthLabel.setFont(boldFont);
349:
350: add(shiftDownMonthBT, BorderLayout.WEST);
351: add(monthLabel, BorderLayout.CENTER);
352: add(shiftUpMonthBT, BorderLayout.EAST);
353:
354: shiftUpMonthBT.setBorder(null);
355: shiftDownMonthBT.setBorder(null);
356:
357: shiftUpMonthBT.addActionListener(new ActionListener() {
358: public void actionPerformed(ActionEvent e) {
359: selectedDate.set(Calendar.YEAR, (selectedDate
360: .get(Calendar.YEAR) + 1));
361:
362: if (selectedDate.getTimeInMillis() > maxValidDate) {
363: // refuse !
364: selectedDate.setTimeInMillis(maxValidDate);
365:
366: }
367:
368: updateLabel();
369: daysPanel.update();
370: }
371: });
372:
373: shiftDownMonthBT.addActionListener(new ActionListener() {
374: public void actionPerformed(ActionEvent e) {
375: selectedDate.set(Calendar.YEAR, (selectedDate
376: .get(Calendar.YEAR) - 1));
377:
378: if (selectedDate.getTimeInMillis() < minValidDate) {
379: // refuse !
380: selectedDate.setTimeInMillis(minValidDate);
381:
382: }
383:
384: updateLabel();
385: daysPanel.update();
386: }
387: });
388:
389: //updateLabel();
390: }
391:
392: public void updateLabel() {
393: monthLabel.setText(yearFormat
394: .format(selectedDate.getTime()));
395: }
396: }
397:
398: /** small helper to allow embedding our date selector in a JPopup
399: */
400: public interface DateSelectorListener {
401: public void newDateSelected(Date newDate);
402: }
403:
404: /** Selects a date in a JPopup. Good UI behaviour: cancel if pressed otherwise, ESC pressed or else.
405:
406: */
407: public static void selectDate(long time,
408: JComponent compForLocation,
409: final DateSelectorListener dateSelectorListener) {
410: final CalendarDateSelectorPanel cdp = new CalendarDateSelectorPanel(
411: DateUtils.getStartOfDayForTime(time));
412: JPopupMenu pd = new JPopupMenu();
413: cdp.setComponentToCloseWhenDayClicked(pd);
414: pd.add(cdp);
415:
416: pd.addPopupMenuListener(new PopupMenuListener() {
417: public void popupMenuCanceled(PopupMenuEvent e) {
418: }
419:
420: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
421: dateSelectorListener.newDateSelected(cdp
422: .getSelectedDate());
423: }
424:
425: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
426:
427: }
428: });
429:
430: pd.show(compForLocation, 0, 0);
431: }
432:
433: public static void main(String[] a) {
434: //testPanel();
435: testPopup();
436: }
437:
438: private static void testPopup() {
439: Locale.setDefault(Locale.ENGLISH);
440: JFrame jf = new JFrame("test");
441: jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
442:
443: final JButton pop = new JButton("pop");
444: pop.addActionListener(new ActionListener() {
445: public void actionPerformed(ActionEvent ae) {
446: selectDate(DateUtils.getMidnightTodayMorning(), pop,
447: new DateSelectorListener() {
448: public void newDateSelected(Date newDate) {
449: System.out.println("selected: "
450: + newDate);
451: }
452: });
453: }
454: });
455:
456: jf.add(pop);
457: //jf.pack();
458: jf.setSize(180, 180);
459: jf.setLocation(400, 300);
460: jf.setVisible(true);
461:
462: }
463:
464: private static void testPanel() {
465: Locale.setDefault(Locale.ENGLISH);
466: JFrame jf = new JFrame("test");
467: jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
468: CalendarDateSelectorPanel dv = new CalendarDateSelectorPanel();
469: dv.setValidDateRange(System.currentTimeMillis() - 1000L * 3600
470: * 24 * 365 * 2, System.currentTimeMillis() + 1000L
471: * 3600 * 24 * 2);
472:
473: jf.add(dv);
474: //jf.pack();
475: jf.setSize(180, 180);
476: jf.setLocation(400, 300);
477: jf.setVisible(true);
478: }
479:
480: } // CalendarDateSelectorPanel
|