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.ActionListener; 009import java.time.Instant; 010import java.time.ZoneId; 011import java.time.ZonedDateTime; 012 013import javax.swing.JCheckBox; 014import javax.swing.JPanel; 015import javax.swing.Timer; 016import javax.swing.event.ChangeListener; 017 018import org.openstreetmap.josm.gui.layer.GpxLayer; 019import org.openstreetmap.josm.gui.widgets.DateEditorWithSlider; 020import org.openstreetmap.josm.spi.preferences.Config; 021import org.openstreetmap.josm.tools.GBC; 022import org.openstreetmap.josm.tools.date.Interval; 023 024/** 025 * A panel that allows the user to input a date range he wants to filter the GPX data for. 026 */ 027public class DateFilterPanel extends JPanel { 028 private final DateEditorWithSlider dateFrom = new DateEditorWithSlider(tr("From")); 029 private final DateEditorWithSlider dateTo = new DateEditorWithSlider(tr("To")); 030 private final JCheckBox noTimestampCb = new JCheckBox(tr("No timestamp")); 031 private final transient GpxLayer layer; 032 033 private transient ActionListener filterAppliedListener; 034 035 private final String prefDate0; 036 private final String prefDateMin; 037 private final String prefDateMax; 038 039 /** 040 * Create the panel to filter tracks on GPX layer {@code layer} by date 041 * Preferences will be stored in {@code preferencePrefix} 042 * If {@code enabled = true}, then the panel is created as active and filtering occurs immediately. 043 * @param layer GPX layer 044 * @param preferencePrefix preference prefix 045 * @param enabled panel initial enabled state 046 */ 047 public DateFilterPanel(GpxLayer layer, String preferencePrefix, boolean enabled) { 048 super(new GridBagLayout()); 049 prefDate0 = preferencePrefix+".showzerotimestamp"; 050 prefDateMin = preferencePrefix+".mintime"; 051 prefDateMax = preferencePrefix+".maxtime"; 052 this.layer = layer; 053 054 Interval interval = layer.data.getMinMaxTimeForAllTracks() 055 .orElseGet(() -> new Interval(ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()).toInstant(), Instant.now())); 056 dateFrom.setDate(interval.getStart()); 057 dateTo.setDate(interval.getEnd()); 058 dateFrom.setRange(interval.getStart(), interval.getEnd()); 059 dateTo.setRange(interval.getStart(), interval.getEnd()); 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 ChangeListener changeListener = e -> { 068 if (isEnabled()) applyFilterWithDelay(); 069 }; 070 071 dateFrom.addDateListener(changeListener); 072 dateTo.addDateListener(changeListener); 073 noTimestampCb.addChangeListener(changeListener); 074 } 075 076 private final Timer t = new Timer(200, e -> applyFilter()); 077 078 /** 079 * Do filtering but little bit later (to reduce cpu load) 080 */ 081 public void applyFilterWithDelay() { 082 if (t.isRunning()) { 083 t.restart(); 084 } else { 085 t.start(); 086 } 087 } 088 089 /** 090 * Applies the filter that was input by the user to the GPX track 091 */ 092 public void applyFilter() { 093 t.stop(); 094 filterTracksByDate(); 095 if (filterAppliedListener != null) 096 filterAppliedListener.actionPerformed(null); 097 } 098 099 /** 100 * Called by other components when it is correct time to save date filtering parameters 101 */ 102 public void saveInPrefs() { 103 Config.getPref().putLong(prefDateMin, dateFrom.getDate().toEpochMilli()); 104 Config.getPref().putLong(prefDateMax, dateTo.getDate().toEpochMilli()); 105 Config.getPref().putBoolean(prefDate0, noTimestampCb.isSelected()); 106 } 107 108 /** 109 * If possible, load date ragne and "zero timestamp" option from preferences 110 * Called by other components when it is needed. 111 */ 112 public void loadFromPrefs() { 113 long t1 = Config.getPref().getLong(prefDateMin, 0); 114 if (t1 != 0) dateFrom.setDate(Instant.ofEpochMilli(t1)); 115 long t2 = Config.getPref().getLong(prefDateMax, 0); 116 if (t2 != 0) dateTo.setDate(Instant.ofEpochMilli(t2)); 117 noTimestampCb.setSelected(Config.getPref().getBoolean(prefDate0, false)); 118 } 119 120 /** 121 * Sets a listener that should be called after the filter was applied 122 * @param filterAppliedListener The listener to call 123 */ 124 public void setFilterAppliedListener(ActionListener filterAppliedListener) { 125 this.filterAppliedListener = filterAppliedListener; 126 } 127 128 private void filterTracksByDate() { 129 Instant from = dateFrom.getDate(); 130 Instant to = dateTo.getDate(); 131 layer.filterTracksByDate(from, to, noTimestampCb.isSelected()); 132 } 133 134 @Override 135 public final void setEnabled(boolean enabled) { 136 super.setEnabled(enabled); 137 for (Component c: getComponents()) { 138 c.setEnabled(enabled); 139 } 140 } 141}