001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.awt.Component;
005import java.awt.Dimension;
006import java.awt.GridBagLayout;
007import java.text.DateFormat;
008import java.text.SimpleDateFormat;
009import java.util.ArrayList;
010import java.util.Date;
011import java.util.List;
012
013import javax.swing.JLabel;
014import javax.swing.JPanel;
015import javax.swing.JSlider;
016import javax.swing.JSpinner;
017import javax.swing.SpinnerDateModel;
018import javax.swing.event.ChangeEvent;
019import javax.swing.event.ChangeListener;
020
021import org.openstreetmap.josm.tools.GBC;
022import org.openstreetmap.josm.tools.date.DateUtils;
023
024/**
025 * Widget originally created for date filtering of GPX tracks.
026 * Allows to enter the date or choose it by using slider
027 * @since 5815
028 */
029public class DateEditorWithSlider extends JPanel {
030    private JSpinner spinner;
031    private JSlider slider;
032    private Date dateMin;
033    private Date dateMax;
034    private static final int MAX_SLIDER = 300;
035    private boolean watchSlider = true;
036
037    private transient List<ChangeListener> listeners = new ArrayList<>();
038
039    /**
040     * Constructs a new {@code DateEditorWithSlider} with a given label
041     * @param labelText The label to display
042     */
043    public DateEditorWithSlider(String labelText) {
044        super(new GridBagLayout());
045        spinner = new JSpinner(new SpinnerDateModel());
046        String pattern = ((SimpleDateFormat) DateUtils.getDateFormat(DateFormat.DEFAULT)).toPattern();
047        JSpinner.DateEditor timeEditor = new JSpinner.DateEditor(spinner, pattern);
048        spinner.setEditor(timeEditor);
049
050        spinner.setPreferredSize(new Dimension(spinner.getPreferredSize().width+5,
051        spinner.getPreferredSize().height));
052
053        slider = new JSlider(0, MAX_SLIDER);
054        spinner.addChangeListener(new ChangeListener() {
055            @Override
056            public void stateChanged(ChangeEvent e) {
057                int i = slider.getValue();
058                Date d = (Date) spinner.getValue();
059                int j = intFromDate(d);
060                if (i != j) {
061                    watchSlider = false;
062                    slider.setValue(j);
063                    watchSlider = true;
064                }
065                for (ChangeListener l : listeners) {
066                    l.stateChanged(e);
067                }
068            }
069        });
070        slider.addChangeListener(new ChangeListener() {
071            @Override
072            public void stateChanged(ChangeEvent e) {
073                if (!watchSlider) return;
074                Date d = (Date) spinner.getValue();
075                Date d1 = dateFromInt(slider.getValue());
076                if (!d.equals(d1)) {
077                    spinner.setValue(d1);
078                }
079            }
080        });
081        add(new JLabel(labelText), GBC.std());
082        add(spinner, GBC.std().insets(10, 0, 0, 0));
083        add(slider, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL));
084
085        dateMin = new Date(0);
086        dateMax = new Date();
087    }
088
089    protected Date dateFromInt(int value) {
090        double k = 1.0*value/MAX_SLIDER;
091        return new Date((long) (dateMax.getTime()*k+ dateMin.getTime()*(1-k)));
092    }
093
094    protected int intFromDate(Date date) {
095        return (int) (300.0*(date.getTime()-dateMin.getTime()) /
096                (dateMax.getTime()-dateMin.getTime()));
097    }
098
099    public void setRange(Date dateMin, Date dateMax) {
100        this.dateMin = dateMin;
101        this.dateMax = dateMax;
102    }
103
104    public void setDate(Date date) {
105        spinner.setValue(date);
106    }
107
108    public Date getDate() {
109        return (Date) spinner.getValue();
110    }
111
112    public void addDateListener(ChangeListener l) {
113        listeners.add(l);
114    }
115
116    public void removeDateListener(ChangeListener l) {
117        listeners.remove(l);
118    }
119
120    @Override
121    public void setEnabled(boolean enabled) {
122        super.setEnabled(enabled);
123        for (Component c: getComponents()) {
124            c.setEnabled(enabled);
125        }
126    }
127}