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