001package jmri.jmrit.symbolicprog;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.awt.event.FocusEvent;
006import java.awt.event.FocusListener;
007import java.util.ResourceBundle;
008import javax.swing.BoxLayout;
009import javax.swing.JLabel;
010import javax.swing.JPanel;
011import javax.swing.JTextField;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * Provide a graphical representation of the DCC address, either long or short
017 *
018 * <p>
019 * Expects one or more of the variables called:
020 * <ul>
021 * <li>Short Address
022 * <li>Long Address
023 * <li>Address Format (an Enum variable to select)
024 * </ul>
025 * and handles the cases where:
026 * <ul>
027 * <li>All three are present - the normal advanced decoder case
028 * <li>Short Address is present and Long Address is not
029 * <li>Long Address is present and Short Address is not
030 * </ul>
031 * At least one of Short Address and Long Address must be present!
032 *
033 * @author Bob Jacobsen Copyright (C) 2001, 2012
034 */
035public class DccAddressPanel extends JPanel {
036
037    JTextField val = new JTextField(6);
038
039    VariableValue primaryAddr = null;
040    VariableValue extendAddr = null;
041    EnumVariableValue addMode = null;
042
043    VariableTableModel variableModel = null;
044
045    /**
046     * Ctor using default label for the address.
047     *
048     * @param mod The current table of variables, used to locate the status
049     *            information needed.
050     */
051    public DccAddressPanel(VariableTableModel mod) {
052        this(mod, ResourceBundle.getBundle("jmri.jmrit.symbolicprog.SymbolicProgBundle").getString("TextDccAddress"));
053    }
054
055    public DccAddressPanel(VariableTableModel mod, String label) {
056        variableModel = mod;
057
058        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
059
060        // arrange for the field to be updated when any of the variables change
061        java.beans.PropertyChangeListener dccNews = new java.beans.PropertyChangeListener() {
062            @Override
063            public void propertyChange(java.beans.PropertyChangeEvent e) {
064                updateDccAddress();
065            }
066        };
067
068        // connect to variables
069        primaryAddr = variableModel.findVar("Short Address");
070        if (primaryAddr == null) {
071            log.debug("DCC Address monitor did not find a Short Address variable");
072        } else {
073            primaryAddr.addPropertyChangeListener(dccNews);
074        }
075
076        extendAddr = variableModel.findVar("Long Address");
077        if (extendAddr == null) {
078            log.debug("DCC Address monitor did not find an Long Address variable");
079        } else {
080            extendAddr.addPropertyChangeListener(dccNews);
081        }
082
083        addMode = (EnumVariableValue) variableModel.findVar("Address Format");
084        if (addMode == null) {
085            log.debug("DCC Address monitor didnt find an Address Format variable");
086        } else {
087            addMode.addPropertyChangeListener(dccNews);
088        }
089
090        // show the selection
091        if (addMode != null) {
092            add(addMode.getNewRep("radiobuttons"));
093        }
094
095        // show address field
096        add(new JLabel(label));
097        val.setToolTipText(ResourceBundle.getBundle("jmri.jmrit.symbolicprog.SymbolicProgBundle").getString("ToolTipDccAddress"));
098        add(val);
099
100        new DccAddressVarHandler(primaryAddr, extendAddr, addMode) {
101            @Override
102            protected void doPrimary() {
103                // short address commonRep will be JTextField if variable, JLabel if constant
104                JTextField f;
105                if (primaryAddr.getCommonRep() instanceof JTextField) {
106                    f = (JTextField) primaryAddr.getCommonRep();
107                } else {
108                    f = new JTextField();
109                    f.setText(((JLabel) primaryAddr.getCommonRep()).getText());
110                }
111                val.setBackground(primaryAddr.getCommonRep().getBackground());
112                val.setDocument(f.getDocument());
113            }
114
115            @Override
116            protected void doExtended() {
117                // long address commonRep will be JTextField if variable, JLabel if constant
118                JTextField f;
119                if (extendAddr.getCommonRep() instanceof JTextField) {
120                    f = (JTextField) extendAddr.getCommonRep();
121                } else {
122                    f = new JTextField();
123                    f.setText(((JLabel) extendAddr.getCommonRep()).getText());
124                }
125                val.setBackground(extendAddr.getCommonRep().getBackground());
126                val.setDocument(f.getDocument());
127            }
128        };
129
130        // start listening for changes to this value
131        val.addActionListener(new ActionListener() {
132            @Override
133            public void actionPerformed(ActionEvent e) {
134                new DccAddressVarHandler(primaryAddr, extendAddr, addMode) {
135                    @Override
136                    protected void doPrimary() {
137                        // short address mode
138                        primaryAddr.updatedTextField();
139                        val.setBackground(primaryAddr.getCommonRep().getBackground());
140                        if (log.isDebugEnabled()) {
141                            log.debug("set color: {}", primaryAddr.getCommonRep().getBackground());
142                        }
143                    }
144
145                    @Override
146                    protected void doExtended() {
147                        // long address
148                        extendAddr.updatedTextField();
149                        val.setBackground(extendAddr.getCommonRep().getBackground());
150                        if (log.isDebugEnabled()) {
151                            log.debug("set color: {}", extendAddr.getCommonRep().getBackground());
152                        }
153                    }
154                };
155            }
156        });
157        val.addFocusListener(new FocusListener() {
158            @Override
159            public void focusGained(FocusEvent e) {
160                if (log.isDebugEnabled()) {
161                    log.debug("focusGained");
162                }
163                enterField();
164            }
165
166            @Override
167            public void focusLost(FocusEvent e) {
168                if (log.isDebugEnabled()) {
169                    log.debug("focusLost");
170                }
171                exitField();
172            }
173        });
174
175    }
176
177    String oldContents = "";
178
179    /**
180     * Handle focus entering the address field by recording the contents.
181     */
182    void enterField() {
183        oldContents = val.getText();
184    }
185
186    /**
187     * Handle focus leaving the address field by checking to see if the contents
188     * changed. We do this because we want to record that change even if it
189     * hasn't been "entered" via return key et al.
190     */
191    void exitField() {
192        if (!oldContents.equals(val.getText())) {
193            new DccAddressVarHandler(primaryAddr, extendAddr, addMode) {
194                @Override
195                protected void doPrimary() {
196                    // short address mode
197                    primaryAddr.updatedTextField();
198                    val.setBackground(primaryAddr.getCommonRep().getBackground());
199                    if (log.isDebugEnabled()) {
200                        log.debug("set color: {}", primaryAddr.getCommonRep().getBackground());
201                    }
202                }
203
204                @Override
205                protected void doExtended() {
206                    // long address
207                    extendAddr.updatedTextField();
208                    val.setBackground(extendAddr.getCommonRep().getBackground());
209                    if (log.isDebugEnabled()) {
210                        log.debug("set color: {}", extendAddr.getCommonRep().getBackground());
211                    }
212                }
213            };
214        }
215    }
216
217    /**
218     * Handle a (possible) update to the active DCC address, either because the
219     * state changed or the address mode changed. Note that value changes of the
220     * active address are directly reflected, so we don't have to do anything on
221     * those, but we still go ahead and update the state color.
222     */
223    void updateDccAddress() {
224        if (log.isDebugEnabled()) {
225            log.debug("updateDccAddress: short {} long {} mode {}", primaryAddr == null ? "<null>" : primaryAddr.getValueString(), extendAddr == null ? "<null>" : extendAddr.getValueString(), addMode == null ? "<null>" : addMode.getValueString());
226        }
227        new DccAddressVarHandler(primaryAddr, extendAddr, addMode) {
228            @Override
229            protected void doPrimary() {
230                // short address commonRep will be JTextField if variable, JLabel if constant
231                JTextField f;
232                if (primaryAddr.getCommonRep() instanceof JTextField) {
233                    f = (JTextField) primaryAddr.getCommonRep();
234                } else {
235                    f = new JTextField();
236                    f.setText(((JLabel) primaryAddr.getCommonRep()).getText());
237                }
238                val.setBackground(primaryAddr.getCommonRep().getBackground());
239                val.setDocument(f.getDocument());
240                if (log.isDebugEnabled()) {
241                    log.debug("set color: {}", primaryAddr.getCommonRep().getBackground());
242                }
243            }
244
245            @Override
246            protected void doExtended() {
247                // long address commonRep will be JTextField if variable, JLabel if constant
248                JTextField f;
249                if (extendAddr.getCommonRep() instanceof JTextField) {
250                    f = (JTextField) extendAddr.getCommonRep();
251                } else {
252                    f = new JTextField();
253                    f.setText(((JLabel) extendAddr.getCommonRep()).getText());
254                }
255                val.setBackground(extendAddr.getCommonRep().getBackground());
256                val.setDocument(f.getDocument());
257                if (log.isDebugEnabled()) {
258                    log.debug("set color: {}", extendAddr.getCommonRep().getBackground());
259                }
260            }
261        };
262    }
263
264    // initialize logging
265    private final static Logger log = LoggerFactory.getLogger(DccAddressPanel.class);
266
267}