JCalendar.java :  » ERP-CRM-Financial » Evaristo-4.0 » org » freixas » jcalendar » Java Open Source

Java Open Source » ERP CRM Financial » Evaristo 4.0 
Evaristo 4.0 » org » freixas » jcalendar » JCalendar.java
//**********************************************************************
// Package
//**********************************************************************

package org.freixas.jcalendar;

//**********************************************************************
// Import list
//**********************************************************************

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

import java.text.*;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;

/**
 * This class displays a panel through which a user can select a date
 * and/or time. A time-only selection can be used to select a
 * duration, as long as the duration is no longer than 23 hours, 59
 * minutes and 59 seconds.
 * <p>
 * The date is selected using a calendar display. The time is selected
 * using a date spinner.
 * <p>
 * In reality, both date and time are part of the same Date returned
 * by getDate() or getCalendar(). If you are selecting only the date,
 * you should ignore the time portion of the Date or Calendar. If you
 * are selecting only the time, ignore the date portion.
 * <p>
 * You can set the pattern used to display the time in the date
 * spinner. The pattern is the same as used by SimpleDateFormat. The
 * default format displays hours, minutes and seconds in a
 * locale-specific way ( some locales use AM/PM, some use a 24-hour
 * clock ). If you want to get a time duration, you will want to use a
 * pattern such as "HH:mm:ss" to eliminate the possibility of an AM/PM
 * field appearing. You can also use setTimePattern() to reduce the
 * precision of the time obtained ( e.g. "HH:mm" ).
 * <p>
 * When the calendar has focus, the following key bindings are
 * supported:
 * <ul>
 * <li>Left Arrow - Move back a month.
 * <li>Right Arrow - Move forward a month.
 * <li>Shift Left Arrow - Move back a year.
 * <li>Shift Right Arrow - Move forward a year.
 * <li>Delete - Unselect all dates ( only if isNullAllowed() is true ).
 * <li>Backspace - Same as Delete.
 * </ul>
 * This is in addition to using Tab and Enter to move through and
 * select the buttons.
 * <p>
 * The time field is divided into hour, minute, second and AM/PM
 * portions. You can select any portion and use the spinner arrows on
 * the right side of the field to increment or decrement that portion.
 * However, the entire time is being incremented or decremented, so
 * that incrementing 1:59:59 by one second will generate 2:00:00.
 * <p>
 * Due to a design limitation in JFormatedTextField, incrementing
 * 24:59:59 will <b>not</b> increment the day. A value in a
 * JFormatedTextField ( which is what the time field is ), only
 * calculates a date from the fields displayed. Since usually we
 * display a HH:mm:ss pattern, the JFormattedTextField will set the
 * date to a default value, not influenced by the date in the calendar
 * <p>
 * It is possible to pass in a time pattern that displays more than just
 * the time -- this is not advisable since the date portion displayed
 * in the JFormatedTextField will be ignored by the JCalendar
 * component.
 * <p>
 * When the time field has focus, the up/down arrow keys increment or
 * decrement the currently selected time portion, just like the
 * spinner keys. The left and right arrow keys can be used to move to
 * the next or previous portion.
 *
 * @see Calendar
 * @see Date
 * @see DateFormat
 * @see SimpleDateFormat
 * @author Antonio Freixas
 */

//Copyright  2003 Antonio Freixas
//All Rights Reserved.
//Copyright (c) 2004-2005 Carlos Correia
//All Rights Reserved.

