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