2. Using a JDateComponent

JDateComponent is the foundation for all date components, and provides the following features for all of its descendants:

This section uses the application shown below to explore all these capabilities. Although the demo application contains a customized instance of JMonthView, the capabilities discussed in this section are inherited by all of JDateComponent's subclasses.

Figure 2.2. A demo application for a JDateComponent

A demo application for a JDateComponent

The top left component is the customized month view. The bottom right text component is an instance of JTextArea, which serves as a log that reports all changes made to the date selection model of the month view.

Try this:

  1. Run DateComponentDemo using Java Web Start or consult the source code for yourself.

  2. Select a date with mouse or keyboard. Information about date selection changes is displayed in the log text area. Dates marked with a red X are disabled and cannot be selected.

  3. Click on the Selection Model combo box and choose "Disable at your choice". Now toggle the "Mark displayed dates" and click on the dates that you want to be disabled. These dates will be marked with a red X and cannot be selected. Click again on "Mark displayed dates" to stop disabling dates.

  4. Change the displayed month using the two top arrow buttons or the popup menu associated with the name of the month. The month is also changed if a trailing date is selected.

  5. Click on the None button to remove all selected dates.

  6. Use the SPACE key to select the next selectable date. This is a custom key binding associated with the component.

2.1. Selecting a Date

By definition, a JDateComponent manages one or more dates by means of its date selection model. A date could be selected directly by using its date selection model or its setSelectedDate method. The following code shows how to select a date:

//use the component's API directly
component.setSelectedDate(date);

//use the component's supporting date selection model
component.getDateSelectionModel().setDateSelectionInterval(date, date);

Although dates can be selected using the underlying date selection model, the setSelectedDate method makes the code cleaner. Not only that but this method can also be used to specify a null date (empty selection), if allowed. The following code shows how to select a null date:

//use the component's API directly
component.setSelectedDate(null);

//use the component's supporting date selection model
component.getDateSelectionModel().removeAllDates();

JDateComponent is the foundation of all date selection components and it provides the setSelectedDate method because it is common for all of them. Other components might supply their own selection methods. For instance, JMonth provides the method setSelectedDates which allows to select more than one date.

2.2. Allowing Empty Selection or Null Dates

Date selection means not only selecting one or more dates, but also selecting no date at all. By empty selection we mean no selected date. Empty selection is also known as null date selection.

The DateComponentDemo program configures the month view to accept empty date selections. Although this is enabled by default, it is shown here for learning purposes.

The following line of code enables empty date selection for the month view date component:

try {
  component.setEmptySelectionAllowed(true);
} catch (Exception e) {
}

When empty selection is not allowed, no matter what operation is performed, at least one date is kept as selected. Changing the model from empty selection allowed to empty selection not allowed there must be at least one date selected because otherwise the model would be in an inconsistent state.

2.3. Concepts: About Date Selection Models

The date components use a selection mechanism to control dates' selection. If you want to know all the details about this mechanism, the javadoc has a detailed description.

A date component's selection model is an instance of a class that implements DateSelectionModel interface. The services this model offers are:

  • Controls the selection type (single, single interval or multiple interval)

  • Manages selected dates

  • Imposes restrictions on selectable dates

  • Controls if empty selection (null date) is allowed

  • Notifies listeners about date selection changes

The com.standbysoft.component.date package provides an AbstractDateSelectionModel that contains an implementation of almost all methods and a DefaultDateSelectionModel that is the selection model used by default in all date components.

If you find these implementations are not enough, it is recommended that you extend the default one to provide the extra functionality you need. Generally, this extra functionality is related to date restriction which is explained below.

2.4. Listening for Changes on Date Selection Model

Sometimes you might just want to check what happens with the selection model. Perhaps to see when the selected dates change.

The DateComponentDemo program uses a date selection listener to display a message when the selected dates change. The following line of code registers an instance of MyDateSelectionListener as a listener on the month view's date selection model:

component.addDateSelectionListener(new MyDateSelectionListener());

Here's the implementation of MyDateSelectionListener:

class MyDateSelectionListener implements DateSelectionListener {
  public void dateSelectionChanged(DateSelectionEvent evt) {
    messageArea.append("[" + evt.getFirstDate() + ", " + evt.getLastDate() + "]\n");
  }
  public void disabledDatesChanged(DateSelectionEvent evt) {}
  public void selectionModeChanged(DateSelectionEvent evt) {}
  public void emptySelectionAllowedChanged(DateSelectionEvent evt) {}
}

You can also extend DateSelectionAdapter if you do not need to provide an implementation for all the methods of the interface as the adapter has an empty implementation for all of them.

2.5. Implementing a Date Selection Model

Often the business logic imposes restrictions on the dates that can be selected by users. Possible solutions are to validate the selected dates against the list of selectable dates or to make it impossible in the first place for those dates to be selected. The date selection model helps you implement the second approach in a flexible manner.

It is most likely that you will not have to create your own model, at least not from scratch by implementing the DateSelectionModel interface. But you will need a custom date selection model if you have to provide your own logic for date restriction.

The DateComponentDemo program uses a custom date selection model to forbid the selection of weekend dates. The following line of code registers an instance of MyDateSelectionModel as a date selection model for month view:

DefaultDateSelectionModel model = new MyDateSelectionModel();
model.setMinimumAllowed(new Date());
component.setDateSelectionModel(model);

Here's the implementation of MyDateSelectionModel:

class MyDateSelectionModel extends DefaultDateSelectionModel {
  private Calendar cal = Calendar.getInstance();

  public boolean isDisabled(Date date) {
    cal.setTime(date);
    return (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) || (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY);
  }
}

In addition to restricting weekend dates, all the dates previous to current date are also restricted. The setMinimumAllowed method which is available from classes extended from AbstractDateSelectionModel is used to accomplish this.

2.6. Listening for Action Events

Action events are a complementary mechanism for outside world notification. For instance, date selection events are fired as the date selection model changes but action events are fired when something happens with the component that cannot be notified somehow else.

Because the action events are generic, one can differentiate between them using the name of the command. Each date component defines its own command names. Context information about the event is supplied by the component itself.

For now, only the JMonth and JMonthView components fire action events when a selectable date is double clicked.

component.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    messageArea.append(e + "\n");
  }
});

2.7. Associating Date Actions with Key Strokes

The month view in the DateComponentDemo supports an extra key binding not provided by default. Pressing SPACE selects the next selectable date.

The following code adds the SPACE key binding to the month view:

InputMap im = component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = component.getActionMap();

KeyStroke keyForward = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
im.put(keyForward, JDateComponent.selectNextDayAction);
am.put(JDateComponent.selectNextDayAction, new JDateComponent.RollDateAction(Calendar.DATE, true));

A JDateComponent contains a few other actions that act on the date selection model, changing the selected dates.

2.8. Disabling the Component

Sometimes, users should only see the selected days and not select them. For this, you can disable the calendar to receive any kind of input (mouse or keyboard).

You can enable or disable a date component using the setEnabled method. To find out whether it is enabled or not use isEnabled method. The following line of code disables a component:

component.setEnabled(false);