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    
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    import javax.swing.BorderFactory;
058    import javax.swing.JButton;
059    import javax.swing.JComboBox;
060    import javax.swing.JLabel;
061    import javax.swing.JPanel;
062    import javax.swing.SwingConstants;
063    
064    import 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     */
073    public 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    }