001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.gpx;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.GridBagLayout;
008import java.awt.event.ActionEvent;
009import java.awt.event.ActionListener;
010import java.util.Date;
011import java.util.GregorianCalendar;
012
013import javax.swing.JCheckBox;
014import javax.swing.JPanel;
015import javax.swing.Timer;
016import javax.swing.event.ChangeEvent;
017import javax.swing.event.ChangeListener;
018
019import org.openstreetmap.josm.Main;
020import org.openstreetmap.josm.gui.layer.GpxLayer;
021import org.openstreetmap.josm.gui.widgets.DateEditorWithSlider;
022import org.openstreetmap.josm.tools.GBC;
023
024public class DateFilterPanel extends JPanel {
025    private final DateEditorWithSlider dateFrom = new DateEditorWithSlider(tr("From"));
026    private final DateEditorWithSlider dateTo = new DateEditorWithSlider(tr("To"));
027    private final JCheckBox noTimestampCb  = new JCheckBox(tr("No timestamp"));
028    private final transient GpxLayer layer;
029
030    private transient ActionListener filterAppliedListener;
031
032    private final String prefDate0;
033    private final String prefDateMin;
034    private final String prefDateMax;
035
036    /**
037     * Create the panel to filter tracks on GPX layer @param layer by date
038     * Preferences will be stored in @param preferencePrefix
039     * If @param enabled = true, then the panel is created as active and filtering occurs immediately.
040     * @param layer GPX layer
041     * @param preferencePrefix preference prefix
042     * @param enabled panel initial enabled state
043     */
044    public DateFilterPanel(GpxLayer layer, String preferencePrefix, boolean enabled) {
045        super(new GridBagLayout());
046        prefDate0 = preferencePrefix+".showzerotimestamp";
047        prefDateMin = preferencePrefix+".mintime";
048        prefDateMax = preferencePrefix+".maxtime";
049        this.layer = layer;
050
051        final Date startTime, endTime;
052        Date[] bounds = layer.data.getMinMaxTimeForAllTracks();
053        startTime = (bounds.length == 0) ? new GregorianCalendar(2000, 1, 1).getTime() : bounds[0];
054        endTime = (bounds.length == 0) ? new Date() : bounds[1];
055
056        dateFrom.setDate(startTime);
057        dateTo.setDate(endTime);
058        dateFrom.setRange(startTime, endTime);
059        dateTo.setRange(startTime, endTime);
060
061        add(noTimestampCb, GBC.std().grid(1, 1).insets(0, 0, 5, 0));
062        add(dateFrom, GBC.std().grid(2, 1).fill(GBC.HORIZONTAL));
063        add(dateTo, GBC.eol().grid(3, 1).fill(GBC.HORIZONTAL));
064
065        setEnabled(enabled);
066
067        dateFrom.addDateListener(changeListener);
068        dateTo.addDateListener(changeListener);
069        noTimestampCb.addChangeListener(changeListener);
070    }
071
072    private final transient ChangeListener changeListener = new ChangeListener() {
073        @Override public void stateChanged(ChangeEvent e) {
074            if (isEnabled()) applyFilterWithDelay();
075        }
076    };
077
078    private final Timer t = new Timer(200, new ActionListener() {
079        @Override  public void actionPerformed(ActionEvent e) {
080            applyFilter();
081        }
082    });
083
084    /**
085     * Do filtering but little bit later (to reduce cpu load)
086     */
087    public void applyFilterWithDelay() {
088        if (t.isRunning()) {
089            t.restart();
090        } else {
091            t.start();
092        }
093    }
094
095    public void applyFilter() {
096        t.stop();
097        filterTracksByDate();
098        if (filterAppliedListener != null)
099           filterAppliedListener.actionPerformed(null);
100    }
101
102    /**
103     * Called by other components when it is correct time to save date filtering parameters
104     */
105    public void saveInPrefs() {
106        Main.pref.putLong(prefDateMin, dateFrom.getDate().getTime());
107        Main.pref.putLong(prefDateMax, dateTo.getDate().getTime());
108        Main.pref.put(prefDate0, noTimestampCb.isSelected());
109    }
110
111    /**
112     * If possible, load date ragne and "zero timestamp" option from preferences
113     * Called by other components when it is needed.
114     */
115    public void loadFromPrefs() {
116        long t1 = Main.pref.getLong(prefDateMin, 0);
117        if (t1 != 0) dateFrom.setDate(new Date(t1));
118        long t2 = Main.pref.getLong(prefDateMax, 0);
119        if (t2 != 0) dateTo.setDate(new Date(t2));
120        noTimestampCb.setSelected(Main.pref.getBoolean(prefDate0, false));
121    }
122
123    public void setFilterAppliedListener(ActionListener filterAppliedListener) {
124        this.filterAppliedListener = filterAppliedListener;
125    }
126
127    private void filterTracksByDate() {
128        Date from = dateFrom.getDate();
129        Date to = dateTo.getDate();
130        layer.filterTracksByDate(from, to, noTimestampCb.isSelected());
131    }
132
133    @Override
134    public final void setEnabled(boolean enabled) {
135        super.setEnabled(enabled);
136        for (Component c: getComponents()) {
137            c.setEnabled(enabled);
138        }
139    }
140}