001package jmri.jmrit.operations.trains.tools;
002
003import java.awt.Dimension;
004import java.awt.GridBagLayout;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.*;
009
010import jmri.InstanceManager;
011import jmri.jmrit.operations.OperationsFrame;
012import jmri.jmrit.operations.OperationsXml;
013import jmri.jmrit.operations.rollingstock.cars.CarTypes;
014import jmri.jmrit.operations.setup.Control;
015import jmri.jmrit.operations.setup.Setup;
016import jmri.jmrit.operations.trains.Train;
017import jmri.jmrit.operations.trains.TrainManager;
018import jmri.util.swing.JmriJOptionPane;
019
020/**
021 * Frame to display which trains service certain car types
022 *
023 * @author Dan Boudreau Copyright (C) 2009, 2022
024 */
025public class TrainsByCarTypeFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
026
027    TrainManager trainManager = InstanceManager.getDefault(TrainManager.class);
028
029    ArrayList<JCheckBox> trainList = new ArrayList<>();
030    JPanel trainCheckBoxes = new JPanel();
031
032    // panels
033    JPanel pTrains;
034
035    // major buttons
036    JButton clearButton = new JButton(Bundle.getMessage("ClearAll"));
037    JButton setButton = new JButton(Bundle.getMessage("SelectAll"));
038    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
039
040    // check boxes
041    JCheckBox copyCheckBox = new JCheckBox(Bundle.getMessage("ButtonCopy"));
042
043    // combo boxes
044    JComboBox<String> typeComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox();
045    JComboBox<String> copyComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox();
046
047    public TrainsByCarTypeFrame() {
048        super(Bundle.getMessage("TitleModifyTrains"));
049    }
050
051    public void initComponents(String carType) {
052
053        // general GUI config
054        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
055
056        // Set up the panels
057        JPanel pCarType = new JPanel();
058        pCarType.setLayout(new GridBagLayout());
059        pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
060        
061        JPanel pCarCopy = new JPanel();
062        pCarCopy.setLayout(new GridBagLayout());
063        addItem(pCarCopy, copyComboBox, 0, 0);
064        pCarCopy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("CopyType")));
065
066        addItem(pCarType, typeComboBox, 0, 0);
067        addItem(pCarType, copyCheckBox, 1, 0);
068        addItem(pCarType, pCarCopy, 2, 0);
069        typeComboBox.setSelectedItem(carType);
070        copyCheckBox.setToolTipText(Bundle.getMessage("TipCopyCarType"));
071
072        pTrains = new JPanel();
073        pTrains.setLayout(new GridBagLayout());
074        JScrollPane trainPane = new JScrollPane(pTrains);
075        trainPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
076        trainPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Trains")));
077        updateTrains();
078
079        JPanel pButtons = new JPanel();
080        pButtons.setLayout(new GridBagLayout());
081        pButtons.setBorder(BorderFactory.createEtchedBorder());
082
083        addItem(pButtons, clearButton, 0, 0);
084        addItem(pButtons, setButton, 1, 0);
085        addItem(pButtons, saveButton, 2, 0);
086
087        getContentPane().add(pCarType);
088        getContentPane().add(trainPane);
089        getContentPane().add(pButtons);
090
091        // setup combo box
092        addComboBoxAction(typeComboBox);
093        addComboBoxAction(copyComboBox);
094
095        // setup buttons
096        addButtonAction(setButton);
097        addButtonAction(clearButton);
098        addButtonAction(saveButton);
099
100        // setup checkbox
101        addCheckBoxAction(copyCheckBox);
102
103        trainManager.addPropertyChangeListener(this);
104        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
105
106        // build menu
107        JMenuBar menuBar = new JMenuBar();
108        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
109        toolMenu.add(new PrintTrainsByCarTypesAction(false));
110        toolMenu.add(new PrintTrainsByCarTypesAction(true));
111        menuBar.add(toolMenu);
112        setJMenuBar(menuBar);
113        addHelpMenu("package.jmri.jmrit.operations.Operations_ModifyTrainsByCarType", true); // NOI18N
114
115        setPreferredSize(null);
116        initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250));
117    }
118
119    @Override
120    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
121        log.debug("combo box action");
122        updateTrains();
123    }
124
125    // Save, Delete, Add
126    @Override
127    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
128        if (ae.getSource() == saveButton) {
129            save();
130        }
131        if (ae.getSource() == setButton) {
132            selectCheckboxes(true);
133        }
134        if (ae.getSource() == clearButton) {
135            selectCheckboxes(false);
136        }
137    }
138
139    /**
140     * Update the car types that trains and tracks service. Note that the
141     * checkbox name is the id of the train or track.
142     */
143    private void save() {
144        if (copyCheckBox.isSelected() &&
145                JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("CopyCarType",
146                        typeComboBox.getSelectedItem(), copyComboBox.getSelectedItem()),
147                        Bundle.getMessage("CopyCarTypeTitle"),
148                        JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
149            return;
150        }
151        log.debug("Save {} trains", trainList.size());
152        removePropertyChangeTrains();
153        // protected against concurrent operation by making a copy
154        for (JCheckBox cb : new ArrayList<>(trainList)) {
155            Train train = trainManager.getTrainById(cb.getName());
156            if (cb.isSelected()) {
157                train.addTypeName((String) typeComboBox.getSelectedItem());
158            } else {
159                train.deleteTypeName((String) typeComboBox.getSelectedItem());
160            }
161        }
162        OperationsXml.save(); // save files
163        updateTrains();
164        if (Setup.isCloseWindowOnSaveEnabled()) {
165            dispose();
166        }
167    }
168
169    private void updateTrains() {
170        log.debug("update trains");
171        removePropertyChangeTrains();
172        trainList.clear();
173        int x = 0;
174        pTrains.removeAll();
175        String carType = (String) typeComboBox.getSelectedItem();
176        if (copyCheckBox.isSelected()) {
177            carType = (String) copyComboBox.getSelectedItem();
178        }
179        List<Train> trains = trainManager.getTrainsByNameList();
180        for (Train train : trains) {
181            train.addPropertyChangeListener(this);
182            JCheckBox cb = new JCheckBox(train.getName());
183            cb.setName(train.getId());
184            cb.setToolTipText(Bundle.getMessage("TipTrainCarType", carType));
185            addCheckBoxAction(cb);
186            trainList.add(cb);
187            cb.setSelected(train.isTypeNameAccepted(carType));
188            addItemLeft(pTrains, cb, 0, x);
189            JLabel description = new JLabel(train.getDescription());
190            addItemLeft(pTrains, description, 1, x++);
191        }
192        pTrains.revalidate();
193        repaint();
194    }
195
196    private void updateComboBox() {
197        log.debug("update combobox");
198        InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox);
199        InstanceManager.getDefault(CarTypes.class).updateComboBox(copyComboBox);
200    }
201
202    private void selectCheckboxes(boolean b) {
203        for (JCheckBox checkBox : new ArrayList<>(trainList)) {
204            checkBox.setSelected(b);
205        }
206    }
207
208    @Override
209    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
210        // copy checkbox?
211        if (ae.getSource() == copyCheckBox) {
212            updateTrains();
213        } else {
214            JCheckBox cb = (JCheckBox) ae.getSource();
215            log.debug("Checkbox {} text: {}", cb.getName(), cb.getText());
216            if (trainList.contains(cb)) {
217                log.debug("Checkbox train {}", cb.getText());
218            } else {
219                log.error("Error checkbox not found");
220            }
221        }
222    }
223
224    private void removePropertyChangeTrains() {
225        for (JCheckBox checkBox : new ArrayList<>(trainList)) {
226            // if object has been deleted, it's not here; ignore it
227            Train train = trainManager.getTrainById(checkBox.getName());
228            if (train != null) {
229                train.removePropertyChangeListener(this);
230            }
231        }
232    }
233
234    @Override
235    public void dispose() {
236        trainManager.removePropertyChangeListener(this);
237        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
238        removePropertyChangeTrains();
239        super.dispose();
240    }
241
242    @Override
243    public void propertyChange(java.beans.PropertyChangeEvent e) {
244        log.debug("Property change {} old: {} new: {}", e.getPropertyName(), e.getOldValue(), e.getNewValue());
245        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY) ||
246                e.getPropertyName().equals(Train.TYPES_CHANGED_PROPERTY) ||
247                e.getPropertyName().equals(Train.NAME_CHANGED_PROPERTY) ||
248                e.getPropertyName().equals(Train.DESCRIPTION_CHANGED_PROPERTY)) {
249            updateTrains();
250        }
251        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) ||
252                e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) {
253            updateComboBox();
254        }
255    }
256
257    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainsByCarTypeFrame.class);
258}