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        if (addressPanel != null && addressPanel.getRosterEntry() != null && useSpeedProfile) {
142            return (addressPanel.getRosterEntry().getSpeedProfile().convertThrottleSettingToScaleSpeedWithUnits(throttleVolume, isForward));
143        } else {
144            return (Bundle.getMessage("ThrottleSpeedPanelError"));
145        }
146
147    }
148
149    @Override
150    public void notifyAddressChosen(LocoAddress l) {
151    }
152
153    @Override
154    public void notifyNewRosterEntryHighlighted(RosterEntry re) {     
155    }
156
157    @Override
158    public void notifyRosterEntryUpdated(RosterEntry re) { 
159        useSpeedProfile = false;  //posit false
160        if (re != null
161                && re.getSpeedProfile() != null
162                && re.getSpeedProfile().getProfileSize() > 0) {
163            useSpeedProfile = true;
164        }
165        updateSpeedLabel(useSpeedProfile, throttle.getSpeedSetting(), throttle.getIsForward());    
166    }   
167
168    @Override
169    public void notifyAddressReleased(LocoAddress la) {
170        if (throttle == null) {
171            log.debug("notifyAddressReleased() throttle alreaday null, called for loc {}",la);
172            return;
173        }        
174        this.setEnabled(false);
175        throttle.removePropertyChangeListener(this);
176        throttle = null;
177        updateSpeedLabel(useSpeedProfile, 0, true);
178    }
179
180    @Override
181    public void notifyAddressThrottleFound(DccThrottle t) {       
182        if (log.isDebugEnabled()) {
183            log.debug("control panel received new throttle {}",t);
184        }
185        this.throttle = t;
186        this.throttle.addPropertyChangeListener(this);
187        if (log.isDebugEnabled()) {
188            jmri.DccLocoAddress Address = (jmri.DccLocoAddress) throttle.getLocoAddress();
189            log.debug("new address is {}", Address.toString());
190        }        
191        notifyRosterEntryUpdated(addressPanel.getRosterEntry());
192    }
193
194    @Override
195    public void notifyConsistAddressChosen(LocoAddress l) {
196        notifyAddressChosen(l);
197    }
198
199    @Override
200    public void notifyConsistAddressReleased(LocoAddress l) {
201        notifyAddressReleased(l);
202    }
203
204    @Override
205    public void notifyConsistAddressThrottleFound(DccThrottle throttle) {
206        if (log.isDebugEnabled()) {
207            log.debug("control panel received consist throttle");
208        }
209        notifyAddressThrottleFound(throttle);
210    }
211
212    public Element getXml() {
213        Element me = new Element("SpeedPanel"); // NOI18N
214        // put nothing
215        return me;
216    }
217
218    public void setXml(Element e) {
219        // do nothing
220    }
221
222    // initialize logging
223    private static final Logger log = LoggerFactory.getLogger(SpeedPanel.class);
224}