001package jmri.jmrit.operations.locations.gui;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006
007import javax.swing.*;
008import javax.swing.event.*;
009import javax.swing.table.TableColumn;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014import jmri.InstanceManager;
015import jmri.jmrit.operations.OperationsPanel;
016import jmri.jmrit.operations.OperationsTableModel;
017import jmri.jmrit.operations.locations.*;
018import jmri.jmrit.operations.locations.tools.TrackEditCommentsFrame;
019import jmri.jmrit.operations.setup.Control;
020import jmri.jmrit.operations.setup.Setup;
021import jmri.util.swing.XTableColumnModel;
022import jmri.util.table.ButtonEditor;
023import jmri.util.table.ButtonRenderer;
024
025/**
026 * Table Model for edit of tracks used by operations
027 *
028 * @author Daniel Boudreau Copyright (C) 2008, 2011, 2012
029 */
030public abstract class TrackTableModel extends OperationsTableModel implements PropertyChangeListener, TableColumnModelListener {
031
032    protected Location _location;
033    protected List<Track> _tracksList = new ArrayList<>();
034    protected String _trackType;
035    protected JTable _table;
036    protected boolean _dirty = false;
037
038    // Defines the columns
039    protected static final int ID_COLUMN = 0;
040    protected static final int NAME_COLUMN = 1;
041    protected static final int LENGTH_COLUMN = 2;
042    protected static final int USED_LENGTH_COLUMN = 3;
043    protected static final int RESERVED_COLUMN = 4;
044    protected static final int MOVES_COLUMN = 5;
045    protected static final int CARS_COLUMN = 6;
046    protected static final int LOCOS_COLUMN = 7;
047    protected static final int PICKUPS_COLUMN = 8;
048    protected static final int SETOUT_COLUMN = 9;
049    protected static final int SCHEDULE_COLUMN = 10;
050    protected static final int ROAD_COLUMN = 11;
051    protected static final int LOAD_COLUMN = 12;
052    protected static final int DEFAULT_LOAD_COLUMN = 13;
053    protected static final int CUSTOM_LOAD_COLUMN = 14;
054    protected static final int DISABLE_LOAD_CHANGE_COLUMN = 15;
055    protected static final int QUICK_SERVICE_COLUMN = 16;
056    protected static final int SHIP_COLUMN = 17;
057    protected static final int RESTRICTION_COLUMN = 18;
058    protected static final int DESTINATION_COLUMN = 19;
059    protected static final int ROUTED_COLUMN = 20;
060    protected static final int HOLD_COLUMN = 21;
061    protected static final int POOL_COLUMN = 22;
062    protected static final int PLANPICKUP_COLUMN = 23;
063    protected static final int ALT_TRACK_COLUMN = 24;
064    protected static final int ORDER_COLUMN = 25;
065    protected static final int TRAIN_DIRECTION_COLUMN = 26;
066    protected static final int REPORTER_COLUMN = 27;
067    protected static final int COMMENT_COLUMN = 28;
068    protected static final int EDIT_COLUMN = 29;
069
070    protected static final int HIGHESTCOLUMN = EDIT_COLUMN + 1;
071
072    public TrackTableModel() {
073        super();
074    }
075
076    protected void initTable(JTable table, Location location, String trackType) {
077        _table = table;
078        _location = location;
079        _trackType = trackType;
080        if (_location != null) {
081            _location.addPropertyChangeListener(this);
082        }
083        Setup.getDefault().addPropertyChangeListener(this);
084        updateList();
085        initTable();
086    }
087
088    private void updateList() {
089        if (_location == null) {
090            return;
091        }
092        // first, remove listeners from the individual objects
093        removePropertyChangeTracks();
094
095        _tracksList = _location.getTracksByNameList(_trackType);
096        // and add them back in
097        for (Track track : _tracksList) {
098            track.addPropertyChangeListener(this);
099        }
100    }
101
102    private void initTable() {
103        // Use XTableColumnModel so we can control which columns are visible
104        XTableColumnModel tcm = new XTableColumnModel();
105        _table.setColumnModel(tcm);
106        _table.createDefaultColumnsFromModel();
107
108        // set column preferred widths
109        tcm.getColumn(ID_COLUMN).setPreferredWidth(40);
110        tcm.getColumn(NAME_COLUMN).setPreferredWidth(200);
111        tcm.getColumn(LENGTH_COLUMN).setPreferredWidth(
112                Math.max(50, new JLabel(getColumnName(LENGTH_COLUMN)).getPreferredSize().width + 10));
113        tcm.getColumn(USED_LENGTH_COLUMN).setPreferredWidth(50);
114        tcm.getColumn(RESERVED_COLUMN).setPreferredWidth(
115                Math.max(65, new JLabel(getColumnName(RESERVED_COLUMN)).getPreferredSize().width + 10));
116        tcm.getColumn(MOVES_COLUMN).setPreferredWidth(60);
117        tcm.getColumn(LOCOS_COLUMN).setPreferredWidth(60);
118        tcm.getColumn(CARS_COLUMN).setPreferredWidth(60);
119        tcm.getColumn(PICKUPS_COLUMN).setPreferredWidth(
120                Math.max(60, new JLabel(getColumnName(PICKUPS_COLUMN)).getPreferredSize().width + 10));
121        tcm.getColumn(SETOUT_COLUMN).setPreferredWidth(
122                Math.max(60, new JLabel(getColumnName(SETOUT_COLUMN)).getPreferredSize().width + 10));
123        tcm.getColumn(SCHEDULE_COLUMN).setPreferredWidth(
124                Math.max(90, new JLabel(getColumnName(SCHEDULE_COLUMN)).getPreferredSize().width + 10));
125        tcm.getColumn(RESTRICTION_COLUMN).setPreferredWidth(90);
126        tcm.getColumn(LOAD_COLUMN).setPreferredWidth(50);
127        tcm.getColumn(DEFAULT_LOAD_COLUMN).setPreferredWidth(60);
128        tcm.getColumn(CUSTOM_LOAD_COLUMN).setPreferredWidth(90);
129        tcm.getColumn(DISABLE_LOAD_CHANGE_COLUMN).setPreferredWidth(50);
130        tcm.getColumn(QUICK_SERVICE_COLUMN).setPreferredWidth(50);
131        tcm.getColumn(SHIP_COLUMN).setPreferredWidth(50);
132        tcm.getColumn(ROAD_COLUMN).setPreferredWidth(50);
133        tcm.getColumn(DESTINATION_COLUMN).setPreferredWidth(50);
134        tcm.getColumn(ROUTED_COLUMN).setPreferredWidth(50);
135        tcm.getColumn(HOLD_COLUMN).setPreferredWidth(50);
136        tcm.getColumn(POOL_COLUMN).setPreferredWidth(70);
137        tcm.getColumn(PLANPICKUP_COLUMN).setPreferredWidth(70);
138        tcm.getColumn(ALT_TRACK_COLUMN).setPreferredWidth(120);
139        tcm.getColumn(ORDER_COLUMN)
140                .setPreferredWidth(Math.max(50, new JLabel(getColumnName(ORDER_COLUMN)).getPreferredSize().width + 10));
141        tcm.getColumn(TRAIN_DIRECTION_COLUMN).setPreferredWidth(30);
142        tcm.getColumn(REPORTER_COLUMN).setPreferredWidth(70);
143        tcm.getColumn(COMMENT_COLUMN).setPreferredWidth(80);
144        tcm.getColumn(EDIT_COLUMN).setPreferredWidth(80);
145
146        tcm.getColumn(COMMENT_COLUMN).setCellRenderer(new ButtonRenderer());
147        tcm.getColumn(COMMENT_COLUMN).setCellEditor(new ButtonEditor(new JButton()));
148        tcm.getColumn(EDIT_COLUMN).setCellRenderer(new ButtonRenderer());
149        tcm.getColumn(EDIT_COLUMN).setCellEditor(new ButtonEditor(new JButton()));
150
151        OperationsPanel.loadTableDetails(_table, "Location");
152        setColumnsVisible();
153        addTableColumnListeners();
154    }
155
156    // only show columns if they are needed
157    private void setColumnsVisible() {
158        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
159        tcm.setColumnVisible(tcm.getColumnByModelIndex(ID_COLUMN),
160                InstanceManager.getDefault(LocationManager.class).isShowIdEnabled());
161        tcm.setColumnVisible(tcm.getColumnByModelIndex(SCHEDULE_COLUMN),
162                _location.hasSchedules() && _trackType.equals(Track.SPUR));
163        tcm.setColumnVisible(tcm.getColumnByModelIndex(RESTRICTION_COLUMN),
164                _location.hasServiceRestrictions() && !_trackType.equals(Track.YARD));
165        tcm.setColumnVisible(tcm.getColumnByModelIndex(LOAD_COLUMN), _location.hasLoadRestrictions());
166        tcm.setColumnVisible(tcm.getColumnByModelIndex(DEFAULT_LOAD_COLUMN), _trackType.equals(Track.STAGING));
167        tcm.setColumnVisible(tcm.getColumnByModelIndex(CUSTOM_LOAD_COLUMN), _trackType.equals(Track.STAGING));
168        tcm.setColumnVisible(tcm.getColumnByModelIndex(DISABLE_LOAD_CHANGE_COLUMN),
169                _location.hasDisableLoadChange() && _trackType.equals(Track.SPUR));
170        tcm.setColumnVisible(tcm.getColumnByModelIndex(QUICK_SERVICE_COLUMN),
171                _location.hasQuickLoadService() && _trackType.equals(Track.SPUR));
172        tcm.setColumnVisible(tcm.getColumnByModelIndex(SHIP_COLUMN), _location.hasShipLoadRestrictions());
173        tcm.setColumnVisible(tcm.getColumnByModelIndex(ROAD_COLUMN), _location.hasRoadRestrictions());
174        tcm.setColumnVisible(tcm.getColumnByModelIndex(DESTINATION_COLUMN), _location.hasDestinationRestrictions() &&
175                (_trackType.equals(Track.INTERCHANGE) || _trackType.equals(Track.STAGING)));
176        tcm.setColumnVisible(tcm.getColumnByModelIndex(ROUTED_COLUMN), _trackType.equals(Track.INTERCHANGE) ||
177                _trackType.equals(Track.STAGING));
178        tcm.setColumnVisible(tcm.getColumnByModelIndex(HOLD_COLUMN),
179                _location.hasSchedules() && _location.hasAlternateTracks() && _trackType.equals(Track.SPUR));
180        tcm.setColumnVisible(tcm.getColumnByModelIndex(PLANPICKUP_COLUMN), _location.hasPlannedPickups());
181        tcm.setColumnVisible(tcm.getColumnByModelIndex(POOL_COLUMN), _location.hasPools());
182        tcm.setColumnVisible(tcm.getColumnByModelIndex(ALT_TRACK_COLUMN), _location.hasAlternateTracks());
183        tcm.setColumnVisible(tcm.getColumnByModelIndex(ORDER_COLUMN),
184                _location.hasOrderRestrictions() && !_trackType.equals(Track.SPUR));
185        tcm.setColumnVisible(tcm.getColumnByModelIndex(TRAIN_DIRECTION_COLUMN),
186                _location.hasTracksWithRestrictedTrainDirections());
187        tcm.setColumnVisible(tcm.getColumnByModelIndex(REPORTER_COLUMN),
188                Setup.isRfidEnabled() && _location.hasReporters());
189        tcm.setColumnVisible(tcm.getColumnByModelIndex(MOVES_COLUMN), Setup.isShowTrackMovesEnabled());
190        tcm.setColumnVisible(tcm.getColumnByModelIndex(COMMENT_COLUMN), _location.hasTrackMessages());
191    }
192
193    /*
194     * Persisting using JmriJTablePersistenceManager doesn't quite work since
195     * the same table name is used for each track type; spur, yard, interchange,
196     * and staging. Plus multiple edit locations can be open at the same time,
197     * again using the same table name. The goal is to have a single change
198     * affect every table for all edit locations. Therefore any changes to
199     * column width or position is saved when the edit location window is
200     * closed.
201     */
202    private void addTableColumnListeners() {
203        Enumeration<TableColumn> e = _table.getColumnModel().getColumns();
204        while (e.hasMoreElements()) {
205            TableColumn column = e.nextElement();
206            column.addPropertyChangeListener(this);
207        }
208        _table.getColumnModel().addColumnModelListener(this);
209    }
210
211    /**
212     * Table is dirty when a column is moved or width is changed.
213     * 
214     * @param dirty set true if dirty
215     */
216    private void setDirty(boolean dirty) {
217        _dirty = dirty;
218    }
219
220    private boolean isDirty() {
221        return _dirty;
222    }
223
224    @Override
225    public int getRowCount() {
226        return _tracksList.size();
227    }
228
229    @Override
230    public int getColumnCount() {
231        return HIGHESTCOLUMN;
232    }
233
234    @Override
235    public String getColumnName(int col) {
236        switch (col) {
237            case ID_COLUMN:
238                return Bundle.getMessage("Id");
239            case NAME_COLUMN:
240                return Bundle.getMessage("TrackName");
241            case LENGTH_COLUMN:
242                return Bundle.getMessage("Length");
243            case USED_LENGTH_COLUMN:
244                return Bundle.getMessage("Used");
245            case RESERVED_COLUMN:
246                return Bundle.getMessage("Reserved");
247            case MOVES_COLUMN:
248                return Bundle.getMessage("Moves");
249            case LOCOS_COLUMN:
250                return Bundle.getMessage("Engines");
251            case CARS_COLUMN:
252                return Bundle.getMessage("Cars");
253            case PICKUPS_COLUMN:
254                return Bundle.getMessage("Pickups");
255            case SETOUT_COLUMN:
256                return Bundle.getMessage("Drop");
257            case SCHEDULE_COLUMN:
258                return Bundle.getMessage("Schedule");
259            case RESTRICTION_COLUMN:
260                return Bundle.getMessage("Restrictions");
261            case LOAD_COLUMN:
262                return Bundle.getMessage("Load");
263            case DEFAULT_LOAD_COLUMN:
264                return Bundle.getMessage("LoadDefaultAbv");
265            case CUSTOM_LOAD_COLUMN:
266                return Bundle.getMessage("LoadCustomAbv");
267            case DISABLE_LOAD_CHANGE_COLUMN:
268                return Bundle.getMessage("DisableLoadChange");
269            case QUICK_SERVICE_COLUMN:
270                return Bundle.getMessage("QuickLoadService");
271            case SHIP_COLUMN:
272                return Bundle.getMessage("Ship");
273            case ROAD_COLUMN:
274                return Bundle.getMessage("Road");
275            case DESTINATION_COLUMN:
276                return Bundle.getMessage("Dest");
277            case ROUTED_COLUMN:
278                return Bundle.getMessage("Routed");
279            case HOLD_COLUMN:
280                return Bundle.getMessage("Hold");
281            case POOL_COLUMN:
282                return Bundle.getMessage("Pool");
283            case PLANPICKUP_COLUMN:
284                return Bundle.getMessage("PlanPickUp");
285            case ALT_TRACK_COLUMN:
286                return Bundle.getMessage("AlternateTrack");
287            case ORDER_COLUMN:
288                return Bundle.getMessage("ServiceOrder");
289            case TRAIN_DIRECTION_COLUMN:
290                return Bundle.getMessage("AbbrevationDirection");
291            case REPORTER_COLUMN:
292                return Bundle.getMessage("Reporters");
293            case COMMENT_COLUMN:
294                return Bundle.getMessage("Comment");
295            case EDIT_COLUMN:
296                return Bundle.getMessage("ButtonEdit");
297            default:
298                return "unknown"; // NOI18N
299        }
300    }
301
302    @Override
303    public Class<?> getColumnClass(int col) {
304        switch (col) {
305            case ID_COLUMN:
306            case NAME_COLUMN:
307            case SCHEDULE_COLUMN:
308            case RESTRICTION_COLUMN:
309            case LOAD_COLUMN:
310            case DEFAULT_LOAD_COLUMN:
311            case CUSTOM_LOAD_COLUMN:
312            case SHIP_COLUMN:
313            case ROAD_COLUMN:
314            case DESTINATION_COLUMN:
315            case POOL_COLUMN:
316            case PLANPICKUP_COLUMN:
317            case ALT_TRACK_COLUMN:
318            case ORDER_COLUMN:
319            case TRAIN_DIRECTION_COLUMN:
320            case REPORTER_COLUMN:
321                return String.class;
322            case LENGTH_COLUMN:
323            case USED_LENGTH_COLUMN:
324            case RESERVED_COLUMN:
325            case MOVES_COLUMN:
326            case LOCOS_COLUMN:
327            case CARS_COLUMN:
328            case PICKUPS_COLUMN:
329            case SETOUT_COLUMN:
330                return Integer.class;
331            case COMMENT_COLUMN:
332            case EDIT_COLUMN:
333                return JButton.class;
334            case DISABLE_LOAD_CHANGE_COLUMN:
335            case QUICK_SERVICE_COLUMN:
336            case ROUTED_COLUMN:
337            case HOLD_COLUMN:
338                return Boolean.class;
339            default:
340                return null;
341        }
342    }
343
344    @Override
345    public boolean isCellEditable(int row, int col) {
346        switch (col) {
347            case MOVES_COLUMN:
348            case DISABLE_LOAD_CHANGE_COLUMN:
349            case QUICK_SERVICE_COLUMN:
350            case ROUTED_COLUMN:
351            case COMMENT_COLUMN:
352            case EDIT_COLUMN:
353                return true;
354            case HOLD_COLUMN:
355                return isHoldCarEnabled(row);
356            default:
357                return false;
358        }
359    }
360
361    private boolean isHoldCarEnabled(int row) {
362        Track track = _tracksList.get(row);
363        return track.getAlternateTrack() != null && track.getSchedule() != null;
364    }
365
366    @Override
367    public Object getValueAt(int row, int col) {
368        if (row >= getRowCount()) {
369            return "ERROR row " + row; // NOI18N
370        }
371        Track track = _tracksList.get(row);
372        if (track == null) {
373            return "ERROR track unknown " + row; // NOI18N
374        }
375        switch (col) {
376            case ID_COLUMN:
377                return track.getId();
378            case NAME_COLUMN:
379                return track.getName();
380            case LENGTH_COLUMN:
381                return track.getLength();
382            case USED_LENGTH_COLUMN:
383                return track.getUsedLength();
384            case RESERVED_COLUMN:
385                return track.getReserved();
386            case MOVES_COLUMN:
387                return track.getMoves();
388            case LOCOS_COLUMN:
389                return track.getNumberEngines();
390            case CARS_COLUMN:
391                return track.getNumberCars();
392            case PICKUPS_COLUMN:
393                return track.getPickupRS();
394            case SETOUT_COLUMN:
395                return track.getDropRS();
396            case SCHEDULE_COLUMN:
397                return track.getScheduleName();
398            case RESTRICTION_COLUMN:
399                return getRestrictions(track);
400            case LOAD_COLUMN:
401                return getModifiedString(track.getLoadNames().length, track.getLoadOption().equals(Track.ALL_LOADS),
402                        track.getLoadOption().equals(Track.INCLUDE_LOADS));
403            case DEFAULT_LOAD_COLUMN:
404                return getDefaultLoadString(track);
405            case CUSTOM_LOAD_COLUMN:
406                return getCustomLoadString(track);
407            case DISABLE_LOAD_CHANGE_COLUMN:
408                return track.isDisableLoadChangeEnabled();
409            case QUICK_SERVICE_COLUMN:
410                return track.isQuickServiceEnabled();
411            case SHIP_COLUMN:
412                return getModifiedString(track.getShipLoadNames().length,
413                        track.getShipLoadOption().equals(Track.ALL_LOADS),
414                        track.getShipLoadOption().equals(Track.INCLUDE_LOADS));
415            case ROAD_COLUMN:
416                return getModifiedString(track.getRoadNames().length, track.getRoadOption().equals(Track.ALL_ROADS),
417                        track.getRoadOption().equals(Track.INCLUDE_ROADS));
418            case DESTINATION_COLUMN: {
419                return getDestinationString(track);
420            }
421            case ROUTED_COLUMN:
422                return track.isOnlyCarsWithFinalDestinationEnabled();
423            case HOLD_COLUMN:
424                return track.isHoldCarsWithCustomLoadsEnabled();
425            case POOL_COLUMN:
426                return track.getPoolName();
427            case PLANPICKUP_COLUMN:
428                if (track.getIgnoreUsedLengthPercentage() > Track.IGNORE_0) {
429                    return track.getIgnoreUsedLengthPercentage() + "%";
430                }
431                return "";
432            case ALT_TRACK_COLUMN:
433                if (track.getAlternateTrack() != null) {
434                    return track.getAlternateTrack().getName();
435                }
436                if (track.isAlternate()) {
437                    return Bundle.getMessage("ButtonYes");
438                }
439                return "";
440            case ORDER_COLUMN:
441                return track.getServiceOrder();
442            case TRAIN_DIRECTION_COLUMN:
443                return getDirection(track);
444            case REPORTER_COLUMN:
445                return track.getReporterName();
446            case COMMENT_COLUMN:
447                if (track.hasMessages()) {
448                    return Bundle.getMessage("ButtonEdit");
449                }
450                return Bundle.getMessage("Add");
451            case EDIT_COLUMN:
452                return Bundle.getMessage("ButtonEdit");
453            default:
454                return "unknown " + col; // NOI18N
455        }
456    }
457
458    private String getRestrictions(Track track) {
459        StringBuffer restrictions = new StringBuffer();
460        if (!track.getDropOption().equals(Track.ANY)) {
461            String suffix = " ";
462            if (track.getDropOption().equals(Track.EXCLUDE_ROUTES) ||
463                    track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
464                suffix = "x";
465            }
466            restrictions.append(Bundle.getMessage("AbbreviationSetOut") + suffix + track.getDropIds().length);
467        }
468        if (!track.getPickupOption().equals(Track.ANY)) {
469            String suffix = " ";
470            if (track.getPickupOption().equals(Track.EXCLUDE_ROUTES) ||
471                    track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
472                suffix = "x";
473            }
474            restrictions.append(" " + Bundle.getMessage("AbbreviationPickUp") + suffix + track.getPickupIds().length);
475        }
476        return restrictions.toString();
477    }
478
479    private String getModifiedString(int number, boolean all, boolean accept) {
480        if (all) {
481            return "";
482        }
483        if (accept) {
484            return "A " + Integer.toString(number); // NOI18N
485        }
486        return "E " + Integer.toString(number); // NOI18N
487    }
488    
489    private String getDefaultLoadString(Track track) {
490        String defaultLoad = "";
491        if (track.isLoadSwapEnabled()) {
492            defaultLoad = Bundle.getMessage("ABV_SwapDefaultLoads");
493        }
494        if (track.isLoadEmptyEnabled()) {
495            defaultLoad = Bundle.getMessage("ABV_EmptyDefaultLoads");
496        }
497        return defaultLoad;
498    }
499    
500    private String getCustomLoadString(Track track) {
501        StringBuffer customLoad = new StringBuffer();
502        if (track.isRemoveCustomLoadsEnabled()) {
503            customLoad.append(Bundle.getMessage("ABV_EmptyCustomLoads") + " ");
504        }
505        if (track.isAddCustomLoadsEnabled()) {
506            customLoad.append(Bundle.getMessage("ABV_GenerateCustomLoad") + " ");
507        }
508        if (track.isAddCustomLoadsAnySpurEnabled()) {
509            customLoad.append(Bundle.getMessage("ABV_GenerateCustomLoadAnySpur") + " ");
510        }
511        if (track.isAddCustomLoadsAnyStagingTrackEnabled()) {
512            customLoad.append(Bundle.getMessage("ABV_GereateCustomLoadStaging"));
513        }
514        if (track.isBlockCarsEnabled()) {
515            customLoad.append(Bundle.getMessage("ABV_CarBlocking"));
516        }
517        return customLoad.toString();
518    }
519
520    private String getDestinationString(Track track) {
521        int size = track.getDestinationListSize();
522        if (track.getDestinationOption().equals(Track.EXCLUDE_DESTINATIONS)) {
523            size = InstanceManager.getDefault(LocationManager.class).getNumberOfLocations() - size;
524        } else if (size == 1 && track.getDestinationOption().equals(Track.INCLUDE_DESTINATIONS)) {
525            // if there's only one destination return the destination name
526            Location loc =
527                    InstanceManager.getDefault(LocationManager.class).getLocationById(track.getDestinationIds()[0]);
528            if (loc != null) {
529                return loc.getName();
530            }
531        }
532        return getModifiedString(size, track.getDestinationOption().equals(Track.ALL_DESTINATIONS),
533                track.getDestinationOption().equals(Track.INCLUDE_DESTINATIONS));
534    }
535
536    private String getDirection(Track track) {
537        int trainDirections = track.getLocation().getTrainDirections() & Setup.getTrainDirection();
538        if (trainDirections != (track.getTrainDirections() & trainDirections)) {
539            switch (track.getTrainDirections() & trainDirections) {
540                case Track.EAST:
541                    return Setup.EAST_DIR;
542                case Track.WEST:
543                    return Setup.WEST_DIR;
544                case Track.SOUTH:
545                    return Setup.SOUTH_DIR;
546                case Track.NORTH:
547                    return Setup.NORTH_DIR;
548                case 0:
549                    return Bundle.getMessage("None");
550                default:
551                    return "X";
552            }
553        }
554        return "";
555    }
556
557    @Override
558    public void setValueAt(Object value, int row, int col) {
559        switch (col) {
560            case DISABLE_LOAD_CHANGE_COLUMN:
561                setDisableLoadChange(row, value);
562                break;
563            case QUICK_SERVICE_COLUMN:
564                setQuickService(row, value);
565                break;
566            case ROUTED_COLUMN:
567                setRouted(row, value);
568                break;
569            case HOLD_COLUMN:
570                setHold(row, value);
571                break;
572            case COMMENT_COLUMN:
573                trackComment(row);
574                break;
575            case EDIT_COLUMN:
576                editTrack(row);
577                break;
578            case MOVES_COLUMN:
579                setMoves(row, value);
580                break;
581            default:
582                break;
583        }
584    }
585
586    TrackEditFrame tef = null;
587
588    abstract protected void editTrack(int row);
589
590    private void setDisableLoadChange(int row, Object value) {
591        Track track = _tracksList.get(row);
592        track.setDisableLoadChangeEnabled(((Boolean) value).booleanValue());
593    }
594
595    private void setQuickService(int row, Object value) {
596        Track track = _tracksList.get(row);
597        track.setQuickServiceEnabled(((Boolean) value).booleanValue());
598    }
599
600    private void setRouted(int row, Object value) {
601        Track track = _tracksList.get(row);
602        track.setOnlyCarsWithFinalDestinationEnabled(((Boolean) value).booleanValue());
603    }
604
605    private void setHold(int row, Object value) {
606        Track track = _tracksList.get(row);
607        track.setHoldCarsWithCustomLoadsEnabled(((Boolean) value).booleanValue());
608    }
609
610    private void setMoves(int row, Object value) {
611        Track track = _tracksList.get(row);
612        track.setMoves((int) value);
613    }
614
615    private void trackComment(int row) {
616        new TrackEditCommentsFrame(_tracksList.get(row));
617    }
618
619    private void removePropertyChangeTracks() {
620        for (Track track : _tracksList) {
621            track.removePropertyChangeListener(this);
622        }
623    }
624
625    public void dispose() {
626        if (_table != null) {
627            _table.getRowSorter().setSortKeys(null);
628            if (isDirty()) {
629                OperationsPanel.cacheState(_table);
630                OperationsPanel.saveTableState();
631                setDirty(false);
632            }
633        }
634        removePropertyChangeTracks();
635        if (_location != null) {
636            _location.removePropertyChangeListener(this);
637        }
638        if (tef != null) {
639            tef.dispose();
640        }
641        Setup.getDefault().removePropertyChangeListener(this);
642        _tracksList.clear();
643        fireTableDataChanged();
644    }
645
646    // this table listens for changes to a location and it's tracks
647    @Override
648    public void propertyChange(PropertyChangeEvent e) {
649        if (Control.SHOW_PROPERTY) {
650            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
651                    e.getNewValue());
652        }
653        if (e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) ||
654                e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY)) {
655            updateList();
656            fireTableDataChanged();
657        }
658        if (e.getPropertyName().equals(Setup.SHOW_TRACK_MOVES_PROPERTY_CHANGE) ||
659                e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
660                e.getPropertyName().equals(Setup.ROUTING_STAGING_PROPERTY_CHANGE)) {
661            setColumnsVisible();
662        }
663        if (e.getSource().getClass().equals(Track.class) &&
664                (e.getPropertyName().equals(Track.DROP_CHANGED_PROPERTY) ||
665                        e.getPropertyName().equals(Track.PICKUP_CHANGED_PROPERTY) ||
666                        e.getPropertyName().equals(Track.SCHEDULE_ID_CHANGED_PROPERTY) ||
667                        e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) ||
668                        e.getPropertyName().equals(Track.ROADS_CHANGED_PROPERTY) ||
669                        e.getPropertyName().equals(Track.DESTINATION_OPTIONS_CHANGED_PROPERTY) ||
670                        e.getPropertyName().equals(Track.POOL_CHANGED_PROPERTY) ||
671                        e.getPropertyName().equals(Track.PLANNED_PICKUPS_CHANGED_PROPERTY) ||
672                        e.getPropertyName().equals(Track.ALTERNATE_TRACK_CHANGED_PROPERTY) ||
673                        e.getPropertyName().equals(Track.SERVICE_ORDER_CHANGED_PROPERTY) ||
674                        e.getPropertyName().equals(Track.LOAD_OPTIONS_CHANGED_PROPERTY) ||
675                        e.getPropertyName().equals(Track.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
676                        e.getPropertyName().equals(Track.TRACK_COMMENT_CHANGED_PROPERTY) ||
677                        e.getPropertyName().equals(Track.TRACK_REPORTER_CHANGED_PROPERTY))) {
678            setColumnsVisible();
679        }
680        if (e.getSource() instanceof TableColumn && e.getPropertyName().equals("preferredWidth")) {
681            log.debug("Column width change");
682            setDirty(true);
683        }
684    }
685
686    @Override
687    public void columnAdded(TableColumnModelEvent e) {
688        // do nothing
689    }
690
691    @Override
692    public void columnRemoved(TableColumnModelEvent e) {
693        // do nothing
694    }
695
696    @Override
697    public void columnMoved(TableColumnModelEvent e) {
698        if (e.getFromIndex() != e.getToIndex()) {
699            log.debug("Table Column Moved");
700            setDirty(true);
701        }
702    }
703
704    @Override
705    public void columnMarginChanged(ChangeEvent e) {
706        // do nothing
707    }
708
709    @Override
710    public void columnSelectionChanged(ListSelectionEvent e) {
711        // do nothing
712    }
713
714    private final static Logger log = LoggerFactory.getLogger(TrackTableModel.class);
715}