| 1 7 package org.jdesktop.swing.calendar; 8 9 import java.awt.*; 10 import java.awt.event.*; 11 import java.text.SimpleDateFormat ; 12 import java.util.Calendar ; 13 import java.util.Date ; 14 import java.util.Locale ; 15 import java.util.TimeZone ; 16 import javax.swing.*; 17 import javax.swing.border.Border ; 18 import org.jdesktop.swing.calendar.*; 19 20 97 public class JXMonthView extends JComponent { 98 99 public static final int NO_SELECTION = 0; 100 101 public static final int SINGLE_SELECTION = 1; 102 103 public static final int MULTIPLE_SELECTION = 2; 104 108 public static final int WEEK_SELECTION = 3; 109 110 114 protected Insets _monthStringInsets = new Insets(0,8,0,8); 115 116 private static final int MONTH_DROP_SHADOW = 1; 117 private static final int MONTH_LINE_DROP_SHADOW = 2; 118 private static final int WEEK_DROP_SHADOW = 4; 119 120 private int _boxPaddingX = 3; 121 private int _boxPaddingY = 3; 122 private static final int CALENDAR_SPACING = 10; 123 private static final int DAYS_IN_WEEK = 7; 124 private static final int MONTHS_IN_YEAR = 12; 125 126 130 private long _firstDisplayedDate; 131 private int _firstDisplayedMonth; 132 private int _firstDisplayedYear; 133 134 private long _lastDisplayedDate; 135 private Font _derivedFont; 136 137 138 private long _startSelectedDate = -1; 139 140 141 private long _endSelectedDate = -1; 142 143 144 private long _pivotDate = -1; 145 146 147 private Rectangle _selectedDateRect = new Rectangle(); 148 149 150 private int _numCalCols = 1; 151 152 153 private int _numCalRows = 1; 154 155 private int _minCalCols = 1; 156 private int _minCalRows = 1; 157 private long _today; 158 private long[] _flaggedDates; 159 private int _selectionMode = SINGLE_SELECTION; 160 private int _boxHeight; 161 private int _boxWidth; 162 private int _calendarWidth; 163 private int _calendarHeight; 164 private int _firstDayOfWeek = Calendar.SUNDAY; 165 private int _startX; 166 private int _startY; 167 private int _dropShadowMask = MONTH_DROP_SHADOW; 168 private boolean _dirty = false; 169 private boolean _antiAlias = false; 170 private boolean _ltr; 171 private boolean _asKirkWouldSay_FIRE = false; 172 private Calendar _cal; 173 private String [] _daysOfTheWeek; 174 private static String [] _monthsOfTheYear; 175 private Dimension _dim = new Dimension(); 176 private Rectangle _bounds = new Rectangle(); 177 private Rectangle _dirtyRect = new Rectangle(); 178 private Color _todayBackgroundColor; 179 private Color _monthStringBackground = Color.LIGHT_GRAY; 180 private Color _selectedBackground = Color.LIGHT_GRAY; 181 private SimpleDateFormat _dayOfMonthFormatter = new SimpleDateFormat ("d"); 182 private String _actionCommand = "selectionChanged"; 183 private Timer _todayTimer = null; 184 185 189 public JXMonthView() { 190 this(new Date ().getTime()); 191 } 192 193 200 public JXMonthView(long initialTime) { 201 super(); 202 203 _ltr = getComponentOrientation().isLeftToRight(); 204 205 _cal = Calendar.getInstance(getLocale()); 207 _cal.setFirstDayOfWeek(_firstDayOfWeek); 208 209 _cal.set(Calendar.HOUR_OF_DAY, 0); 211 _cal.set(Calendar.MINUTE, 0); 212 _cal.set(Calendar.SECOND, 0); 213 _cal.set(Calendar.MILLISECOND, 0); 214 _today = _cal.getTimeInMillis(); 215 216 _cal.setTimeInMillis(initialTime); 217 setFirstDisplayedDate(_cal.getTimeInMillis()); 218 219 _cal.set(Calendar.MONTH, _cal.getMinimum(Calendar.MONTH)); 221 _cal.set(Calendar.DAY_OF_MONTH, 222 _cal.getActualMinimum(Calendar.DAY_OF_MONTH)); 223 _monthsOfTheYear = new String [MONTHS_IN_YEAR]; 224 SimpleDateFormat fullMonthNameFormatter = 225 new SimpleDateFormat ("MMMM"); 226 for (int i = 0; i < MONTHS_IN_YEAR; i++) { 227 _monthsOfTheYear[i] = 228 fullMonthNameFormatter.format(_cal.getTime()); 229 _cal.add(Calendar.MONTH, 1); 230 } 231 232 setOpaque(true); 233 setBackground(Color.WHITE); 234 setFont(new Font("Dialog", Font.PLAIN, 12)); 235 _todayBackgroundColor = getForeground(); 236 237 _cal.setTimeInMillis(_firstDisplayedDate); 239 240 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 241 enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); 242 243 updateUI(); 244 } 245 246 249 public void updateUI() { 250 super.updateUI(); 251 252 String [] daysOfTheWeek = 253 (String [])UIManager.get("JXMonthView.daysOfTheWeek"); 254 if (daysOfTheWeek == null) { 257 daysOfTheWeek = new String [] {"S", "M", "T", "W", "R", "F", "S"}; 258 } 259 setDaysOfTheWeek(daysOfTheWeek); 260 261 Color color = UIManager.getColor("JXMonthView.monthStringBackground"); 262 if (color == null) { 265 color = Color.LIGHT_GRAY; 266 } 267 setMonthStringBackground(color); 268 269 color = UIManager.getColor("JXMonthView.selectedBackground"); 270 if (color == null) { 273 color = Color.LIGHT_GRAY; 274 } 275 setSelectedBackground(color); 276 } 277 278 279 284 public long getFirstDisplayedDate() { 285 return _firstDisplayedDate; 286 } 287 288 296 public void setFirstDisplayedDate(long date) { 297 long old = _firstDisplayedDate; 298 299 _cal.setTimeInMillis(date); 300 _cal.set(Calendar.DAY_OF_MONTH, 1); 301 _cal.set(Calendar.HOUR_OF_DAY, 0); 302 _cal.set(Calendar.MINUTE, 0); 303 _cal.set(Calendar.SECOND, 0); 304 _cal.set(Calendar.MILLISECOND, 0); 305 306 _firstDisplayedDate = _cal.getTimeInMillis(); 307 _firstDisplayedMonth = _cal.get(Calendar.MONTH); 308 _firstDisplayedYear = _cal.get(Calendar.YEAR); 309 310 calculateLastDisplayedDate(); 311 firePropertyChange("firstDisplayedDate", old, _firstDisplayedDate); 312 313 repaint(); 314 } 315 316 322 public long getLastDisplayedDate() { 323 return _lastDisplayedDate; 324 } 325 326 private void calculateLastDisplayedDate() { 327 long old = _lastDisplayedDate; 328 329 _cal.setTimeInMillis(_firstDisplayedDate); 330 331 _cal.add(Calendar.MONTH, ((_numCalCols * _numCalRows) - 1)); 333 _cal.set(Calendar.DAY_OF_MONTH, 334 _cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 335 _cal.set(Calendar.HOUR_OF_DAY, 23); 336 _cal.set(Calendar.MINUTE, 59); 337 _cal.set(Calendar.SECOND, 59); 338 339 _lastDisplayedDate = _cal.getTimeInMillis(); 340 341 firePropertyChange("lastDisplayedDate", old, _lastDisplayedDate); 342 } 343 344 352 public void ensureDateVisible(long date) { 353 if (date < _firstDisplayedDate) { 354 setFirstDisplayedDate(date); 355 } else if (date > _lastDisplayedDate) { 356 _cal.setTimeInMillis(date); 357 int month = _cal.get(Calendar.MONTH); 358 int year = _cal.get(Calendar.YEAR); 359 360 _cal.setTimeInMillis(_lastDisplayedDate); 361 int lastMonth = _cal.get(Calendar.MONTH); 362 int lastYear = _cal.get(Calendar.YEAR); 363 364 int diffMonths = month - lastMonth + 365 ((year - lastYear) * 12); 366 367 _cal.setTimeInMillis(_firstDisplayedDate); 368 _cal.add(Calendar.MONTH, diffMonths); 369 setFirstDisplayedDate(_cal.getTimeInMillis()); 370 } 371 372 if (_startSelectedDate != -1 || _endSelectedDate != -1) { 373 calculateDirtyRectForSelection(); 374 } 375 } 376 377 381 public DateSpan getSelectedDateSpan() { 382 DateSpan result = null; 383 if (_startSelectedDate != -1) { 384 result = new DateSpan(new Date (_startSelectedDate), 385 new Date (_endSelectedDate)); 386 } 387 return result; 388 } 389 390 400 public void setSelectedDateSpan(DateSpan dateSpan) { 401 DateSpan oldSpan = null; 402 if (_startSelectedDate != -1 && _endSelectedDate != -1) { 403 oldSpan = new DateSpan(_startSelectedDate, _endSelectedDate); 404 } 405 406 if (dateSpan == null) { 407 _startSelectedDate = -1; 408 _endSelectedDate = -1; 409 } else { 410 _cal.setTimeInMillis(dateSpan.getStart()); 411 _cal.set(Calendar.HOUR_OF_DAY, 0); 412 _cal.set(Calendar.MINUTE, 0); 413 _cal.set(Calendar.SECOND, 0); 414 _cal.set(Calendar.MILLISECOND, 0); 415 _startSelectedDate = _cal.getTimeInMillis(); 416 417 if (_selectionMode == SINGLE_SELECTION) { 418 _endSelectedDate = _startSelectedDate; 419 } else { 420 _cal.setTimeInMillis(dateSpan.getEnd()); 421 _cal.set(Calendar.HOUR_OF_DAY, 0); 422 _cal.set(Calendar.MINUTE, 0); 423 _cal.set(Calendar.SECOND, 0); 424 _cal.set(Calendar.MILLISECOND, 0); 425 _endSelectedDate = _cal.getTimeInMillis(); 426 427 if (_selectionMode == WEEK_SELECTION) { 428 _cal.setTimeInMillis(_startSelectedDate); 430 int count = 1; 431 while (_cal.getTimeInMillis() < _endSelectedDate) { 432 _cal.add(Calendar.DAY_OF_MONTH, 1); 433 count++; 434 } 435 if (count > 7) { 436 _cal.setTimeInMillis(_startSelectedDate); 439 int dayOfWeek = _cal.get(Calendar.DAY_OF_WEEK); 440 if (dayOfWeek != _firstDayOfWeek) { 441 int daysFromStart = dayOfWeek - _firstDayOfWeek; 444 if (daysFromStart < 0) { 445 daysFromStart += DAYS_IN_WEEK; 446 } 447 _cal.add(Calendar.DAY_OF_MONTH, -daysFromStart); 448 count += daysFromStart; 449 _startSelectedDate = _cal.getTimeInMillis(); 450 } 451 452 int remainder = count % 7; 455 if (remainder != 0) { 456 _cal.setTimeInMillis(_endSelectedDate); 457 _cal.add(Calendar.DAY_OF_MONTH, (7 - remainder)); 458 _endSelectedDate = _cal.getTimeInMillis(); 459 } 460 } 461 } 462 } 463 _cal.setTimeInMillis(_firstDisplayedDate); 465 } 466 467 repaint(_dirtyRect); 468 calculateDirtyRectForSelection(); 469 repaint(_dirtyRect); 470 471 firePropertyChange("selectedDates", oldSpan, dateSpan); 473 } 474 475 480 public int getSelectionMode() { 481 return _selectionMode; 482 } 483 484 489 public void setSelectionMode(int mode) throws IllegalArgumentException { 490 if (mode != SINGLE_SELECTION && mode != MULTIPLE_SELECTION && 491 mode != WEEK_SELECTION && mode != NO_SELECTION) { 492 throw new IllegalArgumentException (mode + 493 " is not a valid selection mode"); 494 } 495 _selectionMode = mode; 496 } 497 498 502 public void setFlaggedDates(long[] flaggedDates) { 503 _flaggedDates = flaggedDates; 504 505 if (_flaggedDates == null) { 506 repaint(); 507 return; 508 } 509 510 for (int i = 0; i < _flaggedDates.length; i++) { 513 _cal.setTimeInMillis(_flaggedDates[i]); 514 515 _cal.set(Calendar.HOUR_OF_DAY, 0); 518 _cal.set(Calendar.MINUTE, 0); 519 _cal.set(Calendar.SECOND, 0); 520 _cal.set(Calendar.MILLISECOND, 0); 521 522 _flaggedDates[i] = _cal.getTimeInMillis(); 523 } 524 525 _cal.setTimeInMillis(_firstDisplayedDate); 527 528 repaint(); 529 } 530 531 534 public int getBoxPaddingX() { 535 return _boxPaddingX; 536 } 537 538 544 public void setBoxPaddingX(int _boxPaddingX) { 545 this._boxPaddingX = _boxPaddingX; 546 _dirty = true; 547 } 548 549 552 public int getBoxPaddingY() { 553 return _boxPaddingY; 554 } 555 556 562 public void setBoxPaddingY(int _boxPaddingY) { 563 this._boxPaddingY = _boxPaddingY; 564 _dirty = true; 565 } 566 567 575 public void setDaysOfTheWeek(String [] days) 576 throws IllegalArgumentException , NullPointerException { 577 if (days == null) { 578 throw new NullPointerException ("Array of days is null."); 579 } else if (days.length != 7) { 580 throw new IllegalArgumentException ( 581 "Array of days is not of length 7 as expected."); 582 } 583 _daysOfTheWeek = days; 584 } 585 586 592 public String [] getDaysOfTheWeek() { 593 String [] days = new String [7]; 594 System.arraycopy(_daysOfTheWeek, 0, days, 0, 7); 595 return days; 596 } 597 598 605 public int getFirstDayOfWeek() { 606 return _firstDayOfWeek; 607 } 608 609 618 public void setFirstDayOfWeek(int firstDayOfWeek) { 619 if (firstDayOfWeek == _firstDayOfWeek) { 620 return; 621 } 622 623 _firstDayOfWeek = firstDayOfWeek; 624 _cal.setFirstDayOfWeek(_firstDayOfWeek); 625 626 repaint(); 627 } 628 629 634 public TimeZone getTimeZone() { 635 return _cal.getTimeZone(); 636 } 637 638 643 public void setTimeZone(TimeZone tz) { 644 _cal.setTimeZone(tz); 645 } 646 647 654 public boolean getAntialiased() { 655 return _antiAlias; 656 } 657 658 664 public void setAntialiased(boolean antiAlias) { 665 if (_antiAlias == antiAlias) { 666 return; 667 } 668 _antiAlias = antiAlias; 669 repaint(); 670 } 671 672 678 679 684 public Color getSelectedBackground() { 685 return _selectedBackground; 686 } 687 688 694 public void setSelectedBackground(Color c) { 695 _selectedBackground = c; 696 } 697 698 703 public Color getTodayBackground() { 704 return _todayBackgroundColor; 705 } 706 707 711 public void setTodayBackground(Color c) { 712 _todayBackgroundColor = c; 713 repaint(); 714 } 715 716 721 public Color getMonthStringBackground() { 722 return _monthStringBackground; 723 } 724 725 729 public void setMonthStringBackground(Color c) { 730 _monthStringBackground = c; 731 repaint(); 732 } 733 734 739 public Insets getMonthStringInsets() { 740 return (Insets)_monthStringInsets.clone(); 741 } 742 743 749 public void setMonthStringInsets(Insets insets) { 750 if (insets == null) { 751 _monthStringInsets.top = 0; 752 _monthStringInsets.left = 0; 753 _monthStringInsets.bottom = 0; 754 _monthStringInsets.right = 0; 755 } else { 756 _monthStringInsets.top = insets.top; 757 _monthStringInsets.left = insets.left; 758 _monthStringInsets.bottom = insets.bottom; 759 _monthStringInsets.right = insets.right; 760 } 761 repaint(); 762 } 763 764 769 public int getPreferredCols() { 770 return _minCalCols; 771 } 772 773 778 public void setPreferredCols(int cols) { 779 if (cols <= 0) { 780 return; 781 } 782 _minCalCols = cols; 783 _dirty = true; 784 revalidate(); 785 repaint(); 786 } 787 788 793 public int getPreferredRows() { 794 return _minCalRows; 795 } 796 797 802 public void setPreferredRows(int rows) { 803 if (rows <= 0) { 804 return; 805 } 806 _minCalRows = rows; 807 _dirty = true; 808 revalidate(); 809 repaint(); 810 } 811 812 private void updateIfNecessary() { 813 if (_dirty) { 814 update(); 815 _dirty = false; 816 } 817 } 818 819 822 private void update() { 823 int currDays; 827 int longestMonth = 0; 828 int daysInLongestMonth = 0; 829 830 int currWidth; 831 int longestMonthWidth = 0; 832 833 _derivedFont = getFont().deriveFont(Font.BOLD); 836 FontMetrics fm = getFontMetrics(_derivedFont); 837 838 _cal.set(Calendar.MONTH, _cal.getMinimum(Calendar.MONTH)); 839 _cal.set(Calendar.DAY_OF_MONTH, 840 _cal.getActualMinimum(Calendar.DAY_OF_MONTH)); 841 for (int i = 0; i < _cal.getMaximum(Calendar.MONTH); i++) { 842 currWidth = fm.stringWidth(_monthsOfTheYear[i]); 843 if (currWidth > longestMonthWidth) { 844 longestMonthWidth = currWidth; 845 } 846 currDays = _cal.getActualMaximum(Calendar.DAY_OF_MONTH); 847 if (currDays > daysInLongestMonth) { 848 longestMonth = _cal.get(Calendar.MONTH); 849 daysInLongestMonth = currDays; 850 } 851 _cal.add(Calendar.MONTH, 1); 852 } 853 854 _cal.set(Calendar.MONTH, longestMonth); 857 _cal.set(Calendar.DAY_OF_MONTH, 858 _cal.getActualMinimum(Calendar.DAY_OF_MONTH)); 859 _boxHeight = fm.getHeight(); 860 for (int i = 0; i < daysInLongestMonth; i++) { 861 currWidth = fm.stringWidth( 862 _dayOfMonthFormatter.format(_cal.getTime())); 863 if (currWidth > _boxWidth) { 864 _boxWidth = currWidth; 865 } 866 _cal.add(Calendar.DAY_OF_MONTH, 1); 867 } 868 869 _dim.width = (_boxWidth + (2 * _boxPaddingX)) * DAYS_IN_WEEK; 871 if (_dim.width < longestMonthWidth) { 872 double diff = longestMonthWidth - _dim.width; 873 _boxWidth += Math.ceil(diff / (double)DAYS_IN_WEEK); 874 _dim.width = (_boxWidth + (2 * _boxPaddingX)) * DAYS_IN_WEEK; 875 } 876 877 _calendarWidth = (_boxWidth + (2 * _boxPaddingX)) * DAYS_IN_WEEK; 879 _calendarHeight = (_boxPaddingY + _boxHeight + _boxPaddingY) * 8; 880 881 _dim.height = (_calendarHeight * _minCalRows) + 883 (CALENDAR_SPACING * (_minCalRows - 1)); 884 885 _dim.width = (_calendarWidth * _minCalCols) + 886 (CALENDAR_SPACING * (_minCalCols - 1)); 887 888 Insets insets = getInsets(); 890 _dim.width += insets.left + insets.right; 891 _dim.height += insets.top + insets.bottom; 892 893 _cal.setTimeInMillis(_firstDisplayedDate); 895 } 896 897 private void updateToday() { 898 _cal.setTimeInMillis(_today); 900 _cal.add(Calendar.DAY_OF_MONTH, 1); 901 _today = _cal.getTimeInMillis(); 902 903 _cal.setTimeInMillis(_firstDisplayedDate); 905 repaint(); 906 } 907 908 913 public Dimension getMinimumSize() { 914 return getPreferredSize(); 915 } 916 917 922 public Dimension getPreferredSize() { 923 updateIfNecessary(); 924 return new Dimension(_dim); 925 } 926 927 932 public Dimension getMaximumSize() { 933 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 934 } 935 936 950 public void setBorder(Border border) { 951 super.setBorder(border); 952 calculateNumDisplayedCals(); 953 calculateStartPosition(); 954 _dirty = true; 955 } 956 957 967 public void setBounds(int x, int y, int width, int height) { 968 super.setBounds(x, y, width, height); 969 970 calculateNumDisplayedCals(); 971 calculateStartPosition(); 972 973 if (_startSelectedDate != -1 || _endSelectedDate != -1) { 974 if (_startSelectedDate > _lastDisplayedDate || 975 _startSelectedDate <
|