1/*
2 *******************************************************************************
3 * Copyright (C) 1997-2007, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7
8package com.ibm.icu.dev.demo.calendar;
9
10import java.awt.Canvas;
11import java.awt.Color;
12import java.awt.Dimension;
13import java.awt.FontMetrics;
14import java.awt.Graphics;
15import java.awt.Point;
16import java.util.Date;
17import java.util.Locale;
18
19import com.ibm.icu.dev.demo.impl.DemoUtility;
20import com.ibm.icu.text.DateFormatSymbols;
21import com.ibm.icu.util.Calendar;
22import com.ibm.icu.util.SimpleTimeZone;
23
24class CalendarPanel extends Canvas {
25
26    /**
27     * For serialization
28     */
29    private static final long serialVersionUID = 625400018027387141L;
30
31    public CalendarPanel( Locale locale ) {
32        setLocale(locale);
33    }
34
35    public void setLocale(Locale locale) {
36        if (fDisplayLocale == null || !fDisplayLocale.equals(locale)) {
37            fDisplayLocale = locale;
38            dirty = true;
39
40            for (int i = 0; i < fCalendar.length; i++) {
41                if (fCalendar[i] != null) {
42                    fSymbols[i] = new DateFormatSymbols(fCalendar[i],
43                                                        fDisplayLocale);
44                }
45            }
46            String lang = locale.getLanguage();
47            leftToRight = !(lang.equals("iw") || lang.equals("ar"));
48
49            repaint();
50        }
51    }
52
53    public void setDate(Date date) {
54        fStartOfMonth = date;
55        dirty = true;
56        repaint();
57    }
58
59    public void add(int field, int delta)
60    {
61        synchronized(fCalendar) {
62            fCalendar[0].setTime(fStartOfMonth);
63            fCalendar[0].add(field, delta);
64            fStartOfMonth = fCalendar[0].getTime();
65        }
66        dirty = true;
67        repaint();
68    }
69
70    public void setColor(int index, Color c) {
71        fColor[index] = c;
72        repaint();
73    }
74
75    public void setCalendar(int index, Calendar c) {
76        Date date = (fCalendar[index] == null) ? new Date()
77                                               : fCalendar[index].getTime();
78
79        fCalendar[index] = c;
80        fCalendar[index].setTime(date);
81
82        fSymbols[index] = new DateFormatSymbols(c, fDisplayLocale);
83        dirty = true;
84        repaint();
85    }
86
87    public Calendar getCalendar(int index) {
88        return fCalendar[index];
89    }
90
91    public Locale getDisplayLocale() {
92        return fDisplayLocale;
93    }
94
95    public Date firstOfMonth() {
96        return fStartOfMonth;
97    }
98
99    private Date startOfMonth(Date dateInMonth)
100    {
101        synchronized(fCalendar) {
102            fCalendar[0].setTime(dateInMonth);
103
104            int era = fCalendar[0].get(Calendar.ERA);
105            int year = fCalendar[0].get(Calendar.YEAR);
106            int month = fCalendar[0].get(Calendar.MONTH);
107
108            fCalendar[0].clear();
109            fCalendar[0].set(Calendar.ERA, era);
110            fCalendar[0].set(Calendar.YEAR, year);
111            fCalendar[0].set(Calendar.MONTH, month);
112            fCalendar[0].set(Calendar.DATE, 1);
113
114            return fCalendar[0].getTime();
115        }
116    }
117
118    private void calculate()
119    {
120        //
121        // As a workaround for JDK 1.1.3 and below, where Calendars and time
122        // zones are a bit goofy, always set my calendar's time zone to UTC.
123        // You would think I would want to do this in the "set" function above,
124        // but if I do that, the program hangs when this class is loaded,
125        // perhaps due to some sort of static initialization ordering problem.
126        // So I do it here instead.
127        //
128        fCalendar[0].setTimeZone(new SimpleTimeZone(0, "UTC"));
129
130        Calendar c = (Calendar)fCalendar[0].clone(); // Temporary copy
131
132        fStartOfMonth = startOfMonth(fStartOfMonth);
133
134        // Stash away a few useful constants for this calendar and display
135        minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
136        daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
137
138        firstDayOfWeek = Calendar.getInstance(fDisplayLocale).getFirstDayOfWeek();
139
140        // Stash away a Date for the start of this month
141
142        // Find the day of week of the first day in this month
143        c.setTime(fStartOfMonth);
144        firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
145        int firstWeek = c.get(Calendar.WEEK_OF_MONTH);
146
147        // Now find the # of days in the month
148        c.roll(Calendar.DATE, false);
149        daysInMonth = c.get(Calendar.DATE);
150
151        // Finally, find the end of the month, i.e. the start of the next one
152        c.roll(Calendar.DATE, true);
153        c.add(Calendar.MONTH, 1);
154        c.getTime();        // JDK 1.1.2 bug workaround
155        c.add(Calendar.SECOND, -1);
156        Date endOfMonth = c.getTime();
157        if(endOfMonth==null){
158         //do nothing
159        }
160        endOfMonth = null;
161        int lastWeek = c.get(Calendar.WEEK_OF_MONTH);
162
163        // Calculate the number of full or partial weeks in this month.
164        numWeeks = lastWeek - firstWeek + 1;
165
166        dirty = false;
167    }
168
169    static final int XINSET = 4;
170    static final int YINSET = 2;
171
172    /*
173     * Convert from the day number within a month (1-based)
174     * to the cell coordinates on the calendar (0-based)
175     */
176    private void dateToCell(int date, Point pos)
177    {
178        int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
179        if (firstDayInMonth < firstDayOfWeek) {
180            cell += daysInWeek;
181        }
182
183        pos.x = cell % daysInWeek;
184        pos.y = cell / daysInWeek;
185    }
186    //private Point dateToCell(int date) {
187    //    Point p = new Point(0,0);
188    //    dateToCell(date, p);
189    //    return p;
190    //}
191
192    public void paint(Graphics g) {
193
194        if (dirty) {
195            calculate();
196        }
197
198        Point cellPos = new Point(0,0);     // Temporary variable
199        Dimension d = this.getSize();
200
201        g.setColor(Color.lightGray);
202        g.fillRect(0,0,d.width,d.height);
203
204        // Draw the day names at the top
205        g.setColor(Color.black);
206        g.setFont(DemoUtility.labelFont);
207        FontMetrics fm = g.getFontMetrics();
208        int labelHeight = fm.getHeight() + YINSET * 2;
209
210        int v = fm.getAscent() + YINSET;
211        for (int i = 0; i < daysInWeek; i++) {
212            int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek + 1;
213            String dayName = fSymbols[0].getWeekdays()[dayNum];
214
215
216            double h;
217            if (leftToRight) {
218                h = d.width*(i + 0.5) / daysInWeek;
219            } else {
220                h = d.width*(daysInWeek - i - 0.5) / daysInWeek;
221            }
222            h -= fm.stringWidth(dayName) / 2;
223
224            g.drawString(dayName, (int)h, v);
225        }
226
227        double cellHeight = (d.height - labelHeight - 1) / numWeeks;
228        double cellWidth = (double)(d.width - 1) / daysInWeek;
229
230        // Draw a white background in the part of the calendar
231        // that displays this month.
232        // First figure out how much of the first week should be shaded.
233        {
234            g.setColor(Color.white);
235            dateToCell(1, cellPos);
236            int width = (int)(cellPos.x*cellWidth);  // Width of unshaded area
237
238            if (leftToRight) {
239                g.fillRect((int)(width), labelHeight ,
240                           d.width - width, (int)cellHeight);
241            } else {
242                g.fillRect(0, labelHeight ,
243                           d.width - width, (int)cellHeight);
244            }
245
246            // All of the intermediate weeks get shaded completely
247            g.fillRect(0, (int)(labelHeight + cellHeight),
248                        d.width, (int)(cellHeight * (numWeeks - 2)));
249
250            // Now figure out the last week.
251            dateToCell(daysInMonth, cellPos);
252            width = (int)((cellPos.x+1)*cellWidth);  // Width of shaded area
253
254            if (leftToRight) {
255                g.fillRect(0, (int)(labelHeight + (numWeeks-1) * cellHeight),
256                           width, (int)cellHeight);
257            } else {
258                g.fillRect(d.width - width, (int)(labelHeight + (numWeeks-1) * cellHeight),
259                           width, (int)cellHeight);
260            }
261
262        }
263        // Draw the X/Y grid lines
264        g.setColor(Color.black);
265        for (int i = 0; i <= numWeeks; i++) {
266            int y = (int)(labelHeight + i * cellHeight);
267            g.drawLine(0, y, d.width - 1, y);
268        }
269        for (int i = 0; i <= daysInWeek; i++) {
270            int x = (int)(i * cellWidth);
271            g.drawLine(x, labelHeight, x, d.height - 1);
272        }
273
274        // Now loop through all of the days in the month, figure out where
275        // they go in the grid, and draw the day # for each one
276
277        // Figure out the date of the first cell in the calendar display
278        int cell = (1 + firstDayInMonth - firstDayOfWeek - minDay);
279        if (firstDayInMonth < firstDayOfWeek) {
280            cell += daysInWeek;
281        }
282
283        Calendar c = (Calendar)fCalendar[0].clone();
284        c.setTime(fStartOfMonth);
285        c.add(Calendar.DATE, -cell);
286
287        StringBuffer buffer = new StringBuffer();
288
289        for (int row = 0; row < numWeeks; row++) {
290            for (int col = 0; col < daysInWeek; col++) {
291
292                g.setFont(DemoUtility.numberFont);
293                g.setColor(Color.black);
294                fm = g.getFontMetrics();
295
296                int cellx;
297                if (leftToRight) {
298                    cellx = (int)((col) * cellWidth);
299                } else {
300                    cellx = (int)((daysInWeek - col - 1) * cellWidth);
301                }
302
303                int celly = (int)(row * cellHeight + labelHeight);
304
305                for (int i = 0; i < 2; i++) {
306                    fCalendar[i].setTime(c.getTime());
307
308                    int date = fCalendar[i].get(Calendar.DATE);
309                    buffer.setLength(0);
310                    buffer.append(date);
311                    String dayNum = buffer.toString();
312
313                    int x;
314
315                    if (leftToRight) {
316                        x = cellx + (int)cellWidth - XINSET - fm.stringWidth(dayNum);
317                    } else {
318                        x = cellx + XINSET;
319                    }
320                    int y = celly + + fm.getAscent() + YINSET + i * fm.getHeight();
321
322                    if (fColor[i] != null) {
323                        g.setColor(fColor[i]);
324                    }
325                    g.drawString(dayNum, x, y);
326
327                    if (date == 1 || row == 0 && col == 0) {
328                        g.setFont(DemoUtility.numberFont);
329                        String month = fSymbols[i].getMonths()[
330                                            fCalendar[i].get(Calendar.MONTH)];
331
332                        if (leftToRight) {
333                            x = cellx + XINSET;
334                        } else {
335                            x = cellx + (int)cellWidth - XINSET - fm.stringWidth(month);
336                        }
337                        g.drawString(month, x, y);
338                    }
339                }
340
341                c.add(Calendar.DATE, 1);
342            }
343        }
344    }
345
346    // Important state variables
347    private Calendar[]          fCalendar = new Calendar[4];
348    private Color[]             fColor = new Color[4];
349
350    private Locale              fDisplayLocale;
351    private DateFormatSymbols[] fSymbols = new DateFormatSymbols[4];
352
353    private Date                fStartOfMonth = new Date();     // 00:00:00 on first day of month
354
355    // Cached calculations to make drawing faster.
356    private transient int       minDay;           // Minimum legal day #
357    private transient int       daysInWeek;       // # of days in a week
358    private transient int       firstDayOfWeek;   // First day to display in week
359    private transient int       numWeeks;         // # full or partial weeks in month
360    private transient int       daysInMonth;      // # days in this month
361    private transient int       firstDayInMonth;  // Day of week of first day in month
362    private transient boolean   leftToRight;
363
364    private transient boolean dirty = true;
365}
366