001package jmri.jmrit.operations.trains.schedules;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.List;
006import java.util.Optional;
007
008import javax.swing.JTable;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.InstanceManager;
014import jmri.jmrit.beantable.EnablingCheckboxRenderer;
015import jmri.jmrit.operations.setup.Control;
016import jmri.jmrit.operations.trains.Train;
017import jmri.jmrit.operations.trains.TrainManager;
018import jmri.swing.JTablePersistenceManager;
019import jmri.util.swing.XTableColumnModel;
020
021/**
022 * Table Model for edit of train schedules used by operations
023 *
024 * @author Daniel Boudreau Copyright (C) 2010, 2012
025 */
026public class TrainsScheduleTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
027
028    TrainManager trainManager = InstanceManager.getDefault(TrainManager.class);
029    TrainScheduleManager scheduleManager = InstanceManager.getDefault(TrainScheduleManager.class);
030
031    // Defines the columns
032    private static final int IDCOLUMN = 0;
033    private static final int NAMECOLUMN = IDCOLUMN + 1;
034    private static final int DESCRIPTIONCOLUMN = NAMECOLUMN + 1;
035
036    private static final int FIXEDCOLUMN = DESCRIPTIONCOLUMN + 1;
037
038    public TrainsScheduleTableModel() {
039        super();
040        trainManager.addPropertyChangeListener(this);
041        scheduleManager.addPropertyChangeListener(this);
042        updateList();
043        addPropertyChangeTrainSchedules();
044    }
045
046    public final int SORTBYNAME = 1;
047    public final int SORTBYTIME = 2;
048    public final int SORTBYDEPARTS = 3;
049    public final int SORTBYTERMINATES = 4;
050    public final int SORTBYROUTE = 5;
051    public final int SORTBYID = 6;
052
053    private int _sort = SORTBYTIME;
054
055    public void setSort(int sort) {
056        _sort = sort;
057        updateList();
058        //fireTableStructureChanged();
059        initTable();
060        if (_table.getRowSorter() != null) {
061            _table.getRowSorter().setSortKeys(null);
062        }
063    }
064
065    private void updateList() {
066        // first, remove listeners from the individual objects
067        removePropertyChangeTrains();
068
069        if (_sort == SORTBYID) {
070            sysList = trainManager.getTrainsByIdList();
071        } else if (_sort == SORTBYNAME) {
072            sysList = trainManager.getTrainsByNameList();
073        } else if (_sort == SORTBYTIME) {
074            sysList = trainManager.getTrainsByTimeList();
075        } else if (_sort == SORTBYDEPARTS) {
076            sysList = trainManager.getTrainsByDepartureList();
077        } else if (_sort == SORTBYTERMINATES) {
078            sysList = trainManager.getTrainsByTerminatesList();
079        } else if (_sort == SORTBYROUTE) {
080            sysList = trainManager.getTrainsByRouteList();
081        }
082
083        // and add listeners back in
084        addPropertyChangeTrains();
085    }
086
087    public List<Train> getSelectedTrainList() {
088        return sysList;
089    }
090
091    List<Train> sysList = null;
092    JTable _table = null;
093    XTableColumnModel _tcm = null;
094    TrainsScheduleTableFrame _frame = null;
095
096    void initTable(JTable table, TrainsScheduleTableFrame frame) {
097        _table = table;
098        _frame = frame;
099        initTable();
100    }
101
102    // only the first three columns of the table have defaults
103    private final int[] tableScheduleColumnWidths = {50, 70, 120};
104
105    void initTable() {
106        if (_table == null) {
107            return;
108        }
109
110        // Save table column order
111        _tcm = new XTableColumnModel();
112        _table.setColumnModel(_tcm);
113        _table.createDefaultColumnsFromModel();
114
115        // Install the button handlers
116        _table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
117
118        // set column preferred widths
119        for (int i = 0; i < tableScheduleColumnWidths.length; i++) {
120            _tcm.getColumn(i).setPreferredWidth(tableScheduleColumnWidths[i]);
121        }
122        _frame.loadTableDetails(_table);
123    }
124
125    @Override
126    public int getRowCount() {
127        return sysList.size();
128    }
129
130    public int getFixedColumn() {
131        return FIXEDCOLUMN;
132    }
133
134    @Override
135    public int getColumnCount() {
136        return getFixedColumn() + scheduleManager.numEntries();
137    }
138
139    @Override
140    public String getColumnName(int col) {
141        switch (col) {
142            case IDCOLUMN:
143                if (_sort == SORTBYID) {
144                    return Bundle.getMessage("Id");
145                }
146                return Bundle.getMessage("Time");
147            case NAMECOLUMN:
148                return Bundle.getMessage("Name");
149            case DESCRIPTIONCOLUMN:
150                return Bundle.getMessage("Description");
151            default:
152                // fall out
153                break;
154        }
155        TrainSchedule ts = getSchedule(col);
156        if (ts != null) {
157            return ts.getName();
158        }
159        return "unknown"; // NOI18N
160    }
161
162    @Override
163    public Class<?> getColumnClass(int col) {
164        switch (col) {
165            case IDCOLUMN:
166                return String.class;
167            case NAMECOLUMN:
168                return String.class;
169            case DESCRIPTIONCOLUMN:
170                return String.class;
171            default:
172                // fall out
173                break;
174        }
175        if (col >= getFixedColumn() && col < getColumnCount()) {
176            return Boolean.class;
177        }
178        return null;
179    }
180
181    @Override
182    public boolean isCellEditable(int row, int col) {
183        switch (col) {
184            case IDCOLUMN:
185            case NAMECOLUMN:
186            case DESCRIPTIONCOLUMN:
187                return false;
188            default:
189                return true;
190        }
191    }
192
193    @Override
194    public Object getValueAt(int row, int col) {
195        if (row >= getRowCount()) {
196            return "ERROR row " + row; // NOI18N
197        }
198        Train train = sysList.get(row);
199        if (train == null) {
200            return "ERROR train unknown " + row; // NOI18N
201        }
202        switch (col) {
203            case IDCOLUMN: {
204                if (_sort == SORTBYID) {
205                    return train.getId();
206                }
207                return train.getDepartureTime();
208            }
209            case NAMECOLUMN:
210                return train.getIconName();
211            case DESCRIPTIONCOLUMN:
212                return train.getDescription();
213            default:
214                // fall out
215                break;
216        }
217        TrainSchedule ts = getSchedule(col);
218        if (ts != null) {
219            return ts.containsTrainId(train.getId());
220        }
221        return "unknown " + col; // NOI18N
222    }
223
224    @Override
225    public void setValueAt(Object value, int row, int col) {
226        TrainSchedule ts = getSchedule(col);
227        if (ts != null) {
228            Train train = sysList.get(row);
229            if (train == null) {
230                log.error("train not found");
231                return;
232            }
233            if (((Boolean) value).booleanValue()) {
234                ts.addTrainId(train.getId());
235            } else {
236                ts.removeTrainId(train.getId());
237            }
238        }
239    }
240
241    @Override
242    public void propertyChange(PropertyChangeEvent e) {
243        if (Control.SHOW_PROPERTY) {
244            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
245                    .getNewValue());
246        }
247        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY) ||
248                e.getPropertyName().equals(TrainManager.TRAIN_ACTION_CHANGED_PROPERTY)) {
249            updateList();
250            fireTableDataChanged();
251        } else if (e.getPropertyName().equals(TrainScheduleManager.LISTLENGTH_CHANGED_PROPERTY) ||
252                e.getPropertyName().equals(TrainSchedule.NAME_CHANGED_PROPERTY)) {
253            propertyChangeNameOrLength(e);
254        } else if (e.getPropertyName().equals(TrainSchedule.SCHEDULE_CHANGED_PROPERTY)) {
255            fireTableDataChanged();
256        } else if (e.getSource().getClass().equals(Train.class)) {
257            Train train = (Train) e.getSource();
258            int row = sysList.indexOf(train);
259            if (Control.SHOW_PROPERTY) {
260                log.debug("Update train table row: {} name: {}", row, train.getName());
261            }
262            if (row >= 0) {
263                fireTableRowsUpdated(row, row);
264            }
265        }
266    }
267
268    private void propertyChangeNameOrLength(PropertyChangeEvent e) {
269        // update property change
270        removePropertyChangeTrainSchedules();
271        addPropertyChangeTrainSchedules();
272        // persistence manager wasn't notified that a column was deleted
273        if (e.getPropertyName().equals(TrainScheduleManager.LISTLENGTH_CHANGED_PROPERTY) &&
274                (Integer) e.getOldValue() > (Integer) e.getNewValue()) {
275            Optional<JTablePersistenceManager> manager =
276                    InstanceManager.getOptionalDefault(JTablePersistenceManager.class);
277            if (manager.isPresent()) {
278                // deletes column data from xml file
279                manager.get().clearState(_table);
280                // force the table to be saved
281                _tcm.getColumn(0).setPreferredWidth(_tcm.getColumn(0).getPreferredWidth() + 1);
282            }
283        }
284        initTable();
285    }
286
287    public TrainSchedule getSchedule(int col) {
288        if (col >= getFixedColumn() && col < getColumnCount()) {
289            List<TrainSchedule> trainSchedules = scheduleManager.getSchedulesByIdList();
290            TrainSchedule ts = trainSchedules.get(col - getFixedColumn());
291            return ts;
292        }
293        return null;
294    }
295
296    private void removePropertyChangeTrainSchedules() {
297        List<TrainSchedule> trainSchedules = scheduleManager.getSchedulesByIdList();
298        for (TrainSchedule ts : trainSchedules) {
299            ts.removePropertyChangeListener(this);
300        }
301    }
302
303    private void addPropertyChangeTrainSchedules() {
304        List<TrainSchedule> trainSchedules = scheduleManager.getSchedulesByIdList();
305        for (TrainSchedule ts : trainSchedules) {
306            ts.addPropertyChangeListener(this);
307        }
308    }
309
310    private void removePropertyChangeTrains() {
311        if (sysList != null) {
312            for (Train train : sysList) {
313                train.removePropertyChangeListener(this);
314            }
315        }
316    }
317
318    private void addPropertyChangeTrains() {
319        if (sysList != null) {
320            for (Train train : sysList) {
321                train.addPropertyChangeListener(this);
322            }
323        }
324    }
325
326    public void dispose() {
327        trainManager.removePropertyChangeListener(this);
328        scheduleManager.removePropertyChangeListener(this);
329        removePropertyChangeTrains();
330        removePropertyChangeTrainSchedules();
331    }
332
333    private final static Logger log = LoggerFactory.getLogger(TrainsScheduleTableModel.class);
334}