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