/** * DateEntryPanel provides a data entry panel for a Java Date.<p> * * Copyright &copy; 1998-89 Martin Minow. All Rights Reserved.<p> * * Permission to use, copy, modify, and redistribute this software and its * documentation for personal, non-commercial use is hereby granted provided that * this copyright notice and appropriate documentation appears in all copies. This * software may not be distributed for fee or as part of commercial, "shareware," * and/or not-for-profit endevors including, but not limited to, CD-ROM collections, * online databases, and subscription services without specific license.<p> * * Based on DateGUI.java in Mastering JavaBeans. * @see <a href="http://www.sybase.com">Sybase</a>, click on *  "Catalog>Updates>Mastering JavaBeans". * @author <a href="mailto:minow@pobox.com">Martin Minow</a> * @version 1.0a0 * Set tabs every 8 characters. * * Bugs: This should probably be in a utility path, rather than in * the astro bean path. * */package org.minow.applets.sunsphere;import java.beans.*;import java.util.*;import java.awt.*;import java.awt.event.*;import java.text.*;public class DateEntryPanel extends PropertyPanel	implements ItemListener{    /*     * These are the property events we distribute.     */    public static final String DATE_CHANGE  = "date";        /*     * Since this panel will be used for the Astro Bean, we limit     * the date range to values reasonable for the underlying     * astronomical procedures. These constants could reasonably     * be turned into properties, of course.     */    public static final int FIRST_YEAR	= 1978;    public static final int LAST_YEAR	= 2005;        protected GregorianCalendar		calendar;    protected Locale			locale;        /*     * These are the popup-menu selections that we construct     * and display. lastMaxDay minimizes menu rebuilding.     */    protected Choice			yearChoice;    protected Choice			monthChoice;    protected Choice			dayChoice;    protected int			lastDayInMonth = -1;            public DateEntryPanel()    {	this(new Date());    }    public DateEntryPanel(	    Date		initialDate	)    {	locale	    = Locale.getDefault();	calendar    = new GregorianCalendar(locale);	/*	 * Build the initial popup menus.	 */	yearChoice  = buildYearChoice();	yearChoice.addItemListener(this);	monthChoice = buildMonthChoice();	monthChoice.addItemListener(this);	dayChoice   = buildDayChoice();	dayChoice.addItemListener(this);	/*	 * Construct the display Panel. We might consider	 * using the locale information to build the	 * data in the user's preferred organization, but	 * ISO date seems to be more appropriate for	 * astronomical data.	 */	GridBagLayout gridbag	= new GridBagLayout();	setLayout(gridbag);	GridBagConstraints c	= new GridBagConstraints();	c.gridy			= 0;	c.anchor		= GridBagConstraints.WEST;	c.fill			= GridBagConstraints.NONE;	addField(c, yearChoice);	addField(c, monthChoice);	addField(c, dayChoice);	/*	 * Display the current date in the menu.	 */	setDate(initialDate);    }    private void addField(	    GridBagConstraints	c,	    Component		component	)    {	add(component);	((GridBagLayout) getLayout()).setConstraints(component, c);    }    /*     * Accessor functions.     */    public void setDate(	    Date	    newDate	)    {    	setDate(newDate, true);    }    public void setDate(    	    Date		newDate,    	    boolean		informClients    	)    {	Date oldDate	    = getDate();	calendar.setTime(newDate);	reflectNewDate();	if (informClients) {	    firePropertyChange(DATE_CHANGE, oldDate, getDate());	}    }    public Date getDate()    {	return (calendar.getTime());    }    /*     * Build menus.     */    protected Choice buildYearChoice()    {	Choice years	    = buildChoiceFromRange(FIRST_YEAR, LAST_YEAR);	return (years);    }    protected Choice buildMonthChoice()    {	String[] monthNames = (new DateFormatSymbols(locale)).getShortMonths();	Choice months	    = buildChoiceFromStringList(monthNames);	return (months);    }    protected Choice buildDayChoice()    {	Choice days	    = buildChoiceFromRange(1, 31);	return (days);    }    /*     * Update the choices to reflect the new date. This is called at     * initialization, and whenever the date changes.     */    protected void reflectNewDate()    {	int currentYear	    = calendar.get(Calendar.YEAR);	int currentMonth    = calendar.get(Calendar.MONTH);	    /* 0-based	*/	int currentDay	    = calendar.get(Calendar.DAY_OF_MONTH);  /* 1-based	*/	/*	 * Fix the month end and fix February for leap years.	 */	setLastDayInMonth(currentYear, currentMonth);	dayChoice.select(currentDay - 1);	monthChoice.select(currentMonth);	yearChoice.select(currentYear - FIRST_YEAR);    }    /*     * Implement the ItemListener interface.     */    public void itemStateChanged(	    ItemEvent	event	)    {	Choice choice	    = (Choice) event.getSource();	int currentYear	    = calendar.get(Calendar.YEAR);	int currentMonth    = calendar.get(Calendar.MONTH);	    /* 0-based	*/	int currentDay	    = calendar.get(Calendar.DAY_OF_MONTH);  /* 1-based	*/	/*	 * Remember the current state of each selection.	 */	int newYear	= yearChoice.getSelectedIndex() + FIRST_YEAR;	int newMonth	= monthChoice.getSelectedIndex();	    /* 0-based	*/	int newDay	= dayChoice.getSelectedIndex() + 1;	    /* 1-based	*/	/*	 * If the user changes the month or year, update the days Choice	 * to reflect the correct maximum number of days in this month.	 */	if (choice == monthChoice || choice == yearChoice) {	    /*	     * If the user had selected a 31 day month, but changed to a	     * shorter day, force the selection to the last day of the month.	     */	    int newLastDay = setLastDayInMonth(newYear, newMonth);	    if (newDay > newLastDay) {		dayChoice.select(newLastDay - 1);	    }	}	/*	 * Now that we've handled the menu updating, change our	 * date property to reflect the new values and issue the	 * PropertyChangeEvent to the listeners.	 */	Date oldDate		= getDate();	newDay			= dayChoice.getSelectedIndex() + 1;	calendar.set(newYear, newMonth, newDay);	firePropertyChange(DATE_CHANGE, oldDate, getDate());    }    /*     * Make sure that the days choice has the correct number of     * days for this month.     */    protected int setLastDayInMonth(	    int		    year,	    int		    month	)    {	GregorianCalendar temp = (GregorianCalendar) calendar.clone();	/*	 * To get the last day in the desired month, we get the zero'th day	 * in the next month. This is a sick, but apparently necessary, hack.	 */	temp.set(year, month + 1, 0);	int lastDayInMonth  = temp.get(Calendar.DAY_OF_MONTH);	/* 1-based  */	// System.out.println("Month: " + month + ", last day: " + lastDayInMonth);	if (lastDayInMonth != this.lastDayInMonth) {	    String[] list   = buildStringListFromRange(1, lastDayInMonth);	    setChoiceList(dayChoice, list);	    this.lastDayInMonth = lastDayInMonth;	}	return (lastDayInMonth);    }	    /*     * Utility functions.     */    /*     * Create a choice of integers.     */    public static Choice buildChoiceFromRange(	    int		first,	    int		last	)    {	String[] list	= buildStringListFromRange(first, last);	Choice choice	= buildChoiceFromStringList(list);	return (choice);    }    /*     * Create a choice from a list of strings.     */    public static Choice buildChoiceFromStringList(	    String[]	list	)    {	Choice choice	= new Choice();	setChoiceList(choice, list);	return (choice);    }    /*     * Create an array of strings containing a range of integers.     */    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);    }    /*     * Set the choice list item to the argument value.     */    public static void setChoiceList(	    Choice	choice,	    String[]	list	)    {	choice.removeAll();	for (int i = 0; i < list.length; i++) {	    choice.add(list[i]);	}    }    /*     * Test     */    public static void main(	    String[]	    args	)    {	final Label	    label   = new Label();	Frame		    window  = new Frame("DateEntryPanel test");	window.addWindowListener(	    new WindowAdapter()	    {		public void windowClosing(			WindowEvent	event		    )		{		    System.exit(0);		}	    }	);	DateEntryPanel   dateEntryPanel  = new DateEntryPanel();	dateEntryPanel.addPropertyChangeListener(	    new PropertyChangeListener()	    {		public void propertyChange(			PropertyChangeEvent event		    )		{		    Date date	= (Date) event.getNewValue();		    label.setText(date.toString());		}	    }	);	window.add("Center",	dateEntryPanel);	window.add("South",	label);	window.setSize(new Dimension(240, 90));	window.setVisible(true);    }}