001package jmri.jmrix.can.cbus.swing.modules;
002
003import java.awt.*;
004import java.util.TimerTask;
005
006import javax.swing.*;
007import javax.swing.border.*;
008import javax.swing.event.ChangeEvent;
009import javax.swing.event.ChangeListener;
010
011import jmri.util.TimerUtil;
012
013
014/**
015 * JSlider with a titled border
016 *
017 * @author Andrew Crosland Copyright (C) 2022
018 */
019public class TitledSlider extends JPanel implements ChangeListener {
020        
021    protected JSlider tSlide;
022    protected int _index;
023    protected String _title;
024    protected UpdateNV _update;
025
026    /**
027     * Construct a new titledSlider
028     * 
029     * @param title to be displayed
030     * @param index of the associated NV 
031     * @param update callback funtion to apply new value
032     */
033    public TitledSlider(String title, int index, UpdateNV update) {
034        super();
035        _title = title;
036        _index = index;
037        _update = update;
038        tSlide = new JSlider();
039    }
040
041    /**
042     * Initialise the slider
043     * 
044     * @param init  Initial value
045     * @param min   Minimum value
046     * @param max   Maximum value
047     */
048    public void init(int min, int max, int init) {
049        GridLayout grid = new GridLayout(1, 1);
050        setLayout(grid);
051
052        Border border = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
053        TitledBorder titled = BorderFactory.createTitledBorder(border, _title);
054        setBorder(titled);
055
056        tSlide.setMinimum(min);
057        tSlide.setMaximum(max);
058        tSlide.setValue(init);
059        tSlide.addChangeListener(this);
060
061        add(tSlide);
062    }
063
064    /**
065     * Set slider value
066     * 
067     * Only do this if value has actually changed, otherwise we can trigger
068     * an endless round of updating to the table and the gui.
069     * 
070     * @param v  value
071     */
072    public void setValue(int v) {
073        if (v != tSlide.getValue()) {
074            tSlide.setValue(v);
075        }
076    }
077
078    /**
079     * Get slider value
080     * 
081     * @return slider value
082     */
083    public int getValue() {
084        return tSlide.getValue();
085    }
086
087    /**
088     * Set the tool tip
089     * 
090     * @param tt tooltip text
091     */
092    public void setToolTip(String tt) {
093        tSlide.setToolTipText(tt);
094    }
095
096    /**
097     * Call back with updated value
098     * 
099     * We restrict the frequency of updates, with a timer, whilst the slider 
100     * is adjusting to, prevent CBUS replies from earlier updates looking
101     * like a new value change and triggering another update as this can
102     * lead to an endless round of updates.
103     * 
104     * @param e the slider change event
105     */
106    @Override
107    public void stateChanged(ChangeEvent e) {
108        JSlider source = (JSlider)e.getSource();
109        if (!source.getValueIsAdjusting()) {
110//            log.debug("TitledSlider.stateChanged() finished adjusting - final update");
111            _update.setNewVal(_index);
112            if (timerTask != null) {
113                timerTask.cancel();
114                timerTask = null;
115            }
116        } else if (timerTask == null) {
117//            log.debug("TitledSlider.stateChanged() update");
118            _update.setNewVal(_index);
119            startTimer();
120        }
121    }
122
123    /**
124     ** The preferred width on the panel must consider the width of the text
125     ** used on the TitledBorder
126     * 
127     * from <a href=https://stackoverflow.com/questions/43425939/how-to-get-the-titledborders-title-to-display-properly-in-the-gui></a>
128     */
129    @Override
130    public Dimension getPreferredSize() {
131
132        Dimension preferredSize = super.getPreferredSize();
133
134        Border border = getBorder();
135        int borderWidth = 0;
136
137        if (border instanceof TitledBorder) {
138            Insets insets = getInsets();
139            TitledBorder titledBorder = (TitledBorder)border;
140            borderWidth = titledBorder.getMinimumSize(this).width + insets.left + insets.right;
141        }
142
143        int preferredWidth = Math.max(preferredSize.width, borderWidth);
144
145        return new Dimension(preferredWidth, preferredSize.height);
146    }
147
148    private TimerTask timerTask;
149
150    /**
151     * Start timer for update throttling
152     */
153    private void startTimer() {
154        timerTask = new TimerTask() {
155            @Override
156            public void run() {
157                timerTask = null;
158            }
159        };
160        TimerUtil.schedule(timerTask, 100);
161    }
162    
163//    private final static Logger log = LoggerFactory.getLogger(TitledSlider.class);
164
165}