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