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