001package jmri.jmrit.operations.rollingstock.cars.gui;
002
003import java.awt.Color;
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyChangeListener;
006import java.util.List;
007
008import javax.swing.*;
009import javax.swing.table.TableCellEditor;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
015import jmri.InstanceManager;
016import jmri.jmrit.operations.OperationsTableModel;
017import jmri.jmrit.operations.rollingstock.cars.*;
018import jmri.jmrit.operations.setup.Control;
019import jmri.jmrit.operations.setup.Setup;
020import jmri.util.swing.XTableColumnModel;
021import jmri.util.table.ButtonEditor;
022import jmri.util.table.ButtonRenderer;
023
024/**
025 * Table Model for edit of cars used by operations
026 *
027 * @author Daniel Boudreau Copyright (C) 2008, 2011, 2012, 2016, 2025, 2026
028 */
029public class CarsTableModel extends OperationsTableModel implements PropertyChangeListener {
030
031    CarManager carManager = InstanceManager.getDefault(CarManager.class); // There is only one manager
032
033    // Defines the columns
034    private static final int SELECT_COLUMN = 0;
035    private static final int NUMBER_COLUMN = 1;
036    private static final int ROAD_COLUMN = 2;
037    private static final int TYPE_COLUMN = 3;
038    private static final int LENGTH_COLUMN = 4;
039    private static final int LOAD_COLUMN = 5;
040    private static final int RWE_LOAD_COLUMN = 6;
041    private static final int RWL_LOAD_COLUMN = 7;
042    private static final int COLOR_COLUMN = 8;
043    private static final int KERNEL_COLUMN = 9;
044    private static final int LOCATION_COLUMN = 10;
045    private static final int RFID_WHERE_LAST_SEEN_COLUMN = 11;
046    private static final int RFID_WHEN_LAST_SEEN_COLUMN = 12;
047    private static final int DESTINATION_COLUMN = 13;
048    private static final int FINAL_DESTINATION_COLUMN = 14;
049    private static final int RWE_DESTINATION_COLUMN = 15;
050    private static final int RWL_DESTINATION_COLUMN = 16;
051    private static final int ROUTE_COLUMN = 17;
052    private static final int LAST_LOCATION_COLUMN = 18;
053    private static final int DIVISION_COLUMN = 19;
054    private static final int TRAIN_COLUMN = 20;
055    private static final int LAST_TRAIN_COLUMN = 21;
056    private static final int MOVES_COLUMN = 22;
057    private static final int BUILT_COLUMN = 23;
058    private static final int OWNER_COLUMN = 24;
059    private static final int VALUE_COLUMN = 25;
060    private static final int RFID_COLUMN = 26;
061    private static final int WAIT_COLUMN = 27;
062    private static final int PICKUP_COLUMN = 28;
063    private static final int SETOUT_COLUMN = 29;
064    private static final int LAST_COLUMN = 30;
065    private static final int COMMENT_COLUMN = 31;
066    private static final int SET_COLUMN = 32;
067    private static final int EDIT_COLUMN = 33;
068
069    private static final int HIGHESTCOLUMN = EDIT_COLUMN + 1;
070
071    public final int SORTBY_NUMBER = 0;
072    public final int SORTBY_ROAD = 1;
073    public final int SORTBY_TYPE = 2;
074    public final int SORTBY_LOCATION = 3;
075    public final int SORTBY_DESTINATION = 4;
076    public final int SORTBY_TRAIN = 5;
077    public final int SORTBY_MOVES = 6;
078    public final int SORTBY_KERNEL = 7;
079    public final int SORTBY_LOAD = 8;
080    public final int SORTBY_COLOR = 9;
081    public final int SORTBY_BUILT = 10;
082    public final int SORTBY_OWNER = 11;
083    public final int SORTBY_RFID = 12;
084    public final int SORTBY_RWE = 13; // return when empty
085    public final int SORTBY_RWL = 14; // return when loaded
086    public final int SORTBY_ROUTE = 15;
087    public final int SORTBY_DIVISION = 16;
088    public final int SORTBY_FINALDESTINATION = 17;
089    public final int SORTBY_VALUE = 18;
090    public final int SORTBY_WAIT = 19;
091    public final int SORTBY_PICKUP = 20;
092    public final int SORTBY_LAST = 21;
093    public final int SORTBY_COMMENT = 22; // also used by PrintCarRosterAction
094
095    private int _sort = SORTBY_NUMBER;
096
097    List<Car> carList = null; // list of cars
098    //    JTable _table;
099    CarsTableFrame _frame;
100
101    public CarsTableModel(boolean showAllCars, String locationName, String trackName) {
102        super();
103        showAll = showAllCars;
104        this.locationName = locationName;
105        this.trackName = trackName;
106        carManager.addPropertyChangeListener(this);
107        updateList();
108    }
109
110    /**
111     * Not all columns in the Cars table are shown. This was done to limit the
112     * width of the table. Only one column from the following groups is shown at
113     * any one time.
114     * <p>
115     * Load, Color, and RWE Load are grouped together.
116     * <p>
117     * Destination, Final Destination, and RWE Destination are grouped together.
118     * <p>
119     * Moves, Built, Owner, Value, RFID, Wait, Pickup, and Last are grouped
120     * together.
121     * 
122     * @param sort The integer sort to use.
123     */
124    public void setSort(int sort) {
125        _sort = sort;
126        updateList();
127        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
128        if (sort == SORTBY_COLOR || sort == SORTBY_LOAD || sort == SORTBY_RWE || sort == SORTBY_RWL) {
129            tcm.setColumnVisible(tcm.getColumnByModelIndex(LOAD_COLUMN), sort == SORTBY_LOAD);
130            tcm.setColumnVisible(tcm.getColumnByModelIndex(COLOR_COLUMN), sort == SORTBY_COLOR);
131            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWE_LOAD_COLUMN), sort == SORTBY_RWE);
132            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWL_LOAD_COLUMN), sort == SORTBY_RWL);
133        }
134        if (sort == SORTBY_DIVISION) {
135            tcm.setColumnVisible(tcm.getColumnByModelIndex(DIVISION_COLUMN), true);
136        }
137        if (sort == SORTBY_TRAIN) {
138            tcm.setColumnVisible(tcm.getColumnByModelIndex(TRAIN_COLUMN), true);
139            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_TRAIN_COLUMN), false);
140        }
141        if (sort == SORTBY_DESTINATION ||
142                sort == SORTBY_FINALDESTINATION ||
143                sort == SORTBY_RWE ||
144                sort == SORTBY_RWL ||
145                sort == SORTBY_ROUTE) {
146            tcm.setColumnVisible(tcm.getColumnByModelIndex(DESTINATION_COLUMN), sort == SORTBY_DESTINATION);
147            tcm.setColumnVisible(tcm.getColumnByModelIndex(FINAL_DESTINATION_COLUMN), sort == SORTBY_FINALDESTINATION);
148            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWE_DESTINATION_COLUMN), sort == SORTBY_RWE);
149            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWL_DESTINATION_COLUMN), sort == SORTBY_RWL);
150            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWE_LOAD_COLUMN), sort == SORTBY_RWE);
151            tcm.setColumnVisible(tcm.getColumnByModelIndex(RWL_LOAD_COLUMN), sort == SORTBY_RWL);
152            tcm.setColumnVisible(tcm.getColumnByModelIndex(ROUTE_COLUMN), sort == SORTBY_ROUTE);
153
154            // show load column if color column isn't visible.
155            tcm.setColumnVisible(tcm.getColumnByModelIndex(LOAD_COLUMN),
156                    sort != SORTBY_RWE &&
157                            sort != SORTBY_RWL &&
158                            !tcm.isColumnVisible(tcm.getColumnByModelIndex(COLOR_COLUMN)));
159        } else if (sort == SORTBY_MOVES ||
160                sort == SORTBY_BUILT ||
161                sort == SORTBY_OWNER ||
162                sort == SORTBY_VALUE ||
163                sort == SORTBY_RFID ||
164                sort == SORTBY_WAIT ||
165                sort == SORTBY_PICKUP ||
166                sort == SORTBY_LAST ||
167                sort == SORTBY_COMMENT) {
168            tcm.setColumnVisible(tcm.getColumnByModelIndex(MOVES_COLUMN), sort == SORTBY_MOVES);
169            tcm.setColumnVisible(tcm.getColumnByModelIndex(BUILT_COLUMN), sort == SORTBY_BUILT);
170            tcm.setColumnVisible(tcm.getColumnByModelIndex(OWNER_COLUMN), sort == SORTBY_OWNER);
171            tcm.setColumnVisible(tcm.getColumnByModelIndex(VALUE_COLUMN), sort == SORTBY_VALUE);
172            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_COLUMN), sort == SORTBY_RFID);
173            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHEN_LAST_SEEN_COLUMN), sort == SORTBY_RFID);
174            tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHERE_LAST_SEEN_COLUMN), sort == SORTBY_RFID);
175            tcm.setColumnVisible(tcm.getColumnByModelIndex(WAIT_COLUMN), sort == SORTBY_WAIT);
176            tcm.setColumnVisible(tcm.getColumnByModelIndex(PICKUP_COLUMN), sort == SORTBY_PICKUP);
177            tcm.setColumnVisible(tcm.getColumnByModelIndex(SETOUT_COLUMN), sort == SORTBY_PICKUP);
178            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_LOCATION_COLUMN), sort == SORTBY_LAST);
179            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_COLUMN), sort == SORTBY_LAST);
180            tcm.setColumnVisible(tcm.getColumnByModelIndex(TRAIN_COLUMN), sort != SORTBY_LAST);
181            tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_TRAIN_COLUMN), sort == SORTBY_LAST);
182            tcm.setColumnVisible(tcm.getColumnByModelIndex(COMMENT_COLUMN), sort == SORTBY_COMMENT);
183        }
184        fireTableDataChanged();
185    }
186
187    public String getSortByName() {
188        return getSortByName(_sort);
189    }
190
191    public String getSortByName(int sort) {
192        switch (sort) {
193            case SORTBY_NUMBER:
194                return Bundle.getMessage("Number");
195            case SORTBY_ROAD:
196                return Bundle.getMessage("Road");
197            case SORTBY_TYPE:
198                return Bundle.getMessage("Type");
199            case SORTBY_COLOR:
200                return Bundle.getMessage("Color");
201            case SORTBY_LOAD:
202                return Bundle.getMessage("Load");
203            case SORTBY_KERNEL:
204                return Bundle.getMessage("Kernel");
205            case SORTBY_LOCATION:
206                return Bundle.getMessage("Location");
207            case SORTBY_DESTINATION:
208                return Bundle.getMessage("Destination");
209            case SORTBY_DIVISION:
210                return Bundle.getMessage("HomeDivision");
211            case SORTBY_TRAIN:
212                return Bundle.getMessage("Train");
213            case SORTBY_FINALDESTINATION:
214                return Bundle.getMessage("FinalDestination");
215            case SORTBY_RWE:
216                return Bundle.getMessage("ReturnWhenEmpty");
217            case SORTBY_RWL:
218                return Bundle.getMessage("ReturnWhenLoaded");
219            case SORTBY_ROUTE:
220                return Bundle.getMessage("Route");
221            case SORTBY_MOVES:
222                return Bundle.getMessage("Moves");
223            case SORTBY_BUILT:
224                return Bundle.getMessage("Built");
225            case SORTBY_OWNER:
226                return Bundle.getMessage("Owner");
227            case SORTBY_VALUE:
228                return Setup.getValueLabel();
229            case SORTBY_RFID:
230                return Setup.getRfidLabel();
231            case SORTBY_WAIT:
232                return Bundle.getMessage("Wait");
233            case SORTBY_PICKUP:
234                return Bundle.getMessage("Pickup");
235            case SORTBY_LAST:
236                return Bundle.getMessage("Last");
237            case SORTBY_COMMENT:
238                return Bundle.getMessage("Comment");
239            default:
240                return Bundle.getMessage("ErrorTitle"); // NOI18N
241        }
242    }
243
244    @Override
245    protected Color getForegroundColor(int row) {
246        Car car = carList.get(row);
247        if (car.getLocation() != null && car.getTrack() == null) {
248            return Color.red;
249        }
250        return super.getForegroundColor(row);
251    }
252
253    public void toggleSelectVisible() {
254        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
255        tcm.setColumnVisible(tcm.getColumnByModelIndex(SELECT_COLUMN),
256                !tcm.isColumnVisible(tcm.getColumnByModelIndex(SELECT_COLUMN)));
257    }
258
259    public void resetCheckboxes() {
260        for (Car car : carList) {
261            car.setSelected(false);
262        }
263    }
264
265    /**
266     * Search for car by road number
267     * 
268     * @param roadNumber The string road number to search for.
269     * @return -1 if not found, table row number if found
270     */
271    public int findCarByRoadNumber(String roadNumber) {
272        return findRollingStockByRoadNumber(roadNumber, carList);
273    }
274
275    public Car getCarAtIndex(int index) {
276        return carList.get(index);
277    }
278
279    private void updateList() {
280        // first, remove listeners from the individual objects
281        removePropertyChangeCars();
282        carList = getSelectedCarList();
283        // and add listeners back in
284        addPropertyChangeCars();
285    }
286
287    public List<Car> getSelectedCarList() {
288        return getCarList(_sort);
289    }
290
291    @SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES", justification = "default case is sort by number") // NOI18N
292    public List<Car> getCarList(int sort) {
293        List<Car> list;
294        switch (sort) {
295            case SORTBY_NUMBER:
296                list = carManager.getByNumberList();
297                break;
298            case SORTBY_ROAD:
299                list = carManager.getByRoadNameList();
300                break;
301            case SORTBY_TYPE:
302                list = carManager.getByTypeList();
303                break;
304            case SORTBY_COLOR:
305                list = carManager.getByColorList();
306                break;
307            case SORTBY_LOAD:
308                list = carManager.getByLoadList();
309                break;
310            case SORTBY_KERNEL:
311                list = carManager.getByKernelList();
312                break;
313            case SORTBY_LOCATION:
314                list = carManager.getByLocationList();
315                break;
316            case SORTBY_DESTINATION:
317                list = carManager.getByDestinationList();
318                break;
319            case SORTBY_TRAIN:
320                list = carManager.getByTrainList();
321                break;
322            case SORTBY_FINALDESTINATION:
323                list = carManager.getByFinalDestinationList();
324                break;
325            case SORTBY_RWE:
326                list = carManager.getByRweList();
327                break;
328            case SORTBY_RWL:
329                list = carManager.getByRwlList();
330                break;
331            case SORTBY_ROUTE:
332                list = carManager.getByRouteList();
333                break;
334            case SORTBY_DIVISION:
335                list = carManager.getByDivisionList();
336                break;
337            case SORTBY_MOVES:
338                list = carManager.getByMovesList();
339                break;
340            case SORTBY_BUILT:
341                list = carManager.getByBuiltList();
342                break;
343            case SORTBY_OWNER:
344                list = carManager.getByOwnerList();
345                break;
346            case SORTBY_VALUE:
347                list = carManager.getByValueList();
348                break;
349            case SORTBY_RFID:
350                list = carManager.getByRfidList();
351                break;
352            case SORTBY_WAIT:
353                list = carManager.getByWaitList();
354                break;
355            case SORTBY_PICKUP:
356                list = carManager.getByPickupList();
357                break;
358            case SORTBY_LAST:
359                list = carManager.getByLastDateList();
360                break;
361            case SORTBY_COMMENT:
362                list = carManager.getByCommentList();
363                break;
364            default:
365                list = carManager.getByNumberList();
366        }
367        filterCarList(list);
368        return list;
369    }
370
371    private void filterCarList(List<Car> list) {
372        if (!Control.showCloneCars) {
373            for (int i = 0; i < list.size(); i++) {
374                Car car = list.get(i);
375                if (car.isClone()) {
376                    list.remove(i--);
377                    continue;
378                }
379            }
380        }
381        filterList(list);
382    }
383
384    void initTable(JTable table, CarsTableFrame frame) {
385        super.initTable(table);
386        _frame = frame;
387        initTable();
388    }
389
390    // Cars frame table column widths, starts with Select column and ends with Edit
391    private final int[] tableColumnWidths = {60, 60, 60, 65, 35, 75, 75, 75, 75, 65, 190, 190, 140, 190, 190, 190, 190,
392            190, 190, 190, 65, 90, 50, 50, 50, 50, 100, 50, 100, 60, 100, 100, 65, 70};
393
394    void initTable() {
395        // Use XTableColumnModel so we can control which columns are visible
396        XTableColumnModel tcm = new XTableColumnModel();
397        _table.setColumnModel(tcm);
398        _table.createDefaultColumnsFromModel();
399
400        // Install the button handlers
401        ButtonRenderer buttonRenderer = new ButtonRenderer();
402        tcm.getColumn(SET_COLUMN).setCellRenderer(buttonRenderer);
403        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
404        tcm.getColumn(SET_COLUMN).setCellEditor(buttonEditor);
405        tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer);
406        tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor);
407
408        // set column preferred widths
409        for (int i = 0; i < tcm.getColumnCount(); i++) {
410            tcm.getColumn(i).setPreferredWidth(tableColumnWidths[i]);
411        }
412        _frame.loadTableDetails(_table);
413
414        // turn off columns
415        tcm.setColumnVisible(tcm.getColumnByModelIndex(COLOR_COLUMN), false);
416        tcm.setColumnVisible(tcm.getColumnByModelIndex(FINAL_DESTINATION_COLUMN), false);
417        tcm.setColumnVisible(tcm.getColumnByModelIndex(RWE_DESTINATION_COLUMN), false);
418        tcm.setColumnVisible(tcm.getColumnByModelIndex(RWE_LOAD_COLUMN), false);
419        tcm.setColumnVisible(tcm.getColumnByModelIndex(RWL_DESTINATION_COLUMN), false);
420        tcm.setColumnVisible(tcm.getColumnByModelIndex(RWL_LOAD_COLUMN), false);
421        tcm.setColumnVisible(tcm.getColumnByModelIndex(ROUTE_COLUMN), false);
422        tcm.setColumnVisible(tcm.getColumnByModelIndex(BUILT_COLUMN), false);
423        tcm.setColumnVisible(tcm.getColumnByModelIndex(OWNER_COLUMN), false);
424        tcm.setColumnVisible(tcm.getColumnByModelIndex(VALUE_COLUMN), false);
425        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_COLUMN), false);
426        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHEN_LAST_SEEN_COLUMN), false);
427        tcm.setColumnVisible(tcm.getColumnByModelIndex(RFID_WHERE_LAST_SEEN_COLUMN), false);
428        tcm.setColumnVisible(tcm.getColumnByModelIndex(WAIT_COLUMN), false);
429        tcm.setColumnVisible(tcm.getColumnByModelIndex(PICKUP_COLUMN), false);
430        tcm.setColumnVisible(tcm.getColumnByModelIndex(SETOUT_COLUMN), false);
431        tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_LOCATION_COLUMN), false);
432        tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_COLUMN), false);
433        tcm.setColumnVisible(tcm.getColumnByModelIndex(LAST_TRAIN_COLUMN), false);
434        tcm.setColumnVisible(tcm.getColumnByModelIndex(COMMENT_COLUMN), false);
435
436        // turn on defaults
437        tcm.setColumnVisible(tcm.getColumnByModelIndex(LOAD_COLUMN), true);
438        tcm.setColumnVisible(tcm.getColumnByModelIndex(DESTINATION_COLUMN), true);
439        tcm.setColumnVisible(tcm.getColumnByModelIndex(MOVES_COLUMN), true);
440        tcm.setColumnVisible(tcm.getColumnByModelIndex(TRAIN_COLUMN), true);
441
442        tcm.setColumnVisible(tcm.getColumnByModelIndex(DIVISION_COLUMN), carManager.isThereDivisions());
443    }
444
445    @Override
446    public int getRowCount() {
447        return carList.size();
448    }
449
450    @Override
451    public int getColumnCount() {
452        return HIGHESTCOLUMN;
453    }
454
455    @Override
456    public String getColumnName(int col) {
457        switch (col) {
458            case SELECT_COLUMN:
459                return Bundle.getMessage("ButtonSelect");
460            case NUMBER_COLUMN:
461                return Bundle.getMessage("Number");
462            case ROAD_COLUMN:
463                return Bundle.getMessage("Road");
464            case LOAD_COLUMN:
465                return Bundle.getMessage("Load");
466            case COLOR_COLUMN:
467                return Bundle.getMessage("Color");
468            case TYPE_COLUMN:
469                return Bundle.getMessage("Type");
470            case LENGTH_COLUMN:
471                return Bundle.getMessage("Len");
472            case KERNEL_COLUMN:
473                return Bundle.getMessage("Kernel");
474            case LOCATION_COLUMN:
475                return Bundle.getMessage("Location");
476            case RFID_WHERE_LAST_SEEN_COLUMN:
477                return Bundle.getMessage("WhereLastSeen");
478            case RFID_WHEN_LAST_SEEN_COLUMN:
479                return Bundle.getMessage("WhenLastSeen");
480            case DESTINATION_COLUMN:
481                return Bundle.getMessage("Destination");
482            case FINAL_DESTINATION_COLUMN:
483                return Bundle.getMessage("FinalDestination");
484            case RWE_DESTINATION_COLUMN:
485                return Bundle.getMessage("RWELocation");
486            case RWE_LOAD_COLUMN:
487                return Bundle.getMessage("RWELoad");
488            case RWL_DESTINATION_COLUMN:
489                return Bundle.getMessage("RWLLocation");
490            case RWL_LOAD_COLUMN:
491                return Bundle.getMessage("RWLLoad");
492            case ROUTE_COLUMN:
493                return Bundle.getMessage("Route");
494            case LAST_LOCATION_COLUMN:
495                return Bundle.getMessage("LastLocation");
496            case DIVISION_COLUMN:
497                return Bundle.getMessage("HomeDivision");
498            case TRAIN_COLUMN:
499                return Bundle.getMessage("Train");
500            case LAST_TRAIN_COLUMN:
501                return Bundle.getMessage("LastTrain");
502            case MOVES_COLUMN:
503                return Bundle.getMessage("Moves");
504            case BUILT_COLUMN:
505                return Bundle.getMessage("Built");
506            case OWNER_COLUMN:
507                return Bundle.getMessage("Owner");
508            case VALUE_COLUMN:
509                return Setup.getValueLabel();
510            case RFID_COLUMN:
511                return Setup.getRfidLabel();
512            case WAIT_COLUMN:
513                return Bundle.getMessage("Wait");
514            case PICKUP_COLUMN:
515                return Bundle.getMessage("Pickup");
516            case SETOUT_COLUMN:
517                return Bundle.getMessage("SetOut");
518            case LAST_COLUMN:
519                return Bundle.getMessage("LastMoved");
520            case COMMENT_COLUMN:
521                return Bundle.getMessage("Comment");
522            case SET_COLUMN:
523                return Bundle.getMessage("Set");
524            case EDIT_COLUMN:
525                return Bundle.getMessage("ButtonEdit"); // titles above all columns
526            default:
527                return "unknown"; // NOI18N
528        }
529    }
530
531    @Override
532    public Class<?> getColumnClass(int col) {
533        switch (col) {
534            case SELECT_COLUMN:
535                return Boolean.class;
536            case SET_COLUMN:
537            case EDIT_COLUMN:
538                return JButton.class;
539            case LENGTH_COLUMN:
540            case MOVES_COLUMN:
541            case WAIT_COLUMN:
542                return Integer.class;
543            default:
544                return String.class;
545        }
546    }
547
548    @Override
549    public boolean isCellEditable(int row, int col) {
550        Car car = carList.get(row);
551        if (car.isClone()) {
552            return false;
553        }
554        switch (col) {
555            case SELECT_COLUMN:
556            case SET_COLUMN:
557            case EDIT_COLUMN:
558            case MOVES_COLUMN:
559            case WAIT_COLUMN:
560            case VALUE_COLUMN:
561            case RFID_COLUMN:
562                return true;
563            default:
564                return false;
565        }
566    }
567
568    @Override
569    public Object getValueAt(int row, int col) {
570        if (row >= getRowCount()) {
571            return "ERROR row " + row; // NOI18N
572        }
573        Car car = carList.get(row);
574        if (car == null) {
575            return "ERROR car unknown " + row; // NOI18N
576        }
577        if (car.isClone()) {
578            setToolTip(Bundle.getMessage("DoNotModifyClone", car.toString()), col);
579        } else {
580            setToolTip(null, col);
581        }
582        switch (col) {
583            case SELECT_COLUMN:
584                return car.isSelected();
585            case NUMBER_COLUMN:
586                return car.getNumber();
587            case ROAD_COLUMN:
588                return car.getRoadName();
589            case LOAD_COLUMN:
590                return getLoadNameString(car);
591            case COLOR_COLUMN:
592                return car.getColor();
593            case LENGTH_COLUMN:
594                return car.getLengthInteger();
595            case TYPE_COLUMN:
596                return car.getTypeName() + car.getTypeExtensions();
597            case KERNEL_COLUMN:
598                if (car.isLead()) {
599                    return car.getKernelName() + "*";
600                }
601                return car.getKernelName();
602            case LOCATION_COLUMN:
603                if (car.getLocation() != null) {
604                    return car.getStatus() + car.getLocationName() + " (" + car.getTrackName() + ")";
605                }
606                return car.getStatus();
607            case RFID_WHERE_LAST_SEEN_COLUMN:
608                return car.getWhereLastSeenName() +
609                        (car.getTrackLastSeenName().equals(Car.NONE) ? "" : " (" + car.getTrackLastSeenName() + ")");
610            case RFID_WHEN_LAST_SEEN_COLUMN: {
611                return car.getWhenLastSeenDate();
612            }
613            case DESTINATION_COLUMN:
614            case FINAL_DESTINATION_COLUMN: {
615                String s = "";
616                if (car.getDestination() != null) {
617                    s = car.getDestinationName() + " (" + car.getDestinationTrackName() + ")";
618                }
619                if (log.isDebugEnabled() &&
620                        car.getDestinationTrack() != null &&
621                        car.getDestinationTrack().getSchedule() != null) {
622                    s = s + " " + car.getScheduleItemId() + " ";
623                }
624                if (car.getFinalDestination() != null) {
625                    s = s + "->" + car.getFinalDestinationName(); // NOI18N
626                }
627                if (car.getFinalDestinationTrack() != null) {
628                    s = s + " (" + car.getFinalDestinationTrackName() + ")";
629                }
630                if (log.isDebugEnabled() &&
631                        !s.contains(car.getScheduleItemId()) &&
632                        car.getFinalDestinationTrack() != null &&
633                        car.getFinalDestinationTrack().getSchedule() != null) {
634                    s = s + " " + car.getScheduleItemId();
635                }
636                return s;
637            }
638            case RWE_DESTINATION_COLUMN: {
639                String s = car.getReturnWhenEmptyDestinationName();
640                if (car.getReturnWhenEmptyDestTrack() != null) {
641                    s = s + " (" + car.getReturnWhenEmptyDestTrackName() + ")";
642                }
643                return s;
644            }
645            case RWE_LOAD_COLUMN:
646                return car.getReturnWhenEmptyLoadName();
647            case RWL_DESTINATION_COLUMN: {
648                String s = car.getReturnWhenLoadedDestinationName();
649                if (car.getReturnWhenLoadedDestTrack() != null) {
650                    s = s + " (" + car.getReturnWhenLoadedDestTrackName() + ")";
651                }
652                return s;
653            }
654            case RWL_LOAD_COLUMN:
655                return car.getReturnWhenLoadedLoadName();
656            case ROUTE_COLUMN:
657                return car.getRoutePath();
658            case DIVISION_COLUMN:
659                return car.getDivisionName();
660            case LAST_LOCATION_COLUMN: {
661                String s = "";
662                if (!car.getLastLocationName().equals(Car.NONE)) {
663                    s = car.getLastLocationName() + " (" + car.getLastTrackName() + ")";
664                }
665                return s;
666            }
667            case TRAIN_COLUMN: {
668                // if train was manually set by user add an asterisk
669                if (car.getTrain() != null && car.getRouteLocation() == null) {
670                    return car.getTrainName() + "*";
671                }
672                return car.getTrainName();
673            }
674            case LAST_TRAIN_COLUMN:
675                return car.getLastTrainName();
676            case MOVES_COLUMN:
677                return car.getMoves();
678            case BUILT_COLUMN:
679                return car.getBuilt();
680            case OWNER_COLUMN:
681                return car.getOwnerName();
682            case VALUE_COLUMN:
683                return car.getValue();
684            case RFID_COLUMN:
685                return car.getRfid();
686            case WAIT_COLUMN:
687                return car.getWait();
688            case PICKUP_COLUMN:
689                return car.getPickupScheduleName();
690            case SETOUT_COLUMN:
691                return car.getSetoutTime();
692            case LAST_COLUMN:
693                return car.getSortDate();
694            case COMMENT_COLUMN:
695                return car.getComment();
696            case SET_COLUMN:
697                return Bundle.getMessage("Set");
698            case EDIT_COLUMN:
699                return Bundle.getMessage("ButtonEdit");
700            default:
701                return "unknown " + col; // NOI18N
702        }
703    }
704
705    private String getLoadNameString(Car car) {
706        StringBuffer sb = new StringBuffer(car.getLoadName());
707        if (car.getLoadPriority().equals(CarLoad.PRIORITY_HIGH)) {
708            sb.append(" " + Bundle.getMessage("(P)"));
709        } else if (car.getLoadPriority().equals(CarLoad.PRIORITY_MEDIUM)) {
710            sb.append(" " + Bundle.getMessage("(M)"));
711        }
712        if (car.isCarLoadHazardous()) {
713            sb.append(" " + Bundle.getMessage("(H)"));
714        }
715        return sb.toString();
716    }
717
718    CarEditFrame cef = null;
719    CarSetFrame csf = null;
720
721    @Override
722    public void setValueAt(Object value, int row, int col) {
723        Car car = carList.get(row);
724        switch (col) {
725            case SELECT_COLUMN:
726                car.setSelected(((Boolean) value).booleanValue());
727                break;
728            case SET_COLUMN:
729                if (csf != null) {
730                    csf.dispose();
731                }
732                // use invokeLater so new window appears on top
733                SwingUtilities.invokeLater(() -> {
734                    csf = new CarSetFrame();
735                    csf.initComponents();
736                    csf.load(car);
737                });
738                break;
739            case EDIT_COLUMN:
740                if (cef != null) {
741                    cef.dispose();
742                }
743                // use invokeLater so new window appears on top
744                SwingUtilities.invokeLater(() -> {
745                    cef = new CarEditFrame();
746                    cef.initComponents();
747                    cef.load(car);
748                });
749                break;
750            case MOVES_COLUMN:
751                try {
752                    car.setMoves(Integer.parseInt(value.toString()));
753                } catch (NumberFormatException e) {
754                    log.error("move count must be a number");
755                }
756                break;
757            case VALUE_COLUMN:
758                car.setValue(value.toString());
759                break;
760            case RFID_COLUMN:
761                car.setRfid(value.toString());
762                break;
763            case WAIT_COLUMN:
764                try {
765                    car.setWait(Integer.parseInt(value.toString()));
766                } catch (NumberFormatException e) {
767                    log.error("wait count must be a number");
768                }
769                break;
770            default:
771                break;
772        }
773    }
774
775    public void dispose() {
776        carManager.removePropertyChangeListener(this);
777        removePropertyChangeCars();
778        if (csf != null) {
779            csf.dispose();
780        }
781        if (cef != null) {
782            cef.dispose();
783        }
784    }
785
786    private void addPropertyChangeCars() {
787        for (Car car : carManager.getList()) {
788            car.addPropertyChangeListener(this);
789        }
790    }
791
792    private void removePropertyChangeCars() {
793        for (Car car : carManager.getList()) {
794            car.removePropertyChangeListener(this);
795        }
796    }
797
798    @Override
799    public void propertyChange(PropertyChangeEvent e) {
800        if (Control.SHOW_PROPERTY) {
801            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
802                    e.getNewValue());
803        }
804        if (e.getPropertyName().equals(CarManager.LISTLENGTH_CHANGED_PROPERTY)) {
805            updateList();
806            fireTableDataChanged();
807        } // must be a car change
808        else if (e.getSource().getClass().equals(Car.class)) {
809            Car car = (Car) e.getSource();
810            int row = carList.indexOf(car);
811            if (Control.SHOW_PROPERTY) {
812                log.debug("Update car table row: {}", row);
813            }
814            if (row >= 0) {
815                fireTableRowsUpdated(row, row);
816                // next is needed when only showing cars at a location or track
817            } else if (e.getPropertyName().equals(Car.TRACK_CHANGED_PROPERTY)) {
818                updateList();
819                fireTableDataChanged();
820            }
821        }
822    }
823
824    private final static Logger log = LoggerFactory.getLogger(CarsTableModel.class);
825}