001package jmri.jmrit.consisttool;
002
003import java.awt.FlowLayout;
004import java.awt.event.ActionEvent;
005import java.awt.event.KeyEvent;
006import java.awt.event.KeyListener;
007import java.beans.PropertyChangeEvent;
008import java.io.IOException;
009import java.util.List;
010import java.util.ArrayList;
011import javax.swing.*;
012
013import jmri.Consist;
014import jmri.ConsistListListener;
015import jmri.ConsistListener;
016import jmri.ConsistManager;
017import jmri.LocoAddress;
018import jmri.DccLocoAddress;
019import jmri.InstanceManager;
020import jmri.jmrit.DccLocoAddressSelector;
021import jmri.jmrit.roster.swing.GlobalRosterEntryComboBox;
022import jmri.jmrit.roster.swing.RosterEntryComboBox;
023import jmri.jmrit.roster.Roster;
024import jmri.jmrit.roster.RosterEntry;
025import jmri.jmrit.symbolicprog.CvTableModel;
026import jmri.jmrit.symbolicprog.CvValue;
027import jmri.jmrit.throttle.ThrottleFrameManager;
028import jmri.jmrit.throttle.interfaces.ThrottleControllerUI;
029import jmri.util.JmriJFrame;
030import jmri.util.gui.GuiLafPreferencesManager;
031import jmri.util.swing.JmriJOptionPane;
032
033import org.jdom2.JDOMException;
034
035/**
036 * Frame object for manipulating consists.
037 *
038 * @author Paul Bender Copyright (C) 2003-2008
039 */
040public class ConsistToolFrame extends JmriJFrame implements ConsistListener, ConsistListListener {
041
042    // GUI member declarations
043    JLabel textAdrLabel = new JLabel();
044    DccLocoAddressSelector adrSelector = new DccLocoAddressSelector();
045    ConsistComboBox consistComboBox;
046    JRadioButton isAdvancedConsist = new JRadioButton(Bundle.getMessage("AdvancedConsistButtonText"));
047    JRadioButton isCSConsist = new JRadioButton(Bundle.getMessage("CommandStationConsistButtonText"));
048    JButton deleteButton = new JButton();
049    JButton throttleButton = new JButton();
050    JButton reverseButton = new JButton();
051    JButton restoreButton = new JButton();
052    JLabel textLocoLabel = new JLabel();
053    DccLocoAddressSelector locoSelector = new DccLocoAddressSelector();
054    RosterEntryComboBox locoRosterBox;
055    JButton addLocoButton = new JButton();
056    JButton resetLocoButton = new JButton();
057    JCheckBox locoDirectionNormal = new JCheckBox(Bundle.getMessage("DirectionNormalText"));
058    ConsistDataModel consistModel = new ConsistDataModel();
059    JTable consistTable = new JTable(consistModel);
060    ConsistManager consistManager = null;
061    JLabel _status = new JLabel(Bundle.getMessage("DefaultStatusText"));
062    private int _Consist_Type = Consist.ADVANCED_CONSIST;
063    private ConsistFile consistFile = null;
064
065    public ConsistToolFrame() {
066        super();
067        init();
068        initGUI();
069    }
070
071    private void init() {
072        consistManager = InstanceManager.getDefault(jmri.ConsistManager.class);
073
074        consistFile = new ConsistFile();
075        try {
076            consistFile.readFile();
077        } catch (IOException | JDOMException e) {
078            log.warn("error reading consist file: {}", e.getMessage());
079        }
080
081        // register to be notified if the consist list changes.
082        consistManager.addConsistListListener(this);
083
084        // request an update from the layout.
085        consistManager.requestUpdateFromLayout();
086    }
087
088    private void initGUI() {
089        // configure items for GUI
090        textAdrLabel.setText(Bundle.getMessage("AddressLabelText"));
091        textAdrLabel.setVisible(true);
092
093        adrSelector.setVisible(true);
094        adrSelector.setToolTipText(Bundle.getMessage("AddressSelectorToolTip"));
095        textAdrLabel.setLabelFor(adrSelector);
096
097        initializeConsistBox();
098
099        consistComboBox = new ConsistComboBox();
100        consistComboBox.addActionListener((ActionEvent e) -> consistSelected());
101
102        if (consistManager.isAdvancedConsistPossible()) {
103            isAdvancedConsist.setSelected(true);
104            isAdvancedConsist.setVisible(true);
105            isAdvancedConsist.setEnabled(false);
106            isAdvancedConsist.addActionListener((ActionEvent e) -> {
107                isAdvancedConsist.setSelected(true);
108                isCSConsist.setSelected(false);
109                _Consist_Type = Consist.ADVANCED_CONSIST;
110                adrSelector.setEnabled(true);
111            });
112            isCSConsist.setSelected(false);
113        } else {
114            isAdvancedConsist.setSelected(false);
115            isAdvancedConsist.setVisible(false);
116            isCSConsist.setSelected(true);
117            _Consist_Type = Consist.CS_CONSIST;
118            adrSelector.setEnabled((consistManager.csConsistNeedsSeperateAddress()));
119            if (! consistManager.csConsistNeedsSeperateAddress()) {
120                textAdrLabel.setText("");                
121            }
122        }
123
124        isCSConsist.setVisible(true);
125        isCSConsist.setEnabled(false);
126        isCSConsist.addActionListener((ActionEvent e) -> {
127            isAdvancedConsist.setSelected(false);
128            isCSConsist.setSelected(true);
129            _Consist_Type = Consist.CS_CONSIST;
130            adrSelector.setEnabled((consistManager.csConsistNeedsSeperateAddress()));
131            if (! consistManager.csConsistNeedsSeperateAddress()) {
132                textAdrLabel.setText("");                
133            }            
134        });
135
136        if (consistManager.isCommandStationConsistPossible()) {
137            isAdvancedConsist.setEnabled(true);
138            isCSConsist.setEnabled(true);
139        }
140
141        // link the protocol selectors if required by ConsistManager
142        if (consistManager.isSingleFormConsistRequired()) {
143            locoSelector.followAnotherSelector(adrSelector);
144        }
145        
146        deleteButton.setText(Bundle.getMessage("ButtonDelete"));
147        deleteButton.setVisible(true);
148        deleteButton.setToolTipText(Bundle.getMessage("DeleteButtonToolTip"));
149        deleteButton.addActionListener(this::deleteButtonActionPerformed);
150
151        throttleButton.setText(Bundle.getMessage("ThrottleButtonText"));
152        throttleButton.setVisible(true);
153        throttleButton.setToolTipText(Bundle.getMessage("ThrottleButtonToolTip"));
154        throttleButton.addActionListener(this::throttleButtonActionPerformed);
155
156        reverseButton.setText(Bundle.getMessage("ReverseButtonText"));
157        reverseButton.setVisible(true);
158        reverseButton.setToolTipText(Bundle.getMessage("ReverseButtonToolTip"));
159        reverseButton.addActionListener(this::reverseButtonActionPerformed);
160
161        restoreButton.setText(Bundle.getMessage("RestoreButtonText"));
162        restoreButton.setVisible(true);
163        restoreButton.setToolTipText(Bundle.getMessage("RestoreButtonToolTip"));
164        restoreButton.addActionListener(this::restoreButtonActionPerformed);
165
166        // Set up the controls for the First Locomotive in the consist.
167        textLocoLabel.setText(Bundle.getMessage("LocoLabelText"));
168        textLocoLabel.setVisible(true);
169
170        locoSelector.setToolTipText(Bundle.getMessage("LocoSelectorToolTip"));
171        locoSelector.setVisible(true);
172        textLocoLabel.setLabelFor(locoSelector);
173
174        locoSelector.addKeyListener(new KeyListener() {
175            @Override
176            public void keyPressed(KeyEvent e) {
177                if ( !consistManager.isSingleFormConsistRequired()) {
178                    // if combo boxes are not locked together, 
179                    // and if user start typing, set the selected index of the locoRosterbox to nothing
180                    // to get the user to make a decision
181                    locoRosterBox.setSelectedIndex(0);
182                }
183            }
184
185            @Override
186            public void keyTyped(KeyEvent e) {
187                // only handling key presses
188            }
189
190            @Override
191            public void keyReleased(KeyEvent e) {
192                // only handling key presses
193            }
194        });
195
196        locoRosterBox = new GlobalRosterEntryComboBox();
197        locoRosterBox.setNonSelectedItem("");
198        locoRosterBox.setSelectedIndex(0);
199
200        locoRosterBox.addPropertyChangeListener("selectedRosterEntries", (PropertyChangeEvent pce) -> locoSelected());
201
202        locoRosterBox.setVisible(true);
203
204        locoDirectionNormal.setToolTipText(Bundle.getMessage("DirectionNormalToolTip"));
205
206        locoDirectionNormal.setSelected(true);
207        locoDirectionNormal.setVisible(true);
208        locoDirectionNormal.setEnabled(false);
209
210        addLocoButton.setText(Bundle.getMessage("ButtonAddText"));
211        addLocoButton.setVisible(true);
212        addLocoButton.setToolTipText(Bundle.getMessage("AddButtonToolTip"));
213        addLocoButton.addActionListener(this::addLocoButtonActionPerformed);
214
215        resetLocoButton.setText(Bundle.getMessage("ButtonReset"));
216        resetLocoButton.setVisible(true);
217        resetLocoButton.setToolTipText(Bundle.getMessage("ResetButtonToolTip"));
218        resetLocoButton.addActionListener(this::resetLocoButtonActionPerformed);
219
220        // general GUI config
221        setTitle(Bundle.getMessage("ConsistToolTitle"));
222        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
223
224        JMenuBar menuBar = new JMenuBar();
225        setJMenuBar(menuBar);
226
227        // add a "File" menu
228        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
229        menuBar.add(fileMenu);
230
231        // Add a save item
232        fileMenu.add(new AbstractAction(Bundle.getMessage("ScanConsists")) {
233            @Override
234            public void actionPerformed(ActionEvent e) {
235                scanRoster();
236                initializeConsistBox();
237                consistModel.fireTableDataChanged();
238                resetLocoButtonActionPerformed(e);
239            }
240        });
241
242        // install items in GUI
243        // The address and related buttons are installed in a single pane
244        JPanel addressPanel = new JPanel();
245        addressPanel.setLayout(new FlowLayout());
246
247        addressPanel.add(isAdvancedConsist);
248        addressPanel.add(isCSConsist);
249        addressPanel.add(textAdrLabel);
250        addressPanel.add(adrSelector.getCombinedJPanel());
251        addressPanel.add(consistComboBox);
252
253        getContentPane().add(addressPanel);
254
255        // The address and related buttons for each Locomotive
256        // are installed in a single pane
257        // New Locomotive
258        JPanel locoPanel = new JPanel();
259        locoPanel.setLayout(new FlowLayout());
260
261        locoPanel.add(textLocoLabel);
262
263        locoPanel.add(locoSelector.getCombinedJPanel());
264
265        locoPanel.add(locoRosterBox);
266        locoPanel.add(locoDirectionNormal);
267
268        locoPanel.add(addLocoButton);
269        locoPanel.add(resetLocoButton);
270
271        getContentPane().add(locoPanel);
272
273        // setup the consist table
274        consistTable.setRowHeight(InstanceManager.getDefault(GuiLafPreferencesManager.class).getFontSize()*2 + 4);
275        // Set up the jtable in a Scroll Pane..
276        JScrollPane consistPane = new JScrollPane(consistTable);
277        consistPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
278        consistModel.initTable(consistTable);
279        getContentPane().add(consistPane);
280
281        // Set up the Control Button panel
282        JPanel controlPanel = new JPanel();
283        controlPanel.setLayout(new FlowLayout());
284
285        controlPanel.add(deleteButton);
286        controlPanel.add(throttleButton);
287        controlPanel.add(reverseButton);
288        controlPanel.add(restoreButton);
289
290        getContentPane().add(controlPanel);
291
292        // add the status line directly to the bottom of the ContentPane.
293        JPanel statusPanel = new JPanel();
294        statusPanel.setLayout(new FlowLayout());
295        statusPanel.add(_status);
296        getContentPane().add(statusPanel);
297
298        addHelpMenu("package.jmri.jmrit.consisttool.ConsistToolFrame", true);
299        pack();
300
301    }
302
303    private void initializeConsistBox() {
304        ArrayList<LocoAddress> existingConsists = consistManager.getConsistList();
305        if (!existingConsists.isEmpty()) {
306            java.util.Collections.sort(existingConsists, new jmri.util.LocoAddressComparator()); // sort the consist list.
307            if (adrSelector.getAddress() != null) {
308                if (consistModel.getConsist() != null) {
309                    consistModel.getConsist().removeConsistListener(this);
310                    setDefaultStatus();
311                }
312                consistModel.setConsist(adrSelector.getAddress());
313                consistModel.getConsist().addConsistListener(this);
314                adrSelector.setEnabled(false);
315            } else {
316                if (consistModel.getConsist() != null) {
317                    consistModel.getConsist().removeConsistListener(this);
318                    setDefaultStatus();
319                }
320                consistModel.setConsist((Consist) null);
321                adrSelector.setEnabled(true);
322            }
323        } else {
324            if (consistModel.getConsist() != null) {
325                consistModel.getConsist().removeConsistListener(this);
326                setDefaultStatus();
327            }
328            consistModel.setConsist((Consist) null);
329            adrSelector.setEnabled(true);
330        }
331    }
332
333    public void deleteButtonActionPerformed(ActionEvent e) {
334        if (adrSelector.getAddress() == null) {
335            reportNoConsistSeletected();
336            return;
337        }
338        DccLocoAddress address = adrSelector.getAddress();
339        consistManager.getConsist(address);
340        // confirm delete
341        if (JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("DeleteWarningDialog", address),
342                Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,
343                JmriJOptionPane.QUESTION_MESSAGE) != JmriJOptionPane.YES_OPTION ) {
344            return; // do not delete
345        }
346        try {
347            adrSelector.reset();
348            consistManager.delConsist(address);
349        } catch (Exception ex) {
350            log.error("Error deleting consist {}", address, ex);
351        }        
352        adrSelector.setEnabled(true);
353        initializeConsistBox();
354        resetLocoButtonActionPerformed(e);
355        canAdd();
356    }
357
358    public void throttleButtonActionPerformed(ActionEvent e) {
359        if (adrSelector.getAddress() == null) {
360            reportNoConsistSeletected();
361            return;
362        }
363        // make sure any new locomotives are added to the consist.
364        addLocoButtonActionPerformed(e);
365        // Create a throttle object with the
366        ThrottleControllerUI tf
367                = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame();        
368
369        // Notify the throttle of the selected consist address
370        tf.setConsistAddress(adrSelector.getAddress());
371        tf.toFront();
372    }
373
374    public void reverseButtonActionPerformed(ActionEvent e) {
375        if (adrSelector.getAddress() == null) {
376            reportNoConsistSeletected();
377            return;
378        }
379        // make sure any new locomotives are added to the consist.
380        addLocoButtonActionPerformed(e);
381
382        /*
383         * get the array list of the locomotives in the consist
384         */
385        DccLocoAddress address = adrSelector.getAddress();
386        Consist tempConsist = consistManager.getConsist(address);
387        tempConsist.reverse();
388        consistManager.notifyConsistListChanged();
389    }
390
391    public void restoreButtonActionPerformed(ActionEvent e) {
392        if (adrSelector.getAddress() == null) {
393            reportNoConsistSeletected();
394            return;
395        }
396        // make sure any new locomotives are added to the consist.
397        addLocoButtonActionPerformed(e);
398
399        /*
400         * get the array list of the locomotives in the consist
401         */
402        DccLocoAddress address = adrSelector.getAddress();
403        Consist tempConsist = consistManager.getConsist(address);
404        tempConsist.restore();
405        consistManager.notifyConsistListChanged();
406    }
407
408    public void consistSelected() {
409        log.debug("Consist Selected");
410        if (consistComboBox.getSelectedIndex() == -1 && adrSelector.getAddress() != null) {
411            log.debug("No Consist Selected");
412            adrSelector.setEnabled(false);
413            recallConsist();
414        } else if (consistComboBox.getSelectedIndex() == -1
415                || consistComboBox.getSelectedItem().equals("") 
416                || consistComboBox.getSelectedItem().equals(Bundle.getMessage("NoConsistSelected"))) {
417            log.debug("Null Consist Selected");
418            adrSelector.reset();
419            adrSelector.setEnabled(true);
420            recallConsist();
421        } else if (((DccLocoAddress) consistComboBox.getSelectedItem()) != adrSelector.getAddress()) {
422            log.debug("Consist {} consistComboBox", consistComboBox.getSelectedItem());
423            adrSelector.setEnabled(false);
424            adrSelector.setAddress((DccLocoAddress) consistComboBox.getSelectedItem());
425            recallConsist();
426        }
427    }
428
429    // Recall the consist
430    private void recallConsist() {
431        if (adrSelector.getAddress() == null) {
432            // Clear any consist information that was present
433            locoSelector.reset();
434            locoRosterBox.setSelectedIndex(0);
435            if (consistModel.getConsist() != null) {
436                consistModel.getConsist().removeConsistListener(this);
437                setDefaultStatus();
438            }
439            consistModel.setConsist((Consist) null);
440
441            canAdd();
442
443            return;
444        }
445        DccLocoAddress address = adrSelector.getAddress();
446        if (consistModel.getConsist() != null) {
447            consistModel.getConsist().removeConsistListener(this);
448            _status.setText(Bundle.getMessage("DefaultStatusText"));
449            setDefaultStatus();
450        }
451        Consist selectedConsist = consistManager.getConsist(address);
452        consistModel.setConsist(selectedConsist);
453        selectedConsist.addConsistListener(this);
454
455        // reset the editable locomotive information.
456        locoSelector.reset();
457        locoRosterBox.setSelectedIndex(0);
458        locoDirectionNormal.setSelected(true);
459
460        // if there aren't any locomotives in the consist, don't let
461        // the user change the direction
462        locoDirectionNormal.setEnabled(consistModel.getRowCount()!=0);
463
464        log.debug("Recall Consist {}", address);
465
466        // What type of consist is this?
467        if (selectedConsist.getConsistType() == Consist.ADVANCED_CONSIST) {
468            log.debug("Consist type is Advanced Consist ");
469            isAdvancedConsist.setSelected(true);
470            isCSConsist.setSelected(false);
471            _Consist_Type = Consist.ADVANCED_CONSIST;
472        } else {
473            // This must be a CS Consist.
474            log.debug("Consist type is Command Station Consist ");
475            isAdvancedConsist.setSelected(false);
476            isCSConsist.setSelected(true);
477            _Consist_Type = Consist.CS_CONSIST;
478        }
479
480        canAdd();
481    }
482
483    public void resetLocoButtonActionPerformed(ActionEvent e) {
484        locoSelector.reset();
485        locoRosterBox.setSelectedIndex(0);
486        locoDirectionNormal.setSelected(true);
487        // if there aren't any locomotives in the consist, don't let
488        // the user change the direction
489        locoDirectionNormal.setEnabled(consistModel.getRowCount() != 0);
490    }
491
492    // Check to see if a consist address is selected, and if it
493    // is, disable the "add button" if the maximum consist size is reached
494    public void canAdd() {
495        // If a consist address is selected, disable the "add button"
496        // if the maximum size is reached
497        if (adrSelector.getAddress() != null) {
498            DccLocoAddress address = adrSelector.getAddress();
499            if (consistModel.getRowCount() == consistManager.getConsist(address).sizeLimit()) {
500                locoSelector.setEnabled(false);
501                locoRosterBox.setEnabled(false);
502                addLocoButton.setEnabled(false);
503                resetLocoButton.setEnabled(false);
504                locoDirectionNormal.setEnabled(false);
505            } else {
506                enableGuiControls();
507            }
508        } else {
509            enableGuiControls();
510        }
511    }
512
513    private void enableGuiControls(){
514        locoSelector.setEnabled(true);
515        locoRosterBox.setEnabled(true);
516        addLocoButton.setEnabled(true);
517        resetLocoButton.setEnabled(true);
518        locoDirectionNormal.setEnabled(false);
519        // if there aren't any locomotives in the consist, don't let
520        // the user change the direction
521        locoDirectionNormal.setEnabled(consistModel.getRowCount() != 0);
522    }
523
524    public void addLocoButtonActionPerformed(ActionEvent e) {
525        if (locoSelector.getAddress() == null) {
526            return;
527        }
528        if (_Consist_Type == Consist.ADVANCED_CONSIST && adrSelector.getAddress() == null) {
529            reportNoConsistSeletected();
530            return;
531        } else if (_Consist_Type == Consist.ADVANCED_CONSIST
532                && adrSelector.getAddress().isLongAddress()) {
533            JmriJOptionPane.showMessageDialog(this,
534                    Bundle.getMessage("RequiresShortConsistError"));
535            return;
536        } else if (_Consist_Type == Consist.CS_CONSIST && adrSelector.getAddress() == null) {
537            if (consistManager.csConsistNeedsSeperateAddress()) {
538                reportNoConsistSeletected();
539                return;
540            } else {
541                // We need to set an identifier so we can recall the
542                // consist.  We're going to use the lead locomotive number
543                // for this.
544                adrSelector.setAddress(locoSelector.getAddress());
545            }
546        }
547        DccLocoAddress address = adrSelector.getAddress();
548        /*
549         * Make sure the marked consist type matches the consist type stored for
550         * this consist
551         */
552        if (_Consist_Type != consistManager.getConsist(address).getConsistType()) {
553            if (log.isDebugEnabled()) {
554                if (_Consist_Type == Consist.ADVANCED_CONSIST) {
555                    log.debug("Setting Consist Type to Advanced Consist");
556                } else if (_Consist_Type == Consist.CS_CONSIST) {
557                    log.debug("Setting Consist Type to Command Station Assisted Consist");
558                }
559            }
560            consistManager.getConsist(address).setConsistType(_Consist_Type);
561        }
562
563        DccLocoAddress locoaddress = locoSelector.getAddress();
564
565        // Make sure the Address in question is allowed for this type of
566        // consist, and add it to the consist if it is
567        if (!consistManager.getConsist(address).isAddressAllowed(locoaddress)) {
568            JmriJOptionPane.showMessageDialog(this,
569                    Bundle.getMessage("AddressNotAllowedError"));
570            return;
571        }
572        if (consistManager.getConsist(address).contains(locoaddress)) {
573            JmriJOptionPane.showMessageDialog(this,
574                    Bundle.getMessage("AddressAlreadyInConsistError"));
575            return;
576        } 
577            
578        Consist tempConsist = consistManager.getConsist(address);
579        tempConsist.add(locoaddress, locoDirectionNormal.isSelected());
580        
581        // Try to get a roster entry
582        RosterEntry re = null;
583        if (locoRosterBox.getSelectedRosterEntries().length == 1) {
584            re = locoRosterBox.getSelectedRosterEntries()[0];
585        } else {
586            List<RosterEntry> res = Roster.getDefault().matchingList(null, null, "" + locoaddress.getNumber(), null, null, null, null);
587            if (!res.isEmpty()) {
588                re = res.get(0);
589            }
590        }
591                        
592        if (re != null) {    
593            tempConsist.setRosterId(locoaddress, re.titleString());
594        }        
595            
596        if (consistComboBox.getSelectedItem() != adrSelector.getAddress()) {
597            initializeConsistBox();
598            consistComboBox.setSelectedItem(adrSelector.getAddress());
599        }
600        consistManager.notifyConsistListChanged();
601        consistModel.fireTableDataChanged();
602        resetLocoButtonActionPerformed(e);        
603    }
604
605    public void locoSelected() {
606        if (locoRosterBox.getSelectedRosterEntries().length == 1) {
607            locoSelector.setAddress(locoRosterBox.getSelectedRosterEntries()[0].getDccLocoAddress());
608        }
609    }
610
611    /**
612     * we're registering as a listener for Consist events, so we need to
613     * implement the interface.
614     * {@inheritDoc}
615     */
616    @Override
617    public void consistReply(LocoAddress locoaddress, int status) {
618        log.debug("Consist Reply received for Locomotive {} with status {}", locoaddress, status);
619        _status.setText(consistManager.decodeErrorCode(status));
620        // For some status codes, we want to trigger specific actions
621        //if((status & jmri.ConsistListener.CONSIST_FULL)!=0) {
622        // canAdd();
623        //} else {
624        canAdd();
625        //}
626        consistModel.fireTableDataChanged();
627        try {
628            consistFile.writeFile(consistManager.getConsistList());
629        } catch (IOException e) {
630            log.warn("error writing consist file: {}", e.getMessage());
631        }
632    }
633
634    @Override
635    public void dispose() {
636        super.dispose();
637        consistComboBox.dispose();
638        // de-register to be notified if the consist list changes.
639        consistManager.removeConsistListListener(this);
640    }
641
642    // ConsistListListener interface
643    /**
644     * {@inheritDoc}
645     */
646    @Override
647    public void notifyConsistListChanged() {
648        // Save consist file
649        try {
650            consistFile.writeFile(consistManager.getConsistList());
651        } catch (IOException e) {
652            log.warn("error writing consist file: {}", e.getMessage());
653        }  
654        // update the consist list.
655        initializeConsistBox();
656    }
657
658    /**
659     * private method to scan the roster for consists
660     */
661    private void scanRoster(){
662       List<RosterEntry> roster = Roster.getDefault().getAllEntries();
663       for(RosterEntry entry:roster){
664            DccLocoAddress address = entry.getDccLocoAddress();
665            CvTableModel  cvTable = new CvTableModel(_status, null);  // will hold CV objects
666            entry.readFile();  // read, but don't yet process
667
668            entry.loadCvModel(null, cvTable);
669            CvValue cv19Value = cvTable.getCvByNumber("19");
670            if(cv19Value!=null && (cv19Value.getValue() & 0x7F)!=0){
671                boolean direction = ((cv19Value.getValue()&0x80)==0);
672                DccLocoAddress consistAddress = new DccLocoAddress((cv19Value.getValue()&0x7f),false);
673                /*
674                 * Make sure the marked consist type is an advanced consist.
675                 * this consist
676                  */
677                Consist consist = consistManager.getConsist(consistAddress);
678                if (Consist.ADVANCED_CONSIST != consist.getConsistType()) {
679                    consist.setConsistType(Consist.ADVANCED_CONSIST);
680                }
681
682                if (!consist.contains(address)) {
683                   consist.add(address, direction );
684                   consist.setRosterId(address, entry.titleString());
685                }
686            }
687       }
688    }
689
690    private void reportNoConsistSeletected(){
691        JmriJOptionPane.showMessageDialog(this,
692                Bundle.getMessage("NoConsistSelectedError"));
693
694    }
695
696    public void setDefaultStatus() {
697        _status.setText(Bundle.getMessage("DefaultStatusText"));
698    }
699
700    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConsistToolFrame.class);
701
702}