001package jmri.jmrit.operations.locations;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.*;
009import javax.swing.table.TableCellEditor;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014import jmri.InstanceManager;
015import jmri.jmrit.operations.setup.Control;
016import jmri.jmrit.operations.setup.Setup;
017import jmri.util.swing.XTableColumnModel;
018import jmri.util.table.ButtonEditor;
019import jmri.util.table.ButtonRenderer;
020
021/**
022 * Table Model for edit of locations used by operations
023 *
024 * @author Daniel Boudreau Copyright (C) 2008, 2013
025 */
026public class LocationsTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
027
028    LocationManager locationManager; // There is only one manager
029    protected JTable _table;
030
031    // Define the columns
032    public static final int ID_COLUMN = 0;
033    public static final int NAME_COLUMN = 1;
034    public static final int TRACK_COLUMN = 2;
035    public static final int NUMBER_COLUMN = 3;
036    public static final int LENGTH_COLUMN = 4;
037    public static final int USED_LENGTH_COLUMN = 5;
038    public static final int ROLLINGSTOCK_COLUMN = 6;
039    protected static final int CARS_COLUMN = 7;
040    protected static final int LOCOS_COLUMN = 8;
041    public static final int PICKUPS_COLUMN = 9;
042    public static final int DROPS_COLUMN = 10;
043    public static final int DIVISION_COLUMN = 11;
044    public static final int REPORTER_COLUMN = 12;
045    public static final int ACTION_COLUMN = 13;
046    public static final int EDIT_COLUMN = 14;
047
048    private static final int HIGHEST_COLUMN = EDIT_COLUMN + 1;
049
050    public LocationsTableModel() {
051        super();
052        locationManager = InstanceManager.getDefault(LocationManager.class);
053        locationManager.addPropertyChangeListener(this);
054        updateList();
055    }
056
057    public final int SORTBYNAME = 1;
058    public final int SORTBYID = 2;
059
060    private int _sort = SORTBYNAME;
061
062    public void setSort(int sort) {
063        _sort = sort;
064        updateList();
065        setColumnsVisible();
066        fireTableDataChanged();
067    }
068
069    private void updateList() {
070        // first, remove listeners from the individual objects
071        removePropertyChangeLocations();
072
073        if (_sort == SORTBYID) {
074            locationsList = locationManager.getLocationsByIdList();
075        } else {
076            locationsList = locationManager.getLocationsByNameList();
077        }
078        // and add them back in
079        for (Location loc : locationsList) {
080            loc.addPropertyChangeListener(this);
081        }
082    }
083
084    List<Location> locationsList = null;
085
086    void initTable(LocationsTableFrame frame, JTable table) {
087        _table = table;
088        // Use XTableColumnModel so we can control which columns are visible
089        XTableColumnModel tcm = new XTableColumnModel();
090        table.setColumnModel(tcm);
091        table.createDefaultColumnsFromModel();
092        // Install the button handlers
093        ButtonRenderer buttonRenderer = new ButtonRenderer();
094        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
095        tcm.getColumn(ACTION_COLUMN).setCellRenderer(buttonRenderer);
096        tcm.getColumn(ACTION_COLUMN).setCellEditor(buttonEditor);
097        tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer);
098        tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor);
099
100        // set column preferred widths
101        table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40);
102        table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(200);
103        table.getColumnModel().getColumn(TRACK_COLUMN).setPreferredWidth(Math.max(60, new JLabel(
104                Bundle.getMessage("Class/Interchange") + Bundle.getMessage("Spurs") + Bundle.getMessage("Yards"))
105                        .getPreferredSize().width +
106                20));
107        table.getColumnModel().getColumn(NUMBER_COLUMN).setPreferredWidth(40);
108        table.getColumnModel().getColumn(LENGTH_COLUMN).setPreferredWidth(
109                Math.max(60, new JLabel(getColumnName(LENGTH_COLUMN)).getPreferredSize().width + 10));
110        table.getColumnModel().getColumn(USED_LENGTH_COLUMN).setPreferredWidth(60);
111        table.getColumnModel().getColumn(ROLLINGSTOCK_COLUMN).setPreferredWidth(
112                Math.max(80, new JLabel(getColumnName(ROLLINGSTOCK_COLUMN)).getPreferredSize().width + 10));
113        table.getColumnModel().getColumn(CARS_COLUMN).setPreferredWidth(60);
114        table.getColumnModel().getColumn(LOCOS_COLUMN).setPreferredWidth(60);
115        table.getColumnModel().getColumn(PICKUPS_COLUMN).setPreferredWidth(
116                Math.max(60, new JLabel(getColumnName(PICKUPS_COLUMN)).getPreferredSize().width + 10));
117        table.getColumnModel().getColumn(DROPS_COLUMN)
118                .setPreferredWidth(Math.max(60, new JLabel(getColumnName(DROPS_COLUMN)).getPreferredSize().width + 10));
119        table.getColumnModel().getColumn(DIVISION_COLUMN).setPreferredWidth(160);
120        table.getColumnModel().getColumn(ACTION_COLUMN).setPreferredWidth(
121                Math.max(80, new JLabel(Bundle.getMessage("Yardmaster")).getPreferredSize().width + 40));
122        table.getColumnModel().getColumn(EDIT_COLUMN).setPreferredWidth(80);
123
124        frame.loadTableDetails(table);
125        setColumnsVisible();
126    }
127
128    protected void setColumnsVisible() {
129        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
130        tcm.setColumnVisible(tcm.getColumnByModelIndex(ID_COLUMN), locationManager.isShowIdEnabled());
131        tcm.setColumnVisible(tcm.getColumnByModelIndex(DIVISION_COLUMN), locationManager.hasDivisions());
132        tcm.setColumnVisible(tcm.getColumnByModelIndex(REPORTER_COLUMN),
133                Setup.isRfidEnabled() && locationManager.hasReporters());
134    }
135
136    @Override
137    public int getRowCount() {
138        return locationsList.size();
139    }
140
141    @Override
142    public int getColumnCount() {
143        return HIGHEST_COLUMN;
144    }
145
146    @Override
147    public String getColumnName(int col) {
148        switch (col) {
149            case ID_COLUMN:
150                return Bundle.getMessage("Id");
151            case NAME_COLUMN:
152                return Bundle.getMessage("Name");
153            case TRACK_COLUMN:
154                return Bundle.getMessage("Track");
155            case NUMBER_COLUMN:
156                return Bundle.getMessage("Number");
157            case LENGTH_COLUMN:
158                return Bundle.getMessage("Length");
159            case USED_LENGTH_COLUMN:
160                return Bundle.getMessage("Used");
161            case ROLLINGSTOCK_COLUMN:
162                return Bundle.getMessage("RollingStock");
163            case LOCOS_COLUMN:
164                return Bundle.getMessage("Engines");
165            case CARS_COLUMN:
166                return Bundle.getMessage("Cars");
167            case PICKUPS_COLUMN:
168                return Bundle.getMessage("Pickups");
169            case DROPS_COLUMN:
170                return Bundle.getMessage("Drop");
171            case DIVISION_COLUMN:
172                return Bundle.getMessage("Division");
173            case REPORTER_COLUMN:
174                return Bundle.getMessage("Reporters");
175            case ACTION_COLUMN:
176                return Bundle.getMessage("Action");
177            case EDIT_COLUMN:
178                return Bundle.getMessage("ButtonEdit"); // titles above all columns
179            default:
180                return "unknown"; // NOI18N
181        }
182    }
183
184    @Override
185    public Class<?> getColumnClass(int col) {
186        switch (col) {
187            case NAME_COLUMN:
188            case TRACK_COLUMN:
189            case DIVISION_COLUMN:
190            case REPORTER_COLUMN:
191                return String.class;
192            case ID_COLUMN:
193            case NUMBER_COLUMN:
194            case LENGTH_COLUMN:
195            case USED_LENGTH_COLUMN:
196            case ROLLINGSTOCK_COLUMN:
197            case LOCOS_COLUMN:
198            case CARS_COLUMN:
199            case PICKUPS_COLUMN:
200            case DROPS_COLUMN:
201                return Integer.class;
202            case ACTION_COLUMN:
203            case EDIT_COLUMN:
204                return JButton.class;
205            default:
206                return null;
207        }
208    }
209
210    @Override
211    public boolean isCellEditable(int row, int col) {
212        switch (col) {
213            case EDIT_COLUMN:
214            case ACTION_COLUMN:
215                return true;
216            default:
217                return false;
218        }
219    }
220
221    @Override
222    public Object getValueAt(int row, int col) {
223        if (row >= getRowCount()) {
224            return "ERROR row " + row; // NOI18N
225        }
226        Location location = locationsList.get(row);
227        if (location == null) {
228            return "ERROR location unknown " + row; // NOI18N
229        }
230        switch (col) {
231            case ID_COLUMN:
232                return Integer.parseInt(location.getId());
233            case NAME_COLUMN:
234                return location.getName();
235            case TRACK_COLUMN:
236                return getTrackTypes(location);
237            case NUMBER_COLUMN:
238                return location.getTracksList().size();
239            case LENGTH_COLUMN:
240                return location.getLength();
241            case USED_LENGTH_COLUMN:
242                return location.getUsedLength();
243            case ROLLINGSTOCK_COLUMN:
244                return location.getNumberRS();
245            case LOCOS_COLUMN:
246                return location.getNumberEngines();
247            case CARS_COLUMN:
248                return location.getNumberCars();
249            case PICKUPS_COLUMN:
250                return location.getPickupRS();
251            case DROPS_COLUMN:
252                return location.getDropRS();
253            case DIVISION_COLUMN:
254                return location.getDivisionName();
255            case REPORTER_COLUMN:
256                return location.getReporterName();
257            case ACTION_COLUMN:
258                return Bundle.getMessage("Yardmaster");
259            case EDIT_COLUMN:
260                return Bundle.getMessage("ButtonEdit");
261            default:
262                return "unknown " + col; // NOI18N
263        }
264    }
265
266    private String getTrackTypes(Location location) {
267        if (location.isStaging()) {
268            return (Bundle.getMessage("Staging"));
269        } else {
270            StringBuffer sb = new StringBuffer();
271            if (location.hasInterchanges()) {
272                sb.append(Bundle.getMessage("Class/Interchange") + " ");
273            }
274            if (location.hasSpurs()) {
275                sb.append(Bundle.getMessage("Spurs") + " ");
276            }
277            if (location.hasYards()) {
278                sb.append(Bundle.getMessage("Yards"));
279            }
280            return sb.toString();
281        }
282    }
283
284    @Override
285    public void setValueAt(Object value, int row, int col) {
286        switch (col) {
287            case ACTION_COLUMN:
288                launchYardmaster(row);
289                break;
290            case EDIT_COLUMN:
291                editLocation(row);
292                break;
293            default:
294                break;
295        }
296    }
297
298    List<LocationEditFrame> frameList = new ArrayList<LocationEditFrame>();
299
300    private void editLocation(int row) {
301        // use invokeLater so new window appears on top
302        SwingUtilities.invokeLater(() -> {
303            Location loc = locationsList.get(row);
304            log.debug("Edit location ({})", loc.getName());
305            for (LocationEditFrame lef : frameList) {
306                if (lef._location == loc) {
307                    lef.dispose();
308                    frameList.remove(lef);
309                    break;
310                }
311            }
312            LocationEditFrame lef = new LocationEditFrame(loc);
313            frameList.add(lef);
314        });
315    }
316
317    private void launchYardmaster(int row) {
318        // use invokeLater so new window appears on top
319        SwingUtilities.invokeLater(() -> {
320            log.debug("Yardmaster");
321            Location loc = locationsList.get(row);
322            new YardmasterFrame(loc);
323        });
324    }
325
326    @Override
327    public void propertyChange(PropertyChangeEvent e) {
328        if (Control.SHOW_PROPERTY) {
329            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
330                    e.getNewValue());
331        }
332        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) {
333            updateList();
334            fireTableDataChanged();
335        } else if (e.getSource().getClass().equals(Location.class)) {
336            Location loc = (Location) e.getSource();
337            int row = locationsList.indexOf(loc);
338            if (Control.SHOW_PROPERTY) {
339                log.debug("Update location table row: {} name: {}", row, loc.getName());
340            }
341            if (row >= 0) {
342                fireTableRowsUpdated(row, row);
343            }
344            if (e.getPropertyName().equals(Location.LOCATION_REPORTER_CHANGED_PROPERTY) ||
345                    e.getPropertyName().equals(Location.LOCATION_DIVISION_CHANGED_PROPERTY)) {
346                setColumnsVisible();
347            }
348        }
349    }
350
351    private void removePropertyChangeLocations() {
352        if (locationsList != null) {
353            for (Location loc : locationsList) {
354                loc.removePropertyChangeListener(this);
355            }
356        }
357    }
358
359    public void dispose() {
360        for (LocationEditFrame lef : frameList) {
361            lef.dispose();
362        }
363        locationManager.removePropertyChangeListener(this);
364        removePropertyChangeLocations();
365    }
366
367    private final static Logger log = LoggerFactory.getLogger(LocationsTableModel.class);
368}