JXDatePicker.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:22k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: JXDatePicker.java,v 1.7 2005/10/10 18:01:54 rbair Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library 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 GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx;
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.text.ParseException;
  25. import java.text.DateFormat;
  26. import java.text.SimpleDateFormat;
  27. import java.text.MessageFormat;
  28. import java.util.Date;
  29. import javax.swing.*;
  30. import javax.swing.JFormattedTextField.AbstractFormatter;
  31. import javax.swing.JFormattedTextField.AbstractFormatterFactory;
  32. import javax.swing.border.*;
  33. import javax.swing.text.DefaultFormatterFactory;
  34. import org.jdesktop.swingx.calendar.*;
  35. /**
  36.  * A component that combines a button, an editable field and a JXMonthView
  37.  * component.  The user can select a date from the calendar component, which
  38.  * appears when the button is pressed.  The selection from the calendar
  39.  * component will be displayed in editable field.  Values may also be modified
  40.  * manually by entering a date into the editable field using one of the
  41.  * supported date formats.
  42.  *
  43.  * @author Joshua Outwater
  44.  */
  45. public class JXDatePicker extends JComponent {
  46.     /** The editable date field that displays the date */
  47.     private JFormattedTextField _dateField;
  48.     /**
  49.      * Popup that displays the month view with controls for
  50.      * traversing/selecting dates.
  51.      */
  52.     private JXDatePickerPopup _popup;
  53.     private JPanel _linkPanel;
  54.     private long _linkDate;
  55.     private MessageFormat _linkFormat;
  56.     private JButton _popupButton;
  57.     private int _popupButtonWidth = 20;
  58.     private JXMonthView _monthView;
  59.     private Handler _handler;
  60.     private String _actionCommand = "selectionChanged";
  61.     /**
  62.      * Create a new date picker using the current date as the initial
  63.      * selection and the default abstract formatter
  64.      * <code>JXDatePickerFormatter</code>.
  65.      */
  66.     public JXDatePicker() {
  67.         this(System.currentTimeMillis());
  68.     }
  69.     /**
  70.      * Create a new date picker using the specified time as the initial
  71.      * selection and the default abstract formatter
  72.      * <code>JXDatePickerFormatter</code>.
  73.      *
  74.      * @param millis initial time in milliseconds
  75.      */
  76.     public JXDatePicker(long millis) {
  77.         _monthView = new JXMonthView();
  78.         _monthView.setTraversable(true);
  79.         _dateField = new JFormattedTextField(new JXDatePickerFormatter());
  80.         _dateField.setName("dateField");
  81.         _dateField.setBorder(null);
  82.         
  83.         _handler = new Handler();
  84.         _popupButton = new JButton();
  85.         _popupButton.setName("popupButton");
  86.         _popupButton.setRolloverEnabled(false);
  87.         _popupButton.addMouseListener(_handler);
  88.         _popupButton.addMouseMotionListener(_handler);
  89.         KeyStroke enterKey =
  90.             KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false);
  91.         InputMap inputMap = _dateField.getInputMap(JComponent.WHEN_FOCUSED);
  92.         inputMap.put(enterKey, "COMMIT_EDIT");
  93.         ActionMap actionMap = _dateField.getActionMap();
  94.         actionMap.put("COMMIT_EDIT", new CommitEditAction());
  95.         KeyStroke spaceKey =
  96.             KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false);
  97.         inputMap = _popupButton.getInputMap(JComponent.WHEN_FOCUSED);
  98.         inputMap.put(spaceKey, "TOGGLE_POPUP");
  99.         actionMap = _popupButton.getActionMap();
  100.         actionMap.put("TOGGLE_POPUP", new TogglePopupAction());
  101.         add(_dateField);
  102.         add(_popupButton);
  103.         updateUI();
  104.         
  105.         _linkDate = System.currentTimeMillis();
  106.         _linkPanel = new TodayPanel();
  107.         
  108.         _dateField.setValue(new Date(millis));
  109.     }
  110.     /**
  111.      * Resets the UI property to a value from the current look and feel.
  112.      */
  113.     public void updateUI() {
  114.         int cols = UIManager.getInt("JXDatePicker.numColumns");
  115.         if (cols == -1) {
  116.             cols = 10;
  117.         }
  118.         _dateField.setColumns(cols);
  119.         String str = UIManager.getString("JXDatePicker.arrowDown.tooltip");
  120.         if (str == null) {
  121.             str = "Show Calendar";
  122.         }
  123.         _popupButton.setToolTipText(str);
  124.         Icon icon = UIManager.getIcon("JXDatePicker.arrowDown.image");
  125.         if (icon == null) {
  126.             icon = (Icon)UIManager.get("Tree.expandedIcon");
  127.         }
  128.         _popupButton.setIcon(icon);
  129.         Border border = UIManager.getBorder("JXDatePicker.border");
  130.         if (border == null) {
  131.             border = BorderFactory.createCompoundBorder(
  132.                     LineBorder.createGrayLineBorder(),
  133.                     BorderFactory.createEmptyBorder(3, 3, 3, 3));
  134.         }
  135.         _dateField.setBorder(border);
  136.         String formatString = UIManager.getString("JXDatePicker.linkFormat");
  137.         if (formatString == null) {
  138.             formatString = "Today is {0,date, dd MMMM yyyy}";
  139.         }
  140.         _linkFormat = new MessageFormat(formatString);
  141.     }
  142.     /**
  143.      * Replaces the currently installed formatter and factory used by the
  144.      * editor.  These string formats are defined by the
  145.      * <code>java.text.SimpleDateFormat</code> class.
  146.      *
  147.      * @param formats The string formats to use.
  148.      * @see java.text.SimpleDateFormat
  149.      */
  150.     public void setFormats(String[] formats) {
  151.         DateFormat[] dateFormats = new DateFormat[formats.length];
  152.         for (int counter = formats.length - 1; counter >= 0; counter--) {
  153.             dateFormats[counter] = new SimpleDateFormat(formats[counter]);
  154.         }
  155.         setFormats(dateFormats);
  156.     }
  157.     /**
  158.      * Replaces the currently installed formatter and factory used by the
  159.      * editor.
  160.      *
  161.      * @param formats The date formats to use.
  162.      */
  163.     public void setFormats(DateFormat[] formats) {
  164.         _dateField.setFormatterFactory(new DefaultFormatterFactory(
  165.                                 new JXDatePickerFormatter(formats)));
  166.     }
  167.     /**
  168.      * Returns an array of the formats used by the installed formatter
  169.      * if it is a subclass of <code>JXDatePickerFormatter<code>.
  170.      * <code>javax.swing.JFormattedTextField.AbstractFormatter</code>
  171.      * and <code>javax.swing.text.DefaultFormatter</code> do not have
  172.      * support for accessing the formats used.
  173.      *
  174.      * @return array of formats or null if unavailable.
  175.      */
  176.     public DateFormat[] getFormats() {
  177.         // Dig this out from the factory, if possible, otherwise return null.
  178.         AbstractFormatterFactory factory = _dateField.getFormatterFactory();
  179.         if (factory != null) {
  180.             AbstractFormatter formatter = factory.getFormatter(_dateField);
  181.             if (formatter instanceof JXDatePickerFormatter) {
  182.                 return ((JXDatePickerFormatter)formatter).getFormats();
  183.             }
  184.         }
  185.         return null;
  186.     }
  187.     /**
  188.      * Set the currently selected date.
  189.      *
  190.      * @param date date
  191.      */
  192.     public void setDate(Date date) {
  193.         _dateField.setValue(date);
  194.     }
  195.     /**
  196.      * Set the currently selected date.
  197.      *
  198.      * @param millis milliseconds
  199.      */
  200.     public void setDateInMillis(long millis) {
  201.         _dateField.setValue(new Date(millis));
  202.     }
  203.     /**
  204.      * Returns the currently selected date.
  205.      *
  206.      * @return Date
  207.      */
  208.     public Date getDate() {
  209.         return (Date)_dateField.getValue();
  210.     }
  211.     /**
  212.      * Returns the currently selected date in milliseconds.
  213.      *
  214.      * @return the date in milliseconds
  215.      */
  216.     public long getDateInMillis() {
  217.         return ((Date)_dateField.getValue()).getTime();
  218.     }
  219.     /**
  220.      * Return the <code>JXMonthView</code> used in the popup to
  221.      * select dates from.
  222.      *
  223.      * @return the month view component
  224.      */
  225.     public JXMonthView getMonthView() {
  226.         return _monthView;
  227.     }
  228.     public void setMonthView(JXMonthView monthView) {
  229.         _monthView = monthView;
  230.         _popup = null;
  231.     }
  232.     
  233.     /**
  234.      * Set the date the link will use and the string defining a MessageFormat
  235.      * to format the link.  If no valid date is in the editor when the popup
  236.      * is displayed the popup will focus on the month the linkDate is in.
  237.      *
  238.      * @param linkDate Date in milliseconds
  239.      * @param linkFormatString String used to format the link
  240.      * @see java.text.MessageFormat
  241.      */
  242.     public void setLinkDate(long linkDate, String linkFormatString) {
  243.         _linkDate = linkDate;
  244.         _linkFormat = new MessageFormat(linkFormatString);
  245.     }
  246.     
  247.     /**
  248.      * Return the panel that is used at the bottom of the popup.  The default
  249.      * implementation shows a link that displays the current month.
  250.      *
  251.      * @return The currently installed link panel
  252.      */
  253.     public JPanel getLinkPanel() {
  254.         return _linkPanel;
  255.     }
  256.     
  257.     /**
  258.      * Set the panel that will be used at the bottom of the popup.
  259.      *
  260.      * @param linkPanel The new panel to install in the popup
  261.      */
  262.     public void setLinkPanel(JPanel linkPanel) {
  263.         // If the popup is null we haven't shown it yet.
  264.         if (_popup != null) {
  265.             _popup.remove(_linkPanel);
  266.             _popup.add(linkPanel, BorderLayout.SOUTH);
  267.         }
  268.         _linkPanel = linkPanel;
  269.     }
  270.     
  271.     /**
  272.      * Returns the formatted text field used to edit the date selection.
  273.      *
  274.      * @return the formatted text field
  275.      */
  276.     public JFormattedTextField getEditor() {
  277.         return _dateField;
  278.     }
  279.     /**
  280.      * Returns true if the current value being edited is valid.
  281.      *
  282.      * @return true if the current value being edited is valid.
  283.      */
  284.     public boolean isEditValid() {
  285.         return _dateField.isEditValid();
  286.     }
  287.     /**
  288.      * Forces the current value to be taken from the AbstractFormatter and
  289.      * set as the current value. This has no effect if there is no current
  290.      * AbstractFormatter installed.
  291.      */
  292.     public void commitEdit() throws ParseException {
  293.         _dateField.commitEdit();
  294.     }
  295.     /**
  296.      * Enables or disables the date picker and all its subcomponents.
  297.      *
  298.      * @param value true to enable, false to disable
  299.      */
  300.     public void setEnabled(boolean value) {
  301.         if (isEnabled() == value) {
  302.             return;
  303.         }
  304.         super.setEnabled(value);
  305.         _dateField.setEnabled(value);
  306.         _popupButton.setEnabled(value);
  307.     }
  308.     /**
  309.      * Returns the string currently used to identiy fired ActionEvents.
  310.      *
  311.      * @return String The string used for identifying ActionEvents.
  312.      */
  313.     public String getActionCommand() {
  314.         return _actionCommand;
  315.     }
  316.     /**
  317.      * Sets the string used to identify fired ActionEvents.
  318.      *
  319.      * @param actionCommand The string used for identifying ActionEvents.
  320.      */
  321.     public void setActionCommand(String actionCommand) {
  322.         _actionCommand = actionCommand;
  323.     }
  324.     /**
  325.      * Adds an ActionListener.
  326.      * <p>
  327.      * The ActionListener will receive an ActionEvent when a selection has
  328.      * been made.
  329.      *
  330.      * @param l The ActionListener that is to be notified
  331.      */
  332.     public void addActionListener(ActionListener l) {
  333.         listenerList.add(ActionListener.class, l);
  334.     }
  335.     /**
  336.      * Removes an ActionListener.
  337.      *
  338.      * @param l The action listener to remove.
  339.      */
  340.     public void removeActionListener(ActionListener l) {
  341.         listenerList.remove(ActionListener.class, l);
  342.     }
  343.     /**
  344.      * Fires an ActionEvent to all listeners.
  345.      */
  346.     protected void fireActionPerformed() {
  347.         Object[] listeners = listenerList.getListenerList();
  348.         ActionEvent e = null;
  349.         for (int i = listeners.length - 2; i >= 0; i -=2) {
  350.             if (listeners[i] == ActionListener.class) {
  351.                 if (e == null) {
  352.                     e = new ActionEvent(JXDatePicker.this,
  353.                             ActionEvent.ACTION_PERFORMED,
  354.                             _actionCommand);
  355.                 }
  356.                 ((ActionListener)listeners[i + 1]).actionPerformed(e);
  357.             }
  358.         }
  359.     }
  360.     /**
  361.      * {@inheritDoc}
  362.      */
  363.     public void doLayout() {
  364.         int width = getWidth();
  365.         int height = getHeight();
  366.         Insets insets = getInsets();
  367.         _dateField.setBounds(insets.left,
  368.                 insets.bottom,
  369.                 width - _popupButtonWidth,
  370.                 height);
  371.         _popupButton.setBounds(width - _popupButtonWidth + insets.left,
  372.                 insets.bottom,
  373.                 _popupButtonWidth,
  374.                 height);
  375.     }
  376.     /**
  377.      * {@inheritDoc}
  378.      */
  379.     public Dimension getMinimumSize() {
  380.         return getPreferredSize();
  381.     }
  382.     /**
  383.      * {@inheritDoc}
  384.      */
  385.     public Dimension getPreferredSize() {
  386.         Dimension dim = _dateField.getPreferredSize();
  387.         dim.width += _popupButton.getPreferredSize().width;
  388.         Insets insets = getInsets();
  389.         dim.width += insets.left + insets.right;
  390.         dim.height += insets.top + insets.bottom;
  391.         return dim;
  392.     }
  393.     /**
  394.      * Action used to commit the current value in the JFormattedTextField.
  395.      * This action is used by the keyboard bindings.
  396.      */
  397.     private class TogglePopupAction extends AbstractAction {
  398.         public TogglePopupAction() {
  399.             super("TogglePopup");
  400.         }
  401.         public void actionPerformed(ActionEvent ev) {
  402.             _handler.toggleShowPopup();
  403.         }
  404.     }
  405.     /**
  406.      * Action used to commit the current value in the JFormattedTextField.
  407.      * This action is used by the keyboard bindings.
  408.      */
  409.     private class CommitEditAction extends AbstractAction {
  410.         public CommitEditAction() {
  411.             super("CommitEditPopup");
  412.         }
  413.         public void actionPerformed(ActionEvent ev) {
  414.             try {
  415.                 // Commit the current value.
  416.                 _dateField.commitEdit();
  417.                 // Reformat the value according to the formatter.
  418.                 _dateField.setValue(_dateField.getValue());
  419.                 fireActionPerformed();
  420.             } catch (java.text.ParseException ex) {
  421.             }
  422.         }
  423.     }
  424.     private class Handler implements MouseListener, MouseMotionListener {
  425.         private boolean _forwardReleaseEvent = false;
  426.         public void mouseClicked(MouseEvent ev) {
  427.         }
  428.         public void mousePressed(MouseEvent ev) {
  429.             if (!isEnabled()) {
  430.                 return;
  431.             }
  432.             if (_dateField.isEditValid()) {
  433.                 try {
  434.                     _dateField.commitEdit();
  435.                 } catch (java.text.ParseException ex) {
  436.                 }
  437.             }
  438.             toggleShowPopup();
  439.         }
  440.         public void mouseReleased(MouseEvent ev) {
  441.             // Retarget mouse event to the month view.
  442.             if (_forwardReleaseEvent) {
  443.                 ev = SwingUtilities.convertMouseEvent(_popupButton, ev,
  444.                         _monthView);
  445.                 _monthView.dispatchEvent(ev);
  446.                 _forwardReleaseEvent = false;
  447.             }
  448.         }
  449.         public void mouseEntered(MouseEvent ev) {
  450.         }
  451.         public void mouseExited(MouseEvent ev) {
  452.         }
  453.         public void mouseDragged(MouseEvent ev) {
  454.             _forwardReleaseEvent = true;
  455.             if (!_popup.isShowing()) {
  456.                 return;
  457.             }
  458.             // Retarget mouse event to the month view.
  459.             ev = SwingUtilities.convertMouseEvent(_popupButton, ev, _monthView);
  460.             _monthView.dispatchEvent(ev);
  461.         }
  462.         public void mouseMoved(MouseEvent ev) {
  463.         }
  464.         public void toggleShowPopup() {
  465.             if (_popup == null) {
  466.                 _popup = new JXDatePickerPopup();
  467.             }
  468.             if (!_popup.isVisible()) {
  469.                 if (_dateField.getValue() == null) {
  470.                     _dateField.setValue(new Date(_linkDate));
  471.                 }
  472.                 DateSpan span =
  473.                         new DateSpan((java.util.Date)_dateField.getValue(),
  474.                                 (java.util.Date)_dateField.getValue());
  475.                 _monthView.setSelectedDateSpan(span);
  476.                 _monthView.ensureDateVisible(
  477.                         ((Date)_dateField.getValue()).getTime());
  478.                 _popup.show(JXDatePicker.this,
  479.                         0, JXDatePicker.this.getHeight());
  480.             } else {
  481.                 _popup.setVisible(false);
  482.             }
  483.         }
  484.     }
  485.     /**
  486.      * Popup component that shows a JXMonthView component along with controlling
  487.      * buttons to allow traversal of the months.  Upon selection of a date the
  488.      * popup will automatically hide itself and enter the selection into the
  489.      * editable field of the JXDatePicker.
  490.      */
  491.     protected class JXDatePickerPopup extends JPopupMenu
  492.             implements ActionListener {
  493.         public JXDatePickerPopup() {
  494.             _monthView.setActionCommand("MONTH_VIEW");
  495.             _monthView.addActionListener(this);
  496.             setLayout(new BorderLayout());
  497.             add(_monthView, BorderLayout.CENTER);
  498.             if (_linkPanel != null) {
  499.                 add(_linkPanel, BorderLayout.SOUTH);
  500.             }
  501.         }
  502.         public void actionPerformed(ActionEvent ev) {
  503.             String command = ev.getActionCommand();
  504.             if ("MONTH_VIEW" == command) {
  505.                 DateSpan span = _monthView.getSelectedDateSpan();
  506.                 _dateField.setValue(span.getStartAsDate());
  507.                 _popup.setVisible(false);
  508.                 fireActionPerformed();
  509.             }
  510.         }
  511.     }
  512.         
  513.     private final class TodayPanel extends JXPanel {
  514.         TodayPanel() {
  515.             super(new FlowLayout());
  516.             setDrawGradient(true);
  517.             setGradientPaint(new GradientPaint(0, 0, new Color(238, 238, 238), 0, 1, Color.WHITE));
  518.             JXHyperlink todayLink = new JXHyperlink(new TodayAction());
  519.             Color textColor = new Color(16, 66, 104);
  520.             todayLink.setUnclickedColor(textColor);
  521.             todayLink.setClickedColor(textColor);
  522.             add(todayLink);
  523.         }
  524.         
  525.         protected void paintComponent(Graphics g) {
  526.             super.paintComponent(g);
  527.             g.setColor(new Color(187, 187, 187));
  528.             g.drawLine(0, 0, getWidth(), 0);
  529.             g.setColor(new Color(221, 221, 221));
  530.             g.drawLine(0, 1, getWidth(), 1);
  531.         }
  532.         
  533.         private final class TodayAction extends AbstractAction {
  534.             TodayAction() {
  535.                 super(_linkFormat.format(new Object[] { new Date(_linkDate) }));
  536.             }
  537.             
  538.             public void actionPerformed(ActionEvent ae) {
  539.                 DateSpan span = new DateSpan(_linkDate, _linkDate);
  540.                 _monthView.ensureDateVisible(span.getStart());
  541.             }
  542.         }
  543.     }        
  544.     
  545.     /**
  546.      * Default formatter for the JXDatePicker component.  This factory
  547.      * creates and returns a formatter that can handle a variety of date
  548.      * formats.
  549.      */
  550.     static class JXDatePickerFormatter extends
  551.             JFormattedTextField.AbstractFormatter {
  552.         private DateFormat _formats[] = null;
  553.         private int _formatIndex = 0;
  554.         public JXDatePickerFormatter() {
  555.             _formats = new DateFormat[3];
  556.             String format = UIManager.getString("JXDatePicker.longFormat");
  557.             if (format == null) {
  558.                 format = "EEE MM/dd/yyyy";
  559.             }
  560.             _formats[0] = new SimpleDateFormat(format);
  561.             format = UIManager.getString("JXDatePicker.mediumFormat");
  562.             if (format == null) {
  563.                 format = "MM/dd/yyyy";
  564.             }
  565.             _formats[1] = new SimpleDateFormat(format);
  566.             format = UIManager.getString("JXDatePicker.shortFormat");
  567.             if (format == null) {
  568.                 format = "MM/dd";
  569.             }
  570.             _formats[2] = new SimpleDateFormat(format);
  571.         }
  572.         public JXDatePickerFormatter(DateFormat formats[]) {
  573.             _formats = formats;
  574.         }
  575.         public DateFormat[] getFormats() {
  576.             return _formats;
  577.         }
  578.         /**
  579.          * {@inheritDoc}
  580.          */
  581.         public Object stringToValue(String text) throws ParseException {
  582.             Object result = null;
  583.             ParseException pex = null;
  584.             if (text == null || text.trim().length() == 0) {
  585.                 return null;
  586.             }
  587.             // If the current formatter did not work loop through the other
  588.             // formatters and see if any of them can parse the string passed
  589.             // in.
  590.             for (int i = 0; i < _formats.length; i++) {
  591.                 try {
  592.                     result = (_formats[i]).parse(text);
  593.                     // We got a successful formatter.  Update the
  594.                     // current formatter index.
  595.                     _formatIndex = i;
  596.                     pex = null;
  597.                     break;
  598.                 } catch (ParseException ex) {
  599.                     pex = ex;
  600.                 }
  601.             }
  602.             if (pex != null) {
  603.                 throw pex;
  604.             }
  605.             
  606.             return result;
  607.         }
  608.         /**
  609.          * {@inheritDoc}
  610.          */
  611.         public String valueToString(Object value) throws ParseException {
  612.             if (value != null) {
  613.                 return _formats[_formatIndex].format(value);
  614.             }
  615.             return null;
  616.         }
  617.     }
  618. }