public class JCalendar
extends JPanel
{
  
  //**********************************************************************
  // Public Constants
  //**********************************************************************
  
  /**
   * Used to indicate that this component should display the date.
   */
  
  public static final int DISPLAY_DATE = 0x01;
  
  /**
   * Used to indicate that this component should display the time.
   */
  
  public static final int DISPLAY_TIME = 0x02;
  
  //**********************************************************************
  // Private Constants
  //**********************************************************************
  
  private static final int MONTH_DECR_BUTTON  = 0;
  private static final int MONTH_INCR_BUTTON  = 1;
  private static final int YEAR_DECR_BUTTON  = 2;
  private static final int YEAR_INCR_BUTTON  = 3;
  
  //**********************************************************************
  // Private Members
  //**********************************************************************
  
  // This determines the components being displayed: the calendar, the
  // time spinner, or both
  
  int selectedComponents;
  
  // The calendar containing a selected day. The selected day may not be
  // always be displayed
  
  Calendar selectedCalendar;
  int selectedYear = -1;
  int selectedMonth = -1;
  int selectedDay = -1;
  int selectedHour = -1;
  private int selectedMinute = -1;
  private int selectedSecond = -1;
  
  // The calendar we display
  
  private Calendar displayCalendar;
  int displayYear;
  int displayMonth;
  
  // The locale to use
  
  Locale locale;
  
  // True if we display today's date
  
  private boolean isTodayDisplayed = false;
  
  // A null date is equivalent to having no date selected. Note that the
  // constructor selects the current date ( today )
  
  private boolean isNullAllowed = true;
  boolean isNullDate = true;
  
  // The time pattern used to set the format of the time spinner
  
  private String timePattern;
  
  // Components
  
  private JButton yearDecrButton;
  private JButton monthDecrButton;
  private JLabel monthYearLabel;
  private JButton monthIncrButton;
  private JButton yearIncrButton;
  
  private JLabel[] dayOfWeekLabels;
  private JToggleButton[][] dayButtons;
  private JToggleButton offScreenButton;
  private ButtonGroup dayGroup;
  
  SpinnerDateModel spinnerDateModel;
  private JSpinner spinner;
  
  // Date formats
  
  private DateFormat formatMonth;
  private DateFormat formatWeekDay;
  
  private String lastMonth;
  private String lastYear;
  
  // These maps are used to bind keyboard keys to methods
  
  static InputMap inputMap = new InputMap();
  static ActionMap actionMap = new ActionMap();
  
  // The input map maps a key to a name. The action map maps a name to
  // an action. The actions below map the action to a method call
  
  private static Action yearBackward = 
    new AbstractAction( "yearBackward" )
    {
      public void actionPerformed( ActionEvent e )
      {
        ((JCalendar)e.getSource()).yearBackward();
      }
    };
  
  private static Action yearForward = 
    new AbstractAction( "yearForward" )
    {
      public void actionPerformed( ActionEvent e )
      {
        ((JCalendar)e.getSource()).yearForward();
      }
    };
  
  private static Action monthBackward = 
    new AbstractAction( "montBackward" )
    {
      public void actionPerformed( ActionEvent e )
      {
        ((JCalendar)e.getSource()).monthBackward();
      }
    };
  
  private static Action monthForward = 
    new AbstractAction( "monthForward" )
    {
      public void actionPerformed( ActionEvent e )
      {
        ((JCalendar)e.getSource()).monthForward();
      }
    };
  
  private static Action setNullDate = 
    new AbstractAction( "setNullDate" )
    {
      public void actionPerformed( ActionEvent e )
      {
        ((JCalendar)e.getSource()).setDate( null );
      }
    };

  // The resource bundle
  
  static ResourceBundle bundle =
    ResourceBundle.getBundle( "org.freixas.jcalendar.Bundle" );

  private boolean displayDayPanel = true;
  
  //**********************************************************************
  // Static Constructors
  //**********************************************************************
  
  static
  {
    // Set up the input map that will be shared by all instances
    
    inputMap.put( KeyStroke.getKeyStroke( "BACK_SPACE" ), "setNullDate" );
    inputMap.put( KeyStroke.getKeyStroke( "DELETE" ), "setNullDate" );
    inputMap.put( KeyStroke.getKeyStroke( "shift LEFT" ), "yearBackward" );
    inputMap.put( KeyStroke.getKeyStroke( "shift RIGHT" ), "yearForward" );
    inputMap.put( KeyStroke.getKeyStroke( "LEFT" ), "monthBackward" );
    inputMap.put( KeyStroke.getKeyStroke( "RIGHT" ), "monthForward" );
    
    actionMap.put( "setNullDate", setNullDate );
    actionMap.put( "yearBackward", yearBackward );
    actionMap.put( "yearForward", yearForward );
    actionMap.put( "monthBackward", monthBackward );
    actionMap.put( "monthForward", monthForward );
  }
  
  //**********************************************************************
  // Constructors
  //**********************************************************************
  
  /**
   * Create an instance of JCalendar using the default calendar and
   * locale. Display the date but not the time. Don't display today's
   * date at the bottom of the panel.
   */
  
  public JCalendar()
  {
    this( 
      Calendar.getInstance(),
      Locale.getDefault(),
      DISPLAY_DATE,
      false,
      true, 
      null );
  }
  
  /**
   * Create an instance of JCalendar using the default calendar and
   * locale. Display the date but not the time. Don't display today's
   * date at the bottom of the panel.
   */
  
  public JCalendar( boolean displayDay )
  {
    this( 
      Calendar.getInstance(),
      Locale.getDefault(),
      DISPLAY_DATE,
      false,
      displayDay, 
      null );
  }
  
  /**
   * Create an instance of JCalendar using the default calendar and
   * locale. Display a calendar and/or a time spinner as requested ( to
   * display both use DISPLAY_DATE | DISPLAY_TIME ). Display today's
   * date if requested. Use the defult pattern to display the time in the
   * time spinner field ( if there is one ).
   *
   * @param selectedComponents Use DISPLAY_DATE, DISPLAY_TIME or
   *   ( DISPLAY_DATE | DISPLAY_TIME ).
   * @param isTodayDisplayed True if today's date should be displayed at
   *   the bottom of the panel.
   */
  
  public JCalendar( int selectedComponents, boolean isTodayDisplayed )
  {
    this( 
      Calendar.getInstance(),
      Locale.getDefault(),
      selectedComponents,
      isTodayDisplayed,
      true, 
      null );
  }
  
  /**
   * Create an instance of JCalendar using the given calendar and
   * locale. Display a calendar and/or a time spinner as requested ( to
   * display both use DISPLAY_DATE | DISPLAY_TIME ). Display today's
   * date if requested. Use the default pattern to display the time in the
   * time spinner field ( if there is one ).
   *
   * @param calendar The calendar to use.
   * @param locale The locale to use.
   * @param selectedComponents Use DISPLAY_DATE, DISPLAY_TIME or
   *   ( DISPLAY_DATE | DISPLAY_TIME ).
   * @param isTodayDisplayed True if today's date should be displayed at
   *   the bottom of the panel.
   */
  
  public JCalendar( 
    Calendar calendar,
    Locale locale,
    int selectedComponents,
    boolean isTodayDisplayed )
  {
    this( 
      calendar,
      locale,
      selectedComponents,
      isTodayDisplayed,
      true, 
      null );
  }
  
  /**
   * Create an instance of JCalendar using the given calendar and
   * locale. Display a calendar and/or a time spinner as requested ( to
   * display both use DISPLAY_DATE | DISPLAY_TIME ). Display today's
   * date if requested. Set the pattern used to display the time in the time
   * spinner field ( if there is one ). If null, use the default MEDIUM
   * format for the given locale. Patterns are from DateFormat and
   * SimpleDateFormat.
   *
   * @param calendar The calendar to use.
   * @param locale The locale to use.
   * @param selectedComponents Use DISPLAY_DATE, DISPLAY_TIME or
   *   ( DISPLAY_DATE | DISPLAY_TIME ).
   * @param isTodayDisplayed True if today's date should be displayed at
   *   the bottom of the panel.
   * @param timePattern The pattern used to display the time in the time
   *   spinner field.
   * @see DateFormat
   * @see SimpleDateFormat
   */
  
  public JCalendar( 
    Calendar calendar,
    Locale locale,
    int selectedComponents,
    boolean isTodayDisplayed,
    boolean displayDayPanel,
    String timePattern )
  {
    this.displayDayPanel = displayDayPanel;
    this.selectedCalendar = ( Calendar )calendar.clone();
    this.displayCalendar = ( Calendar )selectedCalendar.clone();
    this.selectedComponents = selectedComponents;
    if( (selectedComponents & (DISPLAY_DATE | DISPLAY_TIME)) == 0 )
    {
      throw new IllegalStateException( 
        bundle.getString( "IllegalStateException" ) );
    }
    
    this.locale = locale;
    this.isTodayDisplayed = isTodayDisplayed;
    
    if( ( selectedComponents & DISPLAY_TIME ) > 0 )
    {
      if( timePattern == null )
      {
        DateFormat timeFormat =
        DateFormat.getTimeInstance( DateFormat.MEDIUM, locale );
        this.timePattern = "HH:mm:ss";
        if( timeFormat instanceof SimpleDateFormat )
        {
          this.timePattern = ( ( SimpleDateFormat )timeFormat ).toPattern();
        }
      }
      else
      {
        this.timePattern = timePattern;
      }
    }
    
    createCalendarComponents();
    setDate( new Date() );
  }
  
  //**********************************************************************
  // Public
  //**********************************************************************
  
  /**
   * sets the Calendar to use.
   *
   * @param calendar The calendar to use.
   */
  
  public void setCalendar( Calendar calendar )
  {
    this.displayCalendar = ( Calendar )selectedCalendar.clone();
  }
  
  /**
   * Add a date listener. This listener will receive events each time
   * the selected date changes.
   *
   * @param listener The date listener to add.
   */
  
  public void
  addDateListener( 
  DateListener listener )
  {
    listenerList.add( DateListener.class, listener );
  }
  
  /**
   * Remove a date listener.
   *
   * @param listener The date listener to remove.
   */
  
  public void
  removeDateListener( 
  DateListener listener )
  {
    listenerList.remove( DateListener.class, listener );
  }
  
  /**
   * Get whether a null date is allowed.
   *
   * @return Whether a null date is allowed.
   */
  
  public boolean
  isNullAllowed()
  {
    return isNullAllowed;
  }
  
  /**
   * Set whether a null date is allowed. A null date means that no date
   * is selected. The user can select a null date by pressing DELETE
   * anywhere within the calendar.
   * <p>
   * If nulls are not allowed, a setDate( null ) will be ignored without
   * error. The DELETE key will do nothing.
   * <p>
   * If you switch from allowing nulls to not allowing nulls and the
   * current date is null, it will remain null until a date is selected.
   * <p>
   * The component default is to allow nulls.
   *
   * @param isNullAllowed Whether a null date is allowed.
   */
  
  public void setNullAllowed( boolean isNullAllowed )
  {
    this.isNullAllowed = isNullAllowed;
  }
  
  /**
   * Get the date currently displayed by the calendar panel. If no date
   * is selected, null is returned.
   *
   * @return The date currently displayed.
   * @see #getCalendar
   */
  
  public Date getDate()
  {
    if( isNullDate ) 
      return null;
    return selectedCalendar.getTime();
  }
  
  /**
   * Set the calendar panel to display the given date. This will fire a
   * DateEvent. The date may be null. If isNullAllowed() is true, then
   * all dates will be unselected. If isNullAllowed() is false, a null
   * date is ignored.
   *
   * @param date The date to set.
   */
  
  public void setDate(  Date date )
  {
    if( date == null )
    {
      
      // Ignore nulls if nulls aren't allowed
      
      if( !isNullAllowed ) return;
      
      if( !isNullDate )
      {
        isNullDate = true;
        selectedYear = -1;
        selectedMonth = -1;
        selectedDay = -1;
        selectedCalendar.set( Calendar.YEAR, 9999 );
        selectedCalendar.set( Calendar.MONTH, 9 );
        selectedCalendar.set( Calendar.DATE, 9 );
        selectedCalendar.set( Calendar.HOUR_OF_DAY, 0 );
        selectedCalendar.set( Calendar.MINUTE, 0 );
        selectedCalendar.set( Calendar.SECOND, 0 );
        updateCalendarComponents();
        fireDateChange();
      }
    }
    
    else
    {
      int oldYear = selectedYear;
      int oldMonth = selectedMonth;
      int oldDay = selectedDay;
      int oldHour = selectedHour;
      int oldMinute = selectedMinute;
      int oldSecond = selectedSecond;
      
      selectedCalendar.setTime( date );
      selectedYear = selectedCalendar.get( Calendar.YEAR );
      selectedMonth = selectedCalendar.get( Calendar.MONTH );
      selectedDay = selectedCalendar.get( Calendar.DATE );
      selectedHour = selectedCalendar.get( Calendar.HOUR_OF_DAY );
      selectedMinute = selectedCalendar.get( Calendar.MINUTE );
      selectedSecond = selectedCalendar.get( Calendar.SECOND );
      
      if( ( ( ( selectedComponents & DISPLAY_DATE ) > 0 ) &&
              oldDay != selectedDay ||
              oldMonth != selectedMonth ||
              oldYear != selectedYear ) ||
          ( ( ( selectedComponents & DISPLAY_TIME ) > 0 ) &&
            oldHour != selectedHour ||
            oldMinute != selectedMinute ||
            oldSecond != selectedSecond ) )
      {
        
        isNullDate = false;
        displayCalendar.setTime( date );
        updateCalendarComponents();
        fireDateChange();
      }
    }
  }
  
  /**
   * Reset the displayed date without changing the selected date. No
   * event occurs. A null date will reset to today's date. The display
   * date simply selects the calendar page ( month/year ) to display.
   *
   * @param date The date to display.
   */
  
  public void setDisplayDate( Date date )
  {
    if( date == null ) date = new Date();
    
    displayCalendar.setTime( date );
    int oldMonth = displayCalendar.get( Calendar.MONTH );
    int oldYear = displayCalendar.get( Calendar.YEAR );
    if( oldMonth != displayMonth || oldYear != displayYear )
    {
      updateCalendarComponents();
    }
  }
  
  /**
   * Get the pattern used to display the time in the time selection
   * spinner. This is null if the time is not displayed.
   *
   * @return The pattern used to display the time in the time selection
   *   spinner.
   */
  
  public String getTimePattern()
  {
    if( ( selectedComponents & DISPLAY_TIME ) != 0 )
    {
      return timePattern;
    }
    return null;
  }
  
  /**
   * Get a copy of the calendar used by this JCalendar. This calendar
   * will be set to the currently selected date, so it is an alternative
   * to the getDate() method. If no date is selected ( getDate() returns
   * null ), the calendar's selected date is 9/9/9999 and should not be
   * used.
   *
   * @return A copy of the calendar used by JCalendar.
   * @see #getDate
   */
  
  public Calendar getCalendar()
  {
    return ( Calendar )selectedCalendar.clone();
  }
  
  /**
   * Return the locale used by this JCalendar.
   *
   * @return The locale used by this JCalendar.
   */
  
  public Locale
  getLocale()
  {
    return locale;
  }
  
  /**
   * Return the components being displayed:
   * <code>
   *   ( getSelectedComponents() & DISPLAY_DATE ) > 0
   * </code>
   * means that the date calendar is being displayed.
   * <code>
   *  ( getSelectedComponents() & DISPLAY_TIME ) > 0
   * </code>
   * menas that the time spinner field is being displayed.
   *
   * @return The selected components.
   */
  
  public int
  getSelectedComponents()
  {
    return selectedComponents;
  }
  
  
  /**
   * Returns true if today's date is displayed at the bottom of the
   * calendar.
   *
   * @return True if today's date is displayed at the bottom of the
   *   calendar.
   */
  
  public boolean
  isTodayDisplayed()
  {
    return isTodayDisplayed;
  }
  
  /**
   * {@inheritDoc}
   */
  
  public void setEnabled( boolean b )
  {
    if( b != isEnabled() )
    {
      super.setEnabled( b );
      
      if( ( selectedComponents & DISPLAY_DATE ) > 0 )
      {
        yearDecrButton.setEnabled( b );
        monthDecrButton.setEnabled( b );
        monthYearLabel.setEnabled( b );
        monthIncrButton.setEnabled( b );
        yearIncrButton.setEnabled( b );
        
        if( displayDayPanel )
        {
          for( int day = 0; day < 7; day++ )
          {
            dayOfWeekLabels[day].setEnabled( b );
          }

          for( int row = 0; row < 6; row++ )
          {
            for( int day = 0; day < 7; day++ )
            {
              if( dayButtons[row][day].getText().length() > 0 )
              {
                dayButtons[row][day].setEnabled( b );
              }
            }
          }
        }
      }
      
      if( ( selectedComponents & DISPLAY_TIME ) > 0 )
      {
        spinner.setEnabled( b );
      }
    }
  }
  
  /**
   * {@inheritDoc}
   */
  
  public void addNotify()
  {
    setFont( null );
    Font font = getFont();
    
    font = font.deriveFont( ( float )( font.getSize2D() * 9.0 / 11.0 ) );
    setFont( font );
    
    if( displayDayPanel && (selectedComponents & DISPLAY_DATE) > 0 )
    {
      font = dayOfWeekLabels[0].getFont();
      font = font.deriveFont( ( float )( font.getSize2D() * 9.0 / 11.0 ) );
      for( int day = 0; day < 7; day++ )
      {
        dayOfWeekLabels[day].setFont( font );
      }
      
      font = dayButtons[0][0].getFont();
      font = font.deriveFont( ( float )( font.getSize2D() * 9.0 / 11.0 ) );
      for( int row = 0; row < 6; row++ )
      {
        for( int day = 0; day < 7; day++ )
        {
          dayButtons[row][day].setFont( font );
        }
      }
    }
    
    // Nothing special for time field
    
    super.addNotify();
  }
  
  // The default equals and hashCode methods are acceptable. In
  // general, two components are never equal unless they are the same
  // component.
  
  /**
   * {@inheritDoc}
   */
  
  protected String
  paramString()
  {
    String curDate;
    if( ( selectedComponents & DISPLAY_DATE ) == DISPLAY_DATE )
    {
      curDate = DateFormat.getDateInstance( 
      DateFormat.FULL, locale ).format( getDate() );
    }
    else if( ( selectedComponents & DISPLAY_TIME ) == DISPLAY_TIME )
    {
      curDate = DateFormat.getTimeInstance( 
      DateFormat.FULL, locale ).format( getDate() );
    }
    else
    {
      curDate = DateFormat.getDateTimeInstance( 
      DateFormat.FULL, DateFormat.FULL, locale ).format( getDate() );
    }
    
    return super.paramString() + ",selectedDate=" + curDate;
  }
  
  //**********************************************************************
  // Package Public
  //**********************************************************************
  
  private JCalendarCombo comboCalendar;
  
  /**
   * Get the JCalendarCombo component which uses this JCalendar in its
   * combo popup.
   *
   * @return The JCalendarCombo component which uses this JCalendar in
   *   its combo popup.
   * @see JCalendarCombo
   */
  
  JCalendarCombo
  getJCalendarComboParent()
  {
    return comboCalendar;
  }
  
  /**
   * Set the JCalendarCombo component which uses this JCalendar in its
   * combo popup.
   *
   * @param comboCalendar The JCalendarCombo component which uses this
   *   JCalendar in its combo popup.
   * @see JCalendarCombo
   */
  
  void
  setJCalendarComboParent( 
  JCalendarCombo comboCalendar )
  {
    this.comboCalendar = comboCalendar;
  }
  
  /**
   * Add the given input/action maps to the spinner.
   */
  
  void
  addSpinnerMaps( 
  InputMap sim,
  ActionMap sam )
  {
    sim.setParent( spinner.getInputMap( WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ) );
    spinner.setInputMap( WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, sim );
    sam.setParent( spinner.getActionMap() );
    spinner.setActionMap( sam );
  }
  
  //**********************************************************************
  // Protected
  //**********************************************************************
  
  /**
   * Fire a date change. This notifies all listeners that the date has
   * changed.
   */
  
  protected void fireDateChange()
  {
    // Guaranteed to return a non-null array
    
    Object[] listeners = listenerList.getListenerList();
    
    // Process the listeners last to first, notifying
    // those that are interested in this event
    
    for( int i = listeners.length - 2; i >= 0; i -= 2 )
    {
      if( listeners[i] == DateListener.class )
      {
        DateEvent dateEvent;
        if( isNullDate )
        {
          dateEvent = new DateEvent( this, null );
        }
        else
        {
          dateEvent = new DateEvent( this, selectedCalendar );
        }
        ( ( DateListener )listeners[i + 1] ).dateChanged( dateEvent );
      }
    }
  }
  
  //**********************************************************************
  // Private
  //**********************************************************************
  
  /**
   * Set up the calendar panel with the basic layout and components.
   * These are not date specific.
   */
  
  private void createCalendarComponents()
  {
    // The date panel will hold the calendar and/or the time spinner
    
    JPanel datePanel = new JPanel( new BorderLayout( 2, 2 ) );
    
    // Create the calendar if we are displaying a calendar
    
    if( ( selectedComponents & DISPLAY_DATE ) > 0 )
    {
      formatMonth = new SimpleDateFormat( "MMM", locale );
      formatWeekDay = new SimpleDateFormat( "EEE", locale );
      
      // Set up the shared keyboard bindings
      
      setInputMap( WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
      setActionMap( actionMap );
      
      // Set up the decrement buttons
      
      yearDecrButton =
        new JButton( 
          new ButtonAction( 
            "YearDecrButton",
            "YearDecrButtonMnemonic", "YearDecrButtonAccelerator",
            "YearDecrButtonImage",
            "YearDecrButtonShort", "YearDecrButtonLong",
            YEAR_DECR_BUTTON ) );
      monthDecrButton =
        new JButton( 
        new ButtonAction( 
          "MonthDecrButton",
          "MonthDecrButtonMnemonic", "MonthDecrButtonAccelerator",
          "MonthDecrButtonImage",
          "MonthDecrButtonShort", "MonthDecrButtonLong",
          MONTH_DECR_BUTTON ) );
      JPanel decrPanel =
        new JPanel( new FlowLayout( FlowLayout.LEFT, 2, 0 ) );
      decrPanel.add( yearDecrButton );
      decrPanel.add( monthDecrButton );
      
      // Set up the month/year label
      
      monthYearLabel = new JLabel();
      monthYearLabel.setHorizontalAlignment( JLabel.CENTER );
      
      // Set up the increment buttons
      
      monthIncrButton =
        new JButton( 
          new ButtonAction( 
            "MonthIncrButton",
            "MonthIncrButtonMnemonic", "MonthIncrButtonAccelerator",
            "MonthIncrButtonImage",
            "MonthIncrButtonShort", "MonthIncrButtonLong",
            MONTH_INCR_BUTTON  )  );
      yearIncrButton =
        new JButton( 
          new ButtonAction( 
            "YearIncrButton",
            "YearIncrButtonMnemonic", "YearIncrButtonAccelerator",
            "YearIncrButtonImage",
            "YearIncrButtonShort", "YearIncrButtonLong",
            YEAR_INCR_BUTTON ) );
      JPanel incrPanel =
        new JPanel( new FlowLayout( FlowLayout.LEFT, 2, 0 ) );
      incrPanel.add( monthIncrButton );
      incrPanel.add( yearIncrButton );
      
      // Put them all together
      
      JPanel monthYearNavigator = new JPanel( new BorderLayout( 2, 2 ) );
      monthYearNavigator.add( decrPanel, BorderLayout.WEST );
      monthYearNavigator.add( monthYearLabel );
      monthYearNavigator.add( incrPanel, BorderLayout.EAST );

      JPanel dayPanel = null;
      if( displayDayPanel )
      {
        // Set up the day panel

        dayPanel = new JPanel( new GridLayout( 7, 7 ) );
        int firstDay = displayCalendar.getFirstDayOfWeek();

        // Get the week day labels. The following technique is used so
        // that we can start the calendar on the right day of the week and
        // we can get the week day labels properly localized

        Calendar temp = Calendar.getInstance( locale );
        temp.set( 2000, Calendar.MARCH, 15 );
        while( temp.get( Calendar.DAY_OF_WEEK ) != firstDay )
        {
          temp.add( Calendar.DATE, 1 );
        }
        dayOfWeekLabels = new JLabel[7];
        for( int i = 0; i < 7; i++ )
        {
          Date date = temp.getTime();
          String dayOfWeek = formatWeekDay.format( date );
          dayOfWeekLabels[i] = new JLabel( dayOfWeek );
          dayOfWeekLabels[i].setHorizontalAlignment( JLabel.CENTER );
          dayPanel.add( dayOfWeekLabels[i] );
          temp.add( Calendar.DATE, 1 );
        }

        // Add all the day buttons

        dayButtons = new JToggleButton[6][7];
        dayGroup = new ButtonGroup();
        DayListener dayListener = new DayListener();
        for( int row = 0; row < 6; row++ )
        {
          for( int day = 0; day < 7; day++ )
          {
            dayButtons[row][day] = new JToggleButton();
            dayButtons[row][day].addItemListener( dayListener );
            dayPanel.add( dayButtons[row][day] );
            dayGroup.add( dayButtons[row][day] );
          }
        }

        // We add this special button to the button group, so we have a
        // way of unselecting all the visible buttons

        offScreenButton = new JToggleButton( "X" );
        dayGroup.add( offScreenButton );

        // Combine the navigators and days
      }
      datePanel.add( monthYearNavigator, BorderLayout.NORTH );
      if( displayDayPanel )
      {
        datePanel.add( dayPanel );
      }
    }
    
    // Create the time spinner field if we are displaying the time
    
    if( ( selectedComponents & DISPLAY_TIME ) > 0 )
    {
      
      // Create the time component
      
      spinnerDateModel = new SpinnerDateModel();
      spinnerDateModel.addChangeListener( new TimeListener() );
      spinner = new JSpinner( spinnerDateModel );
      
      JSpinner.DateEditor dateEditor =
      new JSpinner.DateEditor( spinner, timePattern );
      dateEditor.getTextField().setEditable( false );
      dateEditor.getTextField().setHorizontalAlignment( JTextField.CENTER );
      spinner.setEditor( dateEditor );
      
      // Set the input/action maps for the spinner. ( Only BACK_SPACE
      // seems to work! )
      
      InputMap sim = new InputMap();
      sim.put( KeyStroke.getKeyStroke( "BACK_SPACE" ), "setNullDate" );
      sim.put( KeyStroke.getKeyStroke( "DELETE" ), "setNullDate" );
      sim.setParent( spinner.getInputMap( WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ) );
      
      ActionMap sam = new ActionMap();
      sam.put( 
      "setNullDate",
      new AbstractAction( "setNullDate" )
      {
        public void actionPerformed( ActionEvent e )
        {
          JCalendar.this.setDate( null );
        }} );
        sam.setParent( spinner.getActionMap() );
        
        spinner.setInputMap( WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, sim );
        spinner.setActionMap( sam );
        
        // Create a special panel for the time display
        
        JPanel timePanel = new JPanel( new FlowLayout( FlowLayout.CENTER, 2, 2 ) );
        timePanel.add( spinner );
        
        // Now add it to the bottom
        
        datePanel.add( timePanel, BorderLayout.SOUTH );
    }
    
    setLayout( new BorderLayout( 2, 2 ) );
    add( datePanel );
    
    // Add today's date at the bottom of the calendar/time, if needed
    
    if( isTodayDisplayed )
    {
      Object[] args =
      {
        new Date()
      };
      String todaysDate =
        MessageFormat.format( bundle.getString( "Today" ), args );
      JLabel todaysLabel = new JLabel( todaysDate );
      todaysLabel.setHorizontalAlignment( JLabel.CENTER );
      
      // Add today's date at the very bottom
      
      add( todaysLabel, BorderLayout.SOUTH );
    }
  }
  
  /**
   * Update the calendar panel to display the currently selected date.
   */
  
  void updateCalendarComponents()
  {
    if( ( selectedComponents & DISPLAY_DATE ) > 0 )
    {
      
      if( displayDayPanel )
      {
        // Unselect all visible dates

        offScreenButton.setSelected( true );
      }
      // Get the display date. We only need the month and year
      
      displayMonth = displayCalendar.get( Calendar.MONTH );
      displayYear = displayCalendar.get( Calendar.YEAR );
      
      // Get the localized display month name and year
      
      String month = formatMonth.format( displayCalendar.getTime() );
      String year = Integer.toString( displayYear );
      
      {
        Object[] args =
        {
          month,
          year
        };
        monthYearLabel.setText( 
        MessageFormat.format( 
        bundle.getString( "MonthYearTitle" ), args ) );
      }
      
      // If the month or year have changed, we need to re-lay out
      // the days. Otherwise, we don't
      
      if( displayDayPanel &&
          (!month.equals( lastMonth ) || !year.equals( lastYear )) )
      {
        
        // Create a temporary calendar that we will use to
        // determine where day 1 goes and how many days there are
        // in this month
        
        Calendar temp = ( Calendar )displayCalendar.clone();
        temp.set( Calendar.DATE, 1 );
        
        int dayOfWeek = temp.get( Calendar.DAY_OF_WEEK );
        int firstDay = temp.getFirstDayOfWeek();
        
        // Determine how many blank slots occur before day 1 of this
        // month
        
        int dayPtr;
        for( dayPtr = 0; dayPtr < 7; dayPtr++ )
        {
          int curDay = ( ( firstDay - 1 ) + dayPtr ) % 7 + 1;
          if( curDay != dayOfWeek )
          {
            dayButtons[0][dayPtr].setText( "" );
            dayButtons[0][dayPtr].setEnabled( false );
          }
          else
          {
            break;
          }
        }
        
        // Determine the number of days in this month
        
        int maxDays = temp.getActualMaximum( Calendar.DATE );
        
        // Fill in the days
        
        int row = 0;
        for( int day = 1; day <= maxDays; day++ )
        {
          dayButtons[row][dayPtr].setText( Integer.toString( day ) );
          dayButtons[row][dayPtr].setEnabled( true );
          
          // If this is the selected date, select the button;
          // otherwise, deselect it
          
          if( day == selectedDay &&
          displayMonth == selectedMonth &&
          displayYear == selectedYear )
          {
            dayButtons[row][dayPtr].setSelected( true );
          }
          else
          {
            dayButtons[row][dayPtr].getModel().setSelected( false );
          }
          
          // Wrap as needed
          
          dayPtr = ( dayPtr + 1 ) % 7;
          if( dayPtr == 0 ) row++;
        }
        
        // Set the blanks slots after the last day
        
        while( row < 6 )
        {
          dayButtons[row][dayPtr].setText( "" );
          dayButtons[row][dayPtr].setEnabled( false );
          dayButtons[row][dayPtr].getModel().setSelected( false );
          dayPtr = ( dayPtr + 1 ) % 7;
          if( dayPtr == 0 ) row++;
        }
      }
    }
    
    // Update the time component, if displayed
    
    if( ( selectedComponents & DISPLAY_TIME ) > 0 )
    {
      
      // If no date is selected, we set the date used by the time
      // field to today @ noon. We also make the field insensitive
      // -- the user must pick a non-null date before being able to
      // change the time ( unless all we have is a time field )
      
      if( isNullDate )
      {
        Calendar temp = ( Calendar )selectedCalendar.clone();
        temp.setTime( new Date() );
        temp.set( Calendar.HOUR, 12 );
        temp.set( Calendar.MINUTE, 0 );
        temp.set( Calendar.SECOND, 0 );
        spinnerDateModel.setValue( temp.getTime() );
        spinner.setEnabled( ( selectedComponents & DISPLAY_DATE ) == 0 );
      }
      
      // If a date is selected, use it
      
      else
      {
        spinner.setEnabled( JCalendar.this.isEnabled() );
        spinnerDateModel.setValue( selectedCalendar.getTime() );
        spinnerDateModel.setStart( null );
        spinnerDateModel.setEnd( null );
        spinner.revalidate();
      }
    }
  }
  
  /**
   * Move backward one year.
   */
  
  void yearBackward()
  {
    displayCalendar.add( Calendar.YEAR, -1 );
    updateCalendarComponents();
    if( !displayDayPanel )
    {
      selectedCalendar.add( Calendar.YEAR, 1 );
      selectedCalendar.set( Calendar.DAY_OF_MONTH, 1 );
      fireDateChange();
    }
  }
  
  /**
   * Move forward one year.
   */
  
  void yearForward()
  {
    displayCalendar.add( Calendar.YEAR, 1 );
    updateCalendarComponents();
    if( !displayDayPanel )
    {
      selectedCalendar.add( Calendar.YEAR, 1 );
      selectedCalendar.set( Calendar.DAY_OF_MONTH, 1 );
      fireDateChange();
    }
  }
  
  /**
   * Move backward one month.
   */
  
  void monthBackward()
  {
    displayCalendar.add( Calendar.MONTH, -1 );
    updateCalendarComponents();
    if( !displayDayPanel )
    {
      selectedCalendar.add( Calendar.MONTH, -1 );
      selectedCalendar.set( Calendar.DAY_OF_MONTH, 1 );
      fireDateChange();
    }
  }
  
  /**
   * Move forward one month.
   */
  
  void monthForward()
  {
    displayCalendar.add( Calendar.MONTH, 1 );
    updateCalendarComponents();
    if( !displayDayPanel )
    {
      selectedCalendar.add( Calendar.MONTH, 1 );
      selectedCalendar.set( Calendar.DAY_OF_MONTH, 1 );
      fireDateChange();
    }
  }
  
  //**********************************************************************
  // Inner Classes
  //**********************************************************************
  
  //**********************************************************************
  // ButtonAction
  //**********************************************************************
  
  // This inner class is used to define the action associated with a
  // button.
  
  private class ButtonAction
  extends AbstractAction
  {
    
    public final static String SMALL = "16.gif";
    public final static String LARGE = "24.gif";
    
    ButtonAction( 
      String name,
      String mnemonic,
      String accelerator,
      String image,
      String shortDescription,
      String longDescription,
      int actionId )
    {
      if( name != null )
      {
        putValue( Action.NAME, bundle.getString( name ) );
      }
      
      if( mnemonic != null )
      {
        String mnemonicString = bundle.getString( mnemonic );
        if( mnemonicString != null && mnemonicString.length() > 0 )
        {
          putValue( Action.MNEMONIC_KEY,
          new Integer( bundle.getString( mnemonic ).charAt( 0 ) ) );
        }
      }
      
      if( accelerator != null )
      {
        String acceleratorString = bundle.getString( accelerator );
        if( accelerator != null && acceleratorString.length() > 0 )
        {
          putValue( 
          Action.ACCELERATOR_KEY,
          KeyStroke.getKeyStroke( acceleratorString ) );
        }
      }
      
      if( image != null )
      {
        String imageName = bundle.getString( image );
        if( imageName != null && imageName.length() > 0 )
        {
          imageName = "images/" + imageName + SMALL;
          URL url = this.getClass().getResource( imageName );
          if( url != null )
          {
            putValue( Action.SMALL_ICON, new ImageIcon( url ) );
          }
        }
      }
      
      if( shortDescription != null )
      {
        String shortString = bundle.getString( shortDescription );
        if( shortString != null & shortString.length() > 0 )
        {
          putValue( Action.SHORT_DESCRIPTION, shortString );
        }
      }
      
      if( longDescription != null )
      {
        String longString = bundle.getString( longDescription );
        if( longString != null && longString.length() > 0 )
        {
          putValue( Action.LONG_DESCRIPTION, longString );
        }
      }
      
      putValue( "buttonAction", new Integer( actionId ) );
    }
    
    public void actionPerformed( ActionEvent e )
    {
      Integer value = ( Integer )getValue( "buttonAction" );
      switch ( value.intValue() )
      {
        case YEAR_DECR_BUTTON:
          yearBackward();
          break;
        case YEAR_INCR_BUTTON:
          yearForward();
          break;
        case MONTH_DECR_BUTTON:
          monthBackward();
          break;
        case MONTH_INCR_BUTTON:
          monthForward();
          break;
      }
    }
    
  }
  
  //**********************************************************************
  // DayListener
  //**********************************************************************
  
  // This is called when a day button is pressed
  
  private class DayListener
  implements ItemListener
  {
    
    public void
    itemStateChanged( 
    ItemEvent e )
    {
      if( e.getStateChange() == ItemEvent.SELECTED )
      {
        int oldDay = selectedDay;
        int oldMonth = selectedMonth;
        int oldYear = selectedYear;
        
        String dayString = ( ( JToggleButton )e.getItem() ).getText();
        try
        {
          selectedDay = Integer.parseInt( dayString );
        }
        catch ( Exception ex )
        {
          selectedDay = 1;
        }
        selectedMonth = displayMonth;
        selectedYear = displayYear;
        
        if( oldDay != selectedDay ||
        oldMonth != selectedMonth ||
        oldYear != selectedYear )
        {
          
          isNullDate = false;
          selectedCalendar.set( Calendar.YEAR, selectedYear );
          selectedCalendar.set( Calendar.MONTH, selectedMonth );
          selectedCalendar.set( Calendar.DATE, selectedDay );
          
          updateCalendarComponents();
          fireDateChange();
        }
      }
    }
    
  }
  
  //**********************************************************************
  // TimeListener
  //**********************************************************************
  
  // Called whenever the time field changes
  
  private class TimeListener
  implements ChangeListener
  {
    
    Calendar lastTemp = null;
    Calendar temp = Calendar.getInstance( locale );
    
    public void
    stateChanged( 
    ChangeEvent e )
    {
      Date date = spinnerDateModel.getDate();
      
      // We only care about the time portion of the field. If no date is
      // selected, we shouldn't be able to change the time ( unless there
      // is no date to select )
      
      if( !isNullDate || ( selectedComponents & DISPLAY_DATE ) == 0 )
      {
        temp.setTime( date );
        temp.set( Calendar.YEAR, selectedCalendar.get( Calendar.YEAR ) );
        temp.set( Calendar.MONTH, selectedCalendar.get( Calendar.MONTH ) );
        temp.set( Calendar.DATE, selectedCalendar.get( Calendar.DATE ) );
        
        // Make sure we change the time only if necessary
        
        if( lastTemp == null || !lastTemp.equals( temp ) )
        {
          setDate( temp.getTime() );
          lastTemp = ( Calendar )temp.clone();
        }
      }
    }
    
  }
  
  //**********************************************************************
  // End Inner Classes
  //**********************************************************************
  
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.