001: /*
002: *******************************************************************************
003: * Copyright (C) 1997-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: package com.ibm.icu.dev.demo.calendar;
009:
010: import java.awt.Canvas;
011: import java.awt.Color;
012: import java.awt.Dimension;
013: import java.awt.FontMetrics;
014: import java.awt.Graphics;
015: import java.awt.Point;
016: import java.util.Date;
017: import java.util.Locale;
018:
019: import com.ibm.icu.dev.demo.impl.DemoUtility;
020: import com.ibm.icu.text.DateFormatSymbols;
021: import com.ibm.icu.util.Calendar;
022: import com.ibm.icu.util.SimpleTimeZone;
023:
024: class CalendarPanel extends Canvas {
025:
026: public CalendarPanel(Locale locale) {
027: setLocale(locale);
028: }
029:
030: public void setLocale(Locale locale) {
031: if (fDisplayLocale == null || !fDisplayLocale.equals(locale)) {
032: fDisplayLocale = locale;
033: dirty = true;
034:
035: for (int i = 0; i < fCalendar.length; i++) {
036: if (fCalendar[i] != null) {
037: fSymbols[i] = new DateFormatSymbols(fCalendar[i],
038: fDisplayLocale);
039: }
040: }
041: String lang = locale.getLanguage();
042: leftToRight = !(lang.equals("iw") || lang.equals("ar"));
043:
044: repaint();
045: }
046: }
047:
048: public void setDate(Date date) {
049: fStartOfMonth = date;
050: dirty = true;
051: repaint();
052: }
053:
054: public void add(int field, int delta) {
055: synchronized (fCalendar) {
056: fCalendar[0].setTime(fStartOfMonth);
057: fCalendar[0].add(field, delta);
058: fStartOfMonth = fCalendar[0].getTime();
059: }
060: dirty = true;
061: repaint();
062: }
063:
064: public void setColor(int index, Color c) {
065: fColor[index] = c;
066: repaint();
067: }
068:
069: public void setCalendar(int index, Calendar c) {
070: Date date = (fCalendar[index] == null) ? new Date()
071: : fCalendar[index].getTime();
072:
073: fCalendar[index] = c;
074: fCalendar[index].setTime(date);
075:
076: fSymbols[index] = new DateFormatSymbols(c, fDisplayLocale);
077: dirty = true;
078: repaint();
079: }
080:
081: public Calendar getCalendar(int index) {
082: return fCalendar[index];
083: }
084:
085: public Locale getDisplayLocale() {
086: return fDisplayLocale;
087: }
088:
089: public Date firstOfMonth() {
090: return fStartOfMonth;
091: }
092:
093: private Date startOfMonth(Date dateInMonth) {
094: synchronized (fCalendar) {
095: fCalendar[0].setTime(dateInMonth);
096:
097: int era = fCalendar[0].get(Calendar.ERA);
098: int year = fCalendar[0].get(Calendar.YEAR);
099: int month = fCalendar[0].get(Calendar.MONTH);
100:
101: fCalendar[0].clear();
102: fCalendar[0].set(Calendar.ERA, era);
103: fCalendar[0].set(Calendar.YEAR, year);
104: fCalendar[0].set(Calendar.MONTH, month);
105: fCalendar[0].set(Calendar.DATE, 1);
106:
107: return fCalendar[0].getTime();
108: }
109: }
110:
111: private void calculate() {
112: //
113: // As a workaround for JDK 1.1.3 and below, where Calendars and time
114: // zones are a bit goofy, always set my calendar's time zone to UTC.
115: // You would think I would want to do this in the "set" function above,
116: // but if I do that, the program hangs when this class is loaded,
117: // perhaps due to some sort of static initialization ordering problem.
118: // So I do it here instead.
119: //
120: fCalendar[0].setTimeZone(new SimpleTimeZone(0, "UTC"));
121:
122: Calendar c = (Calendar) fCalendar[0].clone(); // Temporary copy
123:
124: fStartOfMonth = startOfMonth(fStartOfMonth);
125:
126: // Stash away a few useful constants for this calendar and display
127: minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
128: daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
129:
130: firstDayOfWeek = Calendar.getInstance(fDisplayLocale)
131: .getFirstDayOfWeek();
132:
133: // Stash away a Date for the start of this month
134:
135: // Find the day of week of the first day in this month
136: c.setTime(fStartOfMonth);
137: firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
138: int firstWeek = c.get(Calendar.WEEK_OF_MONTH);
139:
140: // Now find the # of days in the month
141: c.roll(Calendar.DATE, false);
142: daysInMonth = c.get(Calendar.DATE);
143:
144: // Finally, find the end of the month, i.e. the start of the next one
145: c.roll(Calendar.DATE, true);
146: c.add(Calendar.MONTH, 1);
147: c.getTime(); // JDK 1.1.2 bug workaround
148: c.add(Calendar.SECOND, -1);
149: Date endOfMonth = c.getTime();
150: if (endOfMonth == null) {
151: //do nothing
152: }
153: endOfMonth = null;
154: int lastWeek = c.get(Calendar.WEEK_OF_MONTH);
155:
156: // Calculate the number of full or partial weeks in this month.
157: numWeeks = lastWeek - firstWeek + 1;
158:
159: dirty = false;
160: }
161:
162: static final int XINSET = 4;
163: static final int YINSET = 2;
164:
165: /*
166: * Convert from the day number within a month (1-based)
167: * to the cell coordinates on the calendar (0-based)
168: */
169: private void dateToCell(int date, Point pos) {
170: int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
171: if (firstDayInMonth < firstDayOfWeek) {
172: cell += daysInWeek;
173: }
174:
175: pos.x = cell % daysInWeek;
176: pos.y = cell / daysInWeek;
177: }
178:
179: //private Point dateToCell(int date) {
180: // Point p = new Point(0,0);
181: // dateToCell(date, p);
182: // return p;
183: //}
184:
185: public void paint(Graphics g) {
186:
187: if (dirty) {
188: calculate();
189: }
190:
191: Point cellPos = new Point(0, 0); // Temporary variable
192: Dimension d = this .getSize();
193:
194: g.setColor(Color.lightGray);
195: g.fillRect(0, 0, d.width, d.height);
196:
197: // Draw the day names at the top
198: g.setColor(Color.black);
199: g.setFont(DemoUtility.labelFont);
200: FontMetrics fm = g.getFontMetrics();
201: int labelHeight = fm.getHeight() + YINSET * 2;
202:
203: int v = fm.getAscent() + YINSET;
204: for (int i = 0; i < daysInWeek; i++) {
205: int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek
206: + 1;
207: String dayName = fSymbols[0].getWeekdays()[dayNum];
208:
209: double h;
210: if (leftToRight) {
211: h = d.width * (i + 0.5) / daysInWeek;
212: } else {
213: h = d.width * (daysInWeek - i - 0.5) / daysInWeek;
214: }
215: h -= fm.stringWidth(dayName) / 2;
216:
217: g.drawString(dayName, (int) h, v);
218: }
219:
220: double cellHeight = (d.height - labelHeight - 1) / numWeeks;
221: double cellWidth = (double) (d.width - 1) / daysInWeek;
222:
223: // Draw a white background in the part of the calendar
224: // that displays this month.
225: // First figure out how much of the first week should be shaded.
226: {
227: g.setColor(Color.white);
228: dateToCell(1, cellPos);
229: int width = (int) (cellPos.x * cellWidth); // Width of unshaded area
230:
231: if (leftToRight) {
232: g.fillRect((int) (width), labelHeight, d.width - width,
233: (int) cellHeight);
234: } else {
235: g.fillRect(0, labelHeight, d.width - width,
236: (int) cellHeight);
237: }
238:
239: // All of the intermediate weeks get shaded completely
240: g.fillRect(0, (int) (labelHeight + cellHeight), d.width,
241: (int) (cellHeight * (numWeeks - 2)));
242:
243: // Now figure out the last week.
244: dateToCell(daysInMonth, cellPos);
245: width = (int) ((cellPos.x + 1) * cellWidth); // Width of shaded area
246:
247: if (leftToRight) {
248: g.fillRect(0, (int) (labelHeight + (numWeeks - 1)
249: * cellHeight), width, (int) cellHeight);
250: } else {
251: g.fillRect(d.width - width,
252: (int) (labelHeight + (numWeeks - 1)
253: * cellHeight), width, (int) cellHeight);
254: }
255:
256: }
257: // Draw the X/Y grid lines
258: g.setColor(Color.black);
259: for (int i = 0; i <= numWeeks; i++) {
260: int y = (int) (labelHeight + i * cellHeight);
261: g.drawLine(0, y, d.width - 1, y);
262: }
263: for (int i = 0; i <= daysInWeek; i++) {
264: int x = (int) (i * cellWidth);
265: g.drawLine(x, labelHeight, x, d.height - 1);
266: }
267:
268: // Now loop through all of the days in the month, figure out where
269: // they go in the grid, and draw the day # for each one
270:
271: // Figure out the date of the first cell in the calendar display
272: int cell = (1 + firstDayInMonth - firstDayOfWeek - minDay);
273: if (firstDayInMonth < firstDayOfWeek) {
274: cell += daysInWeek;
275: }
276:
277: Calendar c = (Calendar) fCalendar[0].clone();
278: c.setTime(fStartOfMonth);
279: c.add(Calendar.DATE, -cell);
280:
281: StringBuffer buffer = new StringBuffer();
282:
283: for (int row = 0; row < numWeeks; row++) {
284: for (int col = 0; col < daysInWeek; col++) {
285:
286: g.setFont(DemoUtility.numberFont);
287: g.setColor(Color.black);
288: fm = g.getFontMetrics();
289:
290: int cellx;
291: if (leftToRight) {
292: cellx = (int) ((col) * cellWidth);
293: } else {
294: cellx = (int) ((daysInWeek - col - 1) * cellWidth);
295: }
296:
297: int celly = (int) (row * cellHeight + labelHeight);
298:
299: for (int i = 0; i < 2; i++) {
300: fCalendar[i].setTime(c.getTime());
301:
302: int date = fCalendar[i].get(Calendar.DATE);
303: buffer.setLength(0);
304: buffer.append(date);
305: String dayNum = buffer.toString();
306:
307: int x;
308:
309: if (leftToRight) {
310: x = cellx + (int) cellWidth - XINSET
311: - fm.stringWidth(dayNum);
312: } else {
313: x = cellx + XINSET;
314: }
315: int y = celly + +fm.getAscent() + YINSET + i
316: * fm.getHeight();
317:
318: if (fColor[i] != null) {
319: g.setColor(fColor[i]);
320: }
321: g.drawString(dayNum, x, y);
322:
323: if (date == 1 || row == 0 && col == 0) {
324: g.setFont(DemoUtility.numberFont);
325: String month = fSymbols[i].getMonths()[fCalendar[i]
326: .get(Calendar.MONTH)];
327:
328: if (leftToRight) {
329: x = cellx + XINSET;
330: } else {
331: x = cellx + (int) cellWidth - XINSET
332: - fm.stringWidth(month);
333: }
334: g.drawString(month, x, y);
335: }
336: }
337:
338: c.add(Calendar.DATE, 1);
339: }
340: }
341: }
342:
343: // Important state variables
344: private Calendar[] fCalendar = new Calendar[4];
345: private Color[] fColor = new Color[4];
346:
347: private Locale fDisplayLocale;
348: private DateFormatSymbols[] fSymbols = new DateFormatSymbols[4];
349:
350: private Date fStartOfMonth = new Date(); // 00:00:00 on first day of month
351:
352: // Cached calculations to make drawing faster.
353: private transient int minDay; // Minimum legal day #
354: private transient int daysInWeek; // # of days in a week
355: private transient int firstDayOfWeek; // First day to display in week
356: private transient int numWeeks; // # full or partial weeks in month
357: private transient int daysInMonth; // # days in this month
358: private transient int firstDayInMonth; // Day of week of first day in month
359: private transient boolean leftToRight;
360:
361: private transient boolean dirty = true;
362: }
|