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