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