001package jmri.jmrit.operations.routes.tools;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.JButton;
009import javax.swing.JComboBox;
010import javax.swing.JTable;
011import javax.swing.table.TableCellEditor;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import jmri.jmrit.operations.routes.Route;
017import jmri.jmrit.operations.routes.RouteLocation;
018import jmri.jmrit.operations.setup.Control;
019import jmri.util.swing.XTableColumnModel;
020import jmri.util.table.ButtonEditor;
021import jmri.util.table.ButtonRenderer;
022
023/**
024 * Table Model for edit of route blocking used by operations
025 *
026 * @author Daniel Boudreau Copyright (C) 2021
027 */
028public class RouteBlockingOrderEditTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
029
030    // Defines the columns
031    private static final int ID_COLUMN = 0;
032    private static final int NAME_COLUMN = ID_COLUMN + 1;
033    private static final int TRAIN_DIRECTION_COLUMN = NAME_COLUMN + 1;
034    private static final int UP_COLUMN = TRAIN_DIRECTION_COLUMN + 1;
035    private static final int DOWN_COLUMN = UP_COLUMN + 1;
036
037    private static final int HIGHEST_COLUMN = DOWN_COLUMN + 1;
038
039    private JTable _table;
040    private Route _route;
041    private RouteBlockingOrderEditFrame _frame;
042    List<RouteLocation> _blockingOrderList = new ArrayList<>();
043
044    public RouteBlockingOrderEditTableModel() {
045        super();
046    }
047
048    private void updateList() {
049        if (_route == null) {
050            return;
051        }
052        // first, remove listeners from the individual objects
053        removePropertyChangeRouteLocations();
054        _blockingOrderList = _route.getBlockingOrder();
055        // and add them back in
056        for (RouteLocation rl : _blockingOrderList) {
057            rl.addPropertyChangeListener(this);
058        }
059    }
060
061    protected void initTable(RouteBlockingOrderEditFrame frame, JTable table, Route route) {
062        _frame = frame;
063        _table = table;
064        _route = route;
065        if (_route != null) {
066            _route.addPropertyChangeListener(this);
067        }
068        initTable(table);
069    }
070
071    private void initTable(JTable table) {
072        // Use XTableColumnModel so we can control which columns are visible
073        XTableColumnModel tcm = new XTableColumnModel();
074        _table.setColumnModel(tcm);
075        _table.createDefaultColumnsFromModel();
076        // Install the button handlers
077        ButtonRenderer buttonRenderer = new ButtonRenderer();
078        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
079        tcm.getColumn(UP_COLUMN).setCellRenderer(buttonRenderer);
080        tcm.getColumn(UP_COLUMN).setCellEditor(buttonEditor);
081        tcm.getColumn(DOWN_COLUMN).setCellRenderer(buttonRenderer);
082        tcm.getColumn(DOWN_COLUMN).setCellEditor(buttonEditor);
083        table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
084        table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
085
086        // set column preferred widths
087        table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40);
088        table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(150);
089        table.getColumnModel().getColumn(TRAIN_DIRECTION_COLUMN).setPreferredWidth(95);
090        table.getColumnModel().getColumn(UP_COLUMN).setPreferredWidth(60);
091        table.getColumnModel().getColumn(DOWN_COLUMN).setPreferredWidth(70);
092
093        _frame.loadTableDetails(table);
094        // does not use a table sorter
095        table.setRowSorter(null);
096
097        updateList();
098    }
099
100    @Override
101    public int getRowCount() {
102        return _blockingOrderList.size();
103    }
104
105    @Override
106    public int getColumnCount() {
107        return HIGHEST_COLUMN;
108    }
109
110    @Override
111    public String getColumnName(int col) {
112        switch (col) {
113            case ID_COLUMN:
114                return Bundle.getMessage("Id");
115            case NAME_COLUMN:
116                return Bundle.getMessage("Location");
117            case TRAIN_DIRECTION_COLUMN:
118                return Bundle.getMessage("TrainDirection");
119            case UP_COLUMN:
120                return Bundle.getMessage("Up");
121            case DOWN_COLUMN:
122                return Bundle.getMessage("Down");
123            default:
124                return "unknown"; // NOI18N
125        }
126    }
127
128    @Override
129    public Class<?> getColumnClass(int col) {
130        switch (col) {
131            case ID_COLUMN:
132            case NAME_COLUMN:
133            case TRAIN_DIRECTION_COLUMN:
134                return String.class; 
135            case UP_COLUMN:
136            case DOWN_COLUMN:
137                return JButton.class;
138            default:
139                return null;
140        }
141    }
142
143    @Override
144    public boolean isCellEditable(int row, int col) {
145        switch (col) {
146            case UP_COLUMN:
147            case DOWN_COLUMN:
148                return true;
149            default:
150                return false;
151        }
152    }
153
154    @Override
155    public Object getValueAt(int row, int col) {
156        if (row >= getRowCount()) {
157            return "ERROR unknown " + row; // NOI18N
158        }
159        RouteLocation rl = _blockingOrderList.get(row);
160        if (rl == null) {
161            return "ERROR unknown route location " + row; // NOI18N
162        }
163        switch (col) {
164            case ID_COLUMN:
165                return rl.getId();
166            case NAME_COLUMN:
167                return rl.getName();
168            case TRAIN_DIRECTION_COLUMN: {
169                return rl.getTrainDirectionString();
170            }
171            case UP_COLUMN:
172                return Bundle.getMessage("Up");
173            case DOWN_COLUMN:
174                return Bundle.getMessage("Down");
175            default:
176                return "unknown " + col; // NOI18N
177        }
178    }
179
180    @Override
181    public void setValueAt(Object value, int row, int col) {
182        if (value == null) {
183            log.debug("Warning route table row {} still in edit", row);
184            return;
185        }
186        RouteLocation rl = _blockingOrderList.get(row);
187        if (rl == null) {
188            log.error("ERROR unknown route location for row: {}", row); // NOI18N
189        }
190        switch (col) {
191            case UP_COLUMN:
192                moveUpRouteLocation(rl);
193                break;
194            case DOWN_COLUMN:
195                moveDownRouteLocation(rl);
196                break;
197            default:
198                break;
199        }
200    }
201
202    private void moveUpRouteLocation(RouteLocation rl) {
203        log.debug("move location up");
204        _route.setBlockingOrderUp(rl);
205    }
206
207    private void moveDownRouteLocation(RouteLocation rl) {
208        log.debug("move location down");
209        _route.setBlockingOrderDown(rl);
210    }
211
212    // this table listens for changes to a route and it's locations
213    @Override
214    public void propertyChange(PropertyChangeEvent e) {
215        if (Control.SHOW_PROPERTY) {
216            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
217                    .getNewValue());
218        }
219        if (e.getPropertyName().equals(Route.ROUTE_BLOCKING_CHANGED_PROPERTY)) {
220            updateList();
221            fireTableDataChanged();
222        }
223
224        if (e.getSource().getClass().equals(RouteLocation.class)) {
225            RouteLocation rl = (RouteLocation) e.getSource();
226            int row = _blockingOrderList.indexOf(rl);
227            if (Control.SHOW_PROPERTY) {
228                log.debug("Update route table row: {} id: {}", row, rl.getId());
229            }
230            if (row >= 0) {
231                fireTableRowsUpdated(row, row);
232            }
233        }
234    }
235
236    private void removePropertyChangeRouteLocations() {
237        for (RouteLocation rl : _blockingOrderList) {
238            rl.removePropertyChangeListener(this);
239        }
240    }
241
242    public void dispose() {
243        removePropertyChangeRouteLocations();
244        if (_route != null) {
245            _route.removePropertyChangeListener(this);
246        }
247        _blockingOrderList.clear();
248        fireTableDataChanged();
249    }
250
251    private final static Logger log = LoggerFactory.getLogger(RouteBlockingOrderEditTableModel.class);
252}