All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.extended.date.WebDateField Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library 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.
 *
 * WebLookAndFeel library 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 WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.date;

import com.alee.global.StyleConstants;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.button.WebButton;
import com.alee.laf.rootpane.WebWindow;
import com.alee.laf.text.WebFormattedTextField;
import com.alee.managers.hotkey.Hotkey;
import com.alee.managers.settings.SettingsMethods;
import com.alee.utils.CollectionUtils;
import com.alee.utils.CompareUtils;
import com.alee.utils.SizeUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.laf.ShapeProvider;
import com.alee.utils.swing.SizeMethods;

import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * This is a custom component that allows date selection.
 *
 * @author Mikle Garin
 * @see com.alee.extended.date.WebCalendar
 */

public class WebDateField extends WebFormattedTextField implements ShapeProvider, SettingsMethods, SizeMethods
{
    /**
     * todo 1. Change JWindow to WebHeavyWeightPopup
     */

    /**
     * Used icons.
     */
    public static final ImageIcon selectDateIcon = new ImageIcon ( WebDateField.class.getResource ( "icons/date.png" ) );

    /**
     * Date selection listeners.
     */
    protected List dateSelectionListeners = new ArrayList ( 1 );

    /**
     * Date display format.
     */
    protected SimpleDateFormat dateFormat = new SimpleDateFormat ( "dd.MM.yyyy" );

    /**
     * Currently selected date.
     */
    protected Date date = null;

    /**
     * UI components.
     */
    protected WebButton popupButton;
    protected WebWindow popup;
    protected WebCalendar calendar;
    protected DateSelectionListener dateSelectionListener;

    /**
     * Constructs new date field.
     */
    public WebDateField ()
    {
        this ( null );
    }

    /**
     * Constructs new date field.
     *
     * @param drawBorder whether should draw border or not
     */
    public WebDateField ( final boolean drawBorder )
    {
        this ( null, drawBorder );
    }

    /**
     * Constructs new date field with the specified selected date.
     *
     * @param date selected date
     */
    public WebDateField ( final Date date )
    {
        this ( date, WebDateFieldStyle.drawBorder );
    }

