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 10 package com.ibm.icu.dev.demo.calendar; 11 12 import java.awt.BorderLayout; 13 import java.awt.Button; 14 import java.awt.Choice; 15 import java.awt.Color; 16 import java.awt.Component; 17 import java.awt.Container; 18 import java.awt.Dimension; 19 import java.awt.FlowLayout; 20 import java.awt.Font; 21 import java.awt.FontMetrics; 22 import java.awt.Frame; 23 import java.awt.Graphics; 24 import java.awt.GridBagConstraints; 25 import java.awt.GridBagLayout; 26 import java.awt.Label; 27 import java.awt.Panel; 28 import java.awt.Rectangle; 29 import java.awt.event.ActionEvent; 30 import java.awt.event.ActionListener; 31 import java.awt.event.ItemEvent; 32 import java.awt.event.ItemListener; 33 import java.awt.event.WindowAdapter; 34 import java.awt.event.WindowEvent; 35 import java.util.Date; 36 import java.util.Locale; 37 38 import com.ibm.icu.dev.demo.impl.DemoApplet; 39 import com.ibm.icu.dev.demo.impl.DemoUtility; 40 import com.ibm.icu.text.DateFormat; 41 import com.ibm.icu.util.BuddhistCalendar; 42 import com.ibm.icu.util.Calendar; 43 import com.ibm.icu.util.GregorianCalendar; 44 import com.ibm.icu.util.HebrewCalendar; 45 import com.ibm.icu.util.IslamicCalendar; 46 import com.ibm.icu.util.JapaneseCalendar; 47 import com.ibm.icu.util.SimpleTimeZone; 48 49 /** 50 * A Frame is a top-level window with a title. The default layout for a frame 51 * is BorderLayout. The CalendarFrame class defines the window layout of 52 * CalendarDemo. 53 */ 54 class CalendarFrame extends Frame 55 { 56 /** 57 * For serialization 58 */ 59 private static final long serialVersionUID = -4289697663503820619L; 60 61 private static final boolean DEBUG = false; 62 63 private DemoApplet applet; 64 65 /** 66 * Constructs a new CalendarFrame that is initially invisible. 67 */ CalendarFrame(DemoApplet myApplet)68 public CalendarFrame(DemoApplet myApplet) 69 { 70 super("Calendar Demo"); 71 this.applet = myApplet; 72 init(); 73 74 // When the window is closed, we want to shut down the applet or application 75 addWindowListener( 76 new WindowAdapter() { 77 public void windowClosing(WindowEvent e) { 78 setVisible(false); 79 dispose(); 80 81 if (applet != null) { 82 applet.demoClosed(); 83 } else System.exit(0); 84 } 85 } ); 86 } 87 88 private Choice displayMenu; 89 private Locale[] locales = DemoUtility.getG7Locales(); 90 91 private Calendar calendars[] = new Calendar[2]; 92 private Choice calMenu[] = new Choice[2]; 93 private ColoredLabel monthLabel[] = new ColoredLabel[2]; 94 private DateFormat monthFormat[] = new DateFormat[2]; 95 96 private Button prevYear; 97 private Button prevMonth; 98 private Button gotoToday; 99 private Button nextMonth; 100 private Button nextYear; 101 private CalendarPanel calendarPanel; 102 add(Container container, Component component, GridBagLayout g, GridBagConstraints c, int gridwidth, int weightx)103 private static void add(Container container, Component component, 104 GridBagLayout g, GridBagConstraints c, 105 int gridwidth, int weightx) 106 { 107 c.gridwidth = gridwidth; 108 c.weightx = weightx; 109 g.setConstraints(component, c); 110 container.add(component); 111 } 112 113 /** 114 * Initializes the applet. You never need to call this directly, it 115 * is called automatically by the system once the applet is created. 116 */ init()117 public void init() { 118 setBackground(DemoUtility.bgColor); 119 setLayout(new BorderLayout(10,10)); 120 121 Panel topPanel = new Panel(); 122 GridBagLayout g = new GridBagLayout(); 123 topPanel.setLayout(g); 124 GridBagConstraints c = new GridBagConstraints(); 125 c.fill = GridBagConstraints.HORIZONTAL; 126 127 // Build the two menus for selecting which calendar is displayed, 128 // plus the month/year label for each calendar 129 for (int i = 0; i < 2; i++) { 130 calMenu[i] = new Choice(); 131 for (int j = 0; j < CALENDARS.length; j++) { 132 calMenu[i].addItem(CALENDARS[j].name); 133 } 134 calMenu[i].setBackground(DemoUtility.choiceColor); 135 calMenu[i].select(i); 136 calMenu[i].addItemListener(new CalMenuListener()); 137 138 // Label for the current month name 139 monthLabel[i] = new ColoredLabel("", COLORS[i]); 140 monthLabel[i].setFont(DemoUtility.titleFont); 141 142 // And the default calendar to use for this slot 143 calendars[i] = CALENDARS[i].calendar; 144 145 add(topPanel, calMenu[i], g, c, 5, 0); 146 add(topPanel, monthLabel[i], g, c, GridBagConstraints.REMAINDER, 1); 147 } 148 149 // Now add the next/previous year/month buttons: 150 prevYear = new Button("<<"); 151 prevYear.addActionListener(new AddAction(Calendar.YEAR, -1)); 152 153 prevMonth = new Button("<"); 154 prevMonth.addActionListener(new AddAction(Calendar.MONTH, -1)); 155 156 gotoToday = new Button("Today"); 157 gotoToday.addActionListener( new ActionListener() 158 { 159 public void actionPerformed(ActionEvent e) { 160 calendarPanel.setDate( new Date() ); 161 updateMonthName(); 162 } 163 } ); 164 165 nextMonth = new Button(">"); 166 nextMonth.addActionListener(new AddAction(Calendar.MONTH, 1)); 167 168 nextYear = new Button(">>"); 169 nextYear.addActionListener(new AddAction(Calendar.YEAR, 1)); 170 171 c.fill = GridBagConstraints.NONE; 172 add(topPanel, prevYear, g, c, 1, 0); 173 add(topPanel, prevMonth, g, c, 1, 0); 174 add(topPanel, gotoToday, g, c, 1, 0); 175 add(topPanel, nextMonth, g, c, 1, 0); 176 add(topPanel, nextYear, g, c, 1, 0); 177 178 // Now add the menu for selecting the display language 179 Panel displayPanel = new Panel(); 180 { 181 displayMenu = new Choice(); 182 Locale defaultLocale = Locale.getDefault(); 183 int bestMatch = -1, thisMatch = -1; 184 int selectMe = 0; 185 186 for (int i = 0; i < locales.length; i++) { 187 if (i > 0 && 188 locales[i].getLanguage().equals(locales[i-1].getLanguage()) || 189 i < locales.length - 1 && 190 locales[i].getLanguage().equals(locales[i+1].getLanguage())) 191 { 192 displayMenu.addItem( locales[i].getDisplayName() ); 193 } else { 194 displayMenu.addItem( locales[i].getDisplayLanguage()); 195 } 196 197 thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale); 198 199 if (thisMatch >= bestMatch) { 200 bestMatch = thisMatch; 201 selectMe = i; 202 } 203 } 204 205 displayMenu.setBackground(DemoUtility.choiceColor); 206 displayMenu.select(selectMe); 207 208 displayMenu.addItemListener( new ItemListener() 209 { 210 public void itemStateChanged(ItemEvent e) { 211 Locale loc = locales[displayMenu.getSelectedIndex()]; 212 calendarPanel.setLocale( loc ); 213 monthFormat[0] = monthFormat[1] = null; 214 updateMonthName(); 215 repaint(); 216 } 217 } ); 218 219 Label l1 = new Label("Display Language:", Label.RIGHT); 220 l1.setFont(DemoUtility.labelFont); 221 222 displayPanel.setLayout(new FlowLayout()); 223 displayPanel.add(l1); 224 displayPanel.add(displayMenu); 225 226 } 227 c.fill = GridBagConstraints.NONE; 228 c.anchor = GridBagConstraints.EAST; 229 230 add(topPanel, displayPanel, g, c, GridBagConstraints.REMAINDER, 0); 231 232 // The title, buttons, etc. go in a panel at the top of the window 233 add("North", topPanel); 234 235 // The copyright notice goes at the bottom of the window 236 Label copyright = new Label(DemoUtility.copyright1, Label.LEFT); 237 copyright.setFont(DemoUtility.creditFont); 238 add("South", copyright); 239 240 // Now create the big calendar panel and stick it in the middle 241 calendarPanel = new CalendarPanel( locales[displayMenu.getSelectedIndex()] ); 242 add("Center", calendarPanel); 243 244 for (int i = 0; i < 2; i++) { 245 calendarPanel.setCalendar(i, calendars[i]); 246 calendarPanel.setColor(i, COLORS[i]); 247 } 248 249 updateMonthName(); 250 } 251 252 updateMonthName()253 private void updateMonthName() 254 { 255 for (int i = 0; i < 2; i++) { 256 try { 257 if (monthFormat[i] == null) { // TODO: optimize 258 DateFormat f = DateFormat.getDateTimeInstance( 259 calendars[i], DateFormat.MEDIUM, -1, 260 locales[displayMenu.getSelectedIndex()]); 261 if (f instanceof com.ibm.icu.text.SimpleDateFormat) { 262 com.ibm.icu.text.SimpleDateFormat f1 = (com.ibm.icu.text.SimpleDateFormat) f; 263 f1.applyPattern("MMMM, yyyy G"); 264 f1.setTimeZone(new SimpleTimeZone(0, "UTC")); 265 } 266 monthFormat[i] = f; 267 } 268 } catch (ClassCastException e) { 269 //hey {lw} - there's something wrong in this routine that cuases exceptions. 270 System.out.println(e); 271 } 272 273 monthLabel[i].setText( monthFormat[i].format( calendarPanel.firstOfMonth() )); 274 } 275 } 276 277 /** 278 * CalMenuListener responds to events in the two popup menus that select 279 * the calendar systems to be used in the display. It figures out which 280 * of the two menus the event occurred in and updates the corresponding 281 * element of the calendars[] array to match the new selection. 282 */ 283 private class CalMenuListener implements ItemListener 284 { itemStateChanged(ItemEvent e)285 public void itemStateChanged(ItemEvent e) 286 { 287 for (int i = 0; i < calMenu.length; i++) 288 { 289 if (e.getItemSelectable() == calMenu[i]) 290 { 291 // We found the menu that the event happened in. 292 // Figure out which new calendar they selected. 293 Calendar newCal = CALENDARS[ calMenu[i].getSelectedIndex() ].calendar; 294 295 if (newCal != calendars[i]) 296 { 297 // If any of the other menus are set to the same new calendar 298 // we're about to use for this menu, set them to the current 299 // calendar from *this* menu so we won't have two the same 300 for (int j = 0; j < calendars.length; j++) { 301 if (j != i && calendars[j] == newCal) { 302 calendars[j] = calendars[i]; 303 calendarPanel.setCalendar(j, calendars[j]); 304 monthFormat[j] = null; 305 306 for (int k = 0; k < CALENDARS.length; k++) { 307 if (calendars[j] == CALENDARS[k].calendar) { 308 calMenu[j].select(k); 309 break; 310 } 311 } 312 } 313 } 314 // Now update this menu to use the new calendar the user selected 315 calendars[i] = newCal; 316 calendarPanel.setCalendar(i, newCal); 317 monthFormat[i] = null; 318 319 updateMonthName(); 320 } 321 break; 322 } 323 } 324 } 325 } 326 327 /** 328 * AddAction handles the next/previous year/month buttons... 329 */ 330 private class AddAction implements ActionListener { AddAction(int field, int amount)331 AddAction(int field, int amount) { 332 this.field = field; 333 this.amount = amount; 334 } 335 actionPerformed(ActionEvent e)336 public void actionPerformed(ActionEvent e) { 337 calendarPanel.add(field, amount); 338 updateMonthName(); 339 } 340 341 private int field, amount; 342 } 343 344 /** 345 * ColoredLabel is similar to java.awt.Label, with two differences: 346 * 347 * - You can set its text color 348 * 349 * - It draws text using drawString rather than using a host-specific 350 * "Peer" object like AWT does. On 1.2, using drawString gives 351 * us Bidi reordering for free. 352 */ 353 static private class ColoredLabel extends Component { 354 /** 355 * For serialization 356 */ 357 private static final long serialVersionUID = 5004484960341875722L; ColoredLabel(String label)358 public ColoredLabel(String label) { 359 text = label; 360 } 361 ColoredLabel(String label, Color c)362 public ColoredLabel(String label, Color c) { 363 text = label; 364 color = c; 365 } 366 setText(String label)367 public void setText(String label) { 368 text = label; 369 repaint(); 370 } 371 setFont(Font f)372 public void setFont(Font f) { 373 font = f; 374 repaint(); 375 } 376 paint(Graphics g)377 public void paint(Graphics g) { 378 FontMetrics fm = g.getFontMetrics(font); 379 380 Rectangle bounds = getBounds(); 381 382 g.setColor(color); 383 g.setFont(font); 384 g.drawString(text, fm.stringWidth("\u00a0"), 385 bounds.height/2 + fm.getHeight() 386 - fm.getAscent() + fm.getLeading()/2); 387 } 388 getPreferredSize()389 public Dimension getPreferredSize() { 390 return getMinimumSize(); 391 } 392 getMinimumSize()393 public Dimension getMinimumSize() { 394 FontMetrics fm = getFontMetrics(font); 395 396 return new Dimension( fm.stringWidth(text) + 2*fm.stringWidth("\u00a0"), 397 fm.getHeight() + fm.getLeading()*2); 398 } 399 400 String text; 401 Color color = Color.black; 402 Font font = DemoUtility.labelFont; 403 } 404 405 /** 406 * Print out the error message while debugging this program. 407 */ errorText(String s)408 public void errorText(String s) 409 { 410 if (DEBUG) 411 { 412 System.out.println(s); 413 } 414 } 415 416 class CalendarRec { CalendarRec(String nameStr, Calendar cal)417 public CalendarRec(String nameStr, Calendar cal) 418 { 419 name = nameStr; 420 calendar = cal; 421 } 422 423 Calendar calendar; 424 String name; 425 } 426 427 private final CalendarRec[] CALENDARS = { 428 new CalendarRec("Gregorian Calendar", new GregorianCalendar()), 429 new CalendarRec("Hebrew Calendar", new HebrewCalendar()), 430 new CalendarRec("Islamic Calendar", makeIslamic(false)), 431 new CalendarRec("Islamic Civil Calendar ", makeIslamic(true)), 432 new CalendarRec("Buddhist Calendar", new BuddhistCalendar()), 433 new CalendarRec("Japanese Calendar", new JapaneseCalendar()), 434 }; 435 makeIslamic(boolean civil)436 static private final Calendar makeIslamic(boolean civil) { 437 IslamicCalendar cal = new IslamicCalendar(); 438 cal.setCivil(civil); 439 return cal; 440 } 441 442 static final Color[] COLORS = { Color.blue, Color.black }; 443 } 444 445