001package jmri.jmrix.loconet.ds64;
002import java.awt.FlowLayout;
003import javax.swing.BoxLayout;
004import javax.swing.ButtonGroup;
005import javax.swing.JLabel;
006import javax.swing.JPanel;
007import javax.swing.JRadioButton;
008import jmri.util.swing.ValidatedTextField;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Provides a swing object, for use by the Ds64TabbedPanel tool, which allows 
014 * display and configuration of turnout number and position.
015 * <p>
016 * Turnout numbering is the same as seen on a Digitrax throttle display; Tools 
017 * using values from objects of this type must provide the appropriate transform
018 * to create turnout numbering which is suitable for use within LocoNet messaging.
019 *
020 * @author B. Milhaupt Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017
021 */
022public class SimpleTurnoutStateEntry extends SimpleTurnout {
023    private JPanel entryPanel = null;
024
025    /**
026     * Tracks the "user-friendly" turnout address.
027     * <p>
028     * Turnout numbering is the same as seen on a Digitrax throttle display; Tools 
029     * using values from objects of this type must provide the appropriate transform
030     * to create turnout numbering which is suitable for use within LocoNet messaging.
031     */
032    public ValidatedTextField addressField = null;
033
034    /**
035     * Tracks whether the associated turnout is "thrown".
036     */
037    public JRadioButton thrownRadioButton;
038
039    /**
040     * Tracks whether the associated turnout is "closed".
041     */
042    public JRadioButton closedRadioButton;
043
044    /**
045     * Tracks whether the object is in-use or not, as seen in some aspects of
046     * DS64 configuration.
047     */
048    public JRadioButton unusedRadioButton;
049
050    /**
051     * Constructor used when the current address and position are not known.  It
052     * is assumed that the turnout address is 1, that the turnout is "closed", and
053     * that the turnout is "valid".
054     * <p>
055     * Turnout numbering is the same as seen on a Digitrax throttle display; Tools 
056     * using values from objects of this type must provide the appropriate transform
057     * to create turnout numbering which is suitable for use within LocoNet messaging.
058     */
059    public SimpleTurnoutStateEntry() {
060        this(1,true);
061    }
062
063    /**
064     * Constructor used when the current address and position are known.  Turnout
065     * "validity" is assumed to be "valid".
066     * <p>
067     * Turnout numbering is the same as seen on a Digitrax throttle display; Tools 
068     * using values from objects of this type must provide the appropriate transform
069     * to create turnout numbering which is suitable for use within LocoNet messaging.
070     * 
071     * @param address turnout address
072     * @param isClosed  true if turnout is closed, else false
073     */
074    public SimpleTurnoutStateEntry(Integer address, boolean isClosed) {
075        super(address,isClosed);
076        thrownRadioButton = new JRadioButton(Bundle.getMessage("TurnoutStateThrown"));
077        closedRadioButton = new JRadioButton(Bundle.getMessage("TurnoutStateClosed"));
078        unusedRadioButton = null;
079        addressField = new ValidatedTextField(5, true, 1, 2048, Bundle.getMessage("ErrorTextAddressInvalid"));
080        entryPanel = null;
081        addressField.setText(Integer.toString(address));
082        setAddressLastQueriedValue(address);
083        if (isClosed == true) {
084            thrownRadioButton.setSelected(false);
085            closedRadioButton.setSelected(true);
086        }
087        else {
088            thrownRadioButton.setSelected(true);
089            closedRadioButton.setSelected(false);
090        }
091        thrownRadioButton.addFocusListener(new java.awt.event.FocusListener() {
092
093            @Override
094            public void focusGained(java.awt.event.FocusEvent e) {
095                // eat this focus change event.
096            }
097
098            @Override
099            public void focusLost(java.awt.event.FocusEvent e) {
100                if (thrownRadioButton.isSelected()) {
101                    setIsClosed(false);
102                }
103            }
104        });
105
106        closedRadioButton.addFocusListener(new java.awt.event.FocusListener() {
107
108            @Override
109            public void focusGained(java.awt.event.FocusEvent e) {
110                // eat this focus change event.
111            }
112
113            @Override
114            public void focusLost(java.awt.event.FocusEvent e) {
115                if (closedRadioButton.isSelected()) {
116                    setIsClosed(true);
117                }
118            }
119        });
120    }
121    
122    /**
123     * Constructor used when the current address, position, and "validity" 
124     * state are known.
125     * <p>
126     * Turnout numbering is the same as seen on a Digitrax throttle display; Tools 
127     * using values from objects of this type must provide the appropriate transform
128     * to create turnout numbering which is suitable for use within LocoNet messaging.
129     * 
130     * @param address turnout address
131     * @param closed  true if turnout is closed, else false
132     * @param unused  true if turnout is unused, else false
133     */
134    public SimpleTurnoutStateEntry(Integer address, boolean closed, boolean unused) {
135        super(address, closed, unused);
136        thrownRadioButton = new JRadioButton(Bundle.getMessage("TurnoutStateThrown"));
137        closedRadioButton = new JRadioButton(Bundle.getMessage("TurnoutStateClosed"));
138        unusedRadioButton = new JRadioButton(Bundle.getMessage("RadioButtonTextUnused"));
139        addressField = new ValidatedTextField(5, true, 1, 2048, Bundle.getMessage("ErrorTextAddressInvalid"));
140        entryPanel = null;
141        if (unused) {
142            addressField.setText("");
143        } else {
144            addressField.setText(Integer.toString(address));
145        }
146        setAddressLastQueriedValue(address);
147        if (closed == true) {
148            thrownRadioButton.setSelected(false);
149            unusedRadioButton.setSelected(false);
150            closedRadioButton.setSelected(true);
151        }
152        else {
153            thrownRadioButton.setSelected(true);
154            unusedRadioButton.setSelected(false);
155            closedRadioButton.setSelected(false);
156        }
157        
158        thrownRadioButton.addFocusListener(new java.awt.event.FocusListener() {
159            @Override
160            public void focusGained(java.awt.event.FocusEvent e) {
161                // eat this focus change event.
162            }
163
164            @Override
165            public void focusLost(java.awt.event.FocusEvent e) {
166                if (thrownRadioButton.isSelected()) {
167                    setIsClosed(false);
168                }
169            }
170        });
171
172        closedRadioButton.addFocusListener(new java.awt.event.FocusListener() {
173            @Override
174            public void focusGained(java.awt.event.FocusEvent e) {
175                // eat this focus change event.
176            }
177
178            @Override
179            public void focusLost(java.awt.event.FocusEvent e) {
180                if (closedRadioButton.isSelected()) {
181                    setIsClosed(true);
182                }
183            }
184        });
185
186        unusedRadioButton.addFocusListener(new java.awt.event.FocusListener() {
187            @Override
188            public void focusGained(java.awt.event.FocusEvent e) {
189                // eat this focus change event.
190            }
191
192            @Override
193            public void focusLost(java.awt.event.FocusEvent e) {
194                if (unusedRadioButton.isSelected()) {
195                    setIsUnused();
196                    addressField.setText("");
197                }
198            }
199        });
200
201    }
202    
203    /**
204     *
205     * @return the JPanel related to this object
206     */
207    public JPanel getEntryPanel() { return this.entryPanel; }
208
209    /**
210     * Creates a GUI Panel for managing the address and position of a turnout, 
211     * as used in configuring the turnout address of a DS64 output.
212     *
213     * @param label a text string to be displayed in the JPanel with the turnout 
214     *              address and position 
215     * @return a JPanel containing the label, the turnout address text field, and 
216     *              position GUI elements
217     */
218    public JPanel createEntryPanel(String label) {
219        entryPanel = new JPanel();
220        entryPanel.setLayout(new FlowLayout());
221        entryPanel.add(new JLabel(label));
222        entryPanel.add(addressField);
223
224        JPanel p2 = new JPanel();
225        p2.setLayout(new BoxLayout(p2,BoxLayout.X_AXIS));
226        ButtonGroup g = new ButtonGroup();
227        
228        if (addressField.getText().length() == 0) {
229            closedRadioButton.setSelected(false);
230            thrownRadioButton.setSelected(false);
231            if (unusedRadioButton != null) {
232                unusedRadioButton.setSelected(true);
233            }
234            addressField.setText("");
235        } else if (Integer.parseInt(addressField.getText()) == 2048) {
236            closedRadioButton.setSelected(false);
237            thrownRadioButton.setSelected(false);
238            if (unusedRadioButton != null) {
239                unusedRadioButton.setSelected(true);
240            }
241            addressField.setText("");
242        }
243        else {
244            if (unusedRadioButton != null) {
245                unusedRadioButton.setSelected(false);
246            }
247            closedRadioButton.setSelected(getIsClosed());
248            thrownRadioButton.setSelected(!getIsClosed());
249        }
250
251        g.add(thrownRadioButton);
252        g.add(closedRadioButton);
253        p2.add(thrownRadioButton);
254        p2.add(closedRadioButton);
255        if (unusedRadioButton != null) {
256            g.add(unusedRadioButton);
257            p2.add(unusedRadioButton);
258        } 
259        entryPanel.add(p2);
260        return entryPanel;
261    }
262
263    /**
264     * Retrieve the GUI element which holds a turnout address
265     * @return turnout address
266     */
267    public ValidatedTextField getAddressField() {
268        return addressField;
269    }
270
271    @Override
272    public void setAddress(Integer addr) {
273        log.debug("simpleturnoutstateentry - setaddress {}", addr);
274        super.setAddress(addr);
275        if (isValid()) {
276            addressField.setText(String.valueOf(addr));
277        }
278        else {
279            addressField.setText("");
280        }
281        addressField.updateUI();
282    }
283    
284    /**
285     * Establish the most recent value known to be found in the hardware.  
286     * Value is used to help determine colorization of the swing GUI text field.
287     * 
288     * @param addr Turnout address
289     */
290    final public void setAddressLastQueriedValue(Integer addr) {
291        addressField.setLastQueriedValue(String.valueOf(addr));
292    }
293
294    @Override
295    public void setIsClosed(boolean isclosed) {
296        super.setIsClosed(isclosed);
297        closedRadioButton.setSelected(getIsClosed());
298        thrownRadioButton.setSelected(!getIsClosed());
299        closedRadioButton.updateUI();
300        thrownRadioButton.updateUI();
301        if (unusedRadioButton != null) {
302            unusedRadioButton.setSelected(false);
303            unusedRadioButton.updateUI();
304        }
305    }
306
307    private final static Logger log = LoggerFactory.getLogger(SimpleTurnoutStateEntry.class);
308
309}