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.ChangeListener; 019 020import org.openstreetmap.josm.tools.GBC; 021import org.openstreetmap.josm.tools.date.DateUtils; 022 023/** 024 * Widget originally created for date filtering of GPX tracks. 025 * Allows to enter the date or choose it by using slider 026 * @since 5815 027 */ 028public class DateEditorWithSlider extends JPanel { 029 private final JSpinner spinner; 030 private final JSlider slider; 031 private Date dateMin; 032 private Date dateMax; 033 private static final int MAX_SLIDER = 300; 034 private boolean watchSlider = true; 035 036 private final transient List<ChangeListener> listeners = new ArrayList<>(); 037 038 /** 039 * Constructs a new {@code DateEditorWithSlider} with a given label 040 * @param labelText The label to display 041 */ 042 public DateEditorWithSlider(String labelText) { 043 super(new GridBagLayout()); 044 spinner = new JSpinner(new SpinnerDateModel()); 045 String pattern = ((SimpleDateFormat) DateUtils.getDateFormat(DateFormat.DEFAULT)).toPattern(); 046 JSpinner.DateEditor timeEditor = new JSpinner.DateEditor(spinner, pattern); 047 spinner.setEditor(timeEditor); 048 049 spinner.setPreferredSize(new Dimension(spinner.getPreferredSize().width+5, 050 spinner.getPreferredSize().height)); 051 052 slider = new JSlider(0, MAX_SLIDER); 053 spinner.addChangeListener(e -> { 054 int i = slider.getValue(); 055 Date d = (Date) spinner.getValue(); 056 int j = intFromDate(d); 057 if (i != j) { 058 watchSlider = false; 059 slider.setValue(j); 060 watchSlider = true; 061 } 062 for (ChangeListener l : listeners) { 063 l.stateChanged(e); 064 } 065 }); 066 slider.addChangeListener(e -> { 067 if (!watchSlider) return; 068 Date d = (Date) spinner.getValue(); 069 Date d1 = dateFromInt(slider.getValue()); 070 if (!d.equals(d1)) { 071 spinner.setValue(d1); 072 } 073 }); 074 add(new JLabel(labelText), GBC.std()); 075 add(spinner, GBC.std().insets(10, 0, 0, 0)); 076 add(slider, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL)); 077 078 dateMin = new Date(0); 079 dateMax = new Date(); 080 } 081 082 protected Date dateFromInt(int value) { 083 double k = 1.0*value/MAX_SLIDER; 084 return new Date((long) (dateMax.getTime()*k+ dateMin.getTime()*(1-k))); 085 } 086 087 protected int intFromDate(Date date) { 088 return (int) (300.0*(date.getTime()-dateMin.getTime()) / 089 (dateMax.getTime()-dateMin.getTime())); 090 } 091 092 /** 093 * Sets the date range that is available using the slider 094 * @param dateMin The min date 095 * @param dateMax The max date 096 */ 097 public void setRange(Date dateMin, Date dateMax) { 098 this.dateMin = DateUtils.cloneDate(dateMin); 099 this.dateMax = DateUtils.cloneDate(dateMax); 100 } 101 102 /** 103 * Sets the slider to the given value 104 * @param date The date 105 */ 106 public void setDate(Date date) { 107 spinner.setValue(DateUtils.cloneDate(date)); 108 } 109 110 /** 111 * Gets the date that was selected by the user 112 * @return The date 113 */ 114 public Date getDate() { 115 return DateUtils.cloneDate((Date) spinner.getValue()); 116 } 117 118 /** 119 * Adds a change listener to this date editor. 120 * @param l The listener 121 */ 122 public void addDateListener(ChangeListener l) { 123 listeners.add(l); 124 } 125 126 /** 127 * Removes a change listener from this date editor. 128 * @param l The listener 129 */ 130 public void removeDateListener(ChangeListener l) { 131 listeners.remove(l); 132 } 133 134 @Override 135 public void setEnabled(boolean enabled) { 136 super.setEnabled(enabled); 137 for (Component c: getComponents()) { 138 c.setEnabled(enabled); 139 } 140 } 141}