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