001package jmri.jmrit.operations.rollingstock.engines.gui;
002
003import java.awt.event.ActionEvent;
004import java.util.List;
005
006import javax.swing.*;
007import javax.swing.event.TableModelEvent;
008import javax.swing.event.TableModelListener;
009import javax.swing.table.TableColumnModel;
010
011import jmri.InstanceManager;
012import jmri.jmrit.operations.OperationsFrame;
013import jmri.jmrit.operations.OperationsXml;
014import jmri.jmrit.operations.rollingstock.engines.Engine;
015import jmri.jmrit.operations.rollingstock.engines.EngineManager;
016import jmri.jmrit.operations.rollingstock.engines.tools.*;
017import jmri.jmrit.operations.setup.Control;
018import jmri.jmrit.operations.setup.Setup;
019import jmri.swing.JTablePersistenceManager;
020import jmri.util.swing.JmriJOptionPane;
021
022/**
023 * Frame for adding and editing the engine roster for operations.
024 *
025 * @author Bob Jacobsen Copyright (C) 2001
026 * @author Daniel Boudreau Copyright (C) 2008, 2011, 2012, 2013, 2025
027 */
028public class EnginesTableFrame extends OperationsFrame implements TableModelListener {
029
030    public EnginesTableModel enginesTableModel;
031    public JTable enginesTable; // public for testing
032    boolean showAllLocos = true;
033    JScrollPane enginesPane;
034    EngineManager engineManager = InstanceManager.getDefault(EngineManager.class);
035
036    // labels
037    JLabel numEngines = new JLabel();
038    JLabel textEngines = new JLabel(Bundle.getMessage("engines"));
039    JLabel textSep1 = new JLabel("          ");
040
041    // radio buttons
042    JRadioButton sortByNumber = new JRadioButton(Bundle.getMessage("Number"));
043    JRadioButton sortByRoad = new JRadioButton(Bundle.getMessage("Road"));
044    JRadioButton sortByModel = new JRadioButton(Bundle.getMessage("Model"));
045    JRadioButton sortByConsist = new JRadioButton(Bundle.getMessage("Consist"));
046    JRadioButton sortByLocation = new JRadioButton(Bundle.getMessage("Location"));
047    JRadioButton sortByDestination = new JRadioButton(Bundle.getMessage("Destination"));
048    JRadioButton sortByTrain = new JRadioButton(Bundle.getMessage("Train"));
049    JRadioButton sortByMoves = new JRadioButton(Bundle.getMessage("Moves"));
050    JRadioButton sortByBuilt = new JRadioButton(Bundle.getMessage("Built"));
051    JRadioButton sortByOwner = new JRadioButton(Bundle.getMessage("Owner"));
052    JRadioButton sortByValue = new JRadioButton(Setup.getValueLabel());
053    JRadioButton sortByRfid = new JRadioButton(Setup.getRfidLabel());
054    JRadioButton sortByDcc = new JRadioButton(Bundle.getMessage("DccAddress"));
055    JRadioButton sortByLast = new JRadioButton(Bundle.getMessage("Last"));
056    JRadioButton sortByPickup = new JRadioButton(Bundle.getMessage("Pickup"));
057    JRadioButton sortByComment = new JRadioButton(Bundle.getMessage("Comment"));
058    ButtonGroup group = new ButtonGroup();
059
060    // major buttons
061    JButton addButton = new JButton(Bundle.getMessage("TitleEngineAdd"));
062    JButton findButton = new JButton(Bundle.getMessage("Find"));
063    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
064
065    JTextField findEngineTextBox = new JTextField(6);
066
067    public EnginesTableFrame(boolean showAllLocos, String locationName, String trackName) {
068        super(Bundle.getMessage("TitleEnginesTable"));
069        this.showAllLocos = showAllLocos;
070
071        // general GUI config
072        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
073
074        // Set up the jtable in a Scroll Pane..
075        enginesTableModel = new EnginesTableModel(showAllLocos, locationName, trackName);
076        enginesTable = new JTable(enginesTableModel);
077        enginesPane = new JScrollPane(enginesTable);
078        enginesPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
079        enginesTableModel.initTable(enginesTable, this);
080
081        // load the number of engines and listen for changes
082        updateNumEngines();
083        enginesTableModel.addTableModelListener(this);
084
085        // Set up the control panel
086        // row 1
087        JPanel cp1 = new JPanel();
088        cp1.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SortBy")));
089
090        cp1.add(sortByNumber);
091        cp1.add(sortByRoad);
092        cp1.add(sortByModel);
093        cp1.add(sortByConsist);
094        cp1.add(sortByLocation);
095        cp1.add(sortByDestination);
096        cp1.add(sortByTrain);
097        JPanel movep = new JPanel();
098        movep.setBorder(BorderFactory.createTitledBorder(""));
099        movep.add(sortByMoves);
100        movep.add(sortByBuilt);
101        movep.add(sortByOwner);
102        if (Setup.isValueEnabled()) {
103            movep.add(sortByValue);
104        }
105        if (Setup.isRfidEnabled()) {
106            movep.add(sortByRfid);
107        }
108        movep.add(sortByDcc);
109        movep.add(sortByPickup);
110        movep.add(sortByLast);
111        movep.add(sortByComment);
112        cp1.add(movep);
113
114        // row 2
115        JPanel cp2 = new JPanel();
116        cp2.setLayout(new BoxLayout(cp2, BoxLayout.X_AXIS));
117
118        JPanel cp2Add = new JPanel();
119        cp2Add.setBorder(BorderFactory.createTitledBorder(""));
120        addButton.setToolTipText(Bundle.getMessage("TipAddButton"));
121        cp2Add.add(numEngines);
122        cp2Add.add(textEngines);
123        cp2Add.add(textSep1);
124        cp2Add.add(addButton);
125        cp2.add(cp2Add);
126
127        JPanel cp2Find = new JPanel();
128        cp2Find.setBorder(BorderFactory.createTitledBorder(""));
129        findButton.setToolTipText(Bundle.getMessage("findEngine"));
130        findEngineTextBox.setToolTipText(Bundle.getMessage("findEngine"));
131        cp2Find.add(findButton);
132        cp2Find.add(findEngineTextBox);
133        cp2.add(cp2Find);
134
135        JPanel cp2Save = new JPanel();
136        cp2Save.setBorder(BorderFactory.createTitledBorder(""));
137        cp2Save.add(saveButton);
138        cp2.add(cp2Save);
139
140        // place controls in scroll pane
141        JPanel controlPanel = new JPanel();
142        controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
143        controlPanel.add(cp1);
144        controlPanel.add(cp2);
145
146        // some tool tips
147        sortByLast.setToolTipText(Bundle.getMessage("TipLastMoved"));
148
149        JScrollPane controlPane = new JScrollPane(controlPanel);
150
151        getContentPane().add(enginesPane);
152        getContentPane().add(controlPane);
153
154        // setup buttons
155        addButtonAction(addButton);
156        addButtonAction(findButton);
157        addButtonAction(saveButton);
158
159        sortByNumber.setSelected(true);
160        addRadioButtonAction(sortByNumber);
161        addRadioButtonAction(sortByRoad);
162        addRadioButtonAction(sortByModel);
163        addRadioButtonAction(sortByConsist);
164        addRadioButtonAction(sortByLocation);
165        addRadioButtonAction(sortByDestination);
166        addRadioButtonAction(sortByTrain);
167        addRadioButtonAction(sortByMoves);
168        addRadioButtonAction(sortByBuilt);
169        addRadioButtonAction(sortByOwner);
170        addRadioButtonAction(sortByValue);
171        addRadioButtonAction(sortByRfid);
172        addRadioButtonAction(sortByDcc);
173        addRadioButtonAction(sortByPickup);
174        addRadioButtonAction(sortByLast);
175        addRadioButtonAction(sortByComment);
176
177        findEngineTextBox.addActionListener(this::textBoxActionPerformed);
178
179        group.add(sortByNumber);
180        group.add(sortByRoad);
181        group.add(sortByModel);
182        group.add(sortByConsist);
183        group.add(sortByLocation);
184        group.add(sortByDestination);
185        group.add(sortByTrain);
186        group.add(sortByMoves);
187        group.add(sortByBuilt);
188        group.add(sortByOwner);
189        group.add(sortByValue);
190        group.add(sortByRfid);
191        group.add(sortByDcc);
192        group.add(sortByPickup);
193        group.add(sortByLast);
194        group.add(sortByComment);
195        
196        // sort by location
197        if (!showAllLocos) {
198            sortByLocation.doClick();
199            if (locationName != null) {
200                String title = Bundle.getMessage("TitleEnginesTable") + " " + locationName;
201                if (trackName != null) {
202                    title = title + " " + trackName;
203                }
204                setTitle(title);
205            }
206        }
207
208        sortByDcc.setToolTipText(Bundle.getMessage("TipDccAddressFromRoster"));
209
210        // build menu
211        JMenuBar menuBar = new JMenuBar();
212        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
213        toolMenu.add(new EngineRosterMenu(Bundle.getMessage("TitleEngineRoster"), EngineRosterMenu.MAINMENU, this));
214        toolMenu.addSeparator();
215        toolMenu.add(new ShowCheckboxesEnginesTableAction(enginesTableModel));
216        toolMenu.add(new ResetCheckboxesEnginesTableAction(enginesTableModel));
217        toolMenu.addSeparator();
218        toolMenu.add(new NceConsistEngineAction());
219        toolMenu.addSeparator();
220        toolMenu.add(new EnginesSetFrameAction(enginesTable));
221        menuBar.add(toolMenu);
222        menuBar.add(new jmri.jmrit.operations.OperationsMenu());
223        setJMenuBar(menuBar);
224        addHelpMenu("package.jmri.jmrit.operations.Operations_Locomotives", true); // NOI18N
225
226        initMinimumSize();
227
228        addHorizontalScrollBarKludgeFix(controlPane, controlPanel);
229
230        // create ShutDownTasks
231        createShutDownTask();
232    }
233
234    @Override
235    public void radioButtonActionPerformed(ActionEvent ae) {
236        log.debug("radio button activated");
237        // clear any sorts by column
238        clearTableSort(enginesTable);
239        if (ae.getSource() == sortByNumber) {
240            enginesTableModel.setSort(enginesTableModel.SORTBY_NUMBER);
241        }
242        if (ae.getSource() == sortByRoad) {
243            enginesTableModel.setSort(enginesTableModel.SORTBY_ROAD);
244        }
245        if (ae.getSource() == sortByModel) {
246            enginesTableModel.setSort(enginesTableModel.SORTBY_MODEL);
247        }
248        if (ae.getSource() == sortByConsist) {
249            enginesTableModel.setSort(enginesTableModel.SORTBY_CONSIST);
250        }
251        if (ae.getSource() == sortByLocation) {
252            enginesTableModel.setSort(enginesTableModel.SORTBY_LOCATION);
253        }
254        if (ae.getSource() == sortByDestination) {
255            enginesTableModel.setSort(enginesTableModel.SORTBY_DESTINATION);
256        }
257        if (ae.getSource() == sortByTrain) {
258            enginesTableModel.setSort(enginesTableModel.SORTBY_TRAIN);
259        }
260        if (ae.getSource() == sortByMoves) {
261            enginesTableModel.setSort(enginesTableModel.SORTBY_MOVES);
262        }
263        if (ae.getSource() == sortByBuilt) {
264            enginesTableModel.setSort(enginesTableModel.SORTBY_BUILT);
265        }
266        if (ae.getSource() == sortByOwner) {
267            enginesTableModel.setSort(enginesTableModel.SORTBY_OWNER);
268        }
269        if (ae.getSource() == sortByValue) {
270            enginesTableModel.setSort(enginesTableModel.SORTBY_VALUE);
271        }
272        if (ae.getSource() == sortByRfid) {
273            enginesTableModel.setSort(enginesTableModel.SORTBY_RFID);
274        }
275        if (ae.getSource() == sortByPickup) {
276            enginesTableModel.setSort(enginesTableModel.SORTBY_PICKUP);
277        }
278        if (ae.getSource() == sortByLast) {
279            enginesTableModel.setSort(enginesTableModel.SORTBY_LAST);
280        }
281        if (ae.getSource() == sortByDcc) {
282            enginesTableModel.setSort(enginesTableModel.SORTBY_DCC_ADDRESS);
283        }
284        if (ae.getSource() == sortByComment) {
285            enginesTableModel.setSort(enginesTableModel.SORTBY_COMMENT);
286        }
287    }
288
289    public List<Engine> getSortByList() {
290        return enginesTableModel.getSelectedEngineList();
291    }
292
293    EngineEditFrame engineEditFrame = null;
294
295    // add, save or find button
296    @Override
297    public void buttonActionPerformed(ActionEvent ae) {
298        // log.debug("engine button activated");
299        if (ae.getSource() == findButton) {
300            findEngine();
301            return;
302        }
303        if (ae.getSource() == addButton) {
304            if (engineEditFrame != null) {
305                engineEditFrame.dispose();
306            }
307            engineEditFrame = new EngineEditFrame();
308            engineEditFrame.initComponents();
309        }
310        if (ae.getSource() == saveButton) {
311            if (enginesTable.isEditing()) {
312                log.debug("locomotives table edit true");
313                enginesTable.getCellEditor().stopCellEditing();
314            }
315            OperationsXml.save();
316            if (Setup.isCloseWindowOnSaveEnabled()) {
317                dispose();
318            }
319        }
320    }
321
322    public void textBoxActionPerformed(ActionEvent ae) {
323        findEngine();
324    }
325
326    private void findEngine() {
327        int rowindex = enginesTableModel.findEngineByRoadNumber(findEngineTextBox.getText());
328        if (rowindex < 0) {
329            JmriJOptionPane.showMessageDialog(this,
330                    Bundle.getMessage("engineWithRoadNumNotFound", findEngineTextBox.getText()),
331                    Bundle.getMessage("engineCouldNotFind"), JmriJOptionPane.INFORMATION_MESSAGE);
332            return;
333
334        }
335        // clear any sorts by column
336        clearTableSort(enginesTable);
337        enginesTable.changeSelection(rowindex, 0, false, false);
338    }
339
340    protected int[] getCurrentTableColumnWidths() {
341        TableColumnModel tcm = enginesTable.getColumnModel();
342        int[] widths = new int[tcm.getColumnCount()];
343        for (int i = 0; i < tcm.getColumnCount(); i++) {
344            widths[i] = tcm.getColumn(i).getWidth();
345        }
346        return widths;
347    }
348
349    @Override
350    public void dispose() {
351        enginesTableModel.removeTableModelListener(this);
352        enginesTableModel.dispose();
353        if (engineEditFrame != null) {
354            engineEditFrame.dispose();
355        }
356        InstanceManager.getOptionalDefault(JTablePersistenceManager.class).ifPresent(tpm -> {
357            tpm.stopPersisting(enginesTable);
358        });
359        super.dispose();
360    }
361
362    @Override
363    public void tableChanged(TableModelEvent e) {
364        if (Control.SHOW_PROPERTY) {
365            log.debug("Table changed");
366        }
367        updateNumEngines();
368    }
369
370    private void updateNumEngines() {
371        String count = filterList(engineManager.getList());
372        if (showAllLocos) {
373            numEngines.setText(count);
374        } else {
375            String showCount = filterList(getSortByList());
376            numEngines.setText(showCount + "/" + count);
377        }
378    }
379    
380    // only count real engines, ignore clones
381    private String filterList(List<Engine> list) {
382        int count = 0;
383        for (Engine eng : list) {
384            if (!eng.isClone()) {
385                count++;
386            }
387        }
388        return Integer.toString(count);
389    }
390
391    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EnginesTableFrame.class);
392}