001package jmri.jmrit.symbolicprog;
002
003import java.awt.event.ActionListener;
004import java.util.List;
005import javax.swing.BoxLayout;
006import javax.swing.JButton;
007import javax.swing.JComboBox;
008import javax.swing.JLabel;
009import javax.swing.JPanel;
010import javax.swing.border.EmptyBorder;
011import jmri.GlobalProgrammerManager;
012import jmri.InstanceManager;
013import jmri.Programmer;
014import jmri.jmrit.decoderdefn.DecoderFile;
015import jmri.jmrit.decoderdefn.DecoderIndexFile;
016import jmri.jmrit.decoderdefn.IdentifyDecoder;
017import jmri.jmrit.progsupport.ProgModeSelector;
018import jmri.jmrit.roster.Roster;
019import jmri.jmrit.roster.RosterEntry;
020import jmri.jmrit.roster.swing.GlobalRosterEntryComboBox;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024/**
025 * Provide GUI controls to select a decoder for a new loco and/or copy an
026 * existing config.
027 * <p>
028 * The user can select either a loco to copy, or a new decoder type or both.
029 * <p>
030 * When the "open programmer" button is pushed, i.e. the user is ready to
031 * continue, the startProgrammer method is invoked. This should be overridden
032 * (e.g. in a local anonymous class) to create the programmer frame you're
033 * interested in.
034 *
035 * @author Bob Jacobsen Copyright (C) 2001, 2002
036 * @author Howard G. Penny Copyright (C) 2005
037 * @see jmri.jmrit.decoderdefn.IdentifyDecoder
038 * @see jmri.jmrit.roster.IdentifyLoco
039 */
040public class NewLocoSelPane extends jmri.util.swing.JmriPanel {
041
042    public NewLocoSelPane(JLabel s, ProgModeSelector selector) {
043        _statusLabel = s;
044        this.selector = selector;
045        init();
046    }
047
048    public NewLocoSelPane() {
049        init();
050    }
051
052    ProgModeSelector selector;
053
054    public void init() {
055        JLabel last;
056        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
057        add(last = new JLabel(java.util.ResourceBundle.getBundle("jmri/jmrit/symbolicprog/SymbolicProgBundle").getString("NewLocoProgTrack")));
058        last.setBorder(new EmptyBorder(6, 0, 6, 0));
059        add(new JLabel(java.util.ResourceBundle.getBundle("jmri/jmrit/symbolicprog/SymbolicProgBundle").getString("CopySettings")));
060
061        locoBox = new GlobalRosterEntryComboBox();
062        locoBox.addActionListener(new ActionListener() {
063            @Override
064            public void actionPerformed(java.awt.event.ActionEvent e) {
065                if (log.isDebugEnabled()) {
066                    log.debug("Locomotive selected changed");
067                }
068                matchDecoderToLoco();
069            }
070        });
071        add(locoBox);
072
073        JPanel pane1a = new JPanel();
074        pane1a.setLayout(new BoxLayout(pane1a, BoxLayout.X_AXIS));
075        pane1a.add(new JLabel(java.util.ResourceBundle.getBundle("jmri/jmrit/symbolicprog/SymbolicProgBundle").getString("LabelDecoderInstalled")));
076        JButton iddecoder = new JButton(java.util.ResourceBundle.getBundle("jmri/jmrit/symbolicprog/SymbolicProgBundle").getString("IdentifyDecoder"));
077        iddecoder.addActionListener(new ActionListener() {
078            @Override
079            public void actionPerformed(java.awt.event.ActionEvent e) {
080                if (log.isDebugEnabled()) {
081                    log.debug("identify decoder pressed");
082                }
083                startIdentify();
084            }
085        });
086        pane1a.add(iddecoder);
087        pane1a.setAlignmentX(JLabel.LEFT_ALIGNMENT);
088        add(pane1a);
089
090        decoderBox = InstanceManager.getDefault(DecoderIndexFile.class).matchingComboBox(null, null, null, null, null, null);
091        add(decoderBox);
092
093        // Open programmer button
094        JButton go1 = new JButton(java.util.ResourceBundle.getBundle("jmri/jmrit/symbolicprog/SymbolicProgBundle").getString("IdentifyDecoder"));
095        go1.addActionListener(new ActionListener() {
096            @Override
097            public void actionPerformed(java.awt.event.ActionEvent e) {
098                if (log.isDebugEnabled()) {
099                    log.debug("Open programmer pressed");
100                }
101                openButton();
102            }
103        });
104        add(go1);
105        setBorder(new EmptyBorder(6, 6, 6, 6));
106    }
107
108    JLabel _statusLabel = null;
109
110    private void startIdentify() {
111        // start identifying a decoder
112        final NewLocoSelPane me = this;
113        Programmer p = null;
114        if (selector != null && selector.isSelected()) p = selector.getProgrammer();
115        if (p == null) {
116            log.warn("Selector did not provide a programmer, use default");
117            p = jmri.InstanceManager.getDefault(GlobalProgrammerManager.class).getGlobalProgrammer();
118        }
119        IdentifyDecoder id = new IdentifyDecoder(p) {
120            private NewLocoSelPane who = me;
121
122            @Override
123            protected void done(int mfg, int model, int productID) {
124                // if Done, updated the selected decoder
125                who.selectDecoder(mfg, model, productID);
126            }
127
128            @Override
129            protected void message(String m) {
130                if (_statusLabel != null) {
131                    _statusLabel.setText(m);
132                }
133            }
134
135            @Override
136            public void error() {
137            }
138        };
139        id.start();
140    }
141
142    private void selectDecoder(int mfgID, int modelID, int productID) {
143        JComboBox<String> temp = null;
144
145        // if productID present, try with that
146        if (productID != -1) {
147            String sz_productID = Integer.toString(productID);
148            temp = InstanceManager.getDefault(DecoderIndexFile.class).matchingComboBox(null, null, Integer.toString(mfgID), Integer.toString(modelID), sz_productID, null);
149            if (temp.getItemCount() == 0) {
150                log.debug("selectDecoder found no items with product ID {}", productID);
151                temp = null;
152            } else {
153                log.debug("selectDecoder found {} matches with productID {}", temp.getItemCount(), productID);
154            }
155        }
156
157        // try without product ID if needed
158        if (temp == null) {  // i.e. if no match previously
159            temp = InstanceManager.getDefault(DecoderIndexFile.class).matchingComboBox(null, null, Integer.toString(mfgID), Integer.toString(modelID), null, null);
160            if (log.isDebugEnabled()) {
161                log.debug("selectDecoder without productID found {} matches", temp.getItemCount());
162            }
163        }
164
165        // install all those in the JComboBox in place of the longer, original list
166        if (temp.getItemCount() > 0) {
167            decoderBox.setModel(temp.getModel());
168            decoderBox.setSelectedIndex(0);
169        } else {
170            log.warn("Decoder says {} {} decoder, but no such decoder defined", mfgID, modelID);
171        }
172    }
173
174    private void matchDecoderToLoco() {
175        if (((String) locoBox.getSelectedItem()).equals("<none>")) {
176            return;
177        }
178        RosterEntry r = Roster.getDefault().entryFromTitle((String) locoBox.getSelectedItem());
179        String decoderModel = r.getDecoderModel();
180        String decoderFamily = r.getDecoderFamily();
181        if (log.isDebugEnabled()) {
182            log.debug("selected loco uses decoder {} {}", decoderFamily, decoderModel);
183        }
184        // locate a decoder like that.
185        List<DecoderFile> l = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, decoderFamily, null, null, null, decoderModel);
186        if (log.isDebugEnabled()) {
187            log.debug("found {} matches", l.size());
188        }
189        if (l.size() > 0) {
190            DecoderFile d = l.get(0);
191            String title = d.titleString();
192            if (log.isDebugEnabled()) {
193                log.debug("Decoder file title {}", title);
194            }
195            for (int i = 0; i < decoderBox.getItemCount(); i++) {
196                if (title.equals(decoderBox.getItemAt(i))) {
197                    decoderBox.setSelectedIndex(i);
198                }
199            }
200        } else {
201            log.warn("Loco uses {} {} decoder, but no such decoder defined", decoderFamily, decoderModel);
202        }
203    }
204
205    private JComboBox<Object> locoBox = null;
206    private JComboBox<String> decoderBox = null;
207
208    /**
209     * Handle pushing the open programmer button by finding names, then calling
210     * a template method
211     */
212    protected void openButton() {
213
214        // find the decoderFile object
215        DecoderFile decoderFile = InstanceManager.getDefault(DecoderIndexFile.class).fileFromTitle((String) decoderBox.getSelectedItem());
216        if (log.isDebugEnabled()) {
217            log.debug("decoder file: {}", decoderFile.getFileName());
218        }
219
220        // create a dummy RosterEntry with the decoder info
221        RosterEntry re = new RosterEntry();
222        re.setDecoderFamily(decoderFile.getFamily());
223        re.setDecoderModel(decoderFile.getModel());
224        re.setId(Bundle.getMessage("LabelNewDecoder"));
225        // note we're leaving the filename information as null
226        // add the new roster entry to the in-memory roster
227        Roster.getDefault().addEntry(re);
228
229        startProgrammer(decoderFile, re);
230    }
231
232    /**
233     * Meant to be overridden to start the desired type of programmer
234     *
235     * @param decoderFile selected file, passed to eventual implementation
236     * @param r           RosterEntry defining this locomotive, to be filled in
237     *                    later
238     */
239    protected void startProgrammer(DecoderFile decoderFile, RosterEntry r) {
240        log.error("startProgrammer method in NewLocoSelPane should have been overridden");
241    }
242
243    private final static Logger log = LoggerFactory.getLogger(NewLocoSelPane.class);
244
245}