com.github.lgooddatepicker.calendarpanel.CalendarPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of LGoodDatePicker Show documentation
Show all versions of LGoodDatePicker Show documentation
Java 8 Swing Date Picker. Easy to use, good looking, nice features, and
localized. Uses the JSR-310 standard.
package com.github.lgooddatepicker.calendarpanel;
import java.time.*;
import java.time.format.*;
import java.time.chrono.*;
import java.time.temporal.*;
import com.privatejgoodies.forms.layout.FormLayout;
import com.privatejgoodies.forms.factories.CC;
import com.github.lgooddatepicker.datepicker.DatePicker;
import com.github.lgooddatepicker.datepicker.DatePickerSettings;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import java.text.DateFormatSymbols;
import com.github.lgooddatepicker.optionalusertools.DateVetoPolicy;
import com.github.lgooddatepicker.optionalusertools.DateHighlightPolicy;
import com.github.lgooddatepicker.zinternaltools.CalendarSelectionEvent;
import com.github.lgooddatepicker.zinternaltools.InternalUtilities;
import com.github.lgooddatepicker.zinternaltools.JIntegerTextField;
import com.github.lgooddatepicker.zinternaltools.JIntegerTextField.IntegerTextFieldNumberChangeListener;
import com.github.lgooddatepicker.optionalusertools.CalendarSelectionListener;
import com.github.lgooddatepicker.zinternaltools.HighlightInformation;
/**
* CalendarPanel,
*
* This implements a swing component that displays and draws a calendar. The CalendarPanel has
* controls for changing the current month or year, and for selecting dates.
*
* In most cases, you will not need to create your own instances of CalendarPanel. The DatePicker
* class automatically creates its own instances of CalendarPanel whenever the user clicks on the
* "toggle calendar" button. However, the CalendarPanel can also (optionally) be used as an
* independent component when desired.
*
* Life cycle of CalendarPanel inside a DatePicker: Each time that the user clicks the toggle
* calendar button on a date picker, a new CalendarPanel instance is created and displayed, within
* of a new instance of CustomPopup. The calendar panel instance is closed and disposed each time
* that the date picker popup is closed.
*/
public class CalendarPanel
extends JPanel implements IntegerTextFieldNumberChangeListener {
/**
* dateLabels, This holds a list of all the date labels in the calendar, including ones that
* currently have dates or ones that are blank. This should always have exactly 42 labels. Date
* labels are reused when the currently displayed month or year is changed.
*/
private ArrayList dateLabels;
/**
* calendarSelectionListeners, This holds a list of calendar selection listeners that wish to be
* notified each time that a date is selected in the calendar panel.
*/
private ArrayList calendarSelectionListeners =
new ArrayList();
/**
* displayedSelectedDate, This stores a date that will be highlighted in the calendar as the
* "selected date", or it holds null if no date has been selected. This date is copied from the
* date picker when the calendar is opened. This should not be confused with the "lastValidDate"
* of a date picker object. This variable holds the selected date only for display purposes and
* internal CalendarPanel use.
*/
private LocalDate displayedSelectedDate = null;
/**
* displayedYearMonth, This stores the currently displayed year and month. This defaults to the
* current year and month. This will never be null.
*/
private YearMonth displayedYearMonth = YearMonth.now();
/**
* labelIndicatorEmptyBorder, This stores the empty border and the empty border size for the
* following labels: Month indicator, Year indicator, Set date to Today, Clear date.
*/
private EmptyBorder labelIndicatorEmptyBorder = new EmptyBorder(3, 2, 3, 2);
/**
* settings, This holds a reference to the date picker settings for this calendar panel. This
* will never be null.
*
* Programmer note: This should never be set to null, because setting this to null can cause
* NullPointerExceptions. Apparently, in certain operating systems, the pop-up for the calendar
* panel can be closed before the date is set in the parent date picker.
*/
private DatePickerSettings settings;
/**
* weekdayLabels, This holds a list of all the weekday labels in the calendar. This should
* always have exactly 7 labels. Weekday labels are reused when the currently displayed month or
* year is changed.
*/
private ArrayList weekdayLabels;
/**
* yearTextField, The year text field is displayed any time that the user clicks the ellipsis
* (...) inside the year selection drop down menu. This field allows the user to type year
* numbers using the keyboard when desired.
*/
private JIntegerTextField yearTextField;
/**
* JFormDesigner GUI components, These variables are automatically generated by JFormDesigner.
* This section should not be modified by hand, but only modified from within the JFormDesigner
* program.
*/
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel headerControlsPanel;
private JButton buttonPreviousYear;
private JButton buttonPreviousMonth;
private JPanel monthAndYearOuterPanel;
private JPanel monthAndYearInnerPanel;
private JLabel labelMonth;
private JLabel labelYear;
private JButton buttonNextMonth;
private JButton buttonNextYear;
private JPanel weekDaysPanel;
private JPanel datesPanel;
private JPanel footerPanel;
private JLabel labelSetDateToToday;
private JLabel labelClearDate;
private JPanel yearEditorPanel;
private JButton doneEditingYearButton;
// JFormDesigner - End of variables declaration //GEN-END:variables
/**
* Constructor, This creates a calendar panel and stores the parent date picker.
*
* Technical note: This constructor is only called from the DatePicker.openPopup() function. A
* new CalendarPanel is created every time the popup is opened. Therefore, any
* DatePickerSettings variables that are initialized in this constructor are automatically able
* to correctly handle being set either before or after, a DatePicker is constructed.
*/
public CalendarPanel(DatePickerSettings datePickerSettings) {
if (datePickerSettings == null) {
throw new RuntimeException("CalendarPanel Constructor, "
+ "Cannot create a calendar panel with null DatePickerSettings");
}
this.settings = datePickerSettings;
// Initialize the components.
initComponents();
// Create the yearTextField, and add it to the yearEditorPanel.
yearTextField = new JIntegerTextField(4);
yearTextField.setMaximumValue(Year.MAX_VALUE);
yearTextField.setMinimumValue(Year.MIN_VALUE);
yearTextField.setMargin(new Insets(1, 1, 1, 1));
yearEditorPanel.add(yearTextField, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
// Add the text change listener to the yearTextField.
yearTextField.numberChangeListener = this;
// Initialize the doneEditingYearButton.
doneEditingYearButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
doneEditingYearButton.setText("\u2713");
// Set the calendar panel to be opaque.
setOpaque(true);
// Set the component colors
Color calendarPanelBackgroundColor = settings.colorBackgroundCalendarPanel;
setBackground(calendarPanelBackgroundColor);
headerControlsPanel.setBackground(calendarPanelBackgroundColor);
monthAndYearOuterPanel.setBackground(calendarPanelBackgroundColor);
footerPanel.setBackground(calendarPanelBackgroundColor);
Color navigationButtonsColor = settings.colorBackgroundNavigateYearMonthButtons;
if (navigationButtonsColor != null) {
buttonPreviousYear.setBackground(navigationButtonsColor);
buttonNextYear.setBackground(navigationButtonsColor);
buttonPreviousMonth.setBackground(navigationButtonsColor);
buttonNextMonth.setBackground(navigationButtonsColor);
}
// Generate and add the date labels and weekday labels.
addDateLabels();
addWeekdayLabels();
// Shrink the buttons for previous and next year and month.
buttonPreviousYear.setMargin(new java.awt.Insets(1, 2, 1, 2));
buttonNextYear.setMargin(new java.awt.Insets(1, 2, 1, 2));
buttonPreviousMonth.setMargin(new java.awt.Insets(1, 2, 1, 2));
buttonNextMonth.setMargin(new java.awt.Insets(1, 2, 1, 2));
// Set the label indicators to their default states.
labelIndicatorSetBorderToDefaultState(labelMonth);
labelIndicatorSetBorderToDefaultState(labelYear);
labelIndicatorSetBorderToDefaultState(labelSetDateToToday);
labelIndicatorSetBorderToDefaultState(labelClearDate);
// Set the size of the month and year panel to be big enough to hold the largest month text.
setSizeOfMonthYearPanel();
// Set the size of the cell that contains the date panel.
setSizeOfDatePanelCell();
// Set the calendar to show the current month and year by default.
CalendarPanel.this.drawCalendar(YearMonth.now());
}
/**
* addDateLabels, This adds a set of 42 date labels to the calendar, and ties each of those
* labels to a mouse click event handler. The date labels are reused any time that the calendar
* is redrawn.
*/
private void addDateLabels() {
dateLabels = new ArrayList();
for (int i = 0; i < 42; ++i) {
int dateLabelColumnX = ((i % 7));
int dateLabelRowY = ((i / 7) + 1);
JLabel dateLabel = new JLabel();
dateLabel.setHorizontalAlignment(SwingConstants.CENTER);
dateLabel.setVerticalAlignment(SwingConstants.CENTER);
dateLabel.setBackground(Color.white);
dateLabel.setBorder(null);
dateLabel.setOpaque(true);
dateLabel.setText("" + i);
GridBagConstraints gc = InternalUtilities.getConstraints(
dateLabelColumnX, dateLabelRowY);
datesPanel.add(dateLabel, gc);
dateLabels.add(dateLabel);
// Add a mouse click listener for every date label, even the blank ones.
dateLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
dateLabelMouseClicked(e);
}
});
}
}
/**
* addCalendarSelectionListener, This adds a calendar selection listener to this calendar panel.
* For additional details, see the CalendarSelectionListener class documentation.
*/
public void addCalendarSelectionListener(CalendarSelectionListener listener) {
calendarSelectionListeners.add(listener);
}
/**
* addWeekdayLabels, This adds a set of 7 weekday labels to the calendar panel. The text of
* these labels is set with locale sensitive weekday names each time that the calendar is
* redrawn.
*/
private void addWeekdayLabels() {
weekDaysPanel.setBackground(settings.colorBackgroundWeekdayLabels);
weekdayLabels = new ArrayList();
for (int i = 0; i < 7; ++i) {
int weekdayLabelColumnX = (i);
int weekdayLabelRowY = 0;
JLabel weekdayLabel = new JLabel();
weekdayLabel.setHorizontalAlignment(SwingConstants.CENTER);
weekdayLabel.setVerticalAlignment(SwingConstants.CENTER);
weekdayLabel.setBackground(settings.colorBackgroundWeekdayLabels);
weekdayLabel.setOpaque(true);
weekdayLabel.setText("wd" + i);
GridBagConstraints gc = InternalUtilities.getConstraints(
weekdayLabelColumnX, weekdayLabelRowY);
weekDaysPanel.add(weekdayLabel, gc);
weekdayLabels.add(weekdayLabel);
}
}
/**
* buttonNextMonthActionPerformed, This event is called when the next month button is pressed.
* This sets the YearMonth of the calendar to the next month, and redraws the calendar.
*/
private void buttonNextMonthActionPerformed(ActionEvent e) {
drawCalendar(displayedYearMonth.plusMonths(1));
}
/**
* buttonNextYearActionPerformed, This event is called when the next year button is pressed.
* This sets the YearMonth of the calendar to the next year, and redraws the calendar.
*/
private void buttonNextYearActionPerformed(ActionEvent e) {
drawCalendar(displayedYearMonth.plusYears(1));
}
/**
* buttonPreviousMonthActionPerformed, This event is called when the previous month button is
* pressed. This sets the YearMonth of the calendar to the previous month, and redraws the
* calendar.
*/
private void buttonPreviousMonthActionPerformed(ActionEvent e) {
drawCalendar(displayedYearMonth.minusMonths(1));
}
/**
* buttonPreviousYearActionPerformed, This event is called when the previous year button is
* pressed. This sets the YearMonth of the calendar to the previous year, and redraws the
* calendar.
*/
private void buttonPreviousYearActionPerformed(ActionEvent e) {
drawCalendar(displayedYearMonth.minusYears(1));
}
/**
* dateLabelMouseClicked, This event is called any time that the user clicks on a date label in
* the calendar. This sets the date picker to the selected date, and closes the calendar panel.
*/
private void dateLabelMouseClicked(MouseEvent e) {
// Get the label that was clicked.
JLabel label = (JLabel) e.getSource();
// If the label is empty, do nothing and return.
String labelText = label.getText();
if ("".equals(labelText)) {
return;
}
// We have a label with a specific date, so set the date and close the calendar.
int dayOfMonth = Integer.parseInt(labelText);
LocalDate clickedDate = LocalDate.of(
displayedYearMonth.getYear(), displayedYearMonth.getMonth(), dayOfMonth);
userSelectedADate(clickedDate);
}
/**
* drawCalendar, This can be called to redraw the calendar. The calendar will be drawn with the
* currently displayed year and month. This function should not normally need to be called by
* the programmer, because the calendar will automatically redraw itself as needed.
*/
public void drawCalendar() {
drawCalendar(displayedYearMonth);
}
/**
* drawCalendar, This is called whenever the calendar needs to be drawn. This takes a year and a
* month to indicate which month should be drawn in the calendar.
*/
private void drawCalendar(int year, Month month) {
drawCalendar(YearMonth.of(year, month));
}
/**
* drawCalendar, This is called whenever the calendar needs to be drawn. This takes a year and a
* month to indicate which month should be drawn in the calendar.
*/
private void drawCalendar(YearMonth yearMonth) {
// Save the displayed yearMonth.
this.displayedYearMonth = yearMonth;
// Get the displayed month and year.
Month displayedMonth = yearMonth.getMonth();
int displayedYear = yearMonth.getYear();
// Get an instance of the calendar symbols for the current locale.
DateFormatSymbols symbols = DateFormatSymbols.getInstance(settings.getLocale());
// Get the days of the week in the local language.
String localShortDaysOfWeek[] = symbols.getShortWeekdays();
// Get the full month names in the current locale.
int zeroBasedMonthIndex = (displayedMonth.getValue() - 1);
String localizedFullMonth = settings.translationArrayStandaloneLongMonthNames[zeroBasedMonthIndex];
String localizedShortMonth = settings.translationArrayStandaloneShortMonthNames[zeroBasedMonthIndex];
// Get the first day of the month, and the first day of week.
LocalDate firstDayOfMonth = LocalDate.of(displayedYear, displayedMonth, 1);
DayOfWeek firstDayOfWeekOfMonth = firstDayOfMonth.getDayOfWeek();
// Get the last day of the month.
int lastDateOfMonth = getLastDayOfMonth(displayedYearMonth);
// Find out if we have a selected date that is inside the currently displayed month.
boolean selectedDateIsInDisplayedMonth = (displayedSelectedDate != null)
&& (displayedSelectedDate.getYear() == displayedYear)
&& (displayedSelectedDate.getMonth() == displayedMonth);
// Set the month and the year labels.
// Use the short month if the user is currently using the keyboard editor for the year.
if (monthAndYearInnerPanel.isAncestorOf(yearEditorPanel)) {
labelMonth.setText(localizedShortMonth);
} else {
labelMonth.setText(localizedFullMonth);
}
String displayedYearString = "" + displayedYear;
labelYear.setText(displayedYearString);
if (!displayedYearString.equals(yearTextField.getText())) {
yearTextField.skipNotificationOfNumberChangeListenerWhileTrue = true;
yearTextField.setText(displayedYearString);
yearTextField.skipNotificationOfNumberChangeListenerWhileTrue = false;
}
// Set the days of the week labels, and create an array to represent the weekday positions.
ArrayList daysOfWeekAsDisplayed = new ArrayList();
int isoFirstDayOfWeekValue = settings.firstDayOfWeek.getValue();
int isoLastDayOfWeekOverflowed = isoFirstDayOfWeekValue + 6;
int weekdayLabelArrayIndex = 0;
for (int dayOfWeek = isoFirstDayOfWeekValue; dayOfWeek <= isoLastDayOfWeekOverflowed; dayOfWeek++) {
int localShortDaysOfWeekArrayIndex = (dayOfWeek % 7) + 1;
int isoDayOfWeek = (dayOfWeek > 7) ? (dayOfWeek - 7) : dayOfWeek;
DayOfWeek currentDayOfWeek = DayOfWeek.of(isoDayOfWeek);
daysOfWeekAsDisplayed.add(currentDayOfWeek);
weekdayLabels.get(weekdayLabelArrayIndex).setText(localShortDaysOfWeek[localShortDaysOfWeekArrayIndex]);
++weekdayLabelArrayIndex;
}
// Set the dates of the month labels.
// Also save the label for the selected date, if one is present in the current month.
boolean insideValidRange = false;
int dayOfMonth = 1;
JLabel selectedDateLabel = null;
for (int dateLabelArrayIndex = 0; dateLabelArrayIndex < dateLabels.size(); ++dateLabelArrayIndex) {
// Get the current date label.
JLabel dateLabel = dateLabels.get(dateLabelArrayIndex);
// Reset the state of every label to a default state.
dateLabel.setBackground(Color.white);
dateLabel.setForeground(Color.black);
dateLabel.setBorder(null);
dateLabel.setEnabled(true);
dateLabel.setToolTipText(null);
// Calculate the index to use on the daysOfWeekAsDisplayed array.
int daysOfWeekAsDisplayedArrayIndex = dateLabelArrayIndex % 7;
// Check to see if we are inside the valid range for days of this month.
if (daysOfWeekAsDisplayed.get(daysOfWeekAsDisplayedArrayIndex) == firstDayOfWeekOfMonth
&& dateLabelArrayIndex < 7) {
insideValidRange = true;
}
if (dayOfMonth > lastDateOfMonth) {
insideValidRange = false;
}
// While we are inside the valid range, set the date labels with the day of the month.
if (insideValidRange) {
// Get a local date object for the current date.
LocalDate currentDate = LocalDate.of(displayedYear, displayedMonth, dayOfMonth);
DateVetoPolicy vetoPolicy = settings.getVetoPolicy();
DateHighlightPolicy highlightPolicy = settings.highlightPolicy;
boolean dateIsVetoed = InternalUtilities.isDateVetoed(vetoPolicy, currentDate);
HighlightInformation highlightInfo = null;
if (highlightPolicy != null) {
highlightInfo = highlightPolicy.getHighlightInformationOrNull(currentDate);
}
if (dateIsVetoed) {
dateLabel.setEnabled(false);
dateLabel.setBackground(settings.colorBackgroundVetoedDates);
}
if ((!dateIsVetoed) && (highlightInfo != null)) {
// Set the highlight background color (always).
Color colorBackground = settings.colorBackgroundHighlightedDates;
if (highlightInfo.colorBackground != null) {
colorBackground = highlightInfo.colorBackground;
}
dateLabel.setBackground(colorBackground);
// If needed, set the highlight text color.
if (highlightInfo.colorText != null) {
dateLabel.setForeground(highlightInfo.colorText);
}
// If needed, set the highlight tooltip text.
if (highlightInfo.tooltipText != null &&
(!(highlightInfo.tooltipText.isEmpty()))) {
dateLabel.setToolTipText(highlightInfo.tooltipText);
}
}
// If needed, save the label for the selected date.
if (selectedDateIsInDisplayedMonth && displayedSelectedDate != null
&& displayedSelectedDate.getDayOfMonth() == dayOfMonth) {
selectedDateLabel = dateLabel;
}
// Set the text for the current date.
dateLabel.setText("" + dayOfMonth);
++dayOfMonth;
} else {
// We are not inside the valid range, so set this label to an empty string.
dateLabel.setText("");
}
}
// If needed, change the color of the selected date.
if (selectedDateLabel != null) {
selectedDateLabel.setBackground(new Color(163, 184, 204));
selectedDateLabel.setBorder(new LineBorder(new Color(99, 130, 191)));
}
// Set the label for the today button.
String todayDateString = settings.formatForTodayButton.format(LocalDate.now());
String todayLabel = settings.translationToday + ": " + todayDateString;
labelSetDateToToday.setText(todayLabel);
// If today is vetoed, disable the today button.
DateVetoPolicy vetoPolicy = settings.getVetoPolicy();
boolean todayIsVetoed = InternalUtilities.isDateVetoed(
vetoPolicy, LocalDate.now());
labelSetDateToToday.setEnabled(!todayIsVetoed);
// If null is not allowed, then disable and hide the Clear label.
// Note: I had considered centering the today label in the CalendarPanel whenever the
// clear label was hidden. However, it still looks better when it is aligned to the left.
boolean shouldEnableClearButton = settings.getAllowEmptyDates();
labelClearDate.setEnabled(shouldEnableClearButton);
labelClearDate.setVisible(shouldEnableClearButton);
// Set the label for the clear button.
labelClearDate.setText(settings.translationClear);
}
/**
* getCalendarSelectionListeners, This returns a new ArrayList, that contains any calendar
* selection listeners that are registered with this CalendarPanel.
*/
public ArrayList getCalendarSelectionListeners() {
return new ArrayList(calendarSelectionListeners);
}
/**
* getSelectedDate, This returns the date that is currently marked as "selected" in the
* calendar. If no date is selected, then this will return null.
*
* This should not be confused with the "last valid date" of a DatePicker object. This function
* would typically only be needed when the CalendarPanel class is being used independently from
* the DatePicker class.
*/
public LocalDate getSelectedDate() {
return displayedSelectedDate;
}
/**
* getLastDayOfMonth, This returns the last day of the month for the specified year and month.
*
* Implementation notes: As of this writing, the below implementation is verified to work
* correctly for negative years, as those years are to defined in the iso 8601 your format that
* is used by java.time.YearMonth. This functionality can be tested by by checking to see if to
* see if the year "-0004" is correctly displayed as a leap year. Leap years have 29 days in
* February. There should be 29 days in the month of "February 1, -0004".
*/
private int getLastDayOfMonth(YearMonth yearMonth) {
LocalDate firstDayOfMonth = LocalDate.of(yearMonth.getYear(), yearMonth.getMonth(), 1);
int lastDayOfMonth = firstDayOfMonth.lengthOfMonth();
return lastDayOfMonth;
}
/**
* getMonthOrYearMenuLocation, This calculates the position should be used to set the location
* of the month or the year popup menus, relative to their source labels. These menus are used
* to change the current month or current year from within the calendar panel.
*/
private Point getMonthOrYearMenuLocation(JLabel sourceLabel, JPopupMenu filledPopupMenu) {
Rectangle labelBounds = sourceLabel.getBounds();
int menuHeight = filledPopupMenu.getPreferredSize().height;
int popupX = labelBounds.x + labelBounds.width + 1;
int popupY = labelBounds.y + (labelBounds.height / 2) - (menuHeight / 2);
return new Point(popupX, popupY);
}
/**
* labelClearDateMouseClicked, This event is called when the "Clear" label is clicked in a date
* picker. This sets the date picker date to an empty date. (This sets the last valid date to
* null.)
*/
private void labelClearDateMouseClicked(MouseEvent e) {
userSelectedADate(null);
}
/**
* labelIndicatorMouseEntered, This event is called when the user move the mouse inside a
* monitored label. This is used to generate mouse over effects for the calendar panel.
*/
private void labelIndicatorMouseEntered(MouseEvent e) {
JLabel label = ((JLabel) e.getSource());
if (label == labelSetDateToToday) {
DateVetoPolicy vetoPolicy = settings.getVetoPolicy();
boolean todayIsVetoed = InternalUtilities.isDateVetoed(vetoPolicy, LocalDate.now());
if (todayIsVetoed) {
return;
}
}
label.setBackground(new Color(184, 207, 229));
label.setBorder(new CompoundBorder(
new LineBorder(Color.GRAY), labelIndicatorEmptyBorder));
}
/**
* labelIndicatorMouseExited, This event is called when the user move the mouse outside of a
* monitored label. This is used to generate mouse over effects for the calendar panel.
*/
private void labelIndicatorMouseExited(MouseEvent e) {
JLabel label = ((JLabel) e.getSource());
labelIndicatorSetBorderToDefaultState(label);
}
/**
* labelIndicatorSetBorderToDefaultState, This event is called to set a label indicator to the
* state it should have when there is no mouse hovering over it.
*/
private void labelIndicatorSetBorderToDefaultState(JLabel label) {
if (label == null || settings == null) {
return;
}
if (label == labelMonth || label == labelYear) {
label.setBackground(settings.colorBackgroundMonthAndYear);
monthAndYearInnerPanel.setBackground(settings.colorBackgroundMonthAndYear);
} else {
label.setBackground(settings.colorBackgroundTodayAndClear);
}
label.setBorder(new CompoundBorder(
new EmptyBorder(1, 1, 1, 1), labelIndicatorEmptyBorder));
}
/**
* labelMonthIndicatorMousePressed, This event is called any time that the user clicks on the
* month display label in the calendar. This opens a menu that the user can use to select a new
* month in the same year.
*/
private void labelMonthIndicatorMousePressed(MouseEvent e) {
JPopupMenu monthPopupMenu = new JPopupMenu();
String[] allLocalMonths = settings.translationArrayStandaloneLongMonthNames;
for (int i = 0; i < allLocalMonths.length; ++i) {
final String localMonth = allLocalMonths[i];
final int localMonthZeroBasedIndexTemp = i;
if (!localMonth.isEmpty()) {
monthPopupMenu.add(new JMenuItem(new AbstractAction(localMonth) {
int localMonthZeroBasedIndex = localMonthZeroBasedIndexTemp;
@Override
public void actionPerformed(ActionEvent e) {
drawCalendar(displayedYearMonth.getYear(),
Month.of(localMonthZeroBasedIndex + 1));
}
}));
}
}
Point menuLocation = getMonthOrYearMenuLocation(labelMonth, monthPopupMenu);
monthPopupMenu.show(monthAndYearInnerPanel, menuLocation.x, menuLocation.y);
}
/**
* labelSetDateToTodayMouseClicked, This event is called when the "Today" label is clicked in a
* date picker. This sets the date picker date to today.
*/
private void labelSetDateToTodayMouseClicked(MouseEvent e) {
userSelectedADate(LocalDate.now());
}
/**
* labelYearIndicatorMousePressed, This event is called any time that the user clicks on the
* year display label in the calendar. This opens a menu that the user can use to select a new
* year within a chosen range of the previously displayed year.
*/
private void labelYearIndicatorMousePressed(MouseEvent e) {
int firstYearDifference = -11;
int lastYearDifference = +11;
JPopupMenu yearPopupMenu = new JPopupMenu();
for (int yearDifference = firstYearDifference; yearDifference <= lastYearDifference;
++yearDifference) {
// No special processing is required for the BC to AD transition in the
// ISO 8601 calendar system. Year zero does exist in this system.
YearMonth choiceYearMonth = displayedYearMonth.plusYears(yearDifference);
String choiceYearMonthString = "" + choiceYearMonth.getYear();
yearPopupMenu.add(new JMenuItem(new AbstractAction(choiceYearMonthString) {
@Override
public void actionPerformed(ActionEvent e) {
String chosenMenuText = ((JMenuItem) e.getSource()).getText();
int chosenYear = Integer.parseInt(chosenMenuText);
drawCalendar(chosenYear, displayedYearMonth.getMonth());
}
}));
}
String choiceOtherYearString = "( . . . )";
yearPopupMenu.add(new JMenuItem(new AbstractAction(choiceOtherYearString) {
@Override
public void actionPerformed(ActionEvent e) {
otherYearMenuItemClicked();
}
}));
Point menuLocation = getMonthOrYearMenuLocation(labelYear, yearPopupMenu);
yearPopupMenu.show(monthAndYearInnerPanel, menuLocation.x, menuLocation.y);
}
private void doneEditingYearButtonActionPerformed(ActionEvent e) {
monthAndYearInnerPanel.remove(yearEditorPanel);
labelYear.setEnabled(true);
labelYear.setVisible(true);
drawCalendar(displayedYearMonth);
}
private void otherYearMenuItemClicked() {
labelYear.setEnabled(false);
labelYear.setVisible(false);
monthAndYearInnerPanel.add(yearEditorPanel, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
drawCalendar(displayedYearMonth);
yearTextField.requestFocusInWindow();
}
/**
* removeCalendarSelectionListener, This removes the specified calendar selection listener from
* this CalendarPanel.
*/
public void removeCalendarSelectionListener(CalendarSelectionListener listener) {
calendarSelectionListeners.remove(listener);
}
/**
* setSelectedDate, This sets a date that will be marked as "selected" in the calendar. The
* selectedDate will only be visible when the matching YearMonth is being displayed in the
* calendar. Note that this function does -not- change the currently displayed YearMonth.
*/
public void setSelectedDate(LocalDate selectedDate) {
// Save the selected date, redraw the calendar, and notify any listeners.
zInternalChangeSelectedDateProcedure(selectedDate);
}
/**
* setDisplayedYearMonth, This sets the year and month that is currently displayed in the
* calendar. The yearMonth can not be set to null. If the parameter is null, an exception will
* be thrown. Note that this function does -not- change the displayed selected date.
*/
public void setDisplayedYearMonth(YearMonth yearMonth) {
if (yearMonth == null) {
throw new RuntimeException("CalendarPanel.setDisplayedYearMonth(), "
+ "The displayed year and month cannot be set to null.");
}
drawCalendar(yearMonth);
}
/**
* setSizeOfDatePanelCell, This sets the size of the cell holds date panel. By default, the size
* is set in such a way that the grid layout of the date panel will always touch the inside
* edges of the cell. At the time of this writing, the cell that contains the date panel is:
* x=1, y=4, in the calendar panel.
*/
private void setSizeOfDatePanelCell() {
// Get the minimum desired size of the date panel.
int minimumHeight = settings.sizeDatePanelMinimumHeight;
int minimumWidth = settings.sizeDatePanelMinimumWidth;
// Redraw the panel, to make sure the panel is the "default layout size" before starting.
this.doLayout();
this.validate();
// Get the layout for the calendar panel.
GridBagLayout layout = ((GridBagLayout) getLayout());
// Get the current height and width of the date panel.
Dimension previousDatesPanelSize = datesPanel.getSize();
int panelHeight = previousDatesPanelSize.height;
int panelWidth = previousDatesPanelSize.width;
// Apply the desired minimum size of the date panel.
panelHeight = (panelHeight < minimumHeight) ? minimumHeight : panelHeight;
panelWidth = (panelWidth < minimumWidth) ? minimumWidth : panelWidth;
// Force the new size to be multiples of 7 for the columns, and multiples of 6 for the rows.
panelHeight += (panelHeight % 6);
panelWidth += (panelWidth % 7);
// Extra pixels appears to be required to make the date labels touch the inside edges
// of the date panel.
panelHeight += settings.sizeDatePanelPixelsExtraHeight;
panelWidth += settings.sizeDatePanelPixelsExtraWidth;
// Set the containing cell to be the desired size.
layout.rowHeights[4] = panelHeight;
layout.columnWidths[1] = panelWidth;
// Redraw the panel.
this.doLayout();
this.validate();
}
/**
* setSizeOfMonthYearPanel, This sets the size of the panel at the top of the calendar that
* holds the month and the year label. The size is calculated from the largest month name (in
* pixels), that exists in locale and language that is being used by the date picker.
*/
private void setSizeOfMonthYearPanel() {
// Get the font metrics object.
Font font = labelMonth.getFont();
Canvas canvas = new Canvas();
FontMetrics metrics = canvas.getFontMetrics(font);
// Get the height of a line of text in this font.
int height = metrics.getHeight();
// Get the length of the longest translated month string (in pixels).
DateFormatSymbols symbols = DateFormatSymbols.getInstance(settings.getLocale());
String[] allLocalMonths = symbols.getMonths();
int longestMonthPixels = 0;
for (String month : allLocalMonths) {
int monthPixels = metrics.stringWidth(month);
longestMonthPixels = (monthPixels > longestMonthPixels) ? monthPixels : longestMonthPixels;
}
int yearPixels = metrics.stringWidth("_2000");
// Calculate the size of a box to hold the text with some padding.
Dimension size = new Dimension(longestMonthPixels + yearPixels + 12, height + 2);
// Set the monthAndYearPanel to the appropriate constant size.
monthAndYearOuterPanel.setMinimumSize(size);
monthAndYearOuterPanel.setMaximumSize(size);
monthAndYearOuterPanel.setPreferredSize(size);
// Redraw the panel.
this.doLayout();
this.validate();
}
/**
* userSelectedADate, This is called any time that the user makes a date selection on the
* calendar panel, including choosing to clear the date. If this calendar panel is being used
* inside of a DatePicker, then this will save the selected date and close the calendar. The
* only time this function will not be called during an exit event, is if the user left focus of
* the component or pressed escape to cancel choosing a new date.
*/
private void userSelectedADate(LocalDate selectedDate) {
// If a date was selected and the date is vetoed, do nothing.
if (selectedDate != null) {
DateVetoPolicy vetoPolicy = settings.getVetoPolicy();
if (InternalUtilities.isDateVetoed(vetoPolicy, selectedDate)) {
return;
}
}
// Save the selected year and month, in case it is needed later.
if (selectedDate != null) {
YearMonth selectedDateYearMonth = YearMonth.from(selectedDate);
displayedYearMonth = selectedDateYearMonth;
} else {
// The selected date was cleared, so set the displayed month and year to today's values.
displayedYearMonth = YearMonth.now();
}
// Save the selected date, redraw the calendar, and notify any listeners.
zInternalChangeSelectedDateProcedure(selectedDate);
// If this calendar panel is associated with a date picker, then set the DatePicker date
// and close the popup.
if (settings.getParentDatePicker() != null) {
DatePicker parent = settings.getParentDatePicker();
parent.setDate(selectedDate);
parent.closePopup();
}
}
/**
* zInternalChangeSelectedDateProcedure, This should be called whenever we need to change the
* selected date variable. This will store the supplied selected date and redraw the calendar.
* If needed, this will notify all calendar selection listeners that the selected date has been
* changed. This does not perform any other tasks besides those described here.
*
* By intention, this will fire an event even if the user selects the same value twice. This is
* so that programmers can catch all user interactions of interest to them. Duplicate events can
* be detected by using the function CalendarSelectionEvent.isDuplicate().
*/
private void zInternalChangeSelectedDateProcedure(LocalDate newDate) {
LocalDate oldDate = displayedSelectedDate;
displayedSelectedDate = newDate;
drawCalendar(displayedYearMonth);
for (CalendarSelectionListener calendarSelectionListener : calendarSelectionListeners) {
CalendarSelectionEvent dateSelectionEvent = new CalendarSelectionEvent(
this, newDate, oldDate);
calendarSelectionListener.selectionChanged(dateSelectionEvent);
}
}
/**
* integerTextFieldNumberChanged, This function is required for the implementation of
* IntegerTextFieldNumberChangeListener. This is called whenever the number in the integer text
* field has changed.
*/
@Override
public void integerTextFieldNumberChanged(JIntegerTextField source, int newValue) {
YearMonth newYearMonth = YearMonth.of(newValue, displayedYearMonth.getMonth());
drawCalendar(newYearMonth);
}
/**
* initComponents, This initializes the GUI components in the calendar panel. This function is
* automatically generated by JFormDesigner. This function should not be modified by hand, it
* should only be modified from within JFormDesigner.
*/
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
headerControlsPanel = new JPanel();
buttonPreviousYear = new JButton();
buttonPreviousMonth = new JButton();
monthAndYearOuterPanel = new JPanel();
monthAndYearInnerPanel = new JPanel();
labelMonth = new JLabel();
labelYear = new JLabel();
buttonNextMonth = new JButton();
buttonNextYear = new JButton();
weekDaysPanel = new JPanel();
datesPanel = new JPanel();
footerPanel = new JPanel();
labelSetDateToToday = new JLabel();
labelClearDate = new JLabel();
yearEditorPanel = new JPanel();
doneEditingYearButton = new JButton();
//======== this ========
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {5, 0, 5, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {6, 0, 5, 22, 80, 5, 0, 5, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0E-4};
//======== headerControlsPanel ========
{
headerControlsPanel.setLayout(new FormLayout(
"3*(pref), pref:grow, 3*(pref)",
"fill:pref"));
((FormLayout)headerControlsPanel.getLayout()).setColumnGroups(new int[][] {{1, 2, 6, 7}});
//---- buttonPreviousYear ----
buttonPreviousYear.setText("<<");
buttonPreviousYear.setFocusable(false);
buttonPreviousYear.setFocusPainted(false);
buttonPreviousYear.setHorizontalTextPosition(SwingConstants.CENTER);
buttonPreviousYear.setMargin(new Insets(5, 6, 5, 6));
buttonPreviousYear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buttonPreviousYearActionPerformed(e);
}
});
headerControlsPanel.add(buttonPreviousYear, CC.xy(1, 1));
//---- buttonPreviousMonth ----
buttonPreviousMonth.setText("<");
buttonPreviousMonth.setFocusable(false);
buttonPreviousMonth.setFocusPainted(false);
buttonPreviousMonth.setHorizontalTextPosition(SwingConstants.CENTER);
buttonPreviousMonth.setMargin(new Insets(5, 6, 5, 6));
buttonPreviousMonth.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buttonPreviousMonthActionPerformed(e);
}
});
headerControlsPanel.add(buttonPreviousMonth, CC.xy(2, 1));
//======== monthAndYearOuterPanel ========
{
monthAndYearOuterPanel.setLayout(new GridBagLayout());
((GridBagLayout)monthAndYearOuterPanel.getLayout()).columnWidths = new int[] {0, 0, 0, 0};
((GridBagLayout)monthAndYearOuterPanel.getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)monthAndYearOuterPanel.getLayout()).columnWeights = new double[] {1.0, 0.0, 1.0, 1.0E-4};
((GridBagLayout)monthAndYearOuterPanel.getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
//======== monthAndYearInnerPanel ========
{
monthAndYearInnerPanel.setLayout(new GridBagLayout());
((GridBagLayout)monthAndYearInnerPanel.getLayout()).columnWidths = new int[] {0, 1, 0, 0};
((GridBagLayout)monthAndYearInnerPanel.getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)monthAndYearInnerPanel.getLayout()).columnWeights = new double[] {0.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)monthAndYearInnerPanel.getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
//---- labelMonth ----
labelMonth.setText("September");
labelMonth.setHorizontalAlignment(SwingConstants.RIGHT);
labelMonth.setOpaque(true);
labelMonth.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
labelIndicatorMouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
labelIndicatorMouseExited(e);
}
@Override
public void mousePressed(MouseEvent e) {
labelMonthIndicatorMousePressed(e);
}
});
monthAndYearInnerPanel.add(labelMonth, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//---- labelYear ----
labelYear.setText("2100");
labelYear.setOpaque(true);
labelYear.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
labelIndicatorMouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
labelIndicatorMouseExited(e);
}
@Override
public void mousePressed(MouseEvent e) {
labelYearIndicatorMousePressed(e);
}
});
monthAndYearInnerPanel.add(labelYear, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
monthAndYearOuterPanel.add(monthAndYearInnerPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
headerControlsPanel.add(monthAndYearOuterPanel, CC.xy(4, 1));
//---- buttonNextMonth ----
buttonNextMonth.setText(">");
buttonNextMonth.setFocusable(false);
buttonNextMonth.setFocusPainted(false);
buttonNextMonth.setHorizontalTextPosition(SwingConstants.CENTER);
buttonNextMonth.setMargin(new Insets(5, 6, 5, 6));
buttonNextMonth.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buttonNextMonthActionPerformed(e);
}
});
headerControlsPanel.add(buttonNextMonth, CC.xy(6, 1));
//---- buttonNextYear ----
buttonNextYear.setText(">>");
buttonNextYear.setFocusable(false);
buttonNextYear.setFocusPainted(false);
buttonNextYear.setHorizontalTextPosition(SwingConstants.CENTER);
buttonNextYear.setMargin(new Insets(5, 6, 5, 6));
buttonNextYear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buttonNextYearActionPerformed(e);
}
});
headerControlsPanel.add(buttonNextYear, CC.xy(7, 1));
}
add(headerControlsPanel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== weekDaysPanel ========
{
weekDaysPanel.setBorder(new EmptyBorder(0, 4, 0, 4));
weekDaysPanel.setBackground(new Color(51, 51, 255));
weekDaysPanel.setLayout(new GridLayout(1, 7, 5, 0));
}
add(weekDaysPanel, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== datesPanel ========
{
datesPanel.setBorder(new LineBorder(new Color(99, 130, 191)));
datesPanel.setBackground(Color.white);
datesPanel.setLayout(new GridLayout(6, 7));
}
add(datesPanel, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== footerPanel ========
{
footerPanel.setLayout(new GridBagLayout());
((GridBagLayout)footerPanel.getLayout()).columnWidths = new int[] {6, 0, 0, 0, 6, 0};
((GridBagLayout)footerPanel.getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)footerPanel.getLayout()).columnWeights = new double[] {0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)footerPanel.getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
//---- labelSetDateToToday ----
labelSetDateToToday.setText("Today: Feb 12, 2016");
labelSetDateToToday.setHorizontalAlignment(SwingConstants.CENTER);
labelSetDateToToday.setOpaque(true);
labelSetDateToToday.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
labelSetDateToTodayMouseClicked(e);
}
@Override
public void mouseEntered(MouseEvent e) {
labelIndicatorMouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
labelIndicatorMouseExited(e);
}
});
footerPanel.add(labelSetDateToToday, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//---- labelClearDate ----
labelClearDate.setText("Clear");
labelClearDate.setOpaque(true);
labelClearDate.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
labelClearDateMouseClicked(e);
}
@Override
public void mouseEntered(MouseEvent e) {
labelIndicatorMouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
labelIndicatorMouseExited(e);
}
});
footerPanel.add(labelClearDate, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
add(footerPanel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== yearEditorPanel ========
{
yearEditorPanel.setLayout(new GridBagLayout());
((GridBagLayout)yearEditorPanel.getLayout()).columnWidths = new int[] {40, 1, 26, 0};
((GridBagLayout)yearEditorPanel.getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)yearEditorPanel.getLayout()).columnWeights = new double[] {1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)yearEditorPanel.getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
//---- doneEditingYearButton ----
doneEditingYearButton.setFocusPainted(false);
doneEditingYearButton.setFocusable(false);
doneEditingYearButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doneEditingYearButtonActionPerformed(e);
}
});
yearEditorPanel.add(doneEditingYearButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy