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    
044    package org.jfree.ui;
045    
046    import java.awt.BorderLayout;
047    import java.awt.Color;
048    import java.awt.Font;
049    import java.awt.GridLayout;
050    import java.awt.Insets;
051    import java.awt.event.ActionEvent;
052    import java.awt.event.ActionListener;
053    import java.util.Calendar;
054    import java.util.Date;
055    import java.util.Enumeration;
056    import java.util.Vector;
057    
058    import javax.swing.BorderFactory;
059    import javax.swing.JButton;
060    import javax.swing.JComboBox;
061    import javax.swing.JLabel;
062    import javax.swing.JPanel;
063    import javax.swing.SwingConstants;
064    
065    import 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     */
074    public 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    }