net.sf.jmoney.fields.DateControl.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jmoney.fields.DateControl.java

Source

/*
*
*  JMoney - A Personal Finance Manager
*  Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
*
*
*  This program 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 2 of the License, or
*  (at your option) any later version.
*
*  This program 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 this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

package net.sf.jmoney.fields;

import java.util.Calendar;
import java.util.Date;

import net.sf.jmoney.JMoneyPlugin;
import net.sf.jmoney.VerySimpleDateFormat;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

/**
 * A control for entering dates.  This control contains both a
 * text box and a button to the right that pops up a calendar.
 * <P>
 * This control uses the current date format set in the JMoney
 * preferences.
 * 
 * @author Nigel Westbury
 */
public class DateControl extends DateComposite {

    // TODO Listen to date format changes.
    private VerySimpleDateFormat fDateFormat = new VerySimpleDateFormat(JMoneyPlugin.getDefault().getDateFormat());

    /**
     * The text box containing the date
     */
    private Text textControl;

    /**
     * The small button to the right of the date that brings up the date picker
     */
    private Button button;

    static private Image threeDotsImage = null;

    private DateTime swtcal = null;

    /**
     * @param parent
     * @param style
     */
    public DateControl(final Composite parent) {
        super(parent, SWT.NULL);

        setBackgroundMode(SWT.INHERIT_FORCE);

        setLayout(new DialogCellLayout());

        textControl = new Text(this, SWT.LEFT);

        textControl.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                // CTRL + and CTRL - increment and decrement the date respectively.
                // It would be even easier for the user the CTRL did not have to be
                // pressed, but then it would not be possible to have a date format
                // that contains '-'.
                if (e.stateMask == SWT.CONTROL && (e.character == '+' || e.character == '-')) {

                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(fDateFormat.parse(textControl.getText()));

                    if (e.character == '+') {
                        calendar.add(Calendar.DAY_OF_MONTH, 1);
                    } else {
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                    }

                    textControl.setText(fDateFormat.format(calendar.getTime()));

                    e.doit = false;
                }
            }
        });

        button = new Button(this, SWT.DOWN);
        if (threeDotsImage == null) {
            ImageDescriptor descriptor = JMoneyPlugin.createImageDescriptor("dots_button.gif"); //$NON-NLS-1$
            threeDotsImage = descriptor.createImage();
        }
        button.setImage(threeDotsImage);

        button.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                final Shell shell = new Shell(parent.getShell(), SWT.ON_TOP);
                shell.setLayout(new RowLayout());
                swtcal = new DateTime(shell, SWT.CALENDAR);

                // Set the currently set date into the calendar control
                // (If the parse method returned null then the text control did not
                // contain a valid date.  In this case no date is set into the
                // date picker).
                try {
                    String t = textControl.getText();
                    Date date = fDateFormat.parse(t);
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(date);
                    swtcal.setYear(calendar.get(Calendar.YEAR));
                    swtcal.setMonth(calendar.get(Calendar.MONTH));
                    swtcal.setDay(calendar.get(Calendar.DAY_OF_MONTH));
                } catch (IllegalArgumentException e) {
                    // The date format was invalid.
                    // Ignore this error (the calendar control
                    // does not require a date to be set).
                }

                swtcal.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(SelectionEvent e) {
                        Calendar cal = Calendar.getInstance();

                        /*
                        * First reset all fields. Otherwise differences in the time
                        * part, even if the difference is only milliseconds, will cause
                        * the date comparisons to fail.
                        * 
                        * Note also it is critical that whatever is done here is exactly the
                        * same as that done in VerySimpleDateFormat, otherwise dates will not
                        * match.  For example, if you replace clear() here with setTimeInMillis(0)
                        * then we get a different object (because data other than the date and time
                        * such as time zone information will be different).
                        */
                        cal.clear();

                        cal.set(swtcal.getYear(), swtcal.getMonth(), swtcal.getDay());
                        Date date = cal.getTime();
                        textControl.setText(fDateFormat.format(date));
                    }
                });

                shell.pack();

                // Position the calendar shell below the date control,
                // unless the date control is so near the bottom of the display that
                // the calendar control would go off the bottom of the display,
                // in which case position the calendar shell above the date control.
                Display display = getDisplay();
                Rectangle rect = display.map(parent, null, getBounds());
                int calendarShellHeight = shell.getSize().y;
                if (rect.y + rect.height + calendarShellHeight <= display.getBounds().height) {
                    shell.setLocation(rect.x, rect.y + rect.height);
                } else {
                    shell.setLocation(rect.x, rect.y - calendarShellHeight);
                }

                shell.open();

                shell.addShellListener(new ShellAdapter() {
                    @Override
                    public void shellDeactivated(ShellEvent e) {
                        shell.close();
                        swtcal = null;
                    }
                });
            }
        });
    }

    /**
     * Internal class for laying out the dialog.
     */
    private class DialogCellLayout extends Layout {
        @Override
        public void layout(Composite editor, boolean force) {
            Rectangle bounds = editor.getClientArea();
            Point size = textControl.computeSize(SWT.DEFAULT, SWT.DEFAULT, force);
            textControl.setBounds(0, 0, bounds.width - size.y, bounds.height);
            button.setBounds(bounds.width - size.y, 0, size.y, bounds.height);
        }

        @Override
        public Point computeSize(Composite editor, int wHint, int hHint, boolean force) {
            if (JMoneyPlugin.DEBUG)
                System.out.println("wHint =" + wHint + ", " + hHint); //$NON-NLS-1$ //$NON-NLS-2$
            if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT)
                return new Point(wHint, hHint);
            Point contentsSize = textControl.computeSize(SWT.DEFAULT, SWT.DEFAULT, force);
            Point buttonSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, force);
            if (JMoneyPlugin.DEBUG)
                System.out.println("contents =" + contentsSize.x + ", " + contentsSize.y); //$NON-NLS-1$ //$NON-NLS-2$
            if (JMoneyPlugin.DEBUG)
                System.out.println("contents =" + buttonSize.x + ", " + buttonSize.y); //$NON-NLS-1$ //$NON-NLS-2$
            // Just return the button width to ensure the button is not clipped
            // if the label is long.  Date text needs 60
            // The label will just use whatever extra width there is
            Point result = new Point(60 + buttonSize.x,
                    //                             Math.max(contentsSize.y, buttonSize.y));
                    contentsSize.y);
            return result;
        }
    }

    /**
     * @param object
     */
    @Override
    public void setDate(Date date) {
        if (date == null) {
            textControl.setText(""); //$NON-NLS-1$
        } else {
            this.textControl.setText(fDateFormat.format(date));
        }
    }

    /**
     * @return the date, or null if a valid date is not set in
     *             the control
     */
    @Override
    public Date getDate() {
        String text = textControl.getText();
        try {
            return fDateFormat.parse(text);
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    /**
     * @param listener
     */
    public void addModifyListener(ModifyListener listener) {
        textControl.addModifyListener(listener);
    }

    /**
     * @param listener
     */
    public void removeModifyListener(ModifyListener listener) {
        textControl.removeModifyListener(listener);
    }
}