Home | History | Annotate | Download | only in holiday
      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) 1996-2014, International Business Machines Corporation and    *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 
     10 package com.ibm.icu.dev.demo.holiday;
     11 
     12 import java.awt.BorderLayout;
     13 import java.awt.Button;
     14 import java.awt.Canvas;
     15 import java.awt.Choice;
     16 import java.awt.Color;
     17 import java.awt.Component;
     18 import java.awt.Container;
     19 import java.awt.Dimension;
     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.Point;
     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.WindowEvent;
     34 import java.text.DateFormatSymbols;
     35 import java.util.Date;
     36 import java.util.Locale;
     37 import java.util.Vector;
     38 
     39 import com.ibm.icu.dev.demo.impl.DemoApplet;
     40 import com.ibm.icu.dev.demo.impl.DemoTextBox;
     41 import com.ibm.icu.dev.demo.impl.DemoUtility;
     42 import com.ibm.icu.text.DateTimePatternGenerator;
     43 import com.ibm.icu.text.SimpleDateFormat;
     44 import com.ibm.icu.util.Calendar;
     45 import com.ibm.icu.util.Holiday;
     46 
     47 /**
     48  * CalendarDemo demonstrates how Calendar works.
     49  */
     50 public class HolidayCalendarDemo extends DemoApplet
     51 {
     52     /**
     53      * For serialization
     54      */
     55     private static final long serialVersionUID = 4546085430817359372L;
     56 
     57     /**
     58      * The main function which defines the behavior of the CalendarDemo
     59      * applet when an applet is started.
     60      */
     61     public static void main(String argv[]) {
     62 
     63         new HolidayCalendarDemo().showDemo();
     64     }
     65 
     66     /* This creates a CalendarFrame for the demo applet. */
     67     public Frame createDemoFrame(DemoApplet applet) {
     68         return new CalendarFrame(applet);
     69     }
     70 
     71     /**
     72     * A Frame is a top-level window with a title. The default layout for a frame
     73     * is BorderLayout.  The CalendarFrame class defines the window layout of
     74     * CalendarDemo.
     75     */
     76     private static class CalendarFrame extends Frame implements ActionListener,
     77                                                                 ItemListener
     78     {
     79         /**
     80          * For serialization
     81          */
     82         private static final long serialVersionUID = -7023296782393042761L;
     83 
     84         private static final boolean DEBUG = false;
     85 
     86         //private Locale curLocale = Locale.US; // unused
     87 
     88         private DemoApplet applet;
     89 
     90         private static final Locale[] calendars = {
     91             //new Locale("de","AT"),
     92             Locale.CANADA,
     93             Locale.CANADA_FRENCH,
     94             Locale.FRANCE,
     95             Locale.GERMANY,
     96             new Locale("iw","IL"),
     97             new Locale("el","GR"),
     98             //new Locale("es","MX"),
     99             Locale.UK,
    100             Locale.US,
    101         };
    102         private static final Locale[] displays = {
    103             Locale.CANADA,
    104             Locale.UK,
    105             Locale.US,
    106             Locale.FRANCE,
    107             Locale.CANADA_FRENCH,
    108             //new Locale("de","AT"),
    109             Locale.GERMAN,
    110             new Locale("el","GR"),
    111             //new Locale("iw","IL"),
    112             new Locale("es","MX"),
    113         };
    114 
    115         /**
    116         * Constructs a new CalendarFrame that is initially invisible.
    117         */
    118         public CalendarFrame(DemoApplet applet)
    119         {
    120             super("Calendar Demo");
    121             this.applet = applet;
    122             init();
    123             start();
    124             enableEvents(WindowEvent.WINDOW_CLOSING);
    125         }
    126 
    127         /**
    128         * Initializes the applet. You never need to call this directly, it
    129         * is called automatically by the system once the applet is created.
    130         */
    131         public void init()
    132         {
    133             // Get G7 locales only for demo purpose. To get all the locales
    134             // supported, switch to calling Calendar.getAvailableLocales().
    135             // commented
    136             locales = displays;
    137 
    138             buildGUI();
    139         }
    140 
    141         //------------------------------------------------------------
    142         // package private
    143         //------------------------------------------------------------
    144         void addWithFont(Container container, Component foo, Font font) {
    145             if (font != null)
    146                 foo.setFont(font);
    147             container.add(foo);
    148         }
    149 
    150         /**
    151         * Called to start the applet. You never need to call this method
    152         * directly, it is called when the applet's document is visited.
    153         */
    154         public void start()
    155         {
    156             // do nothing
    157         }
    158 
    159         private Choice          localeMenu;
    160         private Choice          displayMenu;
    161         private Locale[]        locales;
    162 
    163         private Label           monthLabel;
    164         private Button          prevYear;
    165         private Button          prevMonth;
    166         private Button          gotoToday;
    167         private Button          nextMonth;
    168         private Button          nextYear;
    169         private CalendarPanel   calendarPanel;
    170 
    171         private static final Locale kFirstLocale = Locale.US;
    172 
    173         private static void add(Container container, Component component,
    174                                 GridBagLayout g, GridBagConstraints c)
    175         {
    176             g.setConstraints(component, c);
    177             container.add(component);
    178         }
    179 
    180         public void buildGUI()
    181         {
    182             setBackground(DemoUtility.bgColor);
    183             setLayout(new BorderLayout(10,10));
    184 
    185             // Label for the demo's title
    186             Label titleLabel = new Label("Calendar Demo", Label.CENTER);
    187             titleLabel.setFont(DemoUtility.titleFont);
    188 
    189             // Label for the current month name
    190             monthLabel = new Label("", Label.LEFT);
    191             monthLabel.setFont(new Font(DemoUtility.titleFont.getName(),
    192                                         DemoUtility.titleFont.getStyle(),
    193                                         (DemoUtility.titleFont.getSize() * 3)/2));
    194 
    195             // Make the locale popup menus
    196             localeMenu= new Choice();
    197             localeMenu.addItemListener(this);
    198             int selectMe = 0;
    199 
    200             for (int i = 0; i < calendars.length; i++) {
    201                 if (i > 0 &&
    202                         calendars[i].getCountry().equals(calendars[i-1].getCountry()) ||
    203                     i < calendars.length - 1 &&
    204                         calendars[i].getCountry().equals(calendars[i+1].getCountry()))
    205                 {
    206                     localeMenu.addItem(calendars[i].getDisplayCountry() + " (" +
    207                                     calendars[i].getDisplayLanguage() + ")");
    208                 } else {
    209                     localeMenu.addItem( calendars[i].getDisplayCountry() );
    210                 }
    211 
    212                 if (calendars[i].equals(kFirstLocale)) {
    213                     selectMe = i;
    214                 }
    215             }
    216 
    217             localeMenu.setBackground(DemoUtility.choiceColor);
    218             localeMenu.select(selectMe);
    219 
    220             displayMenu = new Choice();
    221             displayMenu.addItemListener(this);
    222 
    223             selectMe = 0;
    224             for (int i = 0; i < locales.length; i++) {
    225                 if (i > 0 &&
    226                         locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
    227                     i < locales.length - 1 &&
    228                         locales[i].getLanguage().equals(locales[i+1].getLanguage()))
    229                 {
    230                     displayMenu.addItem( locales[i].getDisplayName() );
    231                 } else {
    232                     displayMenu.addItem( locales[i].getDisplayLanguage());
    233                 }
    234 
    235                 if (locales[i].equals(kFirstLocale)) {
    236                     selectMe = i;
    237                 }
    238             }
    239 
    240             displayMenu.setBackground(DemoUtility.choiceColor);
    241             displayMenu.select(selectMe);
    242 
    243             // Make all the next/previous/today buttons
    244             prevYear = new Button("<<");
    245             prevYear.addActionListener(this);
    246             prevMonth = new Button("<");
    247             prevMonth.addActionListener(this);
    248             gotoToday = new Button("Today");
    249             gotoToday.addActionListener(this);
    250             nextMonth = new Button(">");
    251             nextMonth.addActionListener(this);
    252             nextYear = new Button(">>");
    253             nextYear.addActionListener(this);
    254 
    255             // The month name and the control buttons are bunched together
    256             Panel monthPanel = new Panel();
    257             {
    258                 GridBagLayout g = new GridBagLayout();
    259                 GridBagConstraints c = new GridBagConstraints();
    260                 monthPanel.setLayout(g);
    261 
    262                 c.weightx = 1;
    263                 c.weighty = 1;
    264 
    265                 c.gridwidth = 1;
    266                 c.fill = GridBagConstraints.HORIZONTAL;
    267                 c.gridwidth = GridBagConstraints.REMAINDER;
    268                 add(monthPanel, monthLabel, g, c);
    269 
    270                 c.gridwidth = 1;
    271                 add(monthPanel, prevYear, g, c);
    272                 add(monthPanel, prevMonth, g, c);
    273                 add(monthPanel, gotoToday, g, c);
    274                 add(monthPanel, nextMonth, g, c);
    275                 c.gridwidth = GridBagConstraints.REMAINDER;
    276                 add(monthPanel, nextYear, g, c);
    277             }
    278 
    279             // Stick the menu and buttons in a little "control panel"
    280             Panel menuPanel = new Panel();
    281             {
    282                 GridBagLayout g = new GridBagLayout();
    283                 GridBagConstraints c = new GridBagConstraints();
    284                 menuPanel.setLayout(g);
    285 
    286                 c.weightx = 1;
    287                 c.weighty = 1;
    288 
    289                 c.fill = GridBagConstraints.HORIZONTAL;
    290 
    291                 c.gridwidth = GridBagConstraints.RELATIVE;
    292                 Label l1 = new Label("Holidays");
    293                 l1.setFont(DemoUtility.labelFont);
    294                 add(menuPanel, l1, g, c);
    295 
    296                 c.gridwidth = GridBagConstraints.REMAINDER;
    297                 add(menuPanel, localeMenu, g, c);
    298 
    299                 c.gridwidth = GridBagConstraints.RELATIVE;
    300                 Label l2 = new Label("Display:");
    301                 l2.setFont(DemoUtility.labelFont);
    302                 add(menuPanel, l2, g, c);
    303 
    304                 c.gridwidth = GridBagConstraints.REMAINDER;
    305                 add(menuPanel, displayMenu, g, c);
    306             }
    307 
    308             // The title, buttons, etc. go in a panel at the top of the window
    309             Panel topPanel = new Panel();
    310             {
    311                 topPanel.setLayout(new BorderLayout());
    312 
    313                 //topPanel.add("North", titleLabel);
    314                 topPanel.add("Center", monthPanel);
    315                 topPanel.add("East", menuPanel);
    316             }
    317             add("North", topPanel);
    318 
    319             // The copyright notice goes at the bottom of the window
    320             Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
    321             copyright.setFont(DemoUtility.creditFont);
    322             add("South", copyright);
    323 
    324             // Now create the big calendar panel and stick it in the middle
    325             calendarPanel = new CalendarPanel( kFirstLocale );
    326             add("Center", calendarPanel);
    327 
    328             updateMonthName();
    329         }
    330 
    331         private void updateMonthName()
    332         {
    333             final Locale displayLocale = calendarPanel.getDisplayLocale();
    334             final String pattern = DateTimePatternGenerator.
    335                     getInstance(displayLocale).getBestPattern("MMMMy");
    336             SimpleDateFormat f = new SimpleDateFormat(pattern,
    337                                                         displayLocale);
    338             f.setCalendar(calendarPanel.getCalendar());
    339             monthLabel.setText( f.format( calendarPanel.firstOfMonth() ));
    340         }
    341 
    342         /**
    343         * Handles the event. Returns true if the event is handled and should not
    344         * be passed to the parent of this component. The default event handler
    345         * calls some helper methods to make life easier on the programmer.
    346         */
    347         public void actionPerformed(ActionEvent e)
    348         {
    349             Object obj = e.getSource();
    350 
    351             // *** Button events are handled here.
    352             if (obj instanceof Button) {
    353                 if (obj == nextMonth) {
    354                     calendarPanel.add(Calendar.MONTH, +1);
    355                 }
    356                 else
    357                 if (obj == prevMonth) {
    358                     calendarPanel.add(Calendar.MONTH, -1);
    359                 }
    360                 else
    361                 if (obj == prevYear) {
    362                     calendarPanel.add(Calendar.YEAR, -1);
    363                 }
    364                 else
    365                 if (obj == nextYear) {
    366                     calendarPanel.add(Calendar.YEAR, +1);
    367                 }
    368                 else
    369                 if (obj == gotoToday) {
    370                     calendarPanel.set( new Date() );
    371                 }
    372                 updateMonthName();
    373             }
    374         }
    375 
    376         public void itemStateChanged(ItemEvent e)
    377         {
    378             Object obj = e.getSource();
    379             if (obj == localeMenu) {
    380                 calendarPanel.setCalendarLocale(calendars[localeMenu.getSelectedIndex()]);
    381                 updateMonthName();
    382             }
    383             else
    384                 if (obj == displayMenu) {
    385                     calendarPanel.setDisplayLocale(locales[displayMenu.getSelectedIndex()]);
    386                     updateMonthName();
    387                 }
    388         }
    389 
    390         /**
    391         * Print out the error message while debugging this program.
    392         */
    393         public void errorText(String s)
    394         {
    395             if (DEBUG)
    396             {
    397                 System.out.println(s);
    398             }
    399         }
    400 
    401         protected void processWindowEvent(WindowEvent e)
    402         {
    403             System.out.println("event " + e);
    404             if (e.getID() == WindowEvent.WINDOW_CLOSING) {
    405                 this.hide();
    406                 this.dispose();
    407 
    408                 if (applet != null) {
    409                     applet.demoClosed();
    410                 } else {
    411                     System.exit(0);
    412                 }
    413             }
    414         }
    415     }
    416 
    417 
    418     private static class CalendarPanel extends Canvas {
    419 
    420         /**
    421          * For serialization
    422          */
    423         private static final long serialVersionUID = 1521099412250120821L;
    424 
    425         public CalendarPanel( Locale locale ) {
    426             set(locale, locale, new Date());
    427         }
    428 
    429         public void setCalendarLocale(Locale locale) {
    430             set(locale, fDisplayLocale, fCalendar.getTime());
    431         }
    432 
    433         public void setDisplayLocale(Locale locale) {
    434             set(fCalendarLocale, locale, fCalendar.getTime());
    435         }
    436 
    437         public void set(Date date) {
    438             set(fCalendarLocale, fDisplayLocale, date);
    439         }
    440 
    441         public void set(Locale loc, Locale display, Date date)
    442         {
    443             if (fCalendarLocale == null || !loc.equals(fCalendarLocale)) {
    444                 fCalendarLocale = loc;
    445                 fCalendar = Calendar.getInstance(fCalendarLocale);
    446                 fAllHolidays = Holiday.getHolidays(fCalendarLocale);
    447             }
    448             if (fDisplayLocale == null || !display.equals(fDisplayLocale)) {
    449                 fDisplayLocale = display;
    450                 fSymbols = new DateFormatSymbols(fDisplayLocale);
    451             }
    452 
    453             fStartOfMonth = date;
    454 
    455             dirty = true;
    456             repaint();
    457         }
    458 
    459         public void add(int field, int delta)
    460         {
    461             synchronized(fCalendar) {
    462                 fCalendar.setTime(fStartOfMonth);
    463                 fCalendar.add(field, delta);
    464                 fStartOfMonth = fCalendar.getTime();
    465             }
    466             dirty = true;
    467             repaint();
    468         }
    469 
    470         public com.ibm.icu.util.Calendar getCalendar() {
    471             return fCalendar;
    472         }
    473 
    474         public Locale getCalendarLocale() {
    475             return fCalendarLocale;
    476         }
    477 
    478         public Locale getDisplayLocale() {
    479             return fDisplayLocale;
    480         }
    481 
    482 
    483         public Date firstOfMonth() {
    484             return fStartOfMonth;
    485         }
    486 
    487         private Date startOfMonth(Date dateInMonth)
    488         {
    489             synchronized(fCalendar) {
    490                 fCalendar.setTime(dateInMonth);             // TODO: synchronization
    491 
    492                 int era = fCalendar.get(Calendar.ERA);
    493                 int year = fCalendar.get(Calendar.YEAR);
    494                 int month = fCalendar.get(Calendar.MONTH);
    495 
    496                 fCalendar.clear();
    497                 fCalendar.set(Calendar.ERA, era);
    498                 fCalendar.set(Calendar.YEAR, year);
    499                 fCalendar.set(Calendar.MONTH, month);
    500                 fCalendar.set(Calendar.DATE, 1);
    501 
    502                 return fCalendar.getTime();
    503             }
    504         }
    505 
    506         private void calculate()
    507         {
    508             Calendar c = (Calendar)fCalendar.clone(); // Temporary copy
    509 
    510             fStartOfMonth = startOfMonth(fStartOfMonth);
    511 
    512             // Stash away a few useful constants for this calendar and display
    513             minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
    514             daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
    515 
    516             firstDayOfWeek = Calendar.getInstance(fDisplayLocale).getFirstDayOfWeek();
    517 
    518             // Stash away a Date for the start of this month
    519 
    520             // Find the day of week of the first day in this month
    521             c.setTime(fStartOfMonth);
    522             firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
    523 
    524             // Now find the # of days in the month
    525             c.roll(Calendar.DATE, false);
    526             daysInMonth = c.get(Calendar.DATE);
    527 
    528             // Finally, find the end of the month, i.e. the start of the next one
    529             c.roll(Calendar.DATE, true);
    530             c.add(Calendar.MONTH, 1);
    531             c.getTime();        // JDK 1.1.2 bug workaround
    532             c.add(Calendar.SECOND, -1);
    533             Date endOfMonth = c.getTime();
    534 
    535             //
    536             // Calculate the number of full or partial weeks in this month.
    537             // To do this I can just reuse the code that calculates which
    538             // calendar cell contains a given date.
    539             //
    540             numWeeks = dateToCell(daysInMonth).y - dateToCell(1).y + 1;
    541 
    542             // Remember which holidays fall on which days in this month,
    543             // to save the trouble of having to do it later
    544             fHolidays.setSize(0);
    545 
    546             for (int h = 0; h < fAllHolidays.length; h++)
    547             {
    548                 Date d = fStartOfMonth;
    549                 while ( (d = fAllHolidays[h].firstBetween(d, endOfMonth) ) != null)
    550                 {
    551                     if(d.after(endOfMonth)) {
    552                         throw new InternalError("Error: for " + fAllHolidays[h].getDisplayName()+
    553                                 "  #" + h + "/"+fAllHolidays.length+": " + d +" is after end of month " + endOfMonth);
    554                     }
    555                     c.setTime(d);
    556                     fHolidays.addElement( new HolidayInfo(c.get(Calendar.DATE),
    557                                             fAllHolidays[h],
    558                                             fAllHolidays[h].getDisplayName(fDisplayLocale) ));
    559 
    560                     d.setTime( d.getTime() + 1000 );    // "d++"
    561                 }
    562             }
    563             dirty = false;
    564         }
    565 
    566         static final int INSET = 2;
    567 
    568         /*
    569         * Convert from the day number within a month (1-based)
    570         * to the cell coordinates on the calendar (0-based)
    571         */
    572         private void dateToCell(int date, Point pos)
    573         {
    574             int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
    575             if (firstDayInMonth < firstDayOfWeek) {
    576                 cell += daysInWeek;
    577             }
    578 
    579             pos.x = cell % daysInWeek;
    580             pos.y = cell / daysInWeek;
    581         }
    582         private Point dateToCell(int date) {
    583             Point p = new Point(0,0);
    584             dateToCell(date, p);
    585             return p;
    586         }
    587 
    588         public void paint(Graphics g) {
    589 
    590             if (dirty) {
    591                 calculate();
    592             }
    593 
    594             Point cellPos = new Point(0,0);     // Temporary variable
    595             Dimension d = getSize();
    596 
    597             g.setColor(DemoUtility.bgColor);
    598             g.fillRect(0,0,d.width,d.height);
    599 
    600             // Draw the day names at the top
    601             g.setColor(Color.black);
    602             g.setFont(DemoUtility.labelFont);
    603             FontMetrics fm = g.getFontMetrics();
    604             int labelHeight = fm.getHeight() + INSET * 2;
    605 
    606             int v = fm.getAscent() + INSET;
    607             for (int i = 0; i < daysInWeek; i++) {
    608                 int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek + 1;
    609                 String dayName = fSymbols.getWeekdays()[dayNum];
    610 
    611                 int h = (int) (d.width * (i + 0.5)) / daysInWeek;
    612                 h -= fm.stringWidth(dayName) / 2;
    613 
    614                 g.drawString(dayName, h, v);
    615             }
    616 
    617             double cellHeight = (d.height - labelHeight - 1) / numWeeks;
    618             double cellWidth = (double)(d.width - 1) / daysInWeek;
    619 
    620             // Draw a white background in the part of the calendar
    621             // that displays this month.
    622             // First figure out how much of the first week should be shaded.
    623             {
    624                 g.setColor(Color.white);
    625                 dateToCell(1, cellPos);
    626                 int width = (int)(cellPos.x*cellWidth);  // Width of unshaded area
    627 
    628                 g.fillRect((int)(width), labelHeight ,
    629                         (int)(d.width - width), (int)cellHeight);
    630 
    631                 // All of the intermediate weeks get shaded completely
    632                 g.fillRect(0, (int)(labelHeight + cellHeight),
    633                             d.width, (int)(cellHeight * (numWeeks - 2)));
    634 
    635                 // Now figure out the last week.
    636                 dateToCell(daysInMonth, cellPos);
    637                 width = (int)((cellPos.x+1)*cellWidth);  // Width of shaded area
    638 
    639                 g.fillRect(0, (int)(labelHeight + (numWeeks-1) * cellHeight),
    640                             width, (int)(cellHeight));
    641 
    642             }
    643             // Draw the X/Y grid lines
    644             g.setColor(Color.black);
    645             for (int i = 0; i <= numWeeks; i++) {
    646                 int y = (int)(labelHeight + i * cellHeight);
    647                 g.drawLine(0, y, d.width - 1, y);
    648             }
    649             for (int i = 0; i <= daysInWeek; i++) {
    650                 int x = (int)(i * cellWidth);
    651                 g.drawLine(x, labelHeight, x, d.height - 1);
    652             }
    653 
    654             // Now loop through all of the days in the month, figure out where
    655             // they go in the grid, and draw the day # for each one
    656             Font numberFont = new Font("Helvetica",Font.PLAIN,12);
    657             // not used Font holidayFont = DemoUtility.creditFont;
    658 
    659             Calendar c = (Calendar)fCalendar.clone();
    660             c.setTime(fStartOfMonth);
    661 
    662             for (int i = 1, h = 0; i <= daysInMonth; i++) {
    663                 g.setFont(numberFont);
    664                 g.setColor(Color.black);
    665                 fm = g.getFontMetrics();
    666 
    667                 dateToCell(i, cellPos);
    668                 int x = (int)((cellPos.x + 1) * cellWidth);
    669                 int y = (int)(cellPos.y * cellHeight + labelHeight);
    670 
    671                 StringBuffer buffer = new StringBuffer();
    672                 buffer.append(i);
    673                 String dayNum = buffer.toString();
    674 
    675                 x = x - INSET - fm.stringWidth(dayNum);
    676                 y = y + fm.getAscent() + INSET;
    677 
    678                 g.drawString(dayNum, x, y);
    679 
    680                 // See if any of the holidays land on this day....
    681                 HolidayInfo info = null;
    682 
    683                 // Coordinates of lower-left corner of cell.
    684                 x = (int)((cellPos.x) * cellWidth);
    685                 y = (int)((cellPos.y+1) * cellHeight) + labelHeight;
    686 
    687                 while (h < fHolidays.size() &&
    688                         (info = (HolidayInfo)fHolidays.elementAt(h)).date <= i)
    689                 {
    690                     if (info.date == i) {
    691                         // Draw the holiday here.
    692                         g.setFont(numberFont);
    693                         g.setColor(Color.red);
    694 
    695                         DemoTextBox box = new DemoTextBox(g, info.name, (int)(cellWidth - INSET));
    696                         box.draw(g, x + INSET, y - INSET - box.getHeight());
    697 
    698                         y -= (box.getHeight() + INSET);
    699                     }
    700                     h++;
    701                 }
    702             }
    703         }
    704 
    705         // Important state variables
    706         private Locale              fCalendarLocale;    // Whose calendar
    707         private Calendar            fCalendar;          // Calendar for calculations
    708 
    709         private Locale              fDisplayLocale;     // How to display it
    710         private DateFormatSymbols   fSymbols;           // Symbols for drawing
    711 
    712         private Date                fStartOfMonth;      // 00:00:00 on first day of month
    713 
    714         // Cached calculations to make drawing faster.
    715         private transient int minDay;           // Minimum legal day #
    716         private transient int daysInWeek;       // # of days in a week
    717         private transient int firstDayOfWeek;   // First day to display in week
    718         private transient int numWeeks;         // # full or partial weeks in month
    719         private transient int daysInMonth;      // # days in this month
    720         private transient int firstDayInMonth;  // Day of week of first day in month
    721 
    722         private transient Holiday[] fAllHolidays;
    723         private transient Vector    fHolidays = new Vector(5,5);
    724 
    725         private transient boolean dirty = true;
    726     }
    727 
    728     private static class HolidayInfo {
    729         public HolidayInfo(int date, Holiday holiday, String name) {
    730             this.date = date;
    731             this.holiday = holiday;
    732             this.name = name;
    733         }
    734 
    735         public Holiday holiday;
    736         public int date;
    737         public String name;
    738     }
    739 }
    740 
    741