001 /*
002 * $Id: DefaultDateSelectionModel.java 3100 2008-10-14 22:33:10Z rah003 $
003 *
004 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021 package org.jdesktop.swingx.calendar;
022
023 import java.util.ArrayList;
024 import java.util.Calendar;
025 import java.util.Date;
026 import java.util.Locale;
027 import java.util.SortedSet;
028 import java.util.TreeSet;
029
030 import org.jdesktop.swingx.event.DateSelectionEvent.EventType;
031 import org.jdesktop.swingx.util.Contract;
032
033 /**
034 *
035 * @author Joshua Outwater
036 */
037 public class DefaultDateSelectionModel extends AbstractDateSelectionModel {
038 private SelectionMode selectionMode;
039 private SortedSet<Date> selectedDates;
040 private SortedSet<Date> unselectableDates;
041
042 /**
043 *
044 */
045 public DefaultDateSelectionModel() {
046 this(null);
047 }
048
049 /**
050 * <p>
051 *
052 * The selection mode defaults to SINGLE_SELECTION.
053 */
054 public DefaultDateSelectionModel(Locale locale) {
055 super(locale);
056 this.selectionMode = SelectionMode.SINGLE_SELECTION;
057 this.selectedDates = new TreeSet<Date>();
058 this.unselectableDates = new TreeSet<Date>();
059 }
060 /**
061 * {@inheritDoc}
062 */
063 public SelectionMode getSelectionMode() {
064 return selectionMode;
065 }
066
067 /**
068 * {@inheritDoc}
069 */
070 public void setSelectionMode(final SelectionMode selectionMode) {
071 this.selectionMode = selectionMode;
072 clearSelection();
073 }
074
075
076 //------------------- selection ops
077 /**
078 * {@inheritDoc}
079 */
080 public void addSelectionInterval(Date startDate, Date endDate) {
081 if (startDate.after(endDate)) {
082 return;
083 }
084 boolean added = false;
085 switch (selectionMode) {
086 case SINGLE_SELECTION:
087 if (isSelected(startDate)) return;
088 clearSelectionImpl();
089 added = addSelectionImpl(startDate, startDate);
090 break;
091 case SINGLE_INTERVAL_SELECTION:
092 if (isIntervalSelected(startDate, endDate)) return;
093 clearSelectionImpl();
094 added = addSelectionImpl(startDate, endDate);
095 break;
096 case MULTIPLE_INTERVAL_SELECTION:
097 if (isIntervalSelected(startDate, endDate)) return;
098 added = addSelectionImpl(startDate, endDate);
099 break;
100 default:
101 break;
102 }
103 if (added) {
104 fireValueChanged(EventType.DATES_ADDED);
105 }
106 }
107
108 /**
109 * {@inheritDoc}
110 */
111 public void setSelectionInterval(final Date startDate, Date endDate) {
112 if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) {
113 if (isSelected(startDate)) return;
114 endDate = startDate;
115 } else {
116 if (isIntervalSelected(startDate, endDate)) return;
117 }
118 clearSelectionImpl();
119 if (addSelectionImpl(startDate, endDate)) {
120 fireValueChanged(EventType.DATES_SET);
121 }
122 }
123
124 /**
125 * Checks and returns if the single date interval bounded by startDate and endDate
126 * is selected. This is useful only for SingleInterval mode.
127 *
128 * @param startDate the start of the interval
129 * @param endDate the end of the interval, must be >= startDate
130 * @return true the interval is selected, false otherwise.
131 */
132 private boolean isIntervalSelected(Date startDate, Date endDate) {
133 if (isSelectionEmpty()) return false;
134 return selectedDates.first().equals(startDate)
135 && selectedDates.last().equals(endDate);
136 }
137
138 /**
139 * {@inheritDoc}
140 */
141 public void removeSelectionInterval(final Date startDate, final Date endDate) {
142 if (startDate.after(endDate)) {
143 return;
144 }
145
146 long startDateMs = startDate.getTime();
147 long endDateMs = endDate.getTime();
148 ArrayList<Date> datesToRemove = new ArrayList<Date>();
149 for (Date selectedDate : selectedDates) {
150 long selectedDateMs = selectedDate.getTime();
151 if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) {
152 datesToRemove.add(selectedDate);
153 }
154 }
155
156 if (!datesToRemove.isEmpty()) {
157 selectedDates.removeAll(datesToRemove);
158 fireValueChanged(EventType.DATES_REMOVED);
159 }
160 }
161
162 /**
163 * {@inheritDoc}
164 */
165 public void clearSelection() {
166 if (isSelectionEmpty()) return;
167 clearSelectionImpl();
168 fireValueChanged(EventType.SELECTION_CLEARED);
169 }
170
171 private void clearSelectionImpl() {
172 selectedDates.clear();
173 }
174
175 /**
176 * {@inheritDoc}
177 */
178 public SortedSet<Date> getSelection() {
179 return new TreeSet<Date>(selectedDates);
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 public Date getFirstSelectionDate() {
186 return isSelectionEmpty() ? null : selectedDates.first();
187 }
188
189 /**
190 * {@inheritDoc}
191 */
192 public Date getLastSelectionDate() {
193 return isSelectionEmpty() ? null : selectedDates.last();
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 public boolean isSelected(final Date date) {
200 Contract.asNotNull(date, "date must not be null");
201 return selectedDates.contains(date);
202 }
203
204 /**
205 * {@inheritDoc}
206 */
207 public Date getNormalizedDate(Date date) {
208 return new Date(date.getTime());
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 public boolean isSelectionEmpty() {
215 return selectedDates.isEmpty();
216 }
217
218
219 /**
220 * {@inheritDoc}
221 */
222 public SortedSet<Date> getUnselectableDates() {
223 return new TreeSet<Date>(unselectableDates);
224 }
225
226 /**
227 * {@inheritDoc}
228 */
229 public void setUnselectableDates(SortedSet<Date> unselectableDates) {
230 this.unselectableDates = unselectableDates;
231 for (Date unselectableDate : this.unselectableDates) {
232 removeSelectionInterval(unselectableDate, unselectableDate);
233 }
234 fireValueChanged(EventType.UNSELECTED_DATES_CHANGED);
235 }
236
237 /**
238 * {@inheritDoc}
239 */
240 public boolean isUnselectableDate(Date date) {
241 return upperBound != null && upperBound.getTime() < date.getTime() ||
242 lowerBound != null && lowerBound.getTime() > date.getTime() ||
243 unselectableDates != null && unselectableDates.contains(date);
244 }
245
246
247 private boolean addSelectionImpl(final Date startDate, final Date endDate) {
248 boolean hasAdded = false;
249 calendar.setTime(startDate);
250 Date date = calendar.getTime();
251 while (date.before(endDate) || date.equals(endDate)) {
252 if (!isUnselectableDate(date)) {
253 hasAdded = true;
254 selectedDates.add(date);
255 }
256 calendar.add(Calendar.DATE, 1);
257 date = calendar.getTime();
258 }
259 return hasAdded;
260 }
261
262
263 }