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