Page 1 of 1

Java: Date Picker for Swing

Posted: Wed Feb 07, 2007 2:33 pm
by Chris Corbyn
This requires Java 5. I'm quite new to Java so be critical and teach me something new ;)

I'll tell you one bit I don't like: The locations of Model, Controller and View... but it's Swing and I struggle with that aspect.

I was looking around for a Java/Swing date picker and all I could find were ones you have to pay $100+ for. There is one free one but I don't like it because it was all buttons rather than a nice grid appearance. So, I did what any developer does when they can't find a tool for the job - I wrote my own :D If I make a few small changes to allow the appearance to be customized I'll probably release it free anyway.

Here it is (sorry for the long post):

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import javax.swing.*;
import javax.swing.table.*;
import java.util.Calendar;
import java.awt.*;
import java.util.ArrayList;

/**
 * CalendarPanel is a container (JPanel) which holds the contents of the Calendar
 * It can be added to a Swing layout like any other JPanel
 * @author Chris Corbyn
 */
public class CalendarPanel extends JPanel
{
        /**
         * The controller layer
         */
        protected CalendarEventController controller;
        /**
         * The model
         */
        public CalendarModel model;
        /**
         * The combobox which holds the months of the year
         */
        protected JComboBox monthDropdown;
        /**
         * The spinner to choose a year
         */
        protected JSpinner yearSpinner;
        /**
         * The grid (JTable) containing the days of the month
         */
        protected CalendarTable grid;
        /**
         * A collection of event listeners which respond to changes in date selection
         */
        protected ArrayList<CalendarChangeListener> calendarChangeListeners;

        /**
         * Create an instance of CalendarPanel with a titled border
         * @param title The title to show in a titled border
         */
        public CalendarPanel(String title)
        {
                super(new GridBagLayout());

                this.calendarChangeListeners = new ArrayList<CalendarChangeListener>();

                this.setModel(new CalendarModel());

                this.setController(new CalendarEventController());
                this.getController().setUI(this);


                this.setBorder(BorderFactory.createTitledBorder(title));
                GridBagConstraints c = new GridBagConstraints();

                c.gridx = 0;
                c.gridy = 0;
                c.fill = GridBagConstraints.HORIZONTAL;
                c.anchor = GridBagConstraints.WEST;
                this.addMonths(c);

                c.gridx = 1;
                c.anchor = GridBagConstraints.EAST;
                c.fill = GridBagConstraints.NONE;
                this.addYears(c);

                c.gridx = 0;
                c.gridy = 1;
                c.gridwidth = 2;
                this.addTable(c);
        }
        /**
         * Set the model which this View in rendered from
         * @param model The CalendarModel
         */
        public void setModel(CalendarModel model)
        {
                this.model = model;
        }
        /**
         * Get the current model
         * @return CalendarModel
         */
        public CalendarModel getModel()
        {
                return this.model;
        }
        /**
         * Set the controller which modifies the model of the calendar
         * @param controller The event listening layer/controller
         */
        protected void setController(CalendarEventController controller)
        {
                this.controller = controller;
        }
        /**
         * Get the event listening object which acts as a controller
         * @return CalendarEventController
         */
        protected CalendarEventController getController()
        {
                return this.controller;
        }
        /**
         * Add the month combo box to the layout
         * @param GridBagConstraints To position the combobox on the overall layout
         */
        protected void addMonths(GridBagConstraints c)
        {
                String[] months = this.getModel().getMonthNames();
                if (this.monthDropdown == null)
                        this.monthDropdown = new JComboBox(months);

                int monthNow = this.getModel().getCalendarMonth();
                this.monthDropdown.setSelectedIndex(monthNow);
                this.getController().addMonthDropdown(this.monthDropdown);
                this.add(this.monthDropdown, c);
        }
        /**
         * Get the months combobox component
         * @return JComboBox, containing the months
         */
        public JComboBox getMonthDropdown()
        {
                return this.monthDropdown;
        }
        /**
         * Get the years component
         * @return JSpinner, containing the date model
         */
        public JSpinner getYearSpinner()
        {
                return this.yearSpinner;
        }
        /**
         * Add the years spinner to the layout
         * @param GridBagConstraints To position the spinner on the overall layout
         */
        protected void addYears(GridBagConstraints c)
        {
                this.yearSpinner = new JSpinner(this.getModel().getYearSpinnerModel());
                this.yearSpinner.setEditor(new JSpinner.DateEditor(this.yearSpinner, "yyyy"));
                this.getController().addYearSpinner(this.yearSpinner);
                this.add(this.yearSpinner, c);
        }
        /**
         * Add the Grid to the layout
         * @param GridBagConstraints To position the grid in the overall layout
         */
        protected void addTable(GridBagConstraints c)
        {
                JPanel box = new JPanel(new GridBagLayout());
                GridBagConstraints myC = new GridBagConstraints();
                myC.gridx = 0;
                myC.gridy = 0;
                this.grid = new CalendarTable(new DefaultTableModel());
                this.getModel().addObserver(
                        new CalendarCellDisabler(this.getGrid()));
                this.getGrid().setModel(this.getModel().getTableModel());
                this.getController().addGrid(this.getGrid());
                this.configureTable();

                box.add(this.getGrid().getTableHeader(), myC);

                myC.gridy = 1;
                box.add(this.grid, myC);

                this.add(box, c);
        }
        /**
         * Get the Grid (JTable) which shows the days
         * @return JTable, containing the days of the month
         */
        public CalendarTable getGrid()
        {
                return this.grid;
        }
        /**
         * Set up the renderer and feel of the Grid
         */
        protected void configureTable() //Disable cells which are not used????  We have to read the TableModel here
        {
                this.grid.setDragEnabled(false);
                this.grid.getTableHeader().setReorderingAllowed(false);
                this.grid.getTableHeader().setResizingAllowed(false);
                this.grid.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

                CalendarCellRenderer renderer = new CalendarCellRenderer();
                renderer.setParentUI(this);

                TableColumn col = null;
                for (int i = 0; i < 7; i++)
                {
                        col = this.grid.getColumnModel().getColumn(i);
                        col.setPreferredWidth(27);
                        col.setCellRenderer(renderer);
                }
                this.grid.setSelectionBackground(new Color((float)0.7, (float)0.86, (float)1.0));
                this.grid.setSelectionForeground(Color.black);
                this.grid.setShowGrid(false);
                this.grid.setRowHeight(22);

                int r = this.getModel().getSelectedGridRow();
                this.grid.setRowSelectionInterval(r, r);
                int c = this.getModel().getSelectedGridColumn();
                this.grid.setColumnSelectionInterval(c, c);
        }
        /**
         * Redraw the UI - overridden to add some functionality for redrawing the grid
         */
        public void redraw()
        {
                this.configureTable();
                this.getGrid().updateUI();
                super.updateUI();
        }
        /**
         * Fire a new CalendarChangeEvent
         * @param e The event being fired
         */
        public void fireCalendarChangeEvent(CalendarChangeEvent e)
        {
                for (int i = 0, len = this.calendarChangeListeners.size(); i < len; i++)
                {
                        CalendarChangeListener l = this.calendarChangeListeners.get(i);
                        l.calendarChanged(e);
                }
        }
        /**
         * Add a new listener for CalendarChangeEvent - the controller triggers this event
         * @param listener The event listener to add
         */
        public void addCalendarChangeListener(CalendarChangeListener l)
        {
                this.calendarChangeListeners.add(l);
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.Date;
import javax.swing.table.DefaultTableModel;
import javax.swing.*;
import java.util.Observable;

/**
 * The Model for the Date picker calendar
 * Provides and manipulate dates, contains Swing and AWT models.
 * @author Chris Corbyn
 */
public class CalendarModel extends Observable
{
        /**
         * The calendar storing all the time data which changes the appearance of the calendar
         */
        protected GregorianCalendar calendar;
        /**
         * The currently selected day
         */
        protected Integer selectedDay = null;
        /**
         * The currently selected month - not strictly the same date as the calendar says
         */
        protected Integer selectedMonth = null;
        /**
         * The currently selected year - not strictly the same date as the calendar says
         */
        protected Integer selectedYear = null;
        /**
         * The currently selected row in the calendar
         */
        protected Integer selectedGridRow = null;
        /**
         * The currently selected column in the calendar
         */
        protected Integer selectedGridColumn = null;
        /**
         * The days of the current month in a 2-dimensional array format
         */
        protected Integer[][] days;
        /**
         * The days of the week as an array
         */
        protected String[] dayNames;
        /**
         * The months in the combo box
         */
        protected String[] monthNames;

        /**
         * Get a new instance of the CalendarModel
         */
        public CalendarModel()
        {
                String[] weekdayNames = { "S", "M", "T", "W", "T", "F", "S" };
                String[] monthNames = {
                        "January", "February", "March", "April", "May", "June",
                        "July", "August", "September", "October", "November", "December" };
                this.setMonthNames(monthNames);
                this.setDayNames(weekdayNames);
                this.days = new Integer[6][7];
                this.setCalendar(new GregorianCalendar());
                this.getCalendar().set(Calendar.HOUR, 0);
                this.getCalendar().set(Calendar.MINUTE, 0);
                this.getCalendar().set(Calendar.SECOND, 0);
                this.setSelectedDay(this.getCalendar().get(Calendar.DAY_OF_MONTH));
                this.setSelectedMonth(this.getCalendar().get(Calendar.MONTH));
                this.setSelectedYear(this.getCalendar().get(Calendar.YEAR));
        }
        /**
         * Get the calendar object used to render the current view
         * @return GregorianCalendar, which is responsible for rendering the grid
         */
        public GregorianCalendar getCalendar()
        {
                return this.calendar;
        }
        /**
         * Set the calendar in the Model
         * @param calendar The calendar the GUI is modelled on
         */
        public void setCalendar(GregorianCalendar calendar)
        {
                this.calendar = calendar;
        }
        /**
         * Get the names of all the months
         * @return String[], containing the month names
         */
        public String[] getMonthNames()
        {
                return this.monthNames;
        }
        /**
         * Set the months used in the combo box
         * @param months The months in the list
         */
        public void setMonthNames(String[] months)
        {
                if (months.length > 12)
                {
                        throw new RuntimeException("You cannot set more than 12 months");
                }
                this.monthNames = months;
        }
        /**
         * Get the number of days in the current month
         * @return int, days in the current month
         */
        public int getDaysInMonth()
        {
                int[] daysInMonths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
                int month = this.getCalendar().get(Calendar.MONTH);
                int ret = daysInMonths[month];
                if (month == 1 && this.getCalendar().isLeapYear(this.getCalendar().get(Calendar.YEAR))) ret += 1;
                return ret;
        }
        /**
         * Get the heading for the table
         * @return String[], names of the days in the headings
         */
        public String[] getDayNames()
        {
                return this.dayNames;
        }
        /**
         * Set the heading for the weekdays
         * @param days The days of the week
         */
        public void setDayNames(String[] days)
        {
                if (days.length != 7)
                {
                        throw new RuntimeException("Only 7 days can be set in the heading.");
                }
                this.dayNames = days;
        }
        /**
         * Get the model that makes the table
         * @return DefaultTableModel
         */
        public DefaultTableModel getTableModel()
        {
                String[] headings = this.getDayNames();
                Object[][] data = this.getDays();
                DefaultTableModel model = new DefaultTableModel(data, headings) {
                        public boolean isCellEditable(int row, int col)
                        {
                                return false;
                        }
                };
                return model;
        }
        /**
         * Get the Date model for the Year Spinner
         * @return SpinnerDateModel
         */
        public SpinnerDateModel getYearSpinnerModel()
        {
                Date now = this.getCalendar().getTime();
                int year = this.getCalendar().get(Calendar.YEAR);
                this.getCalendar().add(Calendar.YEAR, -1000);
                Date earliest = this.getCalendar().getTime();
                this.getCalendar().add(Calendar.YEAR, 2000);
                Date latest = this.getCalendar().getTime();
                this.getCalendar().set(Calendar.YEAR, year);
                SpinnerDateModel model = new SpinnerDateModel(now, earliest, latest, Calendar.YEAR);
                return model;
        }
        /**
         * Set the row in the grid which is currently selected
         * @param row The selected row
         */
        public void setSelectedGridRow(int r)
        {
                this.selectedGridRow = r;
        }
        /**
         * Get the row in the grid which is currently selected
         * @return Integer
         */
        public Integer getSelectedGridRow()
        {
                return this.selectedGridRow;
        }
        /**
         * Set the column in the grid which is currently selected
         * @param column The selected column
         */
        public void setSelectedGridColumn(int c)
        {
                this.selectedGridColumn = c;
        }
        /**
         * Get the column in the grid which is currently selected
         * @return Integer
         */
        public Integer getSelectedGridColumn()
        {
                return this.selectedGridColumn;
        }
        /**
         * Get the month which is currently selected - not the same as the one in the calendar
         * @return int
         */
        public int getSelectedMonth()
        {
                return this.selectedMonth;
        }
        /**
         * Set the currently selected month
         * @param month The selected month
         */
        public void setSelectedMonth(int m)
        {
                this.selectedMonth = m;
                this.getCalendar().set(Calendar.DAY_OF_MONTH, 1);
                this.getCalendar().set(Calendar.MONTH, m);
                this.restoreDayOfMonth();
        }
        /**
         * Sets the day of the month back to its original (or next highest) value after a change
         */
        protected void restoreDayOfMonth()
        {
                int daysInMonth = this.getDaysInMonth();
                int lastSelectedDay = this.getSelectedDay();
                if (lastSelectedDay > daysInMonth)
                {
                        this.setSelectedDay(daysInMonth);
                }
                else
                {
                        this.setSelectedDay(lastSelectedDay);
                }
        }
        /**
         * Get the year which as currently selected - not the same as the calendar
         * @return int
         */
        public int getSelectedYear()
        {
                return this.selectedYear;
        }
        /**
         * Set the year which is currently selected
         * @param year The selected year
         */
        public void setSelectedYear(int year)
        {
                this.selectedYear = year;
                this.getCalendar().set(Calendar.DAY_OF_MONTH, 1);
                this.getCalendar().set(Calendar.YEAR, year);
                this.restoreDayOfMonth();
        }
        /**
         * Get the current month in the calendar - maybe not the selected month
         * @return int
         */
        public int getCalendarMonth()
        {
                int currentMonth = this.getCalendar().get(Calendar.MONTH);
                return currentMonth;
        }
        /**
         * Get the last day which was selected in the calendar
         * @return int, day of month
         */
        public Integer getSelectedDay()
        {
                return this.selectedDay;
        }
        /**
         * Set the last selected day in the calendar
         * @param dayOfMonth
         */
        public void setSelectedDay(Integer dayOfMonth)
        {
                this.selectedDay = dayOfMonth;
                this.getCalendar().set(Calendar.DAY_OF_MONTH, dayOfMonth);
        }
        /**
         * Get the days in the current month as a 2-dimensional array
         * @return String[][]
         */
        public Integer[][] getDays()
        {
                int currDay = this.getCalendar().get(Calendar.DAY_OF_MONTH);
                this.getCalendar().set(Calendar.DAY_OF_MONTH, 1);
                int firstDayOfMonthAsDayOfWeek = this.getCalendar().get(Calendar.DAY_OF_WEEK);
                this.getCalendar().set(Calendar.DAY_OF_MONTH, currDay);
                int daysInMonth = this.getDaysInMonth();
                this.getCalendar().add(Calendar.MONTH, -1);
                int daysInLastMonth = this.getDaysInMonth();
                this.getCalendar().add(Calendar.MONTH, 1);
                int row = 0;
                int col = 0;
                Integer dayToAdd = 0;
                Integer lastMonthDayToAdd = 0;
                Integer dayNextMonthToAdd = 0;
                Integer[] disabledCell = { 0 , 0 };

                for (int k = 1; k <= 42; k++)
                {
                        if (k < firstDayOfMonthAsDayOfWeek)
                        {
                                lastMonthDayToAdd = (daysInLastMonth - (firstDayOfMonthAsDayOfWeek - k - 1));
                                this.days[row][col] = lastMonthDayToAdd;
                                disabledCell[0] = row;
                                disabledCell[1] = col;
                                this.setChanged();
                                this.notifyObservers(disabledCell);
                                this.clearChanged();
                        }
                        else if (dayToAdd >= daysInMonth)
                        {
                                dayNextMonthToAdd++;
                                this.days[row][col] = dayNextMonthToAdd;
                                disabledCell[0] = row;
                                disabledCell[1] = col;
                                this.setChanged();
                                this.notifyObservers(disabledCell);
                                this.clearChanged();
                        }
                        else
                        {
                                dayToAdd++;
                                this.days[row][col] = dayToAdd;
                                //Select the currently chosen day
                                if (dayToAdd == this.getSelectedDay())
                                {
                                        this.setSelectedGridRow(row);
                                        this.setSelectedGridColumn(col);
                                }
                        }
                        col++;
                        if (col == 7)
                        {
                                col = 0;
                                row++;
                        }
                }
                return this.days;
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * The event handling layer for the Calendar.
 * This is what I would consider the Controller in MVC.
 * Attaches event listeners to Swing components
 * @author Chris Corbyn
 */
public class CalendarEventController
{
        /**
         * The user interface as a Swing component (Container)
         */
        protected CalendarPanel ui;

        /**
         * Set the user interface component containing the calendar
         * @param CalendarPanel The user interface of the Swing component
         */
        public void setUI(CalendarPanel cal)
        {
                this.ui = cal;
        }
        /**
         * Put the month combobox under the control of the controller
         * @param monthComboBox JComboBox for months
         */
        public void addMonthDropdown(JComboBox months)
        {
                months.addItemListener(new ItemListener() {
                        public void itemStateChanged(ItemEvent e)
                        {
                                if (e.getStateChange() == ItemEvent.SELECTED)
                                {
                                        int monthSelected = ui.monthDropdown.getSelectedIndex();
                                        ui.getModel().setSelectedMonth(monthSelected);
                                        ui.getGrid().resetCellStates();
                                        ui.getGrid().setModel(ui.getModel().getTableModel());
                                        ui.redraw();
                                }
                        }
                });
        }
        /**
         * Put the year Spinner under the control of this controller
         * @param yearSpinner The year spinner (with a SpinnerDateModel)
         */
        public void addYearSpinner(JSpinner years)
        {
                years.addChangeListener(new ChangeListener() {
                        public void stateChanged(ChangeEvent e)
                        {
                                Calendar t = Calendar.getInstance();
                                t.setTime(((SpinnerDateModel)ui.getYearSpinner().getModel()).getDate());
                                ui.getModel().setSelectedYear(t.get(Calendar.YEAR));
                                ui.getGrid().resetCellStates();
                                ui.getGrid().setModel(ui.getModel().getTableModel());
                                ui.redraw();
                        }
                });
        }
        /**
         * Put the grid under the control of this controller
         * @param table The table containing the grid
         */
        public void addGrid(CalendarTable table)
        {
                table.addCellSelectionListener(new CalendarTableCellSelectionListener() {
                        public void cellSelectionChanged(CalendarTableCellSelectionEvent e)
                        {
                                ui.getModel().setSelectedDay((Integer)e.getValue());
                                ui.fireCalendarChangeEvent(
                                        new CalendarChangeEvent(ui.getModel().getCalendar()));
                        }
                });
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.util.HashMap;
import java.util.ArrayList;

/**
 * CalendarTable is a JTable which does not allow empty cells to be selected
 * @author Chris Corbyn
 */
public class CalendarTable extends JTable
{
        /**
         * A matrix of the disabled cell positions
         */
        protected HashMap<Integer,HashMap<Integer,Boolean>> disabledRows;
        /**
         * Event listeners observing when a cell is selected
         */
        protected ArrayList<CalendarTableCellSelectionListener> cellSelectionListeners;

        /**
         * Ctor
         * @param model The table model to render
         */
        public CalendarTable(AbstractTableModel model)
        {
                super(model);
                this.disabledRows = new HashMap<Integer,HashMap<Integer,Boolean>>();
                this.cellSelectionListeners = new ArrayList<CalendarTableCellSelectionListener>();
        }
        /**
         * Disable a cell selection at the coordinates (row,col)
         * @param row The row of the cell
         * @param col The column of the cell
         */
        public void disableCellAt(int row, int col)
        {
                if (!this.disabledRows.containsKey(row))
                {
                        this.disabledRows.put(row, new HashMap<Integer,Boolean>());
                }
                this.disabledRows.get(row).put(col, true);
        }
        /**
         * Clear out disabled cell statuses
         */
        public void resetCellStates()
        {
                this.disabledRows.clear();
        }
        /**
         * Enable a cell if it has been disabled
         * @param row The row of the disabled cell
         * @param col The column of the disabled cell
         */
        public void enableCellAt(int row, int col)
        {
                if (this.isDisabledAt(row, col))
                        this.disabledRows.get(row).remove(col);
        }
        /**
         * Check if the cell at row,col is disabled
         * @param row The row to test
         * @param col The column to test
         * @return true, if the cell is disabled
         */
        public boolean isDisabledAt(int row, int col)
        {
                if (this.disabledRows.containsKey(row))
                {
                        if (this.disabledRows.get(row).containsKey(col) && this.disabledRows.get(row).get(col))
                        {
                                return true;
                        }
                }

                return false;
        }
        /**
         * Overridden to prevent empty cells from being selected
         */
        public void changeSelection(int row, int col, boolean toggle, boolean extend)
        {
                if (!this.isDisabledAt(row, col))
                        super.changeSelection(row, col, toggle, extend);
        }
        /**
         * Fire a CalendarCellSelectionEvent
         * @param event The event being fired
         */
        public void fireCellSelectionEvent(CalendarTableCellSelectionEvent e)
        {
                for (int i = 0, len = this.cellSelectionListeners.size(); i < len; i++)
                {
                        CalendarTableCellSelectionListener l = this.cellSelectionListeners.get(i);
                        l.cellSelectionChanged(e);
                }
        }
        /**
         * Register a new event listener for CalendarChangeEvent
         * @param listener The event listener
         */
        public void addCellSelectionListener(CalendarTableCellSelectionListener l)
        {
                this.cellSelectionListeners.add(l);
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;

/**
 * Styles the grid of the calendar to look beautiful across various L&Fs.
 * This is just a basic extension of the DefaultTableCellRender from the current L&F
 * @author Chris Corbyn
 */
public class CalendarCellRenderer extends DefaultTableCellRenderer
{
        /**
         * The current row being rendered
         */
        protected int row;
        /**
         * The current column being rendered
         */
        protected int col;
        /**
         * If this cell is part of the "selected" row
         */
        protected boolean isSelected;
        /**
         * The table being rendered
         */
        protected JTable tbl;
        /**
         * The GUI itself
         */
        protected CalendarPanel parentUI;

        /**
         * Set the object which renders the GUI of the calendar
         * @param CalendarPanel The Swing component which holds the calendar
         */
        public void setParentUI(CalendarPanel p)
        {
                this.parentUI = p;
        }
        /**
         * Fetch the component which renders the cell ordinarily
         * @param table The current JTable the cell is in
         * @param value The value in the cell
         * @param isSelected If the cell is in the selected row
         * @param isFocused If the cell is in focus
         * @param row The row number of the cell
         * @param column The column number of the cell
         * @return Component
         */
        public Component getTableCellRendererComponent(
                JTable tbl, Object v, boolean isSelected, boolean isFocused, int row, int col)
        {
                if (((CalendarTable)tbl).isDisabledAt(row, col))
                {
                        this.setEnabled(false);
                        isSelected = false;
                }
                else this.setEnabled(true);

                //Store this info for later use
                this.tbl = tbl;
                this.row = row;
                this.col = col;
                this.isSelected = isSelected;

                //and then allow the usual component to be returned
                return super.getTableCellRendererComponent(tbl, v, isSelected, isFocused, row, col);
        }
        /**
         * Set the contents of the cell to v
         * @param value The value to apply to the cell
         */
        protected void setValue(Object v)
        {
                super.setValue(v); //Set the value as requested

                //Set colors dependant upon if the row is selected or not
                if (!this.isSelected)
                {
                        if (!this.isEnabled()) this.setBackground(new Color(178, 204, 251, 110));
                        else this.setBackground(new Color(178, 204, 251));
                }
                else this.setBackground(new Color(160, 160, 180));

                //Set a special highlight color if this actual cell is focused
                if (this.isEnabled() && this.row == this.tbl.getSelectedRow() && this.col == this.tbl.getSelectedColumn())
                {
                        this.setBackground(new Color(127, 200, 120));
                        ((CalendarTable)this.tbl).fireCellSelectionEvent(new CalendarTableCellSelectionEvent((CalendarTable)this.tbl, v));
                }
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import java.util.Observer;
import java.util.Observable;

/**
 * CalendarCellDisabler receives message from CalendarModel to prevent cell selections in the table
 * @author Chris Corbyn
 */
public class CalendarCellDisabler implements Observer
{
        /**
         * The table which this observer disables cells for
         */
        protected CalendarTable tbl;

        /**
         * Ctor
         * @param table The table to disable cells in
         */
        public CalendarCellDisabler(CalendarTable tbl)
        {
                this.tbl = tbl;
        }
        /**
         * Receive a message from an observer to disable a cell
         * @param observable The observable object
         * @param args The disabled cell represented by Integer[2]
         */
        public void update(Observable o, Object args)
        {
                Integer[] cell = (Integer[])args;
                this.tbl.disableCellAt(cell[0], cell[1]);
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import java.util.EventObject;

/**
 * CalendarTableCellSelectionEvent fires when a cell is selected in the table
 * @author Chris Corbyn
 */
public class CalendarTableCellSelectionEvent extends EventObject
{
        /**
         * The value at the selected cell
         */
        protected Object value;

        /**
         * Ctor
         * @param source The CalendarTable object
         * @param v The value at the selected cell
         */
        public CalendarTableCellSelectionEvent(CalendarTable source, Object v)
        {
                super(source);
                this.setValue(v);
        }
        /**
         * Set the value of the selected cell (copy, not reference)
         * @param v The value at the selected cell
         */
        public void setValue(Object v)
        {
                this.value = v;
        }
        /**
         * Get the value at the selected cell
         * @return Integer, usually, but it could be anything
         */
        public Object getValue()
        {
                return this.value;
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */
 
package org.w3style.calendar;

import java.util.EventListener;

/**
 * CalendarTableCellSelectionListener is the interface which allows listening for
 * CalendarTableCellSelectionEvent objects being fired in CalendarTable
 * @author Chris Corbyn
 */
public interface CalendarTableCellSelectionListener extends EventListener
{
        /**
         * Runs every time a CalendarTableCellSelectionEvent is fired
         * @param e The event object
         */
        public void cellSelectionChanged(CalendarTableCellSelectionEvent e);
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

package org.w3style.calendar;

import java.util.EventObject;
import java.util.GregorianCalendar;

/**
 * CalendarChangeEvent fires when a new date is selected in the calendar
 * @author Chris Corbyn
 */
public class CalendarChangeEvent extends EventObject
{
        /**
         * Ctor
         * @param source The GregorianCalendar containing the changed model
         */
        public CalendarChangeEvent(GregorianCalendar source)
        {
                super(source);
        }
}

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */
 
package org.w3style.calendar;

import java.util.EventListener;

/**
 * The interface which listener for calendar date changes via CalendarChangeEvent must implement
 * @author Chris Corbyn
 */
public interface CalendarChangeListener extends EventListener
{
        /**
         * Runs everytime a CalendarChangeEvent object is created
         * @param e The event object
         */
        public void calendarChanged(CalendarChangeEvent e);
}

Posted: Wed Feb 07, 2007 2:38 pm
by Chris Corbyn
Here's a basic example of it's usage, looking like this:

Image

Code: Select all

/*
 * Licensed under the terms of the GNU Lesser General Public License
 * Please read the LICENSE file
 */

import org.w3style.calendar.CalendarPanel;
import org.w3style.calendar.CalendarChangeEvent;
import org.w3style.calendar.CalendarChangeListener;
import javax.swing.*;
import java.awt.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * A small demonstration of the calendar working in a basic Swing layout
 * @author Chris Corbyn
 */
class CalendarDemo implements CalendarChangeListener
{
        /**
         * The label which displays the currently selected date
         */
        protected JLabel selectedDateDisplay;

        /**
         * App entry point
         */
        public static void main(String[] args)
        {
                CalendarDemo me = new CalendarDemo();
                me.demo();
        }
        /**
         * Render the GUI in the event dispatching thread
         */
        public void demo()
        {
                SwingUtilities.invokeLater(new Runnable() {
                        public void run()
                        {
                                renderMe();
                        }
                });
        }
        /**
         * The change listener for the calendar.
         * Updates the JLabel showing the selected date
         * @param e The change event fired from the calendar
         */
        public void calendarChanged(CalendarChangeEvent e)
        {
                this.selectedDateDisplay.setText(this.getDateDisplayFieldName() +
                        this.getFormattedDate((Calendar)e.getSource()));
                this.selectedDateDisplay.updateUI();
        }
        /**
         * Get the name of the date field
         * @return String
         */
        public String getDateDisplayFieldName()
        {
                return "Date: ";
        }
        /**
         * Get the date from the calendar in a nice format
         * @param calendar The Calendar from CalendarModel
         * @return date, formatted as a human readable string
         */
        public String getFormattedDate(Calendar c)
        {
                SimpleDateFormat d = new SimpleDateFormat("EEE, MMM dd, yyyy");
                return d.format(c.getTime());
        }
        /**
         * Render this application's GUI.
         * This should be done in the appropriate thread
         */
        public void renderMe()
        {
                try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                        System.out.println("L&F problem");
                }
                JFrame frame = new JFrame("Calendar Demo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setResizable(false);

                //Create a new calendar component
                CalendarPanel calendar = new CalendarPanel("Example Calendar");

                JPanel panel = new JPanel(new GridBagLayout());
                GridBagConstraints c = new GridBagConstraints();
                c.gridx = 0;
                c.gridy = 0;
                c.insets = new Insets(4, 4, 4, 4);

                //Add the calendar
                panel.add(calendar, c);

                this.selectedDateDisplay = new JLabel(this.getDateDisplayFieldName() +
                        this.getFormattedDate(calendar.getModel().getCalendar()));
                this.selectedDateDisplay.setLabelFor(calendar);

                c.gridy = 1;
                c.anchor = GridBagConstraints.WEST;
                c.insets = new Insets(8, 8, 8, 8);
                panel.add(this.selectedDateDisplay, c);

                frame.getContentPane().add(panel);

                //Register ourseleves as an event listener for the calendar
                calendar.addCalendarChangeListener(this);

                frame.pack();
                frame.setVisible(true);
        }
}
Download: http://www.wstyle.co.uk/~d11wtq/calendar_demo.jar

Code: Select all

java -jar calendar_demo.jar