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));
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            case DISABLE_LOAD_CHANGE_COLUMN:
333            case HOLD_COLUMN:
334            case EDIT_COLUMN:
335            case MOVES_COLUMN:
336                return true;
337            default:
338                return false;
339        }
340    }
341
342    @Override
343    public Object getValueAt(int row, int col) {
344        if (row >= getRowCount()) {
345            return "ERROR row " + row; // NOI18N
346        }
347        Track track = _tracksList.get(row);
348        if (track == null) {
349            return "ERROR track unknown " + row; // NOI18N
350        }
351        switch (col) {
352            case ID_COLUMN:
353                return track.getId();
354            case NAME_COLUMN:
355                return track.getName();
356            case LENGTH_COLUMN:
357                return track.getLength();
358            case USED_LENGTH_COLUMN:
359                return track.getUsedLength();
360            case RESERVED_COLUMN:
361                return track.getReserved();
362            case MOVES_COLUMN:
363                return track.getMoves();
364            case LOCOS_COLUMN:
365                return track.getNumberEngines();
366            case CARS_COLUMN:
367                return track.getNumberCars();
368            case PICKUPS_COLUMN:
369                return track.getPickupRS();
370            case SETOUT_COLUMN:
371                return track.getDropRS();
372            case SCHEDULE_COLUMN:
373                return track.getScheduleName();
374            case RESTRICTION_COLUMN:
375                return getRestrictions(track);
376            case LOAD_COLUMN:
377                return getModifiedString(track.getLoadNames().length, track.getLoadOption().equals(Track.ALL_LOADS),
378                        track.getLoadOption().equals(Track.INCLUDE_LOADS));
379            case DEFAULT_LOAD_COLUMN:
380                return getDefaultLoadString(track);
381            case CUSTOM_LOAD_COLUMN:
382                return getCustomLoadString(track);
383            case DISABLE_LOAD_CHANGE_COLUMN:
384                return track.isDisableLoadChangeEnabled();
385            case SHIP_COLUMN:
386                return getModifiedString(track.getShipLoadNames().length,
387                        track.getShipLoadOption().equals(Track.ALL_LOADS),
388                        track.getShipLoadOption().equals(Track.INCLUDE_LOADS));
389            case ROAD_COLUMN:
390                return getModifiedString(track.getRoadNames().length, track.getRoadOption().equals(Track.ALL_ROADS),
391                        track.getRoadOption().equals(Track.INCLUDE_ROADS));
392            case DESTINATION_COLUMN: {
393                return getDestinationString(track);
394            }
395            case ROUTED_COLUMN:
396                return track.isOnlyCarsWithFinalDestinationEnabled();
397            case HOLD_COLUMN:
398                return track.isHoldCarsWithCustomLoadsEnabled();
399            case POOL_COLUMN:
400                return track.getPoolName();
401            case PLANPICKUP_COLUMN:
402                if (track.getIgnoreUsedLengthPercentage() > Track.IGNORE_0) {
403                    return track.getIgnoreUsedLengthPercentage() + "%";
404                }
405                return "";
406            case ALT_TRACK_COLUMN:
407                if (track.getAlternateTrack() != null) {
408                    return track.getAlternateTrack().getName();
409                }
410                if (track.isAlternate()) {
411                    return Bundle.getMessage("ButtonYes");
412                }
413                return "";
414            case ORDER_COLUMN:
415                return track.getServiceOrder();
416            case TRAIN_DIRECTION_COLUMN:
417                int trainDirections = track.getLocation().getTrainDirections() & Setup.getTrainDirection();
418                if (trainDirections != (track.getTrainDirections() & trainDirections)) {
419                    return "X";
420                }
421                return "";
422            case REPORTER_COLUMN:
423                return track.getReporterName();
424            case EDIT_COLUMN:
425                return Bundle.getMessage("ButtonEdit");
426            default:
427                return "unknown " + col; // NOI18N
428        }
429    }
430
431    private String getRestrictions(Track track) {
432        StringBuffer restrictions = new StringBuffer();
433        if (!track.getDropOption().equals(Track.ANY)) {
434            String suffix = " ";
435            if (track.getDropOption().equals(Track.EXCLUDE_ROUTES) ||
436                    track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
437                suffix = "x";
438            }
439            restrictions.append(Bundle.getMessage("AbbreviationSetOut") + suffix + track.getDropIds().length);
440        }
441        if (!track.getPickupOption().equals(Track.ANY)) {
442            String suffix = " ";
443            if (track.getPickupOption().equals(Track.EXCLUDE_ROUTES) ||
444                    track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
445                suffix = "x";
446            }
447            restrictions.append(" " + Bundle.getMessage("AbbreviationPickUp") + suffix + track.getPickupIds().length);
448        }
449        return restrictions.toString();
450    }
451
452    private String getModifiedString(int number, boolean all, boolean accept) {
453        if (all) {
454            return "";
455        }
456        if (accept) {
457            return "A " + Integer.toString(number); // NOI18N
458        }
459        return "E " + Integer.toString(number); // NOI18N
460    }
461    
462    private String getDefaultLoadString(Track track) {
463        String defaultLoad = "";
464        if (track.isLoadSwapEnabled()) {
465            defaultLoad = Bundle.getMessage("ABV_SwapDefaultLoads");
466        }
467        if (track.isLoadEmptyEnabled()) {
468            defaultLoad = Bundle.getMessage("ABV_EmptyDefaultLoads");
469        }
470        return defaultLoad;
471    }
472    
473    private String getCustomLoadString(Track track) {
474        StringBuffer customLoad = new StringBuffer();
475        if (track.isRemoveCustomLoadsEnabled()) {
476            customLoad.append(Bundle.getMessage("ABV_EmptyCustomLoads") + " ");
477        }
478        if (track.isAddCustomLoadsEnabled()) {
479            customLoad.append(Bundle.getMessage("ABV_GenerateCustomLoad") + " ");
480        }
481        if (track.isAddCustomLoadsAnySpurEnabled()) {
482            customLoad.append(Bundle.getMessage("ABV_GenerateCustomLoadAnySpur") + " ");
483        }
484        if (track.isAddCustomLoadsAnyStagingTrackEnabled()) {
485            customLoad.append(Bundle.getMessage("ABV_GereateCustomLoadStaging"));
486        }
487        if (track.isBlockCarsEnabled()) {
488            customLoad.append(Bundle.getMessage("ABV_CarBlocking"));
489        }
490        return customLoad.toString();
491    }
492
493    private String getDestinationString(Track track) {
494        int size = track.getDestinationListSize();
495        if (track.getDestinationOption().equals(Track.EXCLUDE_DESTINATIONS)) {
496            size = InstanceManager.getDefault(LocationManager.class).getNumberOfLocations() - size;
497        } else if (size == 1 && track.getDestinationOption().equals(Track.INCLUDE_DESTINATIONS)) {
498            // if there's only one destination return the destination name
499            Location loc =
500                    InstanceManager.getDefault(LocationManager.class).getLocationById(track.getDestinationIds()[0]);
501            if (loc != null) {
502                return loc.getName();
503            }
504        }
505        return getModifiedString(size, track.getDestinationOption().equals(Track.ALL_DESTINATIONS),
506                track.getDestinationOption().equals(Track.INCLUDE_DESTINATIONS));
507    }
508
509    @Override
510    public void setValueAt(Object value, int row, int col) {
511        switch (col) {
512            case DISABLE_LOAD_CHANGE_COLUMN:
513                setDisableLoadChange(row, value);
514                break;
515            case ROUTED_COLUMN:
516                setRouted(row, value);
517                break;
518            case HOLD_COLUMN:
519                setHold(row, value);
520                break;
521            case EDIT_COLUMN:
522                editTrack(row);
523                break;
524            case MOVES_COLUMN:
525                setMoves(row, value);
526                break;
527            default:
528                break;
529        }
530    }
531
532    TrackEditFrame tef = null;
533
534    abstract protected void editTrack(int row);
535
536    private void setDisableLoadChange(int row, Object value) {
537        Track track = _tracksList.get(row);
538        track.setDisableLoadChangeEnabled(((Boolean) value).booleanValue());
539    }
540
541    private void setRouted(int row, Object value) {
542        Track track = _tracksList.get(row);
543        track.setOnlyCarsWithFinalDestinationEnabled(((Boolean) value).booleanValue());
544    }
545
546    private void setHold(int row, Object value) {
547        Track track = _tracksList.get(row);
548        track.setHoldCarsWithCustomLoadsEnabled(((Boolean) value).booleanValue());
549    }
550
551    private void setMoves(int row, Object value) {
552        Track track = _tracksList.get(row);
553        track.setMoves((int) value);
554    }
555
556    private void removePropertyChangeTracks() {
557        for (Track track : _tracksList) {
558            track.removePropertyChangeListener(this);
559        }
560    }
561
562    public void dispose() {
563        if (_table != null) {
564            _table.getRowSorter().setSortKeys(null);
565            if (isDirty()) {
566                OperationsPanel.cacheState(_table);
567                OperationsPanel.saveTableState();
568                setDirty(false);
569            }
570        }
571        removePropertyChangeTracks();
572        if (_location != null) {
573            _location.removePropertyChangeListener(this);
574        }
575        if (tef != null) {
576            tef.dispose();
577        }
578        Setup.getDefault().removePropertyChangeListener(this);
579        _tracksList.clear();
580        fireTableDataChanged();
581    }
582
583    // this table listens for changes to a location and it's tracks
584    @Override
585    public void propertyChange(PropertyChangeEvent e) {
586        if (Control.SHOW_PROPERTY) {
587            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
588                    e.getNewValue());
589        }
590        if (e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) ||
591                e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY)) {
592            updateList();
593            fireTableDataChanged();
594        }
595        if (e.getPropertyName().equals(Setup.SHOW_TRACK_MOVES_PROPERTY_CHANGE) ||
596                e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
597                e.getPropertyName().equals(Setup.ROUTING_STAGING_PROPERTY_CHANGE)) {
598            setColumnsVisible();
599        }
600        if (e.getSource().getClass().equals(Track.class) &&
601                (e.getPropertyName().equals(Track.DROP_CHANGED_PROPERTY) ||
602                        e.getPropertyName().equals(Track.PICKUP_CHANGED_PROPERTY) ||
603                        e.getPropertyName().equals(Track.SCHEDULE_ID_CHANGED_PROPERTY) ||
604                        e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) ||
605                        e.getPropertyName().equals(Track.ROADS_CHANGED_PROPERTY) ||
606                        e.getPropertyName().equals(Track.DESTINATION_OPTIONS_CHANGED_PROPERTY) ||
607                        e.getPropertyName().equals(Track.POOL_CHANGED_PROPERTY) ||
608                        e.getPropertyName().equals(Track.PLANNED_PICKUPS_CHANGED_PROPERTY) ||
609                        e.getPropertyName().equals(Track.ALTERNATE_TRACK_CHANGED_PROPERTY) ||
610                        e.getPropertyName().equals(Track.SERVICE_ORDER_CHANGED_PROPERTY) ||
611                        e.getPropertyName().equals(Track.LOAD_OPTIONS_CHANGED_PROPERTY) ||
612                        e.getPropertyName().equals(Track.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
613                        e.getPropertyName().equals(Track.TRACK_REPORTER_CHANGED_PROPERTY))) {
614            setColumnsVisible();
615        }
616        if (e.getSource() instanceof TableColumn && e.getPropertyName().equals("preferredWidth")) {
617            log.debug("Column width change");
618            setDirty(true);
619        }
620    }
621
622    @Override
623    public void columnAdded(TableColumnModelEvent e) {
624        // do nothing
625    }
626
627    @Override
628    public void columnRemoved(TableColumnModelEvent e) {
629        // do nothing
630    }
631
632    @Override
633    public void columnMoved(TableColumnModelEvent e) {
634        if (e.getFromIndex() != e.getToIndex()) {
635            log.debug("Table Column Moved");
636            setDirty(true);
637        }
638    }
639
640    @Override
641    public void columnMarginChanged(ChangeEvent e) {
642        // do nothing
643    }
644
645    @Override
646    public void columnSelectionChanged(ListSelectionEvent e) {
647        // do nothing
648    }
649
650    private final static Logger log = LoggerFactory.getLogger(TrackTableModel.class);
651}