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.time.Instant; 010import java.util.ArrayList; 011import java.util.Date; 012import java.util.List; 013 014import javax.swing.JLabel; 015import javax.swing.JPanel; 016import javax.swing.JSlider; 017import javax.swing.JSpinner; 018import javax.swing.SpinnerDateModel; 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 final JSpinner spinner; 031 private final JSlider slider; 032 private Instant dateMin; 033 private Instant dateMax; 034 private static final int MAX_SLIDER = 300; 035 private boolean watchSlider = true; 036 037 private final 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(e -> { 055 int i = slider.getValue(); 056 Date d = (Date) spinner.getValue(); 057 int j = intFromDate(d); 058 if (i != j) { 059 watchSlider = false; 060 slider.setValue(j); 061 watchSlider = true; 062 } 063 for (ChangeListener l : listeners) { 064 l.stateChanged(e); 065 } 066 }); 067 slider.addChangeListener(e -> { 068 if (!watchSlider) return; 069 Date d = (Date) spinner.getValue(); 070 Date d1 = dateFromInt(slider.getValue()); 071 if (!d.equals(d1)) { 072 spinner.setValue(d1); 073 } 074 }); 075 add(new JLabel(labelText), GBC.std()); 076 add(spinner, GBC.std().insets(10, 0, 0, 0)); 077 add(slider, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL)); 078 079 dateMin = Instant.EPOCH; 080 dateMax = Instant.now(); 081 } 082 083 protected Date dateFromInt(int value) { 084 double k = 1.0*value/MAX_SLIDER; 085 return new Date((long) (dateMax.toEpochMilli()*k+ dateMin.toEpochMilli()*(1-k))); 086 } 087 088 protected int intFromDate(Date date) { 089 return (int) (300.0*(date.getTime()-dateMin.getEpochSecond()) / 090 (dateMax.getEpochSecond()-dateMin.getEpochSecond())); 091 } 092 093 /** 094 * Sets the date range that is available using the slider 095 * @param dateMin The min date 096 * @param dateMax The max date 097 */ 098 public void setRange(Instant dateMin, Instant dateMax) { 099 this.dateMin = dateMin; 100 this.dateMax = dateMax; 101 } 102 103 /** 104 * Sets the slider to the given value 105 * @param date The date 106 */ 107 public void setDate(Instant date) { 108 spinner.setValue(Date.from(date)); 109 } 110 111 /** 112 * Gets the date that was selected by the user 113 * @return The date 114 */ 115 public Instant getDate() { 116 return ((Date) spinner.getValue()).toInstant(); 117 } 118 119 /** 120 * Adds a change listener to this date editor. 121 * @param l The listener 122 */ 123 public void addDateListener(ChangeListener l) { 124 listeners.add(l); 125 } 126 127 /** 128 * Removes a change listener from this date editor. 129 * @param l The listener 130 */ 131 public void removeDateListener(ChangeListener l) { 132 listeners.remove(l); 133 } 134 135 @Override 136 public void setEnabled(boolean enabled) { 137 super.setEnabled(enabled); 138 for (Component c: getComponents()) { 139 c.setEnabled(enabled); 140 } 141 } 142}