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.6 2005/11/16 15:58:41 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;
057
058import javax.swing.BorderFactory;
059import javax.swing.JButton;
060import javax.swing.JComboBox;
061import javax.swing.JLabel;
062import javax.swing.JPanel;
063import javax.swing.SwingConstants;
064
065import org.jfree.date.SerialDate;
066
067/**
068 * A panel that allows the user to select a date.
069 * <P>
070 * This class is incomplete and untested.  You should not use it yet...
071 *
072 * @author David Gilbert
073 */
074public class SerialDateChooserPanel extends JPanel implements ActionListener {
075
076    /** The default background color for the selected date. */
077    public static final Color DEFAULT_DATE_BUTTON_COLOR = Color.red;
078
079    /** The default background color for the current month. */
080    public static final Color DEFAULT_MONTH_BUTTON_COLOR = Color.lightGray;
081
082    /** The date selected in the panel. */
083    private SerialDate date;
084
085    /** The color for the selected date. */
086    private Color dateButtonColor;
087
088    /** The color for dates in the current month. */
089    private Color monthButtonColor;
090
091    /** The color for dates that are visible, but not in the current month. */
092    private Color chosenOtherButtonColor = Color.darkGray;
093
094    /** The first day-of-the-week. */
095    private int firstDayOfWeek = Calendar.SUNDAY;
096
097    /** The range used for selecting years. */
098    private int yearSelectionRange = 20;
099
100    /** The font used to display the date. */
101    private Font dateFont = new Font("SansSerif", Font.PLAIN, 10);
102
103    /** A combo for selecting the month. */
104    private JComboBox monthSelector = null;
105
106    /** A combo for selecting the year. */
107    private JComboBox yearSelector = null;
108
109    /** A button for selecting today's date. */
110    private JButton todayButton = null;
111
112    /** An array of buttons used to display the days-of-the-month. */
113    private JButton[] buttons = null;
114
115    /** A flag that indicates whether or not we are currently refreshing the buttons. */
116    private boolean refreshing = false;
117
118    /**
119     * Constructs a new date chooser panel, using today's date as the initial selection.
120     */
121    public SerialDateChooserPanel() {
122
123        this(SerialDate.createInstance(new Date()), false,
124             DEFAULT_DATE_BUTTON_COLOR,
125             DEFAULT_MONTH_BUTTON_COLOR);
126
127    }
128
129    /**
130     * Constructs a new date chooser panel.
131     *
132     * @param date  the date.
133     * @param controlPanel  a flag that indicates whether or not the 'today' button should
134     *                      appear on the panel.
135     */
136    public SerialDateChooserPanel(final SerialDate date, final boolean controlPanel) {
137
138        this(date, controlPanel,
139             DEFAULT_DATE_BUTTON_COLOR,
140             DEFAULT_MONTH_BUTTON_COLOR);
141
142    }
143
144    /**
145     * Constructs a new date chooser panel.
146     *
147     * @param date  the date.
148     * @param controlPanel  the control panel.
149     * @param dateButtonColor  the date button color.
150     * @param monthButtonColor  the month button color.
151     */
152    public SerialDateChooserPanel(final SerialDate date, final boolean controlPanel,
153                                  final Color dateButtonColor, final Color monthButtonColor) {
154
155        super(new BorderLayout());
156
157        this.date = date;
158        this.dateButtonColor = dateButtonColor;
159        this.monthButtonColor = monthButtonColor;
160
161        add(constructSelectionPanel(), BorderLayout.NORTH);
162        add(getCalendarPanel(), BorderLayout.CENTER);
163        if (controlPanel) {
164            add(constructControlPanel(), BorderLayout.SOUTH);
165        }
166
167    }
168
169    /**
170     * Sets the date chosen in the panel.
171     *
172     * @param date  the new date.
173     */
174    public void setDate(final SerialDate date) {
175
176        this.date = date;
177        this.monthSelector.setSelectedIndex(date.getMonth() - 1);
178        refreshYearSelector();
179        refreshButtons();
180
181    }
182
183    /**
184     * Returns the date selected in the panel.
185     *
186     * @return the selected date.
187     */
188    public SerialDate getDate() {
189        return this.date;
190    }
191
192    /**
193     * Handles action-events from the date panel.
194     *
195     * @param e information about the event that occurred.
196     */
197    public void actionPerformed(final ActionEvent e) {
198
199        if (e.getActionCommand().equals("monthSelectionChanged")) {
200            final JComboBox c = (JComboBox) e.getSource();
201            this.date = SerialDate.createInstance(
202                this.date.getDayOfMonth(), c.getSelectedIndex() + 1, this.date.getYYYY()
203            );
204            refreshButtons();
205        }
206        else if (e.getActionCommand().equals("yearSelectionChanged")) {
207            if (!this.refreshing) {
208                final JComboBox c = (JComboBox) e.getSource();
209                final Integer y = (Integer) c.getSelectedItem();
210                this.date = SerialDate.createInstance(
211                    this.date.getDayOfMonth(), this.date.getMonth(), y.intValue()
212                );
213                refreshYearSelector();
214                refreshButtons();
215            }
216        }
217        else if (e.getActionCommand().equals("todayButtonClicked")) {
218            setDate(SerialDate.createInstance(new Date()));
219        }
220        else if (e.getActionCommand().equals("dateButtonClicked")) {
221            final JButton b = (JButton) e.getSource();
222            final int i = Integer.parseInt(b.getName());
223            final SerialDate first = getFirstVisibleDate();
224            final SerialDate selected = SerialDate.addDays(i, first);
225            setDate(selected);
226        }
227
228    }
229
230    /**
231     * Returns a panel of buttons, each button representing a day in the month.  This is a
232     * sub-component of the DatePanel.
233     *
234     * @return the panel.
235     */
236    private JPanel getCalendarPanel() {
237
238        final JPanel panel = new JPanel(new GridLayout(7, 7));
239        panel.add(new JLabel("Sun", SwingConstants.CENTER));
240        panel.add(new JLabel("Mon", SwingConstants.CENTER));
241        panel.add(new JLabel("Tue", SwingConstants.CENTER));
242        panel.add(new JLabel("Wed", SwingConstants.CENTER));
243        panel.add(new JLabel("Thu", SwingConstants.CENTER));
244        panel.add(new JLabel("Fri", SwingConstants.CENTER));
245        panel.add(new JLabel("Sat", SwingConstants.CENTER));
246
247        this.buttons = new JButton[42];
248        for (int i = 0; i < 42; i++) {
249            final JButton button = new JButton("");
250            button.setMargin(new Insets(1, 1, 1, 1));
251            button.setName(Integer.toString(i));
252            button.setFont(this.dateFont);
253            button.setFocusPainted(false);
254            button.setActionCommand("dateButtonClicked");
255            button.addActionListener(this);
256            this.buttons[i] = button;
257            panel.add(button);
258        }
259        return panel;
260
261    }
262
263    /**
264     * Returns the button color according to the specified date.
265     *
266     * @param targetDate  the target date.
267     *
268     * @return the button color.
269     */
270    protected Color getButtonColor(final SerialDate targetDate) {
271
272        if (this.date.equals(this.date)) {
273            return this.dateButtonColor;
274        }
275        else if (targetDate.getMonth() == this.date.getMonth()) {
276            return this.monthButtonColor;
277        }
278        else {
279            return this.chosenOtherButtonColor;
280        }
281
282    }
283
284    /**
285     * Returns the first date that is visible in the grid.  This should always be in the month
286     * preceding the month of the selected date.
287     *
288     * @return the first visible date.
289     */
290    protected SerialDate getFirstVisibleDate() {
291
292        SerialDate result = SerialDate.createInstance(1, this.date.getMonth(), this.date.getYYYY());
293        result = SerialDate.addDays(-1, result);
294        while (result.getDayOfWeek() != getFirstDayOfWeek()) {
295            result = SerialDate.addDays(-1, result);
296        }
297        return result;
298
299    }
300
301    /**
302     * Returns the first day of the week (controls the labels in the date panel).
303     *
304     * @return the first day of the week.
305     */
306    private int getFirstDayOfWeek() {
307        return this.firstDayOfWeek;
308    }
309
310    /**
311     * Update the button labels and colors to reflect date selection.
312     */
313    protected void refreshButtons() {
314
315        SerialDate current = getFirstVisibleDate();
316        for (int i = 0; i < 42; i++) {
317            final JButton button = this.buttons[i];
318            button.setText(String.valueOf(current.getDayOfWeek()));
319            button.setBackground(getButtonColor(current));
320            current = SerialDate.addDays(1, current);
321        }
322
323    }
324
325    /**
326     * Changes the contents of the year selection JComboBox to reflect the chosen date and the year
327     * range.
328     */
329    private void refreshYearSelector() {
330        if (!this.refreshing) {
331            this.refreshing = true;
332            this.yearSelector.removeAllItems();
333            final Vector v = getYears(this.date.getYYYY());
334            for (Enumeration e = v.elements(); e.hasMoreElements();) {
335                this.yearSelector.addItem(e.nextElement());
336            }
337            this.yearSelector.setSelectedItem(new Integer(this.date.getYYYY()));
338            this.refreshing = false;
339        }
340    }
341
342    /**
343     * Returns a vector of years preceding and following the specified year.  The number of years
344     * preceding and following is determined by the yearSelectionRange attribute.
345     *
346     * @param chosenYear  the current year.
347     *
348     * @return a vector of years.
349     */
350    private Vector getYears(final int chosenYear) {
351        final Vector v = new Vector();
352        for (int i = chosenYear - this.yearSelectionRange; 
353            i <= chosenYear + this.yearSelectionRange; i++) {
354            v.addElement(new Integer(i));
355        }
356        return v;
357    }
358
359    /**
360     * Constructs a panel containing two JComboBoxes (for the month and year) and a button
361     * (to reset the date to TODAY).
362     *
363     * @return the panel.
364     */
365    private JPanel constructSelectionPanel() {
366        final JPanel p = new JPanel();
367        this.monthSelector = new JComboBox(SerialDate.getMonths());
368        this.monthSelector.addActionListener(this);
369        this.monthSelector.setActionCommand("monthSelectionChanged");
370        p.add(this.monthSelector);
371
372        this.yearSelector = new JComboBox(getYears(0));
373        this.yearSelector.addActionListener(this);
374        this.yearSelector.setActionCommand("yearSelectionChanged");
375        p.add(this.yearSelector);
376
377        return p;
378    }
379
380    /**
381     * Returns a panel that appears at the bottom of the calendar panel - contains a button for
382     * selecting today's date.
383     *
384     * @return the panel.
385     */
386    private JPanel constructControlPanel() {
387
388        final JPanel p = new JPanel();
389        p.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
390        this.todayButton = new JButton("Today");
391        this.todayButton.addActionListener(this);
392        this.todayButton.setActionCommand("todayButtonClicked");
393        p.add(this.todayButton);
394        return p;
395
396    }
397
398}