001package jmri.jmrit.operations.rollingstock.engines.gui;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.List;
006
007import javax.swing.*;
008import javax.swing.table.TableCellEditor;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.InstanceManager;
014import jmri.jmrit.operations.OperationsTableModel;
015import jmri.jmrit.operations.rollingstock.RollingStock;
016import jmri.jmrit.operations.rollingstock.engines.*;
017import jmri.jmrit.operations.setup.Control;
018import jmri.jmrit.operations.setup.Setup;
019import jmri.util.swing.XTableColumnModel;
020import jmri.util.table.ButtonEditor;
021import jmri.util.table.ButtonRenderer;
022
023/**
024 * Table Model for edit of engines used by operations
025 *
026 * @author Daniel Boudreau Copyright (C) 2008, 2012, 2025
027 */
028public class EnginesTableModel extends OperationsTableModel implements PropertyChangeListener {
029
030    EngineManager engineManager = InstanceManager.getDefault(EngineManager.class); // There is only one manager
031
032    // Defines the columns
033    private static final int SELECT_COLUMN = 0;
034    private static final int NUM_COLUMN = 1;
035    private static final int ROAD_COLUMN = 2;
036    private static final int MODEL_COLUMN = 3;
037    private static final int HP_COLUMN = 4;
038    private static final int WEIGHT_COLUMN = 5;
039    private static final int TYPE_COLUMN = 6;
040    private static final int LENGTH_COLUMN = 7;
041    private static final int CONSIST_COLUMN = 8;
042    private static final int LOCATION_COLUMN = 9;
043    private static final int RFID_WHERE_LAST_SEEN_COLUMN = 10;
044    private static final int RFID_WHEN_LAST_SEEN_COLUMN = 11;
045    private static final int DESTINATION_COLUMN = 12;
046    private static final int PREVIOUS_LOCATION_COLUMN = 13;
047    private static final int TRAIN_COLUMN = 14;
048    private static final int LAST_TRAIN_COLUMN = 15;
049    private static final int MOVES_COLUMN = 16;
050    private static final int BUILT_COLUMN = 17;
051    private static final int OWNER_COLUMN = 18;
052    private static final int VALUE_COLUMN = 19;
053    private static final int RFID_COLUMN = 20;
054    private static final int LAST_COLUMN = 21;
055    private static final int DCC_ADDRESS_COLUMN = 22;
056    private static final int PICKUP_COLUMN = 23;
057    private static final int SETOUT_COLUMN = 24;
058    private static final int COMMENT_COLUMN = 25;
059    private static final int SET_COLUMN = 26;
060    private static final int EDIT_COLUMN = 27;
061
062    private static final int HIGHEST_COLUMN = EDIT_COLUMN + 1;
063
064    public EnginesTableModel(boolean showAllLocos, String locationName, String trackName) {
065        super();
066        showAll = showAllLocos;
067        this.locationName = locationName;
068        this.trackName = trackName;
069        engineManager.addPropertyChangeListener(this);
070        updateList();
071    }
072
073    public final int SORTBY_NUMBER = 0;
074    public final int SORTBY_ROAD = 1;
075    public final int SORTBY_MODEL = 2;
076    public final int SORTBY_LOCATION = 3;
077    public final int SORTBY_DESTINATION = 4;
078    public final int SORTBY_TRAIN = 5;
079    public final int SORTBY_MOVES = 6;
080    public final int SORTBY_CONSIST = 7;
081    public final int SORTBY_BUILT = 8;
082    public final int SORTBY_OWNER = 9;
083    public final int SORTBY_VALUE = 10;
084    public final int SORTBY_RFID = 11;
085    public final int SORTBY_LAST = 12;
086    public final int SORTBY_HP = 13;
087    public final int SORTBY_DCC_ADDRESS = 14;
088    public final int SORTBY_PICKUP = 15;
089    public final int SORTBY_COMMENT = 16;
090
091    private int _sort = SORTBY_NUMBER;
092
093    /**
094     * Not all columns are visible at the same time.
095     *
096     * @param sort which sort is active
097     */
098    public void setSort(int sort) {
099        _sort = sort;
100        updateList();
101        if (sort == SORTBY_MOVES ||
102                sort == SORTBY_BUILT ||
103                sort == SORTBY_OWNER ||
104                sort == SORTBY_VALUE ||
105                sort == SORTBY_RFID ||
106                sort == SORTBY_LAST ||
107                sort == SORTBY_DCC_ADDRESS ||
108                sort == SORTBY_PICKUP ||
109                sort == SORTBY_COMMENT) {
110            XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
111            tcm.setColumnVisible(tcm.getColumnByModelIndex(MOVES_COLUMN), sort == SORTBY_MOVES);
112            tcm.setColumnVisible(tcm.getColumnByModelIndex(BUILT_COLUMN), sort == SORTBY_BUILT);
113            tcm.setColumnVisible(tcm.getColumnByModelIndex(OWNER_COLUMN), sort == SORTBY_OWNER);
114            tcm.setColumnVisible(tcm.getColumnByModelIndex(VALUE_COLUMN), sort == SORTBY_VALUE);
115            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_COLUMN), sort == SORTBY_RFID);
116            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHEN_LAST_SEEN_COLUMN), sort == SORTBY_RFID);
117            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHERE_LAST_SEEN_COLUMN), sort == SORTBY_RFID);
118            tcm.setColumnVisible(tcm.getColumnByModelIndex(PREVIOUS_LOCATION_COLUMN), sort == SORTBY_LAST);
119            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_COLUMN), sort == SORTBY_LAST);
120            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_TRAIN_COLUMN), sort == SORTBY_LAST);
121            tcm.setColumnVisible(tcm.getColumnByModelIndex(TRAIN_COLUMN), true);
122            tcm.setColumnVisible(tcm.getColumnByModelIndex(PICKUP_COLUMN), sort == SORTBY_PICKUP);
123            tcm.setColumnVisible(tcm.getColumnByModelIndex(SETOUT_COLUMN), sort == SORTBY_PICKUP);
124            tcm.setColumnVisible(tcm.getColumnByModelIndex(DCC_ADDRESS_COLUMN), sort == SORTBY_DCC_ADDRESS);
125            tcm.setColumnVisible(tcm.getColumnByModelIndex(COMMENT_COLUMN), sort == SORTBY_COMMENT);
126        }
127        fireTableDataChanged();
128    }
129
130    public void toggleSelectVisible() {
131        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
132        tcm.setColumnVisible(tcm.getColumnByModelIndex(SELECT_COLUMN),
133                !tcm.isColumnVisible(tcm.getColumnByModelIndex(SELECT_COLUMN)));
134    }
135
136    public void resetCheckboxes() {
137        for (Engine engine : engineList) {
138            engine.setSelected(false);
139        }
140    }
141
142    public String getSortByName() {
143        return getSortByName(_sort);
144    }
145
146    public String getSortByName(int sort) {
147        switch (sort) {
148            case SORTBY_NUMBER:
149                return Bundle.getMessage("Number");
150            case SORTBY_ROAD:
151                return Bundle.getMessage("Road");
152            case SORTBY_MODEL:
153                return Bundle.getMessage("Model");
154            case SORTBY_LOCATION:
155                return Bundle.getMessage("Location");
156            case SORTBY_DESTINATION:
157                return Bundle.getMessage("Destination");
158            case SORTBY_TRAIN:
159                return Bundle.getMessage("Train");
160            case SORTBY_MOVES:
161                return Bundle.getMessage("Moves");
162            case SORTBY_CONSIST:
163                return Bundle.getMessage("Consist");
164            case SORTBY_BUILT:
165                return Bundle.getMessage("Built");
166            case SORTBY_OWNER:
167                return Bundle.getMessage("Owner");
168            case SORTBY_DCC_ADDRESS:
169                return Bundle.getMessage("DccAddress");
170            case SORTBY_HP:
171                return Bundle.getMessage("HP");
172            case SORTBY_VALUE:
173                return Setup.getValueLabel();
174            case SORTBY_RFID:
175                return Setup.getRfidLabel();
176            case SORTBY_LAST:
177                return Bundle.getMessage("Last");
178            case SORTBY_COMMENT:
179                return Bundle.getMessage("Comment");
180            default:
181                return Bundle.getMessage("ErrorTitle"); // NOI18N
182        }
183    }
184
185    /**
186     * Search for engine by road number
187     * 
188     * @param roadNumber The string road number to search for.
189     *
190     * @return -1 if not found, table row number if found
191     */
192    public int findEngineByRoadNumber(String roadNumber) {
193        return findRollingStockByRoadNumber(roadNumber, engineList);
194    }
195
196    public Engine getEngineAtIndex(int index) {
197        return engineList.get(index);
198    }
199
200    private void updateList() {
201        // first, remove listeners from the individual objects
202        removePropertyChangeEngines();
203        engineList = getSelectedEngineList();
204        // and add listeners back in
205        addPropertyChangeEngines();
206    }
207
208    public List<Engine> getSelectedEngineList() {
209        return getEngineList(_sort);
210    }
211
212    public List<Engine> getEngineList(int sort) {
213        List<Engine> list;
214        switch (sort) {
215            case SORTBY_ROAD:
216                list = engineManager.getByRoadNameList();
217                break;
218            case SORTBY_MODEL:
219                list = engineManager.getByModelList();
220                break;
221            case SORTBY_LOCATION:
222                list = engineManager.getByLocationList();
223                break;
224            case SORTBY_DESTINATION:
225                list = engineManager.getByDestinationList();
226                break;
227            case SORTBY_TRAIN:
228                list = engineManager.getByTrainList();
229                break;
230            case SORTBY_MOVES:
231                list = engineManager.getByMovesList();
232                break;
233            case SORTBY_CONSIST:
234                list = engineManager.getByConsistList();
235                break;
236            case SORTBY_OWNER:
237                list = engineManager.getByOwnerList();
238                break;
239            case SORTBY_BUILT:
240                list = engineManager.getByBuiltList();
241                break;
242            case SORTBY_VALUE:
243                list = engineManager.getByValueList();
244                break;
245            case SORTBY_RFID:
246                list = engineManager.getByRfidList();
247                break;
248            case SORTBY_LAST:
249                list = engineManager.getByLastDateList();
250                break;
251            case SORTBY_PICKUP:
252                list = engineManager.getByPickupList();
253                break;
254            case SORTBY_COMMENT:
255                list = engineManager.getByCommentList();
256                break;
257            case SORTBY_NUMBER:
258            default:
259                list = engineManager.getByNumberList();
260        }
261        filterList(list);
262        return list;
263    }
264
265    List<Engine> engineList = null;
266
267    EnginesTableFrame _frame;
268
269    void initTable(JTable table, EnginesTableFrame frame) {
270        _table = table;
271        _frame = frame;
272        initTable();
273    }
274
275    // Default engines frame table column widths, starts with Number column and ends with Edit
276    private final int[] _enginesTableColumnWidths =
277            {60, 60, 60, 65, 50, 65, 65, 35, 75, 190, 190, 190, 140, 190, 65, 90, 50, 50, 50, 50, 100, 130, 50, 50, 50,
278                    100, 65, 70};
279
280    void initTable() {
281        // Use XTableColumnModel so we can control which columns are visible
282        XTableColumnModel tcm = new XTableColumnModel();
283        _table.setColumnModel(tcm);
284        _table.createDefaultColumnsFromModel();
285
286        // Install the button handlers
287        ButtonRenderer buttonRenderer = new ButtonRenderer();
288        tcm.getColumn(SET_COLUMN).setCellRenderer(buttonRenderer);
289        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
290        tcm.getColumn(SET_COLUMN).setCellEditor(buttonEditor);
291        tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer);
292        tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor);
293
294        // set column preferred widths
295        // load defaults, xml file data not found
296        for (int i = 0; i < tcm.getColumnCount(); i++) {
297            tcm.getColumn(i).setPreferredWidth(_enginesTableColumnWidths[i]);
298        }
299        _frame.loadTableDetails(_table);
300
301        // turn off columns
302        tcm.setColumnVisible(tcm.getColumnByModelIndex(BUILT_COLUMN), false);
303        tcm.setColumnVisible(tcm.getColumnByModelIndex(OWNER_COLUMN), false);
304        tcm.setColumnVisible(tcm.getColumnByModelIndex(VALUE_COLUMN), false);
305        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_COLUMN), false);
306        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHEN_LAST_SEEN_COLUMN), false);
307        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHERE_LAST_SEEN_COLUMN), false);
308        tcm.setColumnVisible(tcm.getColumnByModelIndex(PREVIOUS_LOCATION_COLUMN), false);
309        tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_COLUMN), false);
310        tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_TRAIN_COLUMN), false);
311        tcm.setColumnVisible(tcm.getColumnByModelIndex(DCC_ADDRESS_COLUMN), false);
312        tcm.setColumnVisible(tcm.getColumnByModelIndex(PICKUP_COLUMN), false);
313        tcm.setColumnVisible(tcm.getColumnByModelIndex(SETOUT_COLUMN), false);
314        tcm.setColumnVisible(tcm.getColumnByModelIndex(COMMENT_COLUMN), false);
315
316        // turn on default
317        tcm.setColumnVisible(tcm.getColumnByModelIndex(MOVES_COLUMN), true);
318    }
319
320    @Override
321    public int getRowCount() {
322        return engineList.size();
323    }
324
325    @Override
326    public int getColumnCount() {
327        return HIGHEST_COLUMN;
328    }
329
330    @Override
331    public String getColumnName(int col) {
332        switch (col) {
333            case SELECT_COLUMN:
334                return Bundle.getMessage("ButtonSelect");
335            case NUM_COLUMN:
336                return Bundle.getMessage("Number");
337            case ROAD_COLUMN:
338                return Bundle.getMessage("Road");
339            case MODEL_COLUMN:
340                return Bundle.getMessage("Model");
341            case HP_COLUMN:
342                return Bundle.getMessage("HP");
343            case TYPE_COLUMN:
344                return Bundle.getMessage("Type");
345            case LENGTH_COLUMN:
346                return Bundle.getMessage("Len");
347            case WEIGHT_COLUMN:
348                return Bundle.getMessage("Weight");
349            case CONSIST_COLUMN:
350                return Bundle.getMessage("Consist");
351            case LOCATION_COLUMN:
352                return Bundle.getMessage("Location");
353            case RFID_WHERE_LAST_SEEN_COLUMN:
354                return Bundle.getMessage("WhereLastSeen");
355            case RFID_WHEN_LAST_SEEN_COLUMN:
356                return Bundle.getMessage("WhenLastSeen");
357            case DESTINATION_COLUMN:
358                return Bundle.getMessage("Destination");
359            case PREVIOUS_LOCATION_COLUMN:
360                return Bundle.getMessage("LastLocation");
361            case TRAIN_COLUMN:
362                return Bundle.getMessage("Train");
363            case LAST_TRAIN_COLUMN:
364                return Bundle.getMessage("LastTrain");
365            case MOVES_COLUMN:
366                return Bundle.getMessage("Moves");
367            case BUILT_COLUMN:
368                return Bundle.getMessage("Built");
369            case OWNER_COLUMN:
370                return Bundle.getMessage("Owner");
371            case VALUE_COLUMN:
372                return Setup.getValueLabel();
373            case RFID_COLUMN:
374                return Setup.getRfidLabel();
375            case LAST_COLUMN:
376                return Bundle.getMessage("LastMoved");
377            case DCC_ADDRESS_COLUMN:
378                return Bundle.getMessage("DccAddress");
379            case PICKUP_COLUMN:
380                return Bundle.getMessage("Pickup");
381            case SETOUT_COLUMN:
382                return Bundle.getMessage("SetOut");
383            case COMMENT_COLUMN:
384                return Bundle.getMessage("Comment");
385            case SET_COLUMN:
386                return Bundle.getMessage("Set");
387            case EDIT_COLUMN:
388                return Bundle.getMessage("ButtonEdit"); // titles above all columns
389            default:
390                return "unknown"; // NOI18N
391        }
392    }
393
394    @Override
395    public Class<?> getColumnClass(int col) {
396        switch (col) {
397            case SELECT_COLUMN:
398                return Boolean.class;
399            case SET_COLUMN:
400            case EDIT_COLUMN:
401                return JButton.class;
402            case LENGTH_COLUMN:
403            case MOVES_COLUMN:
404                return Integer.class;
405            default:
406                return String.class;
407        }
408    }
409
410    @Override
411    public boolean isCellEditable(int row, int col) {
412        Engine engine = engineList.get(row);
413        if (engine.isClone()) {
414            return false;
415        }
416        switch (col) {
417            case SELECT_COLUMN:
418            case SET_COLUMN:
419            case EDIT_COLUMN:
420            case MOVES_COLUMN:
421            case VALUE_COLUMN:
422            case RFID_COLUMN:
423                return true;
424            default:
425                return false;
426        }
427    }
428
429    @Override
430    public Object getValueAt(int row, int col) {
431        if (row >= getRowCount()) {
432            return "ERROR row " + row; // NOI18N
433        }
434        Engine engine = engineList.get(row);
435        if (engine == null) {
436            return "ERROR engine unknown " + row; // NOI18N
437        }
438        switch (col) {
439            case SELECT_COLUMN:
440                return engine.isSelected();
441            case NUM_COLUMN:
442                return engine.getNumber();
443            case ROAD_COLUMN:
444                return engine.getRoadName();
445            case LENGTH_COLUMN:
446                return engine.getLengthInteger();
447            case MODEL_COLUMN:
448                return engine.getModel();
449            case HP_COLUMN:
450                return engine.getHp();
451            case TYPE_COLUMN: {
452                if (engine.isBunit()) {
453                    return engine.getTypeName() + " " + Bundle.getMessage("(B)");
454                }
455                return engine.getTypeName();
456            }
457            case WEIGHT_COLUMN:
458                return engine.getWeightTons();
459            case CONSIST_COLUMN: {
460                if (engine.isLead()) {
461                    return engine.getConsistName() + "*";
462                }
463                return engine.getConsistName();
464            }
465            case LOCATION_COLUMN: {
466                String s = engine.getStatus();
467                if (!engine.getLocationName().equals(Engine.NONE)) {
468                    s = engine.getStatus() + engine.getLocationName() + " (" + engine.getTrackName() + ")";
469                }
470                return s;
471            }
472            case RFID_WHERE_LAST_SEEN_COLUMN: {
473                return engine.getWhereLastSeenName() +
474                        (engine.getTrackLastSeenName().equals(Engine.NONE) ? "" : " (" + engine.getTrackLastSeenName() + ")");
475            }
476            case RFID_WHEN_LAST_SEEN_COLUMN: {
477                return engine.getWhenLastSeenDate();
478            }
479            case DESTINATION_COLUMN: {
480                String s = "";
481                if (!engine.getDestinationName().equals(Engine.NONE)) {
482                    s = engine.getDestinationName() + " (" + engine.getDestinationTrackName() + ")";
483                }
484                return s;
485            }
486            case PREVIOUS_LOCATION_COLUMN: {
487                String s = "";
488                if (!engine.getLastLocationName().equals(Engine.NONE)) {
489                    s = engine.getLastLocationName() + " (" + engine.getLastTrackName() + ")";
490                }
491                return s;
492            }
493            case TRAIN_COLUMN: {
494                // if train was manually set by user add an asterisk
495                if (engine.getTrain() != null && engine.getRouteLocation() == null) {
496                    return engine.getTrainName() + "*";
497                }
498                return engine.getTrainName();
499            }
500            case LAST_TRAIN_COLUMN:
501                return engine.getLastTrainName();
502            case MOVES_COLUMN:
503                return engine.getMoves();
504            case BUILT_COLUMN:
505                return engine.getBuilt();
506            case OWNER_COLUMN:
507                return engine.getOwnerName();
508            case VALUE_COLUMN:
509                return engine.getValue();
510            case RFID_COLUMN:
511                return engine.getRfid();
512            case LAST_COLUMN:
513                return engine.getSortDate();
514            case DCC_ADDRESS_COLUMN:
515                return engine.getDccAddress();
516            case PICKUP_COLUMN:
517                return engine.getPickupTime();
518            case SETOUT_COLUMN:
519                return engine.getSetoutTime();
520            case COMMENT_COLUMN:
521                return engine.getComment();
522            case SET_COLUMN:
523                return Bundle.getMessage("Set");
524            case EDIT_COLUMN:
525                return Bundle.getMessage("ButtonEdit");
526            default:
527                return "unknown " + col; // NOI18N
528        }
529    }
530
531    EngineEditFrame engineEditFrame = null;
532    EngineSetFrame engineSetFrame = null;
533
534    @Override
535    public void setValueAt(Object value, int row, int col) {
536        Engine engine = engineList.get(row);
537        switch (col) {
538            case SELECT_COLUMN:
539                engine.setSelected(((Boolean) value).booleanValue());
540                break;
541            case MOVES_COLUMN:
542                try {
543                    engine.setMoves(Integer.parseInt(value.toString()));
544                } catch (NumberFormatException e) {
545                    log.error("move count must be a number");
546                }
547                break;
548            case BUILT_COLUMN:
549                engine.setBuilt(value.toString());
550                break;
551            case OWNER_COLUMN:
552                engine.setOwnerName(value.toString());
553                break;
554            case VALUE_COLUMN:
555                engine.setValue(value.toString());
556                break;
557            case RFID_COLUMN:
558                engine.setRfid(value.toString());
559                break;
560            case SET_COLUMN:
561                log.debug("Set engine location");
562                if (engineSetFrame != null) {
563                    engineSetFrame.dispose();
564                }
565                // use invokeLater so new window appears on top
566                SwingUtilities.invokeLater(() -> {
567                    engineSetFrame = new EngineSetFrame();
568                    engineSetFrame.initComponents();
569                    engineSetFrame.load(engine);
570                });
571                break;
572            case EDIT_COLUMN:
573                log.debug("Edit engine");
574                if (engineEditFrame != null) {
575                    engineEditFrame.dispose();
576                }
577                // use invokeLater so new window appears on top
578                SwingUtilities.invokeLater(() -> {
579                    engineEditFrame = new EngineEditFrame();
580                    engineEditFrame.initComponents();
581                    engineEditFrame.load(engine);
582                });
583                break;
584            default:
585                break;
586        }
587    }
588
589    public void dispose() {
590        log.debug("dispose EngineTableModel");
591        engineManager.removePropertyChangeListener(this);
592        removePropertyChangeEngines();
593        if (engineSetFrame != null) {
594            engineSetFrame.dispose();
595        }
596        if (engineEditFrame != null) {
597            engineEditFrame.dispose();
598        }
599    }
600
601    private void addPropertyChangeEngines() {
602        for (RollingStock rs : engineManager.getList()) {
603            rs.addPropertyChangeListener(this);
604        }
605    }
606
607    private void removePropertyChangeEngines() {
608        for (RollingStock rs : engineManager.getList()) {
609            rs.removePropertyChangeListener(this);
610        }
611    }
612
613    @Override
614    public void propertyChange(PropertyChangeEvent e) {
615        if (Control.SHOW_PROPERTY) {
616            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
617                    .getNewValue());
618        }
619        if (e.getPropertyName().equals(EngineManager.LISTLENGTH_CHANGED_PROPERTY) ||
620                e.getPropertyName().equals(ConsistManager.LISTLENGTH_CHANGED_PROPERTY)) {
621            updateList();
622            fireTableDataChanged();
623        }
624        // Engine length, type, and HP are based on model, so multiple changes
625        else if (e.getPropertyName().equals(Engine.LENGTH_CHANGED_PROPERTY) ||
626                e.getPropertyName().equals(Engine.TYPE_CHANGED_PROPERTY) ||
627                e.getPropertyName().equals(Engine.HP_CHANGED_PROPERTY)) {
628            fireTableDataChanged();
629        }
630        // must be a engine change
631        else if (e.getSource().getClass().equals(Engine.class)) {
632            Engine engine = (Engine) e.getSource();
633            int row = engineList.indexOf(engine);
634            if (Control.SHOW_PROPERTY) {
635                log.debug("Update engine table row: {}", row);
636            }
637            if (row >= 0) {
638                fireTableRowsUpdated(row, row);
639                // next is needed when only showing engines at a location or track
640            } else if (e.getPropertyName().equals(Engine.TRACK_CHANGED_PROPERTY)) {
641                updateList();
642                fireTableDataChanged();
643            }
644        }
645    }
646
647    private final static Logger log = LoggerFactory.getLogger(EnginesTableModel.class);
648}