Java Source Code: com.toedter.calendar.JDateChooser


   1: /*
   2:  *  JDateChooser.java  - A bean for choosing a date
   3:  *  Copyright (C) 2004 Kai Toedter
   4:  *  kai@toedter.com
   5:  *  www.toedter.com
   6:  *
   7:  *  This program is free software; you can redistribute it and/or
   8:  *  modify it under the terms of the GNU Lesser General Public License
   9:  *  as published by the Free Software Foundation; either version 2
  10:  *  of the License, or (at your option) any later version.
  11:  *
  12:  *  This program is distributed in the hope that it will be useful,
  13:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15:  *  GNU Lesser General Public License for more details.
  16:  *
  17:  *  You should have received a copy of the GNU Lesser General Public License
  18:  *  along with this program; if not, write to the Free Software
  19:  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20:  */
  21: package com.toedter.calendar;
  22: 
  23: import java.awt.BorderLayout;
  24: import java.awt.Insets;
  25: import java.awt.event.ActionEvent;
  26: import java.awt.event.ActionListener;
  27: import java.awt.event.KeyEvent;
  28: 
  29: import java.beans.PropertyChangeEvent;
  30: import java.beans.PropertyChangeListener;
  31: 
  32: import java.net.URL;
  33: 
  34: import java.util.Calendar;
  35: import java.util.Date;
  36: import java.util.Locale;
  37: 
  38: import javax.swing.ImageIcon;
  39: import javax.swing.JButton;
  40: import javax.swing.JPanel;
  41: import javax.swing.JPopupMenu;
  42: import javax.swing.JSpinner;
  43: import javax.swing.SpinnerDateModel;
  44: import javax.swing.SwingUtilities;
  45: import javax.swing.event.ChangeEvent;
  46: import javax.swing.event.ChangeListener;
  47: 
  48: /**
  49:  * A date chooser containig a date spinner and a button, that makes a JCalendar visible for
  50:  * choosing a date.
  51:  *
  52:  * @author Kai Toedter
  53:  * @version 1.2.2
  54:  */
  55: public class JDateChooser extends JPanel implements ActionListener, PropertyChangeListener,
  56:	      ChangeListener {
  57:    protected JButton calendarButton;
  58:    protected JSpinner dateSpinner;
  59:    protected JSpinner.DateEditor editor;
  60:    protected JCalendar jcalendar;
  61:    protected JPopupMenu popup;
  62:    protected SpinnerDateModel model;
  63:    protected String dateFormatString;
  64:    protected boolean dateSelected;
  65:    protected boolean isInitialized;
  66:    protected Date lastSelectedDate;
  67:    protected boolean startEmpty;
  68:
  69:    /**
  70:     * Creates a new JDateChooser object.
  71:     */
  72:	      public JDateChooser() {
  73:        this(null, null, false, null);
  74:    }
  75:
  76:    /**
  77:     * Creates a new JDateChooser object.
  78:     *
  79:     * @param icon the new icon
  80:     */
  81:	      public JDateChooser(ImageIcon icon) {
  82:        this(null, null, false, icon);
  83:    }
  84:
  85:    /**
  86:     * Creates a new JDateChooser object.
  87:     *
  88:     * @param startEmpty true, if the date field should be empty
  89:     */
  90:	      public JDateChooser(boolean startEmpty) {
  91:        this(null, null, startEmpty, null);
  92:    }
  93:
  94:    /**
  95:     * Creates a new JDateChooser object with given date format string. The default date format
  96:     * string is "MMMMM d, yyyy".
  97:     *
  98:     * @param dateFormatString the date format string
  99:     * @param startEmpty true, if the date field should be empty
 100:     */
 101:	      public JDateChooser(String dateFormatString, boolean startEmpty) {
 102:        this(null, dateFormatString, startEmpty, null);
 103:    }
 104:
 105:    /**
 106:     * Creates a new JDateChooser object from a given JCalendar.
 107:     *
 108:     * @param jcalendar the JCalendar
 109:     */
 110:	      public JDateChooser(JCalendar jcalendar) {
 111:        this(jcalendar, null, false, null);
 112:    }
 113:
 114:    /**
 115:     * Creates a new JDateChooser.
 116:     *
 117:     * @param jcalendar the jcalendar or null
 118:     * @param dateFormatString the date format string or null (then "MMMMM d, yyyy" is used)
 119:     * @param startEmpty true, if the date field should be empty
 120:     * @param icon the icon or null (then an internal icon is used)
 121:     */
 122:    public JDateChooser(JCalendar jcalendar, String dateFormatString, boolean startEmpty,
 123:	          ImageIcon icon) {
 124:	          if (jcalendar == null) {
 125:            jcalendar = new JCalendar();
 126:        }
 127:
 128:        this.jcalendar = jcalendar;
 129:
 130:	          if (dateFormatString == null) {
 131:            dateFormatString = "MMMMM d, yyyy";
 132:        }
 133:
 134:        this.dateFormatString = dateFormatString;
 135:        this.startEmpty = startEmpty;
 136:
 137:        setLayout(new BorderLayout());
 138:
 139:        jcalendar.getDayChooser().addPropertyChangeListener(this);
 140:        jcalendar.getDayChooser().setAlwaysFireDayProperty(true); // always fire "day" property even if the user selects the already selected day again
 141:        model = new SpinnerDateModel();
 142:        
 143:        /*
 144:        The 2 lines below were moved to the setModel method.
 145:        model.setCalendarField(java.util.Calendar.WEEK_OF_MONTH);
 146:        model.addChangeListener(this);
 147:        */
 148:        
 149:        // Begin Code change by Mark Brown on 24 Aug 2004
 150:        setModel(model);
 151:	          dateSpinner = new JSpinner(model) {
 152:	              public void setEnabled(boolean enabled) {
 153:                super.setEnabled(enabled);
 154:                calendarButton.setEnabled(enabled);
 155:            }
 156:        };
 157:        // End Code change by Mark Brown
 158:
 159:        String tempDateFortmatString = "";
 160:
 161:	          if (!startEmpty) {
 162:            tempDateFortmatString = dateFormatString;
 163:        }
 164:
 165:        editor = new JSpinner.DateEditor(dateSpinner, tempDateFortmatString);
 166:        dateSpinner.setEditor(editor);
 167:        add(dateSpinner, BorderLayout.CENTER);
 168:
 169:        // Display a calendar button with an icon
 170:	          if (icon == null) {
 171:            URL iconURL = getClass().getResource("images/JDateChooserIcon.gif");
 172:            icon = new ImageIcon(iconURL);      
 173:        }
 174:
 175:        calendarButton = new JButton(icon);
 176:        calendarButton.setMargin(new Insets(0, 0, 0, 0));
 177:        calendarButton.addActionListener(this);
 178:
 179:        // Alt + 'C' selects the calendar.
 180:        calendarButton.setMnemonic(KeyEvent.VK_C);
 181:
 182:        add(calendarButton, BorderLayout.EAST);
 183:
 184:        calendarButton.setMargin(new Insets(0, 0, 0, 0));
 185:	          popup = new JPopupMenu() {
 186:	                      public void setVisible(boolean b) {
 187:                        Boolean isCanceled = (Boolean) getClientProperty(
 188:                                "JPopupMenu.firePopupMenuCanceled");
 189:
 190:                        if (b || (!b && dateSelected) ||
 191:	                                  ((isCanceled != null) && !b && isCanceled.booleanValue())) {
 192:                            super.setVisible(b);
 193:                        }
 194:                    }
 195:                };
 196:
 197:        popup.setLightWeightPopupEnabled(true);
 198:
 199:        popup.add(jcalendar);
 200:        lastSelectedDate = model.getDate();
 201:        isInitialized = true;
 202:    }
 203:
 204:    /**
 205:     * Called when the jalendar button was pressed.
 206:     *
 207:     * @param e the action event
 208:     */
 209:	      public void actionPerformed(ActionEvent e) {
 210:        int x = calendarButton.getWidth() - (int) popup.getPreferredSize().getWidth();
 211:        int y = calendarButton.getY() + calendarButton.getHeight();
 212:
 213:        Calendar calendar = Calendar.getInstance();
 214:        calendar.setTime(model.getDate());
 215:        jcalendar.setCalendar(calendar);
 216:        popup.show(calendarButton, x, y);
 217:        dateSelected = false;
 218:    }
 219:
 220:    /**
 221:     * Listens for a "date" property change or a "day" property change event from the JCalendar.
 222:     * Updates the dateSpinner and closes the popup.
 223:     *
 224:     * @param evt the event
 225:     */
 226:	      public void propertyChange(PropertyChangeEvent evt) {
 227:	          if (evt.getPropertyName().equals("day")) {
 228:            dateSelected = true;
 229:            popup.setVisible(false);
 230:            setDate(jcalendar.getCalendar().getTime());
 231:            setDateFormatString(dateFormatString);
 232:        } else if (evt.getPropertyName().equals("date")) {
 233:            setDate((Date) evt.getNewValue());
 234:        }
 235:    }
 236:
 237:    /**
 238:     * Updates the UI of itself and the popup.
 239:     */
 240:	      public void updateUI() {
 241:        super.updateUI();
 242:
 243:	          if (jcalendar != null) {
 244:            SwingUtilities.updateComponentTreeUI(popup);
 245:        }
 246:    }
 247:
 248:    /**
 249:     * Sets the locale.
 250:     *
 251:     * @param l The new locale value
 252:     */
 253:	      public void setLocale(Locale l) {
 254:        dateSpinner.setLocale(l);
 255:        editor = new JSpinner.DateEditor(dateSpinner, dateFormatString);
 256:        dateSpinner.setEditor(editor);
 257:        jcalendar.setLocale(l);
 258:    }
 259:
 260:    /**
 261:     * Gets the date format string.
 262:     *
 263:     * @return Returns the dateFormatString.
 264:     */
 265:	      public String getDateFormatString() {
 266:        return dateFormatString;
 267:    }
 268:
 269:    /**
 270:     * Sets the date format string. E.g "MMMMM d, yyyy" will result in "July 21, 2004" if this is
 271:     * the selected date and locale is English.
 272:     *
 273:     * @param dateFormatString The dateFormatString to set.
 274:     */
 275:	      public void setDateFormatString(String dateFormatString) {
 276:        this.dateFormatString = dateFormatString;
 277:        editor.getFormat().applyPattern(dateFormatString);
 278:        invalidate();
 279:    }
 280:
 281:    /**
 282:     * Returns "JDateChooser".
 283:     *
 284:     * @return the name value
 285:     */
 286:	      public String getName() {
 287:        return "JDateChooser";
 288:    }
 289:
 290:    /**
 291:     * Returns the date.
 292:     *
 293:     * @return the current date
 294:     */
 295:	      public Date getDate() {
 296:        return model.getDate();
 297:    }
 298:
 299:    /**
 300:     * Sets the date. Fires the property change "date".
 301:     *
 302:     * @param date the new date.
 303:     */
 304:	      public void setDate(Date date) {
 305:        model.setValue(date);
 306:	          if (getParent() != null) {
 307:            getParent().validate();
 308:        }
 309:    }
 310:
 311:    /**
 312:     * Fires property "date" changes, recting on the spinner's state changes.
 313:     *
 314:     * @param e the change event
 315:     */
 316:	      public void stateChanged(ChangeEvent e) {
 317:	          if (isInitialized) {
 318:            firePropertyChange("date", lastSelectedDate, model.getDate());
 319:            lastSelectedDate = model.getDate();
 320:        }
 321:    }
 322:    
 323:    /*
 324:     * The methods:
 325:     * public JSpinner getSpinner()
 326:     * public SpinnerDateModel getModel()
 327:     * public void setModel(SpinnerDateModel mdl)
 328:     * 
 329:     * were added by Mark Brown on 24 Aug 2004.  They were added to allow the setting
 330:     * of the SpinnerDateModel from a source outside the JDateChooser control.  This 
 331:     * was necessary in order to allow the JDateChooser to be integrated with applications
 332:     * using persistence frameworks like Oracle's ADF/BC4J.
 333:     */
 334:    
 335:    /**
 336:     * Return this controls JSpinner control.
 337:     *
 338:     * @return the JSpinner control
 339:     */
 340:	      public JSpinner getSpinner() {
 341:        return dateSpinner;
 342:    }
 343:    
 344:    /**
 345:     * Return the SpinnerDateModel associated with this control.
 346:     *
 347:     * @return the SpinnerDateModel
 348:     */
 349:	      public SpinnerDateModel getModel() {
 350:        return model;
 351:    }
 352:    
 353:    /**
 354:     * Set the SpinnerDateModel for this control.  This method allows the JDateChooser
 355:     * control to be used with some persistence frameworks (ie. Oracle ADF) to bind the
 356:     * control to the database Date value.
 357:     *
 358:     * @param mdl the SpinnerDateModel
 359:     */
 360:	      public void setModel(SpinnerDateModel mdl) {
 361:        model = mdl;
 362:        model.setCalendarField(java.util.Calendar.WEEK_OF_MONTH);
 363:        model.addChangeListener(this);
 364:        // Begin Code change by Martin Pietruschka on 16 Sep 2004
 365:        if(dateSpinner != null)
 366:            dateSpinner.setModel(model);
 367:        // End Code change by Mark Brown
 368:    }
 369:}