    /**
     * Constructs new date field with the specified selected date.
     *
     * @param drawBorder whether should draw border or not
     * @param date       selected date
     */
    public WebDateField ( final Date date, final boolean drawBorder )
    {
        super ();

        this.date = date;

        // Basic field settings
        setOpaque ( false );
        setWebColored ( WebDateFieldStyle.webColored );
        setDrawBackground ( WebDateFieldStyle.drawBackground );
        setBackground ( WebDateFieldStyle.backgroundColor );
        setWebColored ( WebDateFieldStyle.webColored );
        setDrawFocus ( WebDateFieldStyle.drawFocus );

        // Popup button
        popupButton = WebButton.createIconWebButton ( selectDateIcon, WebDateFieldStyle.round );
        popupButton.setFocusable ( false );
        popupButton.setShadeWidth ( 0 );
        popupButton.setMoveIconOnPress ( false );
        popupButton.setRolloverDecoratedOnly ( true );
        popupButton.setCursor ( Cursor.getDefaultCursor () );
        popupButton.addActionListener ( new ActionListener ()
        {
            @Override
            public void actionPerformed ( final ActionEvent e )
            {
                showCalendarPopup ();
            }
        } );
        setTrailingComponent ( popupButton );

        // Actions
        addActionListener ( new ActionListener ()
        {
            @Override
            public void actionPerformed ( final ActionEvent e )
            {
                setDateFromField ();
            }
        } );
        addMouseListener ( new MouseAdapter ()
        {
            @Override
            public void mousePressed ( final MouseEvent e )
            {
                if ( isEnabled () && SwingUtilities.isRightMouseButton ( e ) )
                {
                    showCalendarPopup ();
                }
            }
        } );
        addFocusListener ( new FocusAdapter ()
        {
            @Override
            public void focusLost ( final FocusEvent e )
            {
                if ( !SwingUtils.isEqualOrChild ( popup, e.getOppositeComponent () ) )
                {
                    setDateFromField ();
                }
            }
        } );
        addKeyListener ( new KeyAdapter ()
        {
            @Override
            public void keyReleased ( final KeyEvent e )
            {
                if ( isEnabled () )
                {
                    if ( Hotkey.ESCAPE.isTriggered ( e ) )
                    {
                        updateFieldFromDate ();
                    }
                    else if ( Hotkey.DOWN.isTriggered ( e ) )
                    {
                        showCalendarPopup ();
                    }
                }
            }
        } );
        addAncestorListener ( new AncestorListener ()
        {
            @Override
            public void ancestorAdded ( final AncestorEvent event )
            {
                hideCalendarPopup ();
            }

            @Override
            public void ancestorRemoved ( final AncestorEvent event )
            {
                hideCalendarPopup ();
            }

            @Override
            public void ancestorMoved ( final AncestorEvent event )
            {
                hideCalendarPopup ();
            }
        } );
        addComponentListener ( new ComponentAdapter ()
        {
            @Override
            public void componentHidden ( final ComponentEvent e )
            {
                hideCalendarPopup ();
            }
        } );

        // Initial field date
        updateFieldFromDate ();

        // Initial styling settings
        setDrawBorder ( drawBorder );
        setRound ( WebDateFieldStyle.round );
        setShadeWidth ( WebDateFieldStyle.shadeWidth );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setRound ( final int round )
    {
        super.setRound ( round );
        popupButton.setRound ( round );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setDrawBorder ( final boolean drawBorder )
    {
        super.setDrawBorder ( drawBorder );
        updateMargin ();
    }

    /**
     * Updates field margin.
     */
    protected void updateMargin ()
    {
        setMargin ( isDrawBorder () ? WebDateFieldStyle.margin : WebDateFieldStyle.undecoratedMargin );
    }

    /**
     * Displays calendar popup.
     */
    protected void showCalendarPopup ()
    {
        // Checking that component is eligable for focus request
        if ( !requestFocusInWindow () && !isFocusOwner () )
        {
            // Cancel operation if component is not eligable for focus yet
            // This might occur if some other component input verifier holds the focus or in some other rare cases
            return;
        }

        // Updating date from field
        setDateFromField ();

        // Create popup if it doesn't exist
        if ( popup == null || calendar == null )
        {
            final Window ancestor = SwingUtils.getWindowAncestor ( this );

            // Calendar
            calendar = new WebCalendar ( date );
            calendar.setPaintFocus ( false );
            calendar.setRound ( StyleConstants.smallRound );
            calendar.setShadeWidth ( 0 );

            // Popup window
            popup = new WebWindow ( ancestor );
            popup.setLayout ( new BorderLayout () );
            popup.setCloseOnFocusLoss ( true );
            popup.setWindowOpaque ( false );
            popup.add ( calendar );
            customizePopup ( popup );
            popup.pack ();

            // Correct popup positioning
            updatePopupLocation ();
            ancestor.addPropertyChangeListener ( WebLookAndFeel.ORIENTATION_PROPERTY, new PropertyChangeListener ()
            {
                @Override
                public void propertyChange ( final PropertyChangeEvent evt )
                {
                    if ( popup.isShowing () )
                    {
                        updatePopupLocation ();
                    }
                }
            } );

            // Selection listener
            dateSelectionListener = new DateSelectionListener ()
            {
                @Override
                public void dateSelected ( final Date date )
                {
                    hideCalendarPopup ();
                    setDateFromCalendar ();
                    requestFocusInWindow ();
                }
            };
            calendar.addDateSelectionListener ( dateSelectionListener );
        }
        else
        {
            // Updating window location
            updatePopupLocation ();
        }

        // Applying orientation to popup
        SwingUtils.copyOrientation ( WebDateField.this, popup );

        // Showing popup and changing focus
        popup.setVisible ( true );
        calendar.transferFocus ();
    }

    @SuppressWarnings ("UnusedParameters")
    protected void customizePopup ( final WebWindow popup )
    {
        // You can customize date field popup window here
    }

    /**
     * Hides calendar popup.
     */
    protected void hideCalendarPopup ()
    {
        if ( popup != null )
        {
            popup.setVisible ( false );
        }
    }

    /**
     * Updates calendar popup location.
     */
    protected void updatePopupLocation ()
    {
        final Point los = WebDateField.this.getLocationOnScreen ();
        final Rectangle gb = popup.getGraphicsConfiguration ().getBounds ();
        final int shadeWidth = isDrawBorder () ? getShadeWidth () : 0;
        final boolean ltr = WebDateField.this.getComponentOrientation ().isLeftToRight ();
        final int w = WebDateField.this.getWidth ();
        final int h = WebDateField.this.getHeight ();

        final int x;
        if ( ltr )
        {
            if ( los.x + shadeWidth + popup.getWidth () <= gb.x + gb.width )
            {
                x = los.x + shadeWidth;
            }
            else
            {
                x = los.x + w - shadeWidth - popup.getWidth ();
            }
        }
        else
        {
            if ( los.x + w - shadeWidth - popup.getWidth () >= gb.x )
            {
                x = los.x + w - shadeWidth - popup.getWidth ();
            }
            else
            {
                x = los.x + shadeWidth;
            }
        }

        final int y;
        if ( los.y + h + popup.getHeight () <= gb.y + gb.height )
        {
            y = los.y + h + ( isDrawBorder () ? 0 : 1 );
        }
        else
        {
            y = los.y - popup.getHeight () - ( isDrawBorder () ? 0 : 1 );
        }

        popup.setLocation ( x, y );
    }

    /**
     * Returns date specified in text field.
     *
     * @return date specified in text field
     */
    protected Date getDateFromField ()
    {
        try
        {
            final String text = getText ();
            if ( text != null && !text.trim ().equals ( "" ) )
            {
                return dateFormat.parse ( text );
            }
            else
            {
                return null;
            }
        }
        catch ( final Throwable ex )
        {
            return date;
        }
    }

    /**
     * Returns text date representation according to date format.
     *
     * @return text date representation according to date format
     */
    protected String getTextDate ()
    {
        return date != null ? dateFormat.format ( date ) : "";
    }

    /**
     * Returns currently selected date.
     *
     * @return currently selected date
     */
    public Date getDate ()
    {
        return date;
    }

    /**
     * Sets currently selected date.
     *
     * @param date new selected date
     */
    public void setDate ( final Date date )
    {
        setDateImpl ( date, UpdateSource.other );
    }

    /**
     * Updates date using the value from field.
     */
    protected void setDateFromField ()
    {
        setDateImpl ( getDateFromField (), UpdateSource.field );
    }

    /**
     * Updates date using the value from calendar.
     */
    protected void setDateFromCalendar ()
    {
        setDateImpl ( calendar.getDate (), UpdateSource.calendar );
    }

    /**
     * Sets currently selected date and updates component depending on update source.
     *
     * @param date new selected date
     */
    protected void setDateImpl ( final Date date, final UpdateSource source )
    {
        final boolean changed = !CompareUtils.equals ( this.date, date );
        this.date = date;

        // Updating field text even if there is no changes
        // Text still might change due to formatting pattern
        updateFieldFromDate ();

        if ( changed )
        {
            // Updating calendar date
            if ( source != UpdateSource.calendar && calendar != null )
            {
                updateCalendarFromDate ( date );
            }

            // Informing about date selection changes
            fireDateSelected ( date );
        }
    }

    /**
     * Updates text field with currently selected date.
     */
    protected void updateFieldFromDate ()
    {
        setText ( getTextDate () );
    }

    /**
     * Updates date displayed in calendar.
     *
     * @param date new displayed date
     */
    protected void updateCalendarFromDate ( final Date date )
    {
        calendar.removeDateSelectionListener ( dateSelectionListener );
        calendar.setDate ( date, false );
        calendar.addDateSelectionListener ( dateSelectionListener );
    }

    /**
     * Returns date format.
     *
     * @return date format
     */
    public SimpleDateFormat getDateFormat ()
    {
        return dateFormat;
    }

    /**
     * Sets date format.
     *
     * @param dateFormat date format
     */
    public void setDateFormat ( final SimpleDateFormat dateFormat )
    {
        this.dateFormat = dateFormat;
        updateFieldFromDate ();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setEnabled ( final boolean enabled )
    {
        super.setEnabled ( enabled );
        popupButton.setEnabled ( enabled );
    }

    /**
     * Adds date selection listener.
     *
     * @param listener date selection listener to add
     */
    public void addDateSelectionListener ( final DateSelectionListener listener )
    {
        dateSelectionListeners.add ( listener );
    }

    /**
     * Removes date selection listener.
     *
     * @param listener date selection listener to remove
     */
    public void removeDateSelectionListener ( final DateSelectionListener listener )
    {
        dateSelectionListeners.remove ( listener );
    }

    /**
     * Notifies about date selection change.
     *
     * @param date new selected date
     */
    public void fireDateSelected ( final Date date )
    {
        for ( final DateSelectionListener listener : CollectionUtils.copy ( dateSelectionListeners ) )
        {
            listener.dateSelected ( date );
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getPreferredWidth ()
    {
        return SizeUtils.getPreferredWidth ( this );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WebDateField setPreferredWidth ( final int preferredWidth )
    {
        return SizeUtils.setPreferredWidth ( this, preferredWidth );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getPreferredHeight ()
    {
        return SizeUtils.getPreferredHeight ( this );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WebDateField setPreferredHeight ( final int preferredHeight )
    {
        return SizeUtils.setPreferredHeight ( this, preferredHeight );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getMinimumWidth ()
    {
        return SizeUtils.getMinimumWidth ( this );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WebDateField setMinimumWidth ( final int minimumWidth )
    {
        return SizeUtils.setMinimumWidth ( this, minimumWidth );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getMinimumHeight ()
    {
        return SizeUtils.getMinimumHeight ( this );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WebDateField setMinimumHeight ( final int minimumHeight )
    {
        return SizeUtils.setMinimumHeight ( this, minimumHeight );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Dimension getPreferredSize ()
    {
        return SizeUtils.getPreferredSize ( this, super.getPreferredSize () );
    }

    /**
     * This enumeration represents the type of source that caused view update.
     */
    protected enum UpdateSource
    {
        /**
         * Text field source.
         */
        field,

        /**
         * Calendar source.
         */
        calendar,

        /**
         * Other source.
         */
        other
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy