001package jmri.jmrit.throttle.panels;
002
003import java.awt.BorderLayout;
004import java.awt.Font;
005import javax.swing.BoxLayout;
006import javax.swing.JLabel;
007import javax.swing.JPanel;
008import jmri.DccThrottle;
009import jmri.LocoAddress;
010import jmri.Throttle;
011import jmri.jmrit.roster.RosterEntry;
012import jmri.jmrit.throttle.interfaces.AddressListener;
013
014import org.jdom2.Element;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * A Panel that contains a label to display scale speed if available
020 * for forward, reverse and STOP. TODO: fix speed increments (14, 28)
021 * 
022 * <hr>
023 * This file is part of JMRI.
024 * <p>
025 * JMRI is free software; you can redistribute it and/or modify it under the
026 * terms of version 2 of the GNU General Public License as published by the Free
027 * Software Foundation. See the "COPYING" file for a copy of this license.
028 * <p>
029 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
030 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
031 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
032 *
033 * @author glen Copyright (C) 2002
034 * @author Bob Jacobsen Copyright (C) 2007
035 * @author Ken Cameron Copyright (C) 2008
036 * @author Steve Gigiel Copyright (C) 2017
037 * @author Lionel Jeanson 2026
038 * 
039 */
040public class SpeedPanel extends JPanel implements java.beans.PropertyChangeListener, AddressListener {
041
042    private DccThrottle throttle;
043
044    private JPanel speedDisplayPanel;
045
046    private JLabel scaleSpeedLabel = new JLabel("", JLabel.CENTER);
047
048    // tracks whether we are using speed profiles
049    private boolean useSpeedProfile = false;
050
051    // last known direction
052    private boolean currentIsForward = true;
053    private float currentThrottleVol = 0.0f;
054
055    //for access to roster entry
056    private AddressPanel addressPanel; //for access to roster entry
057
058    /**
059     * Constructor.
060     */
061    public SpeedPanel() {
062        initGUI();
063    }
064
065    /**
066     * Set the AddressPanel this throttle control is listening for new throttle
067     * event
068     *
069     * @param ap  reference to the addresspanel
070     */
071    public void setAddressPanel(AddressPanel ap) {
072        if (throttle != null) {
073            notifyAddressReleased(throttle.getLocoAddress());
074        }
075        if (addressPanel != null) {
076            addressPanel.removeAddressListener(this);
077        }
078        addressPanel = ap;
079        if (addressPanel != null) {
080            addressPanel.addAddressListener(this);        
081            if (addressPanel.getThrottle() != null ) {
082                notifyAddressThrottleFound(addressPanel.getThrottle());
083            } else {
084                notifyAddressReleased(addressPanel.getCurrentAddress());
085            }
086        }
087    }
088
089    /**
090     * "Destructor"
091     */
092    public void dispose() {
093        if (addressPanel != null) {
094            addressPanel.removeAddressListener(this);
095            addressPanel = null;
096        }
097        if (throttle != null) {
098            throttle.removePropertyChangeListener(this);
099            throttle = null;
100        }
101    }
102
103    /**
104     * Create, initialize and place GUI components.
105     */
106    private void initGUI() {
107        setLayout(new BorderLayout());
108        speedDisplayPanel = new JPanel();
109        speedDisplayPanel.setFont(new Font("", Font.PLAIN, 32));
110        speedDisplayPanel.setLayout(new BoxLayout(speedDisplayPanel, BoxLayout.X_AXIS));
111        speedDisplayPanel.setOpaque(false);
112        add(speedDisplayPanel, BorderLayout.CENTER);
113        speedDisplayPanel.add(scaleSpeedLabel);
114    }
115
116    /**
117     * update the state of this panel if direction or speed change
118     */
119    @Override
120    public void propertyChange(java.beans.PropertyChangeEvent e) {
121        if (e.getPropertyName().equals(Throttle.SPEEDSETTING)) {
122            currentThrottleVol = ((Float) e.getNewValue()).floatValue();
123            scaleSpeedLabel.setText(updateSpeedLabel(useSpeedProfile, currentThrottleVol, currentIsForward));
124        } else if (e.getPropertyName().equals(Throttle.ISFORWARD)) {
125            currentIsForward = (boolean) e.getNewValue();
126            scaleSpeedLabel.setText(updateSpeedLabel(useSpeedProfile, currentThrottleVol, currentIsForward));
127        }
128        if (log.isDebugEnabled()) {
129            log.debug("Property change event received {} / {}", e.getPropertyName(), e.getNewValue());
130        }
131    }
132
133    /**
134     *
135     * @param useSpeedProfile  are we using speed profile
136     * @param throttleVolume   throttle position (percent of 1)
137     * @param isForward        true if going forward.
138     * @return a string for displaying speed if available
139     */
140    private String updateSpeedLabel(boolean useSpeedProfile, float throttleVolume, boolean isForward) {
141        RosterEntry re = addressPanel.getRosterEntry();
142        if (re != null && useSpeedProfile) {
143            return (re.getSpeedProfile().convertThrottleSettingToScaleSpeedWithUnits(throttleVolume, isForward));
144        } else {
145            return (Bundle.getMessage("ThrottleSpeedPanelError"));
146        }
147
148    }
149
150    @Override
151    public void notifyAddressChosen(LocoAddress l) {
152    }
153
154    @Override
155    public void notifyRosterEntrySelected(RosterEntry re) {     
156    }    
157
158    @Override
159    public void notifyAddressReleased(LocoAddress la) {
160        if (throttle == null) {
161            log.debug("notifyAddressReleased() throttle alreaday null, called for loc {}",la);
162            return;
163        }        
164        this.setEnabled(false);
165        throttle.removePropertyChangeListener(this);
166        throttle = null;
167        updateSpeedLabel(useSpeedProfile, 0, true);
168    }
169
170    @Override
171    public void notifyAddressThrottleFound(DccThrottle t) {       
172        if (log.isDebugEnabled()) {
173            log.debug("control panel received new throttle {}",t);
174        }
175        this.throttle = t;
176        this.throttle.addPropertyChangeListener(this);
177        if (log.isDebugEnabled()) {
178            jmri.DccLocoAddress Address = (jmri.DccLocoAddress) throttle.getLocoAddress();
179            log.debug("new address is {}", Address.toString());
180        }
181
182        useSpeedProfile = false;  //posit false
183        RosterEntry re = addressPanel.getRosterEntry();
184        if (re != null
185                && re.getSpeedProfile() != null
186                && re.getSpeedProfile().getProfileSize() > 0) {
187            useSpeedProfile = true;
188        }
189        updateSpeedLabel(useSpeedProfile, t.getSpeedSetting(), t.getIsForward());
190    }
191
192    @Override
193    public void notifyConsistAddressChosen(LocoAddress l) {
194        notifyAddressChosen(l);
195    }
196
197    @Override
198    public void notifyConsistAddressReleased(LocoAddress l) {
199        notifyAddressReleased(l);
200    }
201
202    @Override
203    public void notifyConsistAddressThrottleFound(DccThrottle throttle) {
204        if (log.isDebugEnabled()) {
205            log.debug("control panel received consist throttle");
206        }
207        notifyAddressThrottleFound(throttle);
208    }
209
210    public Element getXml() {
211        Element me = new Element("SpeedPanel"); // NOI18N
212        // put nothing
213        return me;
214    }
215
216    public void setXml(Element e) {
217        // do nothing
218    }
219
220    // initialize logging
221    private static final Logger log = LoggerFactory.getLogger(SpeedPanel.class);
222}