All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.jonestimd.swing.component.CalendarPanel Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
// The MIT License (MIT)
//
// Copyright (c) 2017 Timothy D. Jones
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package io.github.jonestimd.swing.component;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.ResourceBundle;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import io.github.jonestimd.swing.ComponentResources;

/**
 * A panel that displays a calendar for selecting a date.  The calendar shows a single month and indicates the current
 * date and the currently selected date.  The panel also includes controls for selecting the month and year to display
 * in the calendar.  Property change events are fired when the selected date changes.
 */
public class CalendarPanel extends Box {
    private static final String SELECTION_COMPLETE = "selectionComplete";
    private static final String CANCEL_SELECTION = "cancelSelection";
    private static final String NEXT_MONTH = "navigateToNextMonth";
    private static final String PREVIOUS_MONTH = "navigateToPreviousMonth";

    /** The property name used for change events. */
    public static final String DATE_PROPERTY = "date";

    private Calendar navigationCalendar = Calendar.getInstance();
    private Date selectedDate;
    private final Color monthForeground;
    private final Color monthBackground;
    private final Color adjacentForeground;
    private final Color adjacentBackground;
    private final Border selectedBorder;
    private JLabel yearLabel = createYearLabel();
    private JComboBox monthComboBox = createMonthComboBox();
    private final Action selectAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setDate(tableModel.getSelectedDate());
        }
    };
    private final Action cancelAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setDate(selectedDate);
        }
    };
    private final Action nextMonthAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            navigationCalendar.add(Calendar.MONTH, 1);
            updateCalendar();
        }
    };
    private final Action previousMonthAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            navigationCalendar.add(Calendar.MONTH, -1);
            updateCalendar();
        }
    };

    private CalendarTableModel tableModel;
    private JTable calendarTable;

    public CalendarPanel(Date initialValue) {
        this(ComponentResources.BUNDLE, initialValue);
    }

    public CalendarPanel(ResourceBundle bundle, Date initialValue) {
        super(BoxLayout.Y_AXIS);
        monthForeground = (Color) bundle.getObject("calendarPanel.month.foreground");
        monthBackground = (Color) bundle.getObject("calendarPanel.month.background");
        adjacentForeground = (Color) bundle.getObject("calendarPanel.month.adjacent.foreground");
        adjacentBackground = (Color) bundle.getObject("calendarPanel.month.adjacent.background");
        selectedBorder = BorderFactory.createLineBorder((Color) bundle.getObject("calendarPanel.selected.border"));
        setBackground(monthBackground);
        setOpaque(true);

        Box navigationPanel = Box.createHorizontalBox();
        add(navigationPanel);
        navigationPanel.add(createPreviousNextPanel(bundle, monthComboBox, new MonthHandler(), "calendar.tooltip.month"));
        navigationPanel.add(createPreviousNextPanel(bundle, yearLabel, new YearHandler(), "calendar.tooltip.year"));

        createTable(bundle);
        add(calendarTable.getTableHeader());
        add(calendarTable);
        setDate(initialValue);
    }

    /**
     * Overridden to set focus to the calendar contained in this panel.
     */
    @Override
    public void requestFocus() {
        super.requestFocus();
        calendarTable.requestFocus();
    }

    private PreviousNextPanel createPreviousNextPanel(ResourceBundle bundle, Component valueComponent,
            PreviousNextListener handler, String resourcePrefix) {
        PreviousNextPanel previousNextPanel = new PreviousNextPanel(valueComponent,
                bundle.getString(resourcePrefix + ".previous"), bundle.getString(resourcePrefix + ".next"));
        previousNextPanel.addPreviousNextListener(handler);
        return previousNextPanel;
    }

    private JLabel createYearLabel() {
        JLabel label = new JLabel(String.format("%tY", navigationCalendar.getTime()));
        label.setBorder(new EmptyBorder(0, 2, 0, 2));
        return label;
    }

    private JComboBox createMonthComboBox() {
        String[] months = DateFormatSymbols.getInstance().getMonths();
        months = Arrays.copyOfRange(months,
                navigationCalendar.getMinimum(Calendar.MONTH), navigationCalendar.getMaximum(Calendar.MONTH)+1);
        JComboBox comboBox = new JComboBox<>(months);
        comboBox.addItemListener(new MonthChangeListener());
        return comboBox;
    }

    private void createTable(ResourceBundle bundle) {
        tableModel = new CalendarTableModel();
        calendarTable = new JTable(tableModel);
        calendarTable.getTableHeader().setReorderingAllowed(false);
        calendarTable.getTableHeader().setResizingAllowed(false);
        calendarTable.setCellSelectionEnabled(true);
        calendarTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        calendarTable.setDefaultRenderer(Date.class, new CellRenderer());
        calendarTable.setToolTipText(bundle.getString("calendar.tooltip"));
        calendarTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                setDate(tableModel.getSelectedDate());
            }
        });

        ActionMap actionMap = new ActionMap();
        actionMap.setParent(calendarTable.getActionMap());
        actionMap.put(SELECTION_COMPLETE, selectAction);
        actionMap.put(CANCEL_SELECTION, cancelAction);
        actionMap.put(NEXT_MONTH, nextMonthAction);
        actionMap.put(PREVIOUS_MONTH, previousMonthAction);
        calendarTable.setActionMap(actionMap);
        InputMap inputMap = new InputMap();
        inputMap.setParent(calendarTable.getInputMap());
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), SELECTION_COMPLETE);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), CANCEL_SELECTION);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), NEXT_MONTH);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), PREVIOUS_MONTH);
        calendarTable.setInputMap(WHEN_FOCUSED, inputMap);
        setPreferredWidth();
    }

    private void setPreferredWidth() {
        int max = 0;
        for (int i=0; i Calendar.SATURDAY) j = Calendar.SUNDAY;
            }
            int rowCount = (Calendar.getInstance().getMaximum(Calendar.DATE) / weekdays.length) + 2;
            dates = new Date[rowCount][weekdays.length];
            for (int i = 0; i < dates.length; i++) {
                dates[i] = new Date[weekdays.length];
            }
        }

        public Class getColumnClass(int columnIndex) {
            return Date.class;
        }

        public String getColumnName(int column) {
            return column < weekdays.length ? weekdays[column] : null;
        }

        public int getColumnCount() {
            return weekdays.length;
        }

        public int getRowCount() {
            return dates.length;
        }

        public void populateTable() {
            calendarTable.clearSelection();
            Calendar calendar = (Calendar) navigationCalendar.clone();
            calendar.add(Calendar.DATE, -(calendar.get(Calendar.DAY_OF_WEEK) - calendar.getFirstDayOfWeek()));
            for (int week = 0; week < dates.length; week++) {
                for (int dow = 0; dow < dates[week].length; dow++) {
                    Date date = calendar.getTime();
                    dates[week][dow] = date;
                    if (date.equals(selectedDate)) {
                        calendarTable.setColumnSelectionInterval(dow, dow);
                        calendarTable.setRowSelectionInterval(week, week);
                    }
                    calendar.add(Calendar.DATE, 1);
                }
            }
            fireTableRowsUpdated(0, dates.length-1);
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            if (rowIndex < dates.length && columnIndex < weekdays.length) {
                return dates[rowIndex][columnIndex];
            }
            return null;
        }

        public Date getSelectedDate() {
            if (calendarTable.getSelectedRowCount() == 0 || calendarTable.getSelectedColumnCount() == 0) {
                return null;
            }
            return dates[calendarTable.getSelectedRow()][calendarTable.getSelectedColumn()];
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy