001package jmri.jmrit.operations.locations.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.locations.*;
014import jmri.jmrit.operations.rollingstock.cars.CarTypes;
015import jmri.jmrit.operations.setup.Control;
016import jmri.jmrit.operations.setup.Setup;
017import jmri.util.swing.JmriJOptionPane;
018
019/**
020 * Frame to display which locations service certain car types
021 *
022 * @author Dan Boudreau Copyright (C) 2009, 2011, 2022
023 */
024public class LocationsByCarTypeFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
025
026    LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
027
028    // checkboxes have the location id or track id as the checkbox name
029    ArrayList<JCheckBox> locationCheckBoxList = new ArrayList<>();
030    ArrayList<JCheckBox> trackCheckBoxList = new ArrayList<>();
031    JPanel locationCheckBoxes = new JPanel();
032
033    // panels
034    JPanel pLocations;
035
036    // major buttons
037    JButton clearButton = new JButton(Bundle.getMessage("ClearAll"));
038    JButton setButton = new JButton(Bundle.getMessage("SelectAll"));
039    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
040
041    // check boxes
042    JCheckBox copyCheckBox = new JCheckBox(Bundle.getMessage("ButtonCopy"));
043
044    // combo boxes
045    JComboBox<String> typeComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox();
046    JComboBox<String> copyComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox();
047
048    // selected location
049    Location _location;
050
051    public LocationsByCarTypeFrame() {
052        super();
053    }
054
055    @Override
056    public void initComponents() {
057        initComponents(NONE);
058    }
059
060    public void initComponents(Location location) {
061        this._location = location;
062        initComponents(NONE);
063    }
064
065    public void initComponents(Location location, String carType) {
066        this._location = location;
067        initComponents(carType);
068    }
069
070    public void initComponents(String carType) {
071
072        // load managers
073        locationManager = InstanceManager.getDefault(LocationManager.class);
074
075        // general GUI config
076        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
077
078        // Set up the panels
079        JPanel pCarType = new JPanel();
080        pCarType.setLayout(new GridBagLayout());
081        pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
082        
083        JPanel pCarCopy = new JPanel();
084        pCarCopy.setLayout(new GridBagLayout());
085        addItem(pCarCopy, copyComboBox, 0, 0);
086        pCarCopy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("CopyType")));
087
088        addItem(pCarType, typeComboBox, 0, 0);
089        addItem(pCarType, copyCheckBox, 1, 0);
090        addItem(pCarType, pCarCopy, 2, 0);
091        
092        typeComboBox.setSelectedItem(carType);
093        copyCheckBox.setToolTipText(Bundle.getMessage("TipCopyCarType"));
094
095        pLocations = new JPanel();
096        pLocations.setLayout(new GridBagLayout());
097        JScrollPane locationPane = new JScrollPane(pLocations);
098        locationPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
099        locationPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Locations")));
100        updateLocations();
101
102        JPanel pButtons = new JPanel();
103        pButtons.setLayout(new GridBagLayout());
104        pButtons.setBorder(BorderFactory.createTitledBorder(""));
105
106        addItem(pButtons, clearButton, 0, 0);
107        addItem(pButtons, setButton, 1, 0);
108        addItem(pButtons, saveButton, 2, 0);
109
110        getContentPane().add(pCarType);
111        getContentPane().add(locationPane);
112        getContentPane().add(pButtons);
113
114        // setup combo box
115        addComboBoxAction(typeComboBox);
116        addComboBoxAction(copyComboBox);
117
118        // setup buttons
119        addButtonAction(setButton);
120        addButtonAction(clearButton);
121        addButtonAction(saveButton);
122
123        // setup checkbox
124        addCheckBoxAction(copyCheckBox);
125
126        locationManager.addPropertyChangeListener(this);
127        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
128
129        // build menu
130        JMenuBar menuBar = new JMenuBar();
131        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
132        toolMenu.add(new PrintLocationsByCarTypesAction(false));
133        toolMenu.add(new PrintLocationsByCarTypesAction(true));
134        menuBar.add(toolMenu);
135        setJMenuBar(menuBar);
136        addHelpMenu("package.jmri.jmrit.operations.Operations_ModifyLocationsByCarType", true); // NOI18N
137
138        if (_location != null) {
139            setTitle(Bundle.getMessage("TitleModifyLocation"));
140        } else {
141            setTitle(Bundle.getMessage("TitleModifyLocations"));
142        }
143        
144        setPreferredSize(null); // we need to resize this frame
145        initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250));
146        setSize(getWidth() + 25, getHeight()); // make a bit wider to eliminate scroll bar
147    }
148
149    @Override
150    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
151        log.debug("combo box action");
152        updateLocations();
153    }
154
155    // Save, Delete, Add
156    @Override
157    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
158        if (ae.getSource() == saveButton) {
159            save();
160        }
161        if (ae.getSource() == setButton) {
162            selectCheckboxes(true);
163        }
164        if (ae.getSource() == clearButton) {
165            selectCheckboxes(false);
166        }
167    }
168
169    /**
170     * Update the car types that locations and tracks service. Note that the
171     * checkbox name is the id of the location or track.
172     */
173    private void save() {
174        if (copyCheckBox.isSelected() &&
175                JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("CopyCarType",
176                        typeComboBox.getSelectedItem(), copyComboBox.getSelectedItem()),
177                        Bundle.getMessage("CopyCarTypeTitle"),
178                        JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
179            return;
180        }
181        log.debug("save {} locations", locationCheckBoxList.size());
182        removePropertyChangeLocations();
183        for (JCheckBox cb : new ArrayList<>(locationCheckBoxList)) {
184            Location loc = locationManager.getLocationById(cb.getName());
185            if (cb.isSelected()) {
186                loc.addTypeName((String) typeComboBox.getSelectedItem());
187                // save tracks that have the same id as the location
188                for (JCheckBox cbt : new ArrayList<>(trackCheckBoxList)) {
189                    String[] id = cbt.getName().split(Location.LOC_TRACK_REGIX);
190                    if (loc.getId().equals(id[0])) {
191                        Track track = loc.getTrackById(cbt.getName());
192                        if (cbt.isSelected()) {
193                            track.addTypeName((String) typeComboBox.getSelectedItem());
194                        } else {
195                            track.deleteTypeName((String) typeComboBox.getSelectedItem());
196                        }
197                    }
198                }
199            } else {
200                loc.deleteTypeName((String) typeComboBox.getSelectedItem());
201            }
202        }
203        OperationsXml.save();
204        updateLocations();
205        if (Setup.isCloseWindowOnSaveEnabled()) {
206            dispose();
207        }
208    }
209
210    private void updateLocations() {
211        log.debug("update checkboxes");
212        removePropertyChangeLocations();
213        locationCheckBoxList.clear();
214        trackCheckBoxList.clear();
215        int x = 0;
216        pLocations.removeAll();
217        String carType = (String) typeComboBox.getSelectedItem();
218        if (copyCheckBox.isSelected()) {
219            carType = (String) copyComboBox.getSelectedItem();
220        }
221        // did the location get deleted?
222        if (_location != null && locationManager.getLocationByName(_location.getName()) == null) {
223            _location = null;
224        }
225        List<Location> locations = locationManager.getLocationsByNameList();
226        for (Location loc : locations) {
227            // show only one location?
228            if (_location != null && _location != loc) {
229                continue;
230            }
231            loc.addPropertyChangeListener(this);
232            JCheckBox cb = new JCheckBox(loc.getName());
233            cb.setName(loc.getId());
234            cb.setToolTipText(Bundle.getMessage("TipLocCarType", carType));
235            addCheckBoxAction(cb);
236            locationCheckBoxList.add(cb);
237            boolean locAcceptsType = loc.acceptsTypeName(carType);
238            cb.setSelected(locAcceptsType);
239            addItemLeft(pLocations, cb, 0, x++);
240            List<Track> tracks = loc.getTracksByNameList(null);
241            for (Track track : tracks) {
242                track.addPropertyChangeListener(this);
243                cb = new JCheckBox(track.getName());
244                cb.setName(track.getId());
245                cb.setToolTipText(Bundle.getMessage("TipTrackCarType", carType));
246                addCheckBoxAction(cb);
247                trackCheckBoxList.add(cb);
248                cb.setSelected(track.isTypeNameAccepted(carType));
249                addItemLeft(pLocations, cb, 1, x++);
250            }
251        }
252        pLocations.revalidate();
253        repaint();
254    }
255
256    private void updateComboBox() {
257        log.debug("update combobox");
258        InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox);
259        InstanceManager.getDefault(CarTypes.class).updateComboBox(copyComboBox);
260    }
261
262    private void selectCheckboxes(boolean select) {
263        for (JCheckBox cb : new ArrayList<>(locationCheckBoxList)) {
264            cb.setSelected(select);
265        }
266        for (JCheckBox cb : new ArrayList<>(trackCheckBoxList)) {
267            cb.setSelected(select);
268        }
269    }
270
271    @Override
272    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
273        // copy checkbox?
274        if (ae.getSource() == copyCheckBox) {
275            updateLocations();
276        } else {
277            JCheckBox cb = (JCheckBox) ae.getSource();
278            log.debug("Checkbox {} text: {}", cb.getName(), cb.getText());
279            if (locationCheckBoxList.contains(cb)) {
280                log.debug("Checkbox location {}", cb.getText());
281                // must deselect tracks if location is deselect
282                if (!cb.isSelected()) {
283                    String locId = cb.getName();
284                    for (JCheckBox tcb : new ArrayList<>(trackCheckBoxList)) {
285                        String[] id = tcb.getName().split(Location.LOC_TRACK_REGIX);
286                        if (locId.equals(id[0])) {
287                            tcb.setSelected(false);
288                        }
289                    }
290                }
291
292            } else if (trackCheckBoxList.contains(cb)) {
293                log.debug("Checkbox track {}", cb.getText());
294                // Must select location if track is selected
295                if (cb.isSelected()) {
296                    String[] loc = cb.getName().split(Location.LOC_TRACK_REGIX);
297                    for (JCheckBox lcb : new ArrayList<>(locationCheckBoxList)) {
298                        if (lcb.getName().equals(loc[0])) {
299                            lcb.setSelected(true);
300                            break;
301                        }
302                    }
303                }
304            } else {
305                log.error("Error checkbox not found");
306            }
307        }
308    }
309
310    private void removePropertyChangeLocations() {
311        for (JCheckBox cb : new ArrayList<>(locationCheckBoxList)) {
312            // checkbox name is the location id
313            Location loc = locationManager.getLocationById(cb.getName());
314            if (loc != null) {
315                loc.removePropertyChangeListener(this);
316                List<Track> tracks = loc.getTracksList();
317                for (Track track : tracks) {
318                    track.removePropertyChangeListener(this);
319                }
320            }
321        }
322    }
323
324    @Override
325    public void dispose() {
326        locationManager.removePropertyChangeListener(this);
327        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
328        removePropertyChangeLocations();
329        super.dispose();
330    }
331
332    @Override
333    public void propertyChange(java.beans.PropertyChangeEvent e) {
334        log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
335                .getNewValue());
336        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) ||
337                e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) ||
338                e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY) ||
339                e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) ||
340                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY) ||
341                e.getPropertyName().equals(Track.NAME_CHANGED_PROPERTY)) {
342            updateLocations();
343        }
344        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) ||
345                e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) {
346            updateComboBox();
347        }
348    }
349
350    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocationsByCarTypeFrame.class);
351}