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 }