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}