import java.beans.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class DateGUI extends Panel implements ItemListener { // static constants protected final static int FIRST_YEAR = 1980; protected final static int LAST_YEAR = 2005; protected final static int DAYS_IN_FEBRUARY = 28; protected final static int DAYS_IN_LEAP_FEB = 29; protected final static String[] monthNames = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; protected final static int[] numDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // instance fields protected GregorianCalendar calendar; // GUI variables protected Choice dayChoice, monthChoice, yearChoice; protected int lastMaxDay = -1; // PropertyChange helper object protected PropertyChangeSupport butler; //------------------------------------------------------------------- // DateGUI Constructor //------------------------------------------------------------------- public DateGUI() { butler = new PropertyChangeSupport(this); // our date editor is internally reflected by a Calendar/Time object calendar = new GregorianCalendar(); calendar.setTime(new Date()); // default "date" prop equals "now" // create the day, month, year choices and register for select events dayChoice = buildDayChoice(); dayChoice.addItemListener(this); monthChoice = buildMonthChoice(); monthChoice.addItemListener(this); yearChoice = buildYearChoice(); yearChoice.addItemListener(this); // arrange the Choices next to each other using default LayoutManager add(monthChoice); add(dayChoice); add(yearChoice); // to polish GUI, we select Choice items to reflect current date reflectNewDate(); } //------------------------------------------------------------------- // set()/get() for 'Date' property //------------------------------------------------------------------- public void setDate(Date value) { Date prevDate = getDate(); calendar.setTime(value); reflectNewDate(); butler.firePropertyChange("date", prevDate, getDate()); } public Date getDate() { return calendar.getTime(); } //------------------------------------------------------------------- // Choice builders //------------------------------------------------------------------- protected Choice buildDayChoice() { Choice days; days = buildChoiceFromRange(1, 31); // default month day range days.setSize(20,16); return days; // needs to be filled according to month } //------------------------------------------------------------------- protected Choice buildMonthChoice() { Choice months; months = buildChoiceFromStringList(monthNames); months.setSize(80,16); return months; } //------------------------------------------------------------------- protected Choice buildYearChoice() { Choice years; years = buildChoiceFromRange(FIRST_YEAR, LAST_YEAR); years.setSize(60,16); return years; } //------------------------------------------------------------------- // Ensure that the days Choice only shows maxDay days (i.e. 28, 30, 31) //------------------------------------------------------------------- protected void setDaysChoiceLastDay(int maxDay) { // avoid expensive Choice massaging if possible if ( maxDay != lastMaxDay ) { String[] list = buildStringListFromRange(1, maxDay); setChoiceList(dayChoice, list); lastMaxDay = maxDay; } } //------------------------------------------------------------------- // Utility function to create a Choice of integers [first..last] //------------------------------------------------------------------- public static Choice buildChoiceFromRange(int first, int last) { String[] listOfIntegers; listOfIntegers = buildStringListFromRange(first, last); return buildChoiceFromStringList(listOfIntegers); } //------------------------------------------------------------------- // Utility function to create a Choice from a list of Strings //------------------------------------------------------------------- public static Choice buildChoiceFromStringList(String[] list) { Choice choice = new Choice(); setChoiceList(choice, list); return choice; } //------------------------------------------------------------------- // Utility function to create an array of Strings containing a range // of integer values //------------------------------------------------------------------- public static String[] buildStringListFromRange(int first, int last) { String[] list = new String[last - first + 1]; for (int i=first; i <= last; i++) { list[i-first] = String.valueOf(i); } return list; } //------------------------------------------------------------------- // Utility function to set the list of Choice items from a String[] //------------------------------------------------------------------- public static void setChoiceList(Choice choice, String[] list) { choice.removeAll(); for (int i=0; i < list.length; i++) { choice.add(list[i]); } } //------------------------------------------------------------------- // Select Choice items to reflect date property //------------------------------------------------------------------- protected void reflectNewDate() { int currentDay,currentMonth,currentYear; currentDay = calendar.get(calendar.DAY_OF_MONTH); // 1-based currentMonth = calendar.get(Calendar.MONTH); // 0-based currentYear = calendar.get(Calendar.YEAR); // handle feb and leap years setDaysChoice(currentYear, currentMonth); dayChoice.select(currentDay -1 ); monthChoice.select(currentMonth); yearChoice.select(currentYear -FIRST_YEAR); } //------------------------------------------------------------------- // Modify days Choice component so that it lists the correct number of // days for this year and month. //------------------------------------------------------------------- protected void setDaysChoice(int currentYear, int currentMonth) { if ( currentMonth == Calendar.FEBRUARY) { if (calendar.isLeapYear(currentYear)) { setDaysChoiceLastDay(DAYS_IN_LEAP_FEB); } else { setDaysChoiceLastDay(DAYS_IN_FEBRUARY); } } else { setDaysChoiceLastDay( numDays[currentMonth] ); } } //------------------------------------------------------------------- // Preferred size settings so we don't get squashed //------------------------------------------------------------------- public Dimension preferredSize() { // 1.0-style return new Dimension(200,40); } public Dimension getPreferredSize() { // 1.1-style return preferredSize(); } //------------------------------------------------------------------- // The ItemListener interface method //------------------------------------------------------------------- public void itemStateChanged(ItemEvent itemEvent) { Choice choice; int day, month, year; int maxDaysForMonth; // find out which Choice fired the item selection event choice = (Choice) itemEvent.getSource(); // note the new state of each Choice selection year = yearChoice.getSelectedIndex() + FIRST_YEAR; month = monthChoice.getSelectedIndex(); day = dayChoice.getSelectedIndex(); // 0..n-1 // if user selected a new month or a new year, // update the days Choice to reflect the correct max # of days for month if ( choice == monthChoice || choice == yearChoice) { // set correct # of days for month (incl feb & leap years) setDaysChoice(year, month); // re-select old day if possible, otherwise last day of month maxDaysForMonth = dayChoice.getItemCount(); day = Math.min(day, maxDaysForMonth-1 ); dayChoice.select(day); } // Now that we've taken care of Feb and other classic calendar details // change our date property to reflect GUI and issue PropertyChangeEvent Date prevDate = getDate(); day = dayChoice.getSelectedIndex() + 1; // year and month already set calendar.set(year, month, day); butler.firePropertyChange("date", prevDate, getDate()); } //------------------------------------------------------------------- // PropertyChange Listeners management //------------------------------------------------------------------- public void addPropertyChangeListener(PropertyChangeListener listener) { butler.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { butler.removePropertyChangeListener(listener); } //------------------------------------------------------------------- // Self-test code //------------------------------------------------------------------- public static void main (String[] args) { Frame window; DateGUI dateEditor; window = new Frame("Date editor tester"); dateEditor = new DateGUI(); window.add("Center", dateEditor); window.setSize(new Dimension(210,70)); window.setVisible(true); } } // End of Class DateGUI