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