org.jdesktop.swingx.calendar.SingleDaySelectionModel Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id: SingleDaySelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $
*
* Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx.calendar;
import java.util.Date;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jdesktop.swingx.event.DateSelectionEvent.EventType;
import org.jdesktop.swingx.util.Contract;
/**
* DateSelectionModel which allows a single selection only.
*
* Temporary quick & dirty class to explore requirements as needed by
* a DatePicker. Need to define the states more exactly. Currently
*
*
takes all Dates as-are (that is the normalized is the same as the given):
* selected, unselectable, lower/upper bounds
* interprets any Date between the start/end of day of the selected as selected
* interprets any Date between the start/end of an unselectable date as unselectable
* interprets the lower/upper bounds as being the start/end of the given
* dates, that is any Date after the start of day of the lower and before the end of
* day of the upper is selectable.
*
*
* @author Jeanette Winzenburg
*/
public class SingleDaySelectionModel extends AbstractDateSelectionModel {
private SortedSet selectedDates;
private SortedSet unselectableDates;
/**
* Instantiates a SingleDaySelectionModel with default locale.
*/
public SingleDaySelectionModel() {
this(null);
}
/**
* Instantiates a SingleSelectionModel with the given locale. If the locale is
* null, the Locale's default is used.
*
* PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this
* with components anyway?
*
* @param locale the Locale to use with this model, defaults to Locale.default()
* if null.
*/
public SingleDaySelectionModel(Locale locale) {
super(locale);
this.selectedDates = new TreeSet();
this.unselectableDates = new TreeSet();
}
/**
* {@inheritDoc}
*/
@Override
public SelectionMode getSelectionMode() {
return SelectionMode.SINGLE_SELECTION;
}
/**
* {@inheritDoc}
*
* Implemented to do nothing.
*
*/
@Override
public void setSelectionMode(final SelectionMode selectionMode) {
}
//---------------------- selection ops
/**
* {@inheritDoc}
*
* Implemented to call setSelectionInterval with startDate for both
* parameters.
*/
@Override
public void addSelectionInterval(Date startDate, Date endDate) {
setSelection(startDate);
}
/**
* {@inheritDoc}
*
* PENDING JW: define what happens if we have a selection but the interval
* isn't selectable.
*/
@Override
public void setSelectionInterval(Date startDate, Date endDate) {
setSelection(startDate);
}
/**
* {@inheritDoc}
*/
@Override
public void removeSelectionInterval(Date startDate, Date endDate) {
Contract.asNotNull(startDate, "date must not be null");
if (isSelectionEmpty()) return;
if (isSelectionInInterval(startDate, endDate)) {
selectedDates.clear();
fireValueChanged(EventType.DATES_REMOVED);
}
}
/**
* Checks and returns whether the selected date is contained in the interval
* given by startDate/endDate. The selection must not be empty when
* calling this method.
*
* This implementation interprets the interval between the start of the day
* of startDay to the end of the day of endDate.
*
* @param startDate the start of the interval, must not be null
* @param endDate the end of the interval, must not be null
* @return true if the selected date is contained in the interval
*/
protected boolean isSelectionInInterval(Date startDate, Date endDate) {
if (selectedDates.first().before(startOfDay(startDate))
|| (selectedDates.first().after(endOfDay(endDate)))) return false;
return true;
}
/**
* Selects the given date if it is selectable and not yet selected.
* Does nothing otherwise.
* If this operation changes the current selection, it will fire a
* DateSelectionEvent of type DATES_SET.
*
* @param date the Date to select, must not be null.
*/
protected void setSelection(Date date) {
Contract.asNotNull(date, "date must not be null");
if (isSelectedStrict(date)) return;
if (isSelectable(date)) {
selectedDates.clear();
// PENDING JW: use normalized
selectedDates.add(date);
fireValueChanged(EventType.DATES_SET);
}
}
/**
* Checks and returns whether the given date is contained in the selection.
* This differs from isSelected in that it tests for the exact (normalized)
* Date instead of for the same day.
*
* @param date the Date to test.
* @return true if the given date is contained in the selection,
* false otherwise
*
*/
private boolean isSelectedStrict(Date date) {
if (!isSelectionEmpty()) {
// PENDING JW: use normalized
return selectedDates.first().equals(date);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Date getFirstSelectionDate() {
return isSelectionEmpty() ? null : selectedDates.first();
}
/**
* {@inheritDoc}
*/
@Override
public Date getLastSelectionDate() {
return isSelectionEmpty() ? null : selectedDates.last();
}
/**
* Returns a boolean indicating whether the given date is selectable.
*
* @param date the date to check for selectable, must not be null.
* @return true if the given date is selectable, false if not.
*/
public boolean isSelectable(Date date) {
if (outOfBounds(date)) return false;
return !inUnselectables(date);
}
/**
* @param date
* @return
*/
private boolean inUnselectables(Date date) {
for (Date unselectable : unselectableDates) {
if (isSameDay(unselectable, date)) return true;
}
return false;
}
/**
* Returns a boolean indication whether the given date is below
* the lower or above the upper bound.
*
* @param date
* @return
*/
private boolean outOfBounds(Date date) {
if (belowLowerBound(date)) return true;
if (aboveUpperBound(date)) return true;
return false;
}
/**
* @param date
* @return
*/
private boolean aboveUpperBound(Date date) {
if (upperBound != null) {
return endOfDay(upperBound).before(date);
}
return false;
}
/**
* @param date
* @return
*/
private boolean belowLowerBound(Date date) {
if (lowerBound != null) {
return startOfDay(lowerBound).after(date);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void clearSelection() {
if (isSelectionEmpty()) return;
selectedDates.clear();
fireValueChanged(EventType.SELECTION_CLEARED);
}
/**
* {@inheritDoc}
*/
@Override
public SortedSet getSelection() {
return new TreeSet(selectedDates);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSelected(Date date) {
Contract.asNotNull(date, "date must not be null");
if (isSelectionEmpty()) return false;
return isSameDay(selectedDates.first(), date);
}
/**
* {@inheritDoc}
*
* Implemented to return the date itself.
*/
@Override
public Date getNormalizedDate(Date date) {
return new Date(date.getTime());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSelectionEmpty() {
return selectedDates.isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public SortedSet getUnselectableDates() {
return new TreeSet(unselectableDates);
}
/**
* {@inheritDoc}
*/
@Override
public void setUnselectableDates(SortedSet unselectables) {
Contract.asNotNull(unselectables, "unselectable dates must not be null");
this.unselectableDates.clear();
for (Date unselectableDate : unselectables) {
removeSelectionInterval(unselectableDate, unselectableDate);
unselectableDates.add(unselectableDate);
}
fireValueChanged(EventType.UNSELECTED_DATES_CHANGED);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isUnselectableDate(Date date) {
return !isSelectable(date);
}
}