    001/* ========================================================================
    002 * JCommon : a free general purpose class library for the Java(tm) platform
    003 * ========================================================================
    004 *
    005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jcommon/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025 * in the United States and other countries.]
    026 *
    027 * ---------------------------
    028 * SerialDateChooserPanel.java
    029 * ---------------------------
    030 * (C) Copyright 2001-2005, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   -;
    034 *
    035 * $Id: SerialDateChooserPanel.java,v 1.7 2007/11/02 17:50:36 taqua Exp $
    036 *
    037 * Changes
    038 * -------
    039 * 08-Dec-2001 : Version 1 (DG);
    040 * 14-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    041 *
    042 */
    043
    044package org.jfree.ui;
    045
    046import java.awt.BorderLayout;
    047import java.awt.Color;
    048import java.awt.Font;
    049import java.awt.GridLayout;
    050import java.awt.Insets;
    051import java.awt.event.ActionEvent;
    052import java.awt.event.ActionListener;
    053import java.util.Calendar;
    054import java.util.Date;
    055import java.util.Enumeration;
    056import java.util.Vector;
    057import javax.swing.BorderFactory;
    058import javax.swing.JButton;
    059import javax.swing.JComboBox;
    060import javax.swing.JLabel;
    061import javax.swing.JPanel;
    062import javax.swing.SwingConstants;
    063
    064import org.jfree.date.SerialDate;
    065
    066/**
    067 * A panel that allows the user to select a date.
    068 * <P>
    069 * This class is incomplete and untested.  You should not use it yet...
    070 *
    071 * @author David Gilbert
    072 */
    073public class SerialDateChooserPanel extends JPanel implements ActionListener {
    074
    075    /** The default background color for the selected date. */
    076    public static final Color DEFAULT_DATE_BUTTON_COLOR = Color.red;
    077
    078    /** The default background color for the current month. */
    079    public static final Color DEFAULT_MONTH_BUTTON_COLOR = Color.lightGray;
    080
    081    /** The date selected in the panel. */
    082    private SerialDate date;
    083
    084    /** The color for the selected date. */
    085    private Color dateButtonColor;
    086
    087    /** The color for dates in the current month. */
    088    private Color monthButtonColor;
    089
    090    /** The color for dates that are visible, but not in the current month. */
    091    private Color chosenOtherButtonColor = Color.darkGray;
    092
    093    /** The first day-of-the-week. */
    094    private int firstDayOfWeek = Calendar.SUNDAY;
    095
    096    /** The range used for selecting years. */
    097    private int yearSelectionRange = 20;
    098
    099    /** The font used to display the date. */
    100    private Font dateFont = new Font("SansSerif", Font.PLAIN, 10);
    101
    102    /** A combo for selecting the month. */
    103    private JComboBox monthSelector = null;
    104
    105    /** A combo for selecting the year. */
    106    private JComboBox yearSelector = null;
    107
    108    /** A button for selecting today's date. */
    109    private JButton todayButton = null;
    110
    111    /** An array of buttons used to display the days-of-the-month. */
    112    private JButton[] buttons = null;
    113
    114    /** A flag that indicates whether or not we are currently refreshing the buttons. */
    115    private boolean refreshing = false;
    116
    117    /**
    118     * Constructs a new date chooser panel, using today's date as the initial selection.
    119     */
    120    public SerialDateChooserPanel() {
    121
    122        this(SerialDate.createInstance(new Date()), false,
    123             DEFAULT_DATE_BUTTON_COLOR,
    124             DEFAULT_MONTH_BUTTON_COLOR);
    125
    126    }
    127
    128    /**
    129     * Constructs a new date chooser panel.
    130     *
    131     * @param date  the date.
    132     * @param controlPanel  a flag that indicates whether or not the 'today' button should
    133     *                      appear on the panel.
    134     */
    135    public SerialDateChooserPanel(final SerialDate date, final boolean controlPanel) {
    136
    137        this(date, controlPanel,
    138             DEFAULT_DATE_BUTTON_COLOR,
    139             DEFAULT_MONTH_BUTTON_COLOR);
    140
    141    }
    142
    143    /**
    144     * Constructs a new date chooser panel.
    145     *
    146     * @param date  the date.
    147     * @param controlPanel  the control panel.
    148     * @param dateButtonColor  the date button color.
    149     * @param monthButtonColor  the month button color.
    150     */
    151    public SerialDateChooserPanel(final SerialDate date, final boolean controlPanel,
    152                                  final Color dateButtonColor, final Color monthButtonColor) {
    153
    154        super(new BorderLayout());
    155
    156        this.date = date;
    157        this.dateButtonColor = dateButtonColor;
    158        this.monthButtonColor = monthButtonColor;
    159
    160        add(constructSelectionPanel(), BorderLayout.NORTH);
    161        add(getCalendarPanel(), BorderLayout.CENTER);
    162        if (controlPanel) {
    163            add(constructControlPanel(), BorderLayout.SOUTH);
    164        }
    165
    166    }
    167
    168    /**
    169     * Sets the date chosen in the panel.
    170     *
    171     * @param date  the new date.
    172     */
    173    public void setDate(final SerialDate date) {
    174
    175        this.date = date;
    176        this.monthSelector.setSelectedIndex(date.getMonth() - 1);
    177        refreshYearSelector();
    178        refreshButtons();
    179
    180    }
    181
    182    /**
    183     * Returns the date selected in the panel.
    184     *
    185     * @return the selected date.
    186     */
    187    public SerialDate getDate() {
    188        return this.date;
    189    }
    190
    191    /**
    192     * Handles action-events from the date panel.
    193     *
    194     * @param e information about the event that occurred.
    195     */
    196    public void actionPerformed(final ActionEvent e) {
    197
    198        if (e.getActionCommand().equals("monthSelectionChanged")) {
    199            final JComboBox c = (JComboBox) e.getSource();
    200            this.date = SerialDate.createInstance(
    201                this.date.getDayOfMonth(), c.getSelectedIndex() + 1, this.date.getYYYY()
    202            );
    203            refreshButtons();
    204        }
    205        else if (e.getActionCommand().equals("yearSelectionChanged")) {
    206            if (!this.refreshing) {
    207                final JComboBox c = (JComboBox) e.getSource();
    208                final Integer y = (Integer) c.getSelectedItem();
    209                this.date = SerialDate.createInstance(
    210                    this.date.getDayOfMonth(), this.date.getMonth(), y.intValue()
    211                );
    212                refreshYearSelector();
    213                refreshButtons();
    214            }
    215        }
    216        else if (e.getActionCommand().equals("todayButtonClicked")) {
    217            setDate(SerialDate.createInstance(new Date()));
    218        }
    219        else if (e.getActionCommand().equals("dateButtonClicked")) {
    220            final JButton b = (JButton) e.getSource();
    221            final int i = Integer.parseInt(b.getName());
    222            final SerialDate first = getFirstVisibleDate();
    223            final SerialDate selected = SerialDate.addDays(i, first);
    224            setDate(selected);
    225        }
    226
    227    }
    228
    229    /**
    230     * Returns a panel of buttons, each button representing a day in the month.  This is a
    231     * sub-component of the DatePanel.
    232     *
    233     * @return the panel.
    234     */
    235    private JPanel getCalendarPanel() {
    236
    237        final JPanel panel = new JPanel(new GridLayout(7, 7));
    238        panel.add(new JLabel("Sun", SwingConstants.CENTER));
    239        panel.add(new JLabel("Mon", SwingConstants.CENTER));
    240        panel.add(new JLabel("Tue", SwingConstants.CENTER));
    241        panel.add(new JLabel("Wed", SwingConstants.CENTER));
    242        panel.add(new JLabel("Thu", SwingConstants.CENTER));
    243        panel.add(new JLabel("Fri", SwingConstants.CENTER));
    244        panel.add(new JLabel("Sat", SwingConstants.CENTER));
    245
    246        this.buttons = new JButton[42];
    247        for (int i = 0; i < 42; i++) {
    248            final JButton button = new JButton("");
    249            button.setMargin(new Insets(1, 1, 1, 1));
    250            button.setName(Integer.toString(i));
    251            button.setFont(this.dateFont);
    252            button.setFocusPainted(false);
    253            button.setActionCommand("dateButtonClicked");
    254            button.addActionListener(this);
    255            this.buttons[i] = button;
    256            panel.add(button);
    257        }
    258        return panel;
    259
    260    }
    261
    262    /**
    263     * Returns the button color according to the specified date.
    264     *
    265     * @param targetDate  the target date.
    266     *
    267     * @return the button color.
    268     */
    269    protected Color getButtonColor(final SerialDate targetDate) {
    270
    271        if (this.date.equals(this.date)) {
    272            return this.dateButtonColor;
    273        }
    274        else if (targetDate.getMonth() == this.date.getMonth()) {
    275            return this.monthButtonColor;
    276        }
    277        else {
    278            return this.chosenOtherButtonColor;
    279        }
    280
    281    }
    282
    283    /**
    284     * Returns the first date that is visible in the grid.  This should always be in the month
    285     * preceding the month of the selected date.
    286     *
    287     * @return the first visible date.
    288     */
    289    protected SerialDate getFirstVisibleDate() {
    290
    291        SerialDate result = SerialDate.createInstance(1, this.date.getMonth(), this.date.getYYYY());
    292        result = SerialDate.addDays(-1, result);
    293        while (result.getDayOfWeek() != getFirstDayOfWeek()) {
    294            result = SerialDate.addDays(-1, result);
    295        }
    296        return result;
    297
    298    }
    299
    300    /**
    301     * Returns the first day of the week (controls the labels in the date panel).
    302     *
    303     * @return the first day of the week.
    304     */
    305    private int getFirstDayOfWeek() {
    306        return this.firstDayOfWeek;
    307    }
    308
    309    /**
    310     * Update the button labels and colors to reflect date selection.
    311     */
    312    protected void refreshButtons() {
    313
    314        SerialDate current = getFirstVisibleDate();
    315        for (int i = 0; i < 42; i++) {
    316            final JButton button = this.buttons[i];
    317            button.setText(String.valueOf(current.getDayOfWeek()));
    318            button.setBackground(getButtonColor(current));
    319            current = SerialDate.addDays(1, current);
    320        }
    321
    322    }
    323
    324    /**
    325     * Changes the contents of the year selection JComboBox to reflect the chosen date and the year
    326     * range.
    327     */
    328    private void refreshYearSelector() {
    329        if (!this.refreshing) {
    330            this.refreshing = true;
    331            this.yearSelector.removeAllItems();
    332            final Vector v = getYears(this.date.getYYYY());
    333            for (Enumeration e = v.elements(); e.hasMoreElements();) {
    334                this.yearSelector.addItem(e.nextElement());
    335            }
    336            this.yearSelector.setSelectedItem(new Integer(this.date.getYYYY()));
    337            this.refreshing = false;
    338        }
    339    }
    340
    341    /**
    342     * Returns a vector of years preceding and following the specified year.  The number of years
    343     * preceding and following is determined by the yearSelectionRange attribute.
    344     *
    345     * @param chosenYear  the current year.
    346     *
    347     * @return a vector of years.
    348     */
    349    private Vector getYears(final int chosenYear) {
    350        final Vector v = new Vector();
    351        for (int i = chosenYear - this.yearSelectionRange;
    352            i <= chosenYear + this.yearSelectionRange; i++) {
    353            v.addElement(new Integer(i));
    354        }
    355        return v;
    356    }
    357
    358    /**
    359     * Constructs a panel containing two JComboBoxes (for the month and year) and a button
    360     * (to reset the date to TODAY).
    361     *
    362     * @return the panel.
    363     */
    364    private JPanel constructSelectionPanel() {
    365        final JPanel p = new JPanel();
    366        this.monthSelector = new JComboBox(SerialDate.getMonths());
    367        this.monthSelector.addActionListener(this);
    368        this.monthSelector.setActionCommand("monthSelectionChanged");
    369        p.add(this.monthSelector);
    370
    371        this.yearSelector = new JComboBox(getYears(0));
    372        this.yearSelector.addActionListener(this);
    373        this.yearSelector.setActionCommand("yearSelectionChanged");
    374        p.add(this.yearSelector);
    375
    376        return p;
    377    }
    378
    379    /**
    380     * Returns a panel that appears at the bottom of the calendar panel - contains a button for
    381     * selecting today's date.
    382     *
    383     * @return the panel.
    384     */
    385    private JPanel constructControlPanel() {
    386
    387        final JPanel p = new JPanel();
    388        p.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
    389        this.todayButton = new JButton("Today");
    390        this.todayButton.addActionListener(this);
    391        this.todayButton.setActionCommand("todayButtonClicked");
    392        p.add(this.todayButton);
    393        return p;
    394
    395    }
    396
    397}