/*
* DefaultDateChooseModel.java
*
* Created on 20 ��� 2006 �., 20:02
*
*/
package datechooser.model;
import datechooser.events.*;
import datechooser.model.exeptions.IncompatibleDataExeption;
import datechooser.model.multiple.Period;
import datechooser.model.multiple.PeriodSet;
import static datechooser.beans.locale.LocaleUtils.getErrorsLocaleString;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.util.*;
import javax.swing.event.EventListenerList;
/**
* Dafault date selection model.
* Uses one fully visible month and partially two neighbour months.
* Provides basic functionality for month scrolling.
* Only selection functions are abstract.<br>
* ������ ������ ���� �� ���������.
* ������������ ������ ����� � �������� ���������� � ���������.
* ������������ ��� ������� ���������������� �� ��������� �������.
* ���������� ������ ������� ����������������� ������.
* @author Androsov Vadim
* @since 1.0
* @see datechooser.model.DateChoose
*/
public abstract class AbstractDateChooseModel implements DateChoose {
private int rowsCount;
private int colsCount;
private int selRow;
private int selCol;
private boolean isValid;
private boolean enabled;
private boolean autoScroll;
private boolean showNeighbourMonth;
private Locale locale;
private int firstWeekDay;
private PropertyChangeSupport changeSupport;
protected boolean changeEventsOn;
private EventListenerList listenerList = new EventListenerList();
/**
* ������ ������� ���� �� ��������� ��� ������.
*/
private Calendar first;
/**
* ����������� ���������� ��� ������ ����.
* ==null - ������� ����, ��� ����������� �����������.
*/
private Calendar minConstraint;
/**
* ������������ ���������� ��� ������ ����.
* ==null - ������� ����, ��� ����������� �����������.
*/
private Calendar maxConstraint;
/**
* ������� ����.
*/
private Calendar current;
private Calendar cursor;
private Calendar defaultDate;
private Calendar tempDate;
private PeriodSet forbidden = null;
private boolean locked;
private boolean nothingAllowed;
protected abstract void selectColumn(int column);
/**
* Constructor for abstract model.<br>
* ����������� ����������� ������ ������.
* @param current Current date.<br>
* ������� ����.
* @param rowsCount Rows count in day selection grid.<br>
* ���������� ����� � ����� ������ ����.
* @param colsCount Columns count in day selection grid.<br>
* ���������� ������� � ����� ������ ����.
*/
public AbstractDateChooseModel(Calendar current, int rowsCount, int colsCount) {
changeSupport = new PropertyChangeSupport(this);
setChangeEventsOn(false);
this.current = (Calendar)current.clone();
tempDate = (Calendar)current.clone();
first = (Calendar)current.clone();
defaultDate = (Calendar)current.clone();
cursor = (Calendar)current.clone();
forbidden = new PeriodSet();
setAutoScroll(true);
setShowNeighbourMonth(true);
setEnabled(true);
setLocked(false);
setNothingAllowed(true);
defaultDate = (Calendar) current.clone();
this.rowsCount = rowsCount;
this.colsCount = colsCount;
setLocale(Locale.getDefault());
initFirst();
invalidate();
setMinConstraint(null);
setMaxConstraint(null);
setChangeEventsOn(true);
}
public int getRowsCount() {
return rowsCount;
}
public int getColsCount() {
return colsCount;
}
public boolean isCursor(int row, int column) {
tempDate = getCellDate(row, column);
return DateUtils.equals(tempDate, getCurrent());
}
public CellState getCellState(int row, int column) {
tempDate = getCellDate(row, column);
if (tempDate.get(Calendar.MONTH) !=
getNextMonth(first.get(Calendar.MONTH))) {
return CellState.NORMAL_SCROLL;
}
if (!isEnabled()) {
return CellState.UNACCESSIBLE;
}
if (!checkConstraints(tempDate) || isDateForbidden(tempDate)) {
return CellState.UNACCESSIBLE;
}
if (isSelected(tempDate)) {
return CellState.SELECTED;
}
if ((getDefaultDate() != null) && (DateUtils.equals(tempDate, getDefaultDate()))) {
return CellState.NOW;
}
return CellState.NORMAL;
}
public String getCellCaption(int row, int column) {
return getCellDate(row, column).get(Calendar.DAY_OF_MONTH) + "";
}
public Calendar getCellDate(int row, int column) {
//����� ����� ������� �����������!
tempDate.setTime(first.getTime());
tempDate.add(Calendar.DAY_OF_MONTH, (row ) * 7 + column);
return tempDate;
}
public void setConstraints(Calendar min, Calendar max) {
setMinConstraint(min);
setMaxConstraint(max);
}
/**
* Note: returns validation flag and <b>resets</b> it.<br>
* ��������! ��������� ������ ������������� ����������, ���������� ��� -
* ���� ������������� �� ����, �� ������ �� ��������.
*/
public boolean needsFullValidation() {
if (isValid) {
return false;
} else {
isValid = false;
return true;
}
}
public boolean select(int row, int column) {
if ((row < 0) && (column >= 0)) {
selectColumn(column);
}
if (!isEnabled()) return false;
tempDate = getCellDate(row, column);
return select(tempDate);
}
private boolean isInVisibleMonth(Calendar aDate) {
int visibleMonth = getNextMonth(first.get(Calendar.MONTH));
return (aDate.get(Calendar.MONTH) == visibleMonth) &&
(aDate.get(Calendar.YEAR) ==
first.get(Calendar.YEAR) + ((visibleMonth == 0) ? 1 : 0));
}
public boolean select(Calendar aDate) {
if (aDate == null) {
return true; //���� ��� �� ���� �������
}
if (!isEnabled()) return false;
tempDate.setTime(aDate.getTime());
if ((!isInVisibleMonth(tempDate)) && (!isAutoScroll())) {
return false;
}
if (DateUtils.equals(tempDate, getCurrent())) {
return false;
}
if (!checkConstraints(tempDate)) {
return false;
}
getCurrent().setTime(tempDate.getTime());
selRow = getSelectedRow();
selCol = getSelectedColumn();
if (!isInVisibleMonth(tempDate)) {
initFirst();
invalidate();
}
firePropertyChange("selected", null, aDate);
fireCursorMove();
return true;
}
/**
* ���������� ���������� ���� �� ������� �������� �� ��������.
*/
private int getDaysPassed() {
return first.getActualMaximum(Calendar.DAY_OF_MONTH) -
first.get(Calendar.DAY_OF_MONTH) + 1 +
getCurrent().get(Calendar.DAY_OF_MONTH);
}
private int getSelectedRow() {
return getDaysPassed() / getColsCount();
}
private int getSelectedColumn() {
return getDaysPassed() % getColsCount();
}
protected void invalidate() {
isValid = false;
}
/**
* ��������� ����������� �� ���������� ���� ���������� ���������.
* @param check ����������� ����
* @return ���� ���� ��������� ���������� ������.
*/
private boolean checkConstraints(Calendar check) {
if (getMinConstraint() != null) {
if (getMinConstraint().after(check)) {
return false;
}
}
if (getMaxConstraint() != null) {
if (getMaxConstraint().before(check)) {
return false;
}
}
// if (forbidden != null) {
// if (forbidden.contains(check)) {
// return false;
// }
// }
return true;
}
/**
* ���������� ������� �������� ��� ������ � ��������� � ������ ������.
*/
private int getDayOfWeek(int calendarConstant) {
return calendarConstant >= getFirstWeekDay() ?
(calendarConstant - getFirstWeekDay() + 1) :
((7 - getFirstWeekDay() + 1) + calendarConstant);
}
/**
* �������� ��������� �����.
* ��������!!! ������ ���������� � ����.
*/
private int getNextMonth(int month) {
if (month == 11) {
return 0;
}
return month + 1;
}
/**
* ���������� ����, ������� ��������� � ����� ������� ���� ���� ������.
* (���������� ������� ����)
*/
private void initFirst() {
initFirst(getCurrent());
}
/**
* ���������� ����, ������� ��������� � ����� ������� ���� ���� ������.
* (�������� - ����, ������� ������ ���� �����)
*/
private void initFirst(Calendar aDate) {
first.set(aDate.get(Calendar.YEAR), aDate.get(Calendar.MONTH), 1);
int cellsPassed = 0;
// �������� ���� ������ ��� ������� �����.
int firstDayOfWeek = getDayOfWeek(first.get(Calendar.DAY_OF_WEEK));
if (firstDayOfWeek == 1) {
/*
* ���� ������ ����� ������ �� �����������, ������ �������
* ������� �� ����������� ������.
* ��� ����� �������� ������.
*/
cellsPassed = 7;
} else {
/*
* ����� ������� � ������������ ���������� ����� �������� ������.
* ��� ����� �������� ������� ���� �� ���������� ������������.
*/
cellsPassed = firstDayOfWeek - 1;
}
first.add(Calendar.DAY_OF_MONTH, -cellsPassed);
/*
* ������ ���������� ��� �������� ������.
*/
cellsPassed += aDate.get(Calendar.DAY_OF_MONTH);
/*
* ����� �������� ������� ������� ����� ����� ������ �� ����������
* �������� (�������� ������� ������ ���� ��������).
*/
selRow = cellsPassed / getColsCount();
selCol = cellsPassed - getColsCount() * selRow - 1;
}
private static boolean equalsYearMonth(Calendar dat1, Calendar dat2) {
return (dat1.get(Calendar.YEAR) == dat2.get(Calendar.YEAR)) &&
(dat1.get(Calendar.MONTH) == dat2.get(Calendar.MONTH));
}
public Calendar getCurrent() {
return current;
}
public void showMonthYear(int month, int year) {
tempDate.set(year, month, 1);
initFirst(tempDate);
invalidate();
firePropertyChange("year_month", null, null);
}
public Calendar getVisibleDate() {
//����� �����, ��������� ��� ������� ����� ������ (first)
tempDate.setTime(first.getTime());
tempDate.set(Calendar.DAY_OF_MONTH, 1);
tempDate.add(Calendar.MONTH, 1);
return tempDate;
}
public Calendar getDefaultDate() {
return defaultDate;
}
public void setDefaultDate(Calendar aDate) throws IncompatibleDataExeption {
if (isDateForbidden(aDate)) {
throw new IncompatibleDataExeption(getErrorsLocaleString("Date_forbidden"));
}
defaultDate.setTime(aDate.getTime());
}
/**
* ����� �� �������� ���������� ��������� ���� (����, �������, ���).
*/
private void dateShift(int field, int shift) {
tempDate.setTime(getCurrent().getTime());
tempDate.add(field, shift);
select(tempDate);
}
public void shift(int rowShift, int columnShift) {
if ((rowShift == 0) && (columnShift == 0)) return;
dateShift(Calendar.DAY_OF_MONTH, rowShift * getColsCount() + columnShift);
}
public void monthShift(int shift) {
if (shift == 0) return;
dateShift(Calendar.MONTH, shift);
}
public void yearShift(int shift) {
if (shift == 0) return;
dateShift(Calendar.YEAR, shift);
}
public boolean isAutoScroll() {
return autoScroll;
}
public void setAutoScroll(boolean autoScroll) {
this.autoScroll = autoScroll;
}
public boolean isShowNeighbourMonth() {
return showNeighbourMonth;
}
public void setShowNeighbourMonth(boolean showNeighbourMonth) {
this.showNeighbourMonth = showNeighbourMonth;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
boolean oldEnabled = isEnabled();
this.enabled = enabled;
firePropertyChange("enabled", oldEnabled, enabled);
}
public PeriodSet getForbiddenSet() {
return forbidden;
}
protected boolean isForbiddenDefault(PeriodSet forbiddenPeriods) {
return (forbiddenPeriods != null) && (forbiddenPeriods.contains(getDefaultDate()));
}
public void setForbiddenSet(PeriodSet forbiddenPeriods) throws IncompatibleDataExeption {
if (isForbiddenDefault(forbiddenPeriods)) {
throw new IncompatibleDataExeption(getErrorsLocaleString("Forbidden_default"));
}
PeriodSet oldForbid = getForbiddenSet();
forbidden.set(forbiddenPeriods);
firePropertyChange("forbidDates", null, null);
}
public Iterable<Period> getForbidden() {
return forbidden.getPeriods();
}
public void setForbidden(Iterable<Period> forbiddenPeriods) {
forbidden.set(forbiddenPeriods);
firePropertyChange("forbidDates", null, null);
}
protected boolean isPeriodForbidden(Period period) {
return (forbidden != null) && forbidden.intersects(period);
}
protected boolean isDateForbidden(Calendar date) {
return (forbidden != null) && forbidden.contains(date);
}
public Calendar getMinConstraint() {
return minConstraint;
}
public void setMinConstraint(Calendar minConstraint) {
this.minConstraint = minConstraint;
}
public Calendar getMaxConstraint() {
return maxConstraint;
}
public void setMaxConstraint(Calendar maxConstraint) {
this.maxConstraint = maxConstraint;
}
public void setSelectedDate(Calendar aDate) {
select(aDate);
tryApplySelection();
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
if (locale == null) return;
Locale oldLocale = getLocale();
this.locale = locale;
setFirstWeekDay(Calendar.getInstance(locale).getFirstDayOfWeek());
initFirst();
firePropertyChange("locale", oldLocale, locale);
}
private int getFirstWeekDay() {
return firstWeekDay;
}
private void setFirstWeekDay(int firstWeekDay) {
this.firstWeekDay = firstWeekDay;
}
public void commit() {
fireCommit();
}
public final void tryApplySelection() {
if (isLocked()) return;
applySelection();
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public void setNothingAllowed(boolean allow) {
if (isNothingSelected() && (!allow)) return;
boolean wasAllowed = isNothingAllowed();
nothingAllowed = allow;
firePropertyChange("nothingAllowed", wasAllowed, isNothingAllowed());
}
public boolean isNothingAllowed() {
return nothingAllowed;
}
public boolean isChangeEventsOn() {
return changeEventsOn;
}
public void setChangeEventsOn(boolean changeEventsOn) {
this.changeEventsOn = changeEventsOn;
}
public void selectNothing() {
if (!isNothingAllowed()) return;
applySelectNothing();
}
public void addSelectionChangedListener(SelectionChangedListener listener) {
listenerList.add(SelectionChangedListener.class, listener);
}
public void removeSelectionChangedListener(SelectionChangedListener listener) {
listenerList.remove(SelectionChangedListener.class, listener);
}
public void fireSelectionChange() {
SelectionChangedEvent evt = new SelectionChangedEvent(this);
SelectionChangedListener[] listeners =
listenerList.getListeners(SelectionChangedListener.class);
for (SelectionChangedListener listener : listeners) {
listener.onSelectionChange(evt);
}
}
public void addCommitListener(CommitListener listener) {
listenerList.add(CommitListener.class, listener);
}
public void removeCommitListener(CommitListener listener) {
listenerList.remove(CommitListener.class, listener);
}
private void fireCommit() {
CommitEvent evt = new CommitEvent(this);
CommitListener[] listeners =
listenerList.getListeners(CommitListener.class);
for (CommitListener listener : listeners) {
listener.onCommit(evt);
}
}
public void addCursorMoveListener(CursorMoveListener listener) {
listenerList.add(CursorMoveListener.class, listener);
}
public void removeCursorMoveListener(CursorMoveListener listener) {
listenerList.remove(CursorMoveListener.class, listener);
}
protected void fireCursorMove() {
CursorMoveEvent evt = new CursorMoveEvent(this);
CursorMoveListener[] listeners =
listenerList.getListeners(CursorMoveListener.class);
for (CursorMoveListener listener : listeners) {
listener.onCursorMove(evt);
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void firePropertyChange(String name, Object oldValue, Object newValue) {
if (!isChangeEventsOn()) return;
changeSupport.firePropertyChange(name, oldValue, newValue);
}
/**
* ������ ������� ������� ������ ��� ��������. ��� ������ ������ ������� ��
* ���������� ��������� ������.
*/
protected abstract void applySelection();
/**
* ������� �� ������� �� ����� ����.
*/
protected abstract void applySelectNothing();
}