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 }