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