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