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