001/* 002 * $Id: DefaultDateSelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ 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 */ 021package org.jdesktop.swingx.calendar; 022 023import java.util.ArrayList; 024import java.util.Calendar; 025import java.util.Date; 026import java.util.Locale; 027import java.util.SortedSet; 028import java.util.TreeSet; 029 030import org.jdesktop.swingx.event.DateSelectionEvent.EventType; 031import org.jdesktop.swingx.util.Contract; 032 033/** 034 * 035 * @author Joshua Outwater 036 */ 037public 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 @Override 064 public SelectionMode getSelectionMode() { 065 return selectionMode; 066 } 067 068 /** 069 * {@inheritDoc} 070 */ 071 @Override 072 public void setSelectionMode(final SelectionMode selectionMode) { 073 this.selectionMode = selectionMode; 074 clearSelection(); 075 } 076 077 078//------------------- selection ops 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 public void addSelectionInterval(Date startDate, Date endDate) { 084 if (startDate.after(endDate)) { 085 return; 086 } 087 boolean added = false; 088 switch (selectionMode) { 089 case SINGLE_SELECTION: 090 if (isSelected(startDate)) return; 091 clearSelectionImpl(); 092 added = addSelectionImpl(startDate, startDate); 093 break; 094 case SINGLE_INTERVAL_SELECTION: 095 if (isIntervalSelected(startDate, endDate)) return; 096 clearSelectionImpl(); 097 added = addSelectionImpl(startDate, endDate); 098 break; 099 case MULTIPLE_INTERVAL_SELECTION: 100 if (isIntervalSelected(startDate, endDate)) return; 101 added = addSelectionImpl(startDate, endDate); 102 break; 103 default: 104 break; 105 } 106 if (added) { 107 fireValueChanged(EventType.DATES_ADDED); 108 } 109 } 110 111 /** 112 * {@inheritDoc} 113 */ 114 @Override 115 public void setSelectionInterval(final Date startDate, Date endDate) { 116 if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) { 117 if (isSelected(startDate)) return; 118 endDate = startDate; 119 } else { 120 if (isIntervalSelected(startDate, endDate)) return; 121 } 122 clearSelectionImpl(); 123 if (addSelectionImpl(startDate, endDate)) { 124 fireValueChanged(EventType.DATES_SET); 125 } 126 } 127 128 /** 129 * Checks and returns if the single date interval bounded by startDate and endDate 130 * is selected. This is useful only for SingleInterval mode. 131 * 132 * @param startDate the start of the interval 133 * @param endDate the end of the interval, must be >= startDate 134 * @return true the interval is selected, false otherwise. 135 */ 136 private boolean isIntervalSelected(Date startDate, Date endDate) { 137 if (isSelectionEmpty()) return false; 138 return selectedDates.first().equals(startDate) 139 && selectedDates.last().equals(endDate); 140 } 141 142 /** 143 * {@inheritDoc} 144 */ 145 @Override 146 public void removeSelectionInterval(final Date startDate, final Date endDate) { 147 if (startDate.after(endDate)) { 148 return; 149 } 150 151 long startDateMs = startDate.getTime(); 152 long endDateMs = endDate.getTime(); 153 ArrayList<Date> datesToRemove = new ArrayList<Date>(); 154 for (Date selectedDate : selectedDates) { 155 long selectedDateMs = selectedDate.getTime(); 156 if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) { 157 datesToRemove.add(selectedDate); 158 } 159 } 160 161 if (!datesToRemove.isEmpty()) { 162 selectedDates.removeAll(datesToRemove); 163 fireValueChanged(EventType.DATES_REMOVED); 164 } 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public void clearSelection() { 172 if (isSelectionEmpty()) return; 173 clearSelectionImpl(); 174 fireValueChanged(EventType.SELECTION_CLEARED); 175 } 176 177 private void clearSelectionImpl() { 178 selectedDates.clear(); 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public SortedSet<Date> getSelection() { 186 return new TreeSet<Date>(selectedDates); 187 } 188 189 /** 190 * {@inheritDoc} 191 */ 192 @Override 193 public Date getFirstSelectionDate() { 194 return isSelectionEmpty() ? null : selectedDates.first(); 195 } 196 197 /** 198 * {@inheritDoc} 199 */ 200 @Override 201 public Date getLastSelectionDate() { 202 return isSelectionEmpty() ? null : selectedDates.last(); 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 @Override 209 public boolean isSelected(final Date date) { 210 Contract.asNotNull(date, "date must not be null"); 211 return selectedDates.contains(date); 212 } 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public Date getNormalizedDate(Date date) { 219 return new Date(date.getTime()); 220 } 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override 226 public boolean isSelectionEmpty() { 227 return selectedDates.isEmpty(); 228 } 229 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override 235 public SortedSet<Date> getUnselectableDates() { 236 return new TreeSet<Date>(unselectableDates); 237 } 238 239 /** 240 * {@inheritDoc} 241 */ 242 @Override 243 public void setUnselectableDates(SortedSet<Date> unselectableDates) { 244 this.unselectableDates = unselectableDates; 245 for (Date unselectableDate : this.unselectableDates) { 246 removeSelectionInterval(unselectableDate, unselectableDate); 247 } 248 fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); 249 } 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override 255 public boolean isUnselectableDate(Date date) { 256 return upperBound != null && upperBound.getTime() < date.getTime() || 257 lowerBound != null && lowerBound.getTime() > date.getTime() || 258 unselectableDates != null && unselectableDates.contains(date); 259 } 260 261 262 private boolean addSelectionImpl(final Date startDate, final Date endDate) { 263 boolean hasAdded = false; 264 calendar.setTime(startDate); 265 Date date = calendar.getTime(); 266 while (date.before(endDate) || date.equals(endDate)) { 267 if (!isUnselectableDate(date)) { 268 hasAdded = true; 269 selectedDates.add(date); 270 } 271 calendar.add(Calendar.DATE, 1); 272 date = calendar.getTime(); 273 } 274 return hasAdded; 275 } 276 277 278}