/* Calendar.java: Java program to display a calendar for any month * after 1582. The user inputs the year into a TextField and chooses * the month from a Choice object (drop-down combo box), then clicks * 'New Calendar' to show the calendar for that month and year. * * The applet assumes a 421x481 pixel surface on which to draw. */ import java.applet.Applet; import java.awt.*; import java.util.Date; public class Calendar extends Applet { static final int YTOP = 90; /* y-size of margin above calendar box */ static final int YHEADER = 30; /* y-size of horz strip with day names */ static final int NCELLX = 7; /* number of cells across */ static final int CELLSIZE = 60; /* size of each square cell */ static final int MARGIN = 8; /* margin from number to cell top, right */ static final int FEBRUARY = 1; /* special month during leap years */ // Data entry controls at top. Label yearLabel = new Label("Year:"); TextField yearTextField = new TextField("1996", 5); Label monthLabel = new Label("Month:"); Choice monthChoice = new Choice(); Button newCalButton = new Button("New Calendar"); // Date object to get current month and year. Date now = new Date(); // Font for controls at top. Font smallArialFont = new Font("Arial", Font.PLAIN, 15); // Font for for month/year caption above calendar. Font largeArialFont = new Font("Arial", Font.BOLD, 30); String days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; String months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; int DaysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Month and year entered by user. int userMonth; int userYear; public void init() /* USE: Get current month and year, initialize controls. NOTE: Called automatically when applet starts. */ { setBackground(Color.white); // Init month and year to current values. userMonth = now.getMonth(); userYear = now.getYear() + 1900; // "Year:" label. yearLabel.setFont(smallArialFont); add(yearLabel); // Text field to get year from user. yearTextField.setFont(smallArialFont); yearTextField.setText(String.valueOf(userYear)); add(yearTextField); // "Month:" label. monthLabel.setFont(smallArialFont); add(monthLabel); // Combo box to get month from user: add months, set default to now. monthChoice.setFont(smallArialFont); for (int i = 0; i < 12; i++) monthChoice.addItem(months[i]); monthChoice.select(userMonth); add(monthChoice); // "New calendar" button. newCalButton.setFont(smallArialFont); add(newCalButton); } // init public void paint(Graphics g) /* USE: Draw calendar using globals userMonth and userYear. NOTE: Called automatically whenever surface needs to be redrawn; also when user clicks 'New Calendar' button, triggering repaint. */ { FontMetrics fm; /* to get font info */ int fontAscent; /* character height */ int dayPos; /* y-position of day strings */ int xSize, ySize; /* size of calendar body (cell table) */ int numRows; /* number of rows in cell table (4, 5, 6) */ int xNum, yNum; /* number position at top right of cells */ int numDays; /* number of days in month */ String dayStr; /* day of the week as a string */ int marg; /* margin of month string baseline from cell table */ String caption; /* month string at top center */ // Get font info for number string positioning (default small font). fm = g.getFontMetrics(); fontAscent = fm.getAscent(); dayPos = YTOP + (YHEADER + fontAscent) / 2; // Get x-size of calendar body (cell table). xSize = NCELLX * CELLSIZE; // Header rectangle across top for day names. g.drawRect(0, YTOP, xSize, YHEADER); // Put days at top of each column, centered. for (int i = 0; i < NCELLX; i++) g.drawString(days[i], (CELLSIZE-fm.stringWidth(days[i]))/2 + i*CELLSIZE, dayPos); // Get number of calendar rows needed for this month. numRows = NumberRowsNeeded(userYear, userMonth); // Vertical lines of cell table. ySize = numRows * CELLSIZE; for (int i = 0; i <= xSize; i += CELLSIZE) g.drawLine(i, YTOP + YHEADER, i, YTOP + YHEADER + ySize); // Horizontal lines of cell table. for (int i = 0, j = YTOP + YHEADER; i <= numRows; i++, j += CELLSIZE) g.drawLine(0, j, xSize, j); // Init number positions (upper right of cell). xNum = (CalcFirstOfMonth(userYear, userMonth) + 1) * CELLSIZE - MARGIN; yNum = YTOP + YHEADER + MARGIN + fontAscent; // Get number of days in month, adding one if February of leap year. numDays = DaysInMonth[userMonth] + ((IsLeapYear(userYear) && (userMonth == FEBRUARY)) ? 1 : 0); // Show numbers at top right of each cell, right justified. for (int day = 1; day <= numDays; day++) { dayStr = String.valueOf(day); g.drawString(dayStr, xNum - fm.stringWidth(dayStr), yNum); xNum += CELLSIZE; // If xNum to right of calendar, 'new line'. if (xNum > xSize) { xNum = CELLSIZE - MARGIN; yNum += CELLSIZE; } // if } // for // Set large font for month/year caption. g.setFont(largeArialFont); // Get font info for string positioning (large font now current). fm = g.getFontMetrics(); // Set margin for y-positioning of caption. marg = 2 * fm.getDescent(); // Set caption to month string and center at top. caption = months[userMonth] + " " + String.valueOf(userYear); g.drawString(caption, (xSize-fm.stringWidth(caption))/2, YTOP - marg); } // paint public boolean action(Event e, Object o) /* USE: Update month and year globals, paint when user clicks button. */ { int userYearInt; if (e.target instanceof Button) { if ("New Calendar".equals((String)o)) { // Get month from combo box (Choice control). userMonth = monthChoice.getSelectedIndex(); // Get year from TextField, update userYear only if year ok. userYearInt = Integer.parseInt(yearTextField.getText(), 10); if (userYearInt > 1581) userYear = userYearInt; // Call paint() to draw new calendar. repaint(); return true; } // inner if } // outer if return false; } // action int NumberRowsNeeded(int year, int month) /* USE: Calculates number of rows needed for calendar. IN: year = given year after 1582 (start of the Gregorian calendar). month = 0 for January, 1 for February, etc. OUT: Number of rows: 5 or 6, except for a 28 day February with the first of the month on Sunday, requiring only four rows. */ { int firstDay; /* day of week for first day of month */ int numCells; /* number of cells needed by the month */ /* Start at 1582, when modern calendar starts. */ if (year < 1582) return (-1); /* Catch month out of range. */ if ((month < 0) || (month > 11)) return (-1); /* Get first day of month. */ firstDay = CalcFirstOfMonth(year, month); /* Non leap year February with 1st on Sunday: 4 rows. */ if ((month == FEBRUARY) && (firstDay == 0) && !IsLeapYear(year)) return (4); /* Number of cells needed = blanks on 1st row + days in month. */ numCells = firstDay + DaysInMonth[month]; /* One more cell needed for the Feb 29th in leap year. */ if ((month == FEBRUARY) && (IsLeapYear(year))) numCells++; /* 35 cells or less is 5 rows; more is 6. */ return ((numCells <= 35) ? 5 : 6); } // NumberRowsNeeded int CalcFirstOfMonth(int year, int month) /* USE: Calculates day of the week the first day of the month falls on. IN: year = given year after 1582 (start of the Gregorian calendar). month = 0 for January, 1 for February, etc. OUT: First day of month: 0 = Sunday, 1 = Monday, etc. */ { int firstDay; /* day of week for Jan 1, then first day of month */ int i; /* to traverse months before given month */ /* Start at 1582, when modern calendar starts. */ if (year < 1582) return (-1); /* Catch month out of range. */ if ((month < 0) || (month > 11)) return (-1); /* Get day of week for Jan 1 of given year. */ firstDay = CalcJanuaryFirst(year); /* Increase firstDay by days in year before given month to get first day * of month. */ for (i = 0; i < month; i++) firstDay += DaysInMonth[i]; /* Increase by one if month after February and leap year. */ if ((month > FEBRUARY) && IsLeapYear(year)) firstDay++; /* Convert to day of the week and return. */ return (firstDay % 7); } // CalcFirstOfMonth boolean IsLeapYear(int year) /* USE: Determines if given year is a leap year. IN: year = given year after 1582 (start of the Gregorian calendar). OUT: TRUE if given year is leap year, FALSE if not. NOTE: Formulas capture definition of leap years; cf CalcLeapYears(). */ { /* If multiple of 100, leap year iff multiple of 400. */ if ((year % 100) == 0) return((year % 400) == 0); /* Otherwise leap year iff multiple of 4. */ return ((year % 4) == 0); } // IsLeapYear int CalcJanuaryFirst(int year) /* USE: Calculate day of the week on which January 1 falls for given year. IN: year = given year after 1582 (start of the Gregorian calendar). OUT: Day of week for January 1: 0 = Sunday, 1 = Monday, etc. NOTE: Formula starts with a 5, since January 1, 1582 was a Friday; then advances the day of the week by one for every year, adding the number of leap years intervening, because those years Jan 1 advanced by two days. Calculate mod 7 to get the day of the week. */ { /* Start at 1582, when modern calendar starts. */ if (year < 1582) return (-1); /* Start Fri 01-01-1582; advance a day for each year, 2 for leap yrs. */ return ((5 + (year - 1582) + CalcLeapYears(year)) % 7); } // CalcJanuaryFirst int CalcLeapYears(int year) /* USE: Calculate number of leap years since 1582. IN: year = given year after 1582 (start of the Gregorian calendar). OUT: number of leap years since the given year, -1 if year < 1582 NOTE: Count doesn't include the given year if it is a leap year. In the Gregorian calendar, used since 1582, every fourth year is a leap year, except for years that are a multiple of a hundred, but not a multiple of 400, which are no longer leap years. Years that are a multiple of 400 are still leap years: 1700, 1800, 1990 were not leap years, but 2000 will be. */ { int leapYears; /* number of leap years to return */ int hundreds; /* number of years multiple of a hundred */ int fourHundreds; /* number of years multiple of four hundred */ /* Start at 1582, when modern calendar starts. */ if (year < 1582) return (-1); /* Calculate number of years in interval that are a multiple of 4. */ leapYears = (year - 1581) / 4; /* Calculate number of years in interval that are a multiple of 100; * subtract, since they are not leap years. */ hundreds = (year - 1501) / 100; leapYears -= hundreds; /* Calculate number of years in interval that are a multiple of 400; * add back in, since they are still leap years. */ fourHundreds = (year - 1201) / 400; leapYears += fourHundreds; return (leapYears); } // CalcLeapYears } // class Calendar