/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.swing; /** * Widget allowing to edit a date with a calendar popup * * @author sguerin * */ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class DateSelector extends TextFieldCustomPopup<Date> { public static final Font NORMAL_FONT = new Font("SansSerif", Font.PLAIN, 11); static final String[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static final String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; private Calendar _cal; private SimpleDateFormat formatter; Date _revertValue; /* -------------- GUI Components --------------------------- */ CalendarPanel calendarPanel; public DateSelector() { super(null); } public DateSelector(Date d) { this(); setEditedObject(d); _revertValue = (Date) d.clone(); } public Calendar getCalendar() { if (_cal == null) { _cal = Calendar.getInstance(Locale.ENGLISH); } return _cal; } @Override protected ResizablePanel createCustomPanel(Date editedObject) { calendarPanel = new CalendarPanel(); return calendarPanel; } @Override public Date getEditedObject() { return getDate(); } @Override public void setEditedObject(Date object) { super.setEditedObject(object); if (object != null) { getCalendar().setTime(object); } } /** * Returns the Date associated with the picker * * @return The current selected date. */ public Date getDate() { return getCalendar().getTime(); } /** * Sets the date for the picker. All GUI elements associated with the picker are updated and the grid of date buttons is rebuilt * * @param d * the new date for this date picker. */ public void setDate(Date d) { if (d == null) { d = new Date(); } setEditedObject(d); } @Override public void updateCustomPanel(Date editedObject) { if (calendarPanel != null) { calendarPanel.update(); } } @Override public String renderedString(Date editedObject) { if (formatter == null) { formatter = new SimpleDateFormat(); } formatter.applyPattern("MMMM d, yyyy"); return formatter.format(getDate()); } protected class CalendarPanel extends ResizablePanel { ButtonsControlPanel controlPanel; private JButton prevMonth; private JButton nextMonth; JComboBox monthComboBox; JSpinner yearSpinner; private JPanel dayButtons; void update() { yearSpinner.setValue(new Integer(getCalendar().get(Calendar.YEAR))); monthComboBox.setSelectedIndex(getCalendar().get(Calendar.MONTH)); rebuildDayButtons(); } protected CalendarPanel() { super(); // Create the month chooser as a comboBox monthComboBox = new JComboBox(months); monthComboBox.setLightWeightPopupEnabled(false); monthComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getCalendar().set(Calendar.MONTH, monthComboBox.getSelectedIndex()); fireEditedObjectChanged(); } }); // create the spinner that selects the current year int currentYear = Calendar.getInstance().get(Calendar.YEAR); // Range is current year +/- 100 , step size 1 SpinnerNumberModel yearModel = new SpinnerNumberModel(currentYear, currentYear - 100, currentYear + 100, 1); yearSpinner = new JSpinner(yearModel); yearSpinner.setEditor(new JSpinner.NumberEditor(yearSpinner, "#")); yearSpinner.setMinimumSize(yearSpinner.getPreferredSize()); // GUITools.getTextField(yearSpinner).addActionListener(this); // //just gets texfield of the spinners jformatted textfield yearSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == yearSpinner) { getCalendar().set(Calendar.YEAR, ((Number) yearSpinner.getValue()).intValue()); } fireEditedObjectChanged(); } }); // Create next/prev buttons prevMonth = new JButton("<"); nextMonth = new JButton(">"); // setup actionlisteners prevMonth.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getCalendar().add(Calendar.MONTH, -1); fireEditedObjectChanged(); } }); nextMonth.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getCalendar().add(Calendar.MONTH, 1); fireEditedObjectChanged(); } }); // Build the calendar Panel setLayout(new BorderLayout()); // Build the month / year selectors JPanel monthPanel = new JPanel(new BorderLayout()); JPanel choosersPanel = new JPanel(); choosersPanel.add(monthComboBox); choosersPanel.add(yearSpinner); monthPanel.add(prevMonth, BorderLayout.WEST); monthPanel.add(choosersPanel, BorderLayout.CENTER); monthPanel.add(nextMonth, BorderLayout.EAST); add(monthPanel, BorderLayout.NORTH); calendarPanel = this; // Day button grid dayButtons = new JPanel(new GridBagLayout()); rebuildDayButtons(); add(dayButtons, BorderLayout.CENTER); controlPanel = new ButtonsControlPanel() { @Override public String localizedForKeyAndButton(String key, JButton component) { return DateSelector.this.localizedForKeyAndButton(key, component); } }; controlPanel.addButton("apply", new ActionListener() { @Override public void actionPerformed(ActionEvent e) { apply(); } }); controlPanel.addButton("cancel", new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cancel(); } }); controlPanel.addButton("reset", new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setEditedObject(null); apply(); } }); controlPanel.applyFocusTraversablePolicyTo(this, true); add(controlPanel, BorderLayout.SOUTH); } @Override public Dimension getDefaultSize() { return getSize(); } @Override public void setPreferredSize(Dimension preferredSize) { } /** * Anytime the date buttons need to be changes, this should be called When the month or year changes, or when any dae button is * clicked. Clicked day buttons are highlighted, and so the grid is redrawn */ private void rebuildDayButtons() { // Completely cleanout the panel and rebuild it from scratch. dayButtons.removeAll(); // Build the Date Grid GridBagConstraints c = new GridBagConstraints(); c.weightx = c.weighty = 1; c.gridx = c.gridy = 0; c.fill = GridBagConstraints.BOTH; int dayOfWeek = 0; // This is needed because Caldendar does not provide an ordinal day // of week field. switch (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) // find // out // what // day // of // the // week // today // is { case Calendar.SUNDAY: dayOfWeek = 0; break; case Calendar.MONDAY: dayOfWeek = 1; break; case Calendar.TUESDAY: dayOfWeek = 2; break; case Calendar.WEDNESDAY: dayOfWeek = 3; break; case Calendar.THURSDAY: dayOfWeek = 4; break; case Calendar.FRIDAY: dayOfWeek = 5; break; case Calendar.SATURDAY: dayOfWeek = 6; break; } for (int i = 0; i < 7; i++) { c.gridx = i; JLabel l = new JLabel(days[i], SwingConstants.CENTER); l.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15)); // Make the Day of the week bold if it is today if (i == dayOfWeek && getCalendar().get(Calendar.YEAR) == Calendar.getInstance().get(Calendar.YEAR) && getCalendar().get(Calendar.MONTH) == Calendar.getInstance().get(Calendar.MONTH)) { l.setFont(l.getFont().deriveFont(Font.BOLD)); } dayButtons.add(l, c); } Calendar constructionCal = (Calendar) getCalendar().clone(); for (int i = 1; i <= getCalendar().getActualMaximum(Calendar.DAY_OF_MONTH); i++) { constructionCal.set(Calendar.DAY_OF_MONTH, i); c.gridx = constructionCal.get(Calendar.DAY_OF_WEEK) - 1; // start // at // gridx // = 0 c.gridy = constructionCal.get(Calendar.WEEK_OF_MONTH); // start // at // gridy // = 1, // because // of // the // day // labels DateButton b = new DateButton(constructionCal); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getCalendar().setTime(((DateButton) e.getSource()).getDate()); fireEditedObjectChanged(); } }); dayButtons.add(b, c); } if (c.gridy < 6) { c.gridy = 6; dayButtons.add(Box.createRigidArea(new Dimension(25, 20)), c); } calendarPanel.validate(); calendarPanel.repaint(); } /** * An extension of JButton that shows a date as a digit indicating the day of the month */ private class DateButton extends JButton { private boolean selectedDay = false; private boolean busy = false; // any events occuring today? private Date date = new Date(); public DateButton(Calendar cal) { super(""); date = cal.getTime(); // create a date specifically for this // button setFont(NORMAL_FONT); // If this button represents today, set appropriate status. // comaparing dates doesnt work since those are only equal if // they match upto the millisecond. if (cal.get(Calendar.DAY_OF_YEAR) == Calendar.getInstance().get(Calendar.DAY_OF_YEAR) && cal.get(Calendar.YEAR) == Calendar.getInstance().get(Calendar.YEAR)) { setFont(getFont().deriveFont(Font.BOLD)); } setText(Integer.toString(cal.get(Calendar.DAY_OF_MONTH))); setForeground(Color.BLUE); setPreferredSize(new Dimension(30, 20)); setMinimumSize(getPreferredSize()); setMaximumSize(getPreferredSize()); } public void setDateStyle() { if (selectedDay) { setBackground(Color.GREEN); } else if (busy) { setBackground(Color.GRAY); } else { setBackground(Color.YELLOW); } } public Date getDate() { return date; } public boolean isBusy() { return busy; } public void setBusy(boolean b) { this.busy = b; } public boolean isSelectedDay() { return selectedDay; } public void setSelectedDay(boolean selected) { this.selectedDay = selected; } } } @Override public void apply() { setRevertValue(getEditedObject()); closePopup(); super.apply(); } @Override public void cancel() { setEditedObject(_revertValue); closePopup(); super.cancel(); } @Override public void setRevertValue(Date oldValue) { _revertValue = oldValue; } public Date getRevertValue() { return _revertValue; } @Override protected void openPopup() { super.openPopup(); if (calendarPanel != null) { calendarPanel.controlPanel.applyFocusTraversablePolicyTo(calendarPanel, true); } } // Override if required public String localizedForKeyAndButton(String key, JButton component) { return key; } }