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.*;
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 loads
021 *
022 * @author Dan Boudreau Copyright (C) 2014
023 */
024public class LocationsByCarLoadFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
025
026    private final static String RECEIVE = "r";
027    private final static String SHIP = "s";
028    private final static String HYPHEN = "-";
029
030    LocationManager locationManager;
031
032    // checkboxes track id as the checkbox name
033    ArrayList<JCheckBox> trackCheckBoxList = new ArrayList<>();
034    JPanel locationCheckBoxes = new JPanel();
035
036    // panels
037    JPanel pLocations;
038
039    // major buttons
040    JButton clearButton = new JButton(Bundle.getMessage("ClearAll"));
041    JButton setButton = new JButton(Bundle.getMessage("SelectAll"));
042    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
043
044    // check boxes
045    JCheckBox loadAndTypeCheckBox = new JCheckBox(Bundle.getMessage("TypeAndLoad"));
046
047    // radio buttons
048    // text field
049    // combo boxes
050    JComboBox<String> typeComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox();
051    JComboBox<String> loadComboBox = InstanceManager.getDefault(CarLoads.class).getComboBox(null);
052
053    // selected location
054    Location _location;
055
056    public LocationsByCarLoadFrame() {
057        super();
058    }
059
060    public void initComponents(Location location) {
061        this._location = location;
062        initComponents();
063    }
064
065    @Override
066    public void initComponents() {
067
068        // load managers
069        locationManager = InstanceManager.getDefault(LocationManager.class);
070
071        // general GUI config
072        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
073
074        // Set up the panels
075        JPanel pCarType = new JPanel();
076        pCarType.setLayout(new GridBagLayout());
077        pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
078
079        addItem(pCarType, typeComboBox, 0, 0);
080
081        JPanel pLoad = new JPanel();
082        pLoad.setLayout(new GridBagLayout());
083        pLoad.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Load")));
084
085        addItem(pLoad, loadComboBox, 0, 0);
086        addItem(pLoad, loadAndTypeCheckBox, 1, 0);
087
088        pLocations = new JPanel();
089        pLocations.setLayout(new GridBagLayout());
090        JScrollPane locationPane = new JScrollPane(pLocations);
091        locationPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
092        locationPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Locations")));
093        updateLoadComboBox();
094
095        JPanel pButtons = new JPanel();
096        pButtons.setLayout(new GridBagLayout());
097        pButtons.setBorder(BorderFactory.createTitledBorder(""));
098
099        addItem(pButtons, clearButton, 0, 0);
100        addItem(pButtons, setButton, 1, 0);
101        addItem(pButtons, saveButton, 2, 0);
102
103        getContentPane().add(pCarType);
104        getContentPane().add(pLoad);
105        getContentPane().add(locationPane);
106        getContentPane().add(pButtons);
107
108        // setup combo box
109        addComboBoxAction(typeComboBox);
110        addComboBoxAction(loadComboBox);
111
112        // setup buttons
113        addButtonAction(setButton);
114        addButtonAction(clearButton);
115        addButtonAction(saveButton);
116
117        // setup checkbox
118        addCheckBoxAction(loadAndTypeCheckBox);
119
120        locationManager.addPropertyChangeListener(this);
121        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
122        InstanceManager.getDefault(CarLoads.class).addPropertyChangeListener(this);
123
124        // build menu
125        JMenuBar menuBar = new JMenuBar();
126        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
127        toolMenu.add(new PrintLocationsByCarTypesAction(false));
128        toolMenu.add(new PrintLocationsByCarTypesAction(true));
129        menuBar.add(toolMenu);
130        setJMenuBar(menuBar);
131        addHelpMenu("package.jmri.jmrit.operations.Operations_ModifyLocationsByCarType", true); // NOI18N
132
133        setPreferredSize(null); // we need to resize this frame
134        pack();
135        setMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250));
136        setSize(getWidth() + 25, getHeight()); // make a bit wider to eliminate scroll bar
137        if (_location != null) {
138            setTitle(Bundle.getMessage("TitleModifyLocationLoad"));
139        } else {
140            setTitle(Bundle.getMessage("TitleModifyLocationsLoad"));
141        }
142        setVisible(true);
143    }
144
145    @Override
146    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
147        log.debug("combo box action");
148        if (ae.getSource() == loadComboBox) {
149            log.debug("Load combobox change, selected load: ({})", loadComboBox.getSelectedItem());
150            if (loadComboBox.isEnabled() && loadComboBox.getSelectedItem() != null) {
151                updateLocations();
152            }
153        }
154        if (ae.getSource() == typeComboBox) {
155            updateLoadComboBox();
156        }
157    }
158
159    // Save, Delete, Add
160    @Override
161    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
162        if (ae.getSource() == saveButton) {
163            save();
164        }
165        if (ae.getSource() == setButton) {
166            selectCheckboxes(true);
167        }
168        if (ae.getSource() == clearButton) {
169            selectCheckboxes(false);
170        }
171    }
172
173    /**
174     * Update the car types that locations and tracks service. Note that the
175     * checkbox name is the id of the location or track.
176     */
177    private void save() {
178        log.debug("save");
179        OperationsXml.save();
180        if (Setup.isCloseWindowOnSaveEnabled()) {
181            dispose();
182        }
183    }
184
185    /**
186     * Update tracks at locations based on car type serviced, or car loads
187     * serviced. If car loads, disable any tracks that don't service the car
188     * type selected.
189     */
190    private void updateLocations() {
191        log.debug("update checkboxes");
192        removePropertyChangeLocations();
193        trackCheckBoxList.clear();
194        int x = 0;
195        pLocations.removeAll();
196        String type = (String) typeComboBox.getSelectedItem();
197        String load = (String) loadComboBox.getSelectedItem();
198        log.debug("Selected car type : ({}) load ({})", type, load);
199        // did the location get deleted?
200        if (_location != null && locationManager.getLocationByName(_location.getName()) == null) {
201            _location = null;
202        }
203        List<Location> locations = locationManager.getLocationsByNameList();
204        for (Location location : locations) {
205            // show only one location?
206            if (_location != null && _location != location) {
207                continue;
208            }
209            location.addPropertyChangeListener(this);
210            JLabel locationName = new JLabel(location.getName());
211            addItemLeft(pLocations, locationName, 0, x++);
212            List<Track> tracks = location.getTracksByNameList(null);
213            for (Track track : tracks) {
214                track.addPropertyChangeListener(this);
215                JCheckBox cb = new JCheckBox(track.getName());
216                cb.setName(track.getId() + HYPHEN + RECEIVE);
217                addCheckBoxAction(cb);
218                trackCheckBoxList.add(cb);
219                cb.setEnabled(track.isTypeNameAccepted(type));
220                cb.setSelected(track.isLoadNameAndCarTypeAccepted(load, type));
221                addItemLeft(pLocations, cb, 1, x++);
222                if (cb.isEnabled()) {
223                    cb.setToolTipText(Bundle.getMessage("TipTrackCarLoad", load));
224                } else {
225                    cb.setToolTipText(Bundle.getMessage("TipTrackNotThisType", type));
226                }
227            }
228            if (location.isStaging()) {
229                JLabel ships = new JLabel(location.getName() + " (" + Bundle.getMessage("Ships") + ")");
230                addItemLeft(pLocations, ships, 0, x++);
231                for (Track track : tracks) {
232                    JCheckBox cb = new JCheckBox(track.getName());
233                    cb.setName(track.getId() + HYPHEN + SHIP);
234                    addCheckBoxAction(cb);
235                    trackCheckBoxList.add(cb);
236                    cb.setEnabled(track.isTypeNameAccepted(type));
237                    cb.setSelected(track.isLoadNameAndCarTypeShipped(load, type));
238                    addItemLeft(pLocations, cb, 1, x++);
239                    if (cb.isEnabled()) {
240                        cb.setToolTipText(Bundle.getMessage("TipTrackCarShipsLoad", load));
241                    } else {
242                        cb.setToolTipText(Bundle.getMessage("TipTrackNotThisType", type));
243                    }
244                }
245
246            }
247        }
248        pLocations.revalidate();
249        repaint();
250    }
251
252    private void updateTypeComboBox() {
253        log.debug("update type combobox");
254        InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox);
255    }
256
257    private void updateLoadComboBox() {
258        log.debug("update load combobox");
259        if (typeComboBox.getSelectedItem() != null) {
260            String type = (String) typeComboBox.getSelectedItem();
261            String load = (String) loadComboBox.getSelectedItem();
262            log.debug("Selected car type : ({}) load ({})", type, load);
263            InstanceManager.getDefault(CarLoads.class).updateComboBox(type, loadComboBox);
264            loadComboBox.setEnabled(false); // used as a flag to prevent updateLocations()
265            if (load != null) {
266                loadComboBox.setSelectedItem(load);
267            }
268            loadComboBox.setEnabled(true);
269            updateLocations();
270        }
271    }
272
273    private void updateLoadStatusCheckBox(Track track) {
274        String type = (String) typeComboBox.getSelectedItem();
275        String load = (String) loadComboBox.getSelectedItem();
276        log.debug("Update load status for track ({}, {})", track.getLocation().getName(), track.getName());
277        for (JCheckBox cb : trackCheckBoxList) {
278            if (cb.getName().split(HYPHEN)[0].equals(track.getId())) {
279                if (cb.getName().split(HYPHEN)[1].equals(RECEIVE)) {
280                    cb.setSelected(track.isLoadNameAndCarTypeAccepted(load, type));
281                } else {
282                    cb.setSelected(track.isLoadNameAndCarTypeShipped(load, type));
283                }
284            }
285        }
286    }
287
288    private void selectCheckboxes(boolean select) {
289        for (JCheckBox cb : trackCheckBoxList) {
290            if (cb.isEnabled()) {
291                cb.setSelected(select);
292                checkBoxUpdate(cb);
293            }
294        }
295    }
296
297    TrackLoadEditFrame tlef; // if there's an issue bring up the load edit window
298
299    @Override
300    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
301        if (ae.getSource() == loadAndTypeCheckBox) {
302            updateLocations();
303            return;
304        }
305        JCheckBox cb = (JCheckBox) ae.getSource();
306        checkBoxUpdate(cb);
307    }
308
309    private void checkBoxUpdate(JCheckBox cb) {
310        String[] locId = cb.getName().split(HYPHEN);
311        Location loc = locationManager.getLocationById(locId[0].split(Location.LOC_TRACK_REGIX)[0]);
312        if (loc != null) {
313            Track track = loc.getTrackById(locId[0]);
314            // if enabled track services this car type
315            log.debug("CheckBox : {} track: ({}) isEnabled: {} isSelected: {}", cb.getName(), track.getName(), cb
316                    .isEnabled(), cb.isSelected());
317            if (cb.isEnabled()) {
318                boolean needLoadTrackEditFrame = false;
319                String loadName = (String) loadComboBox.getSelectedItem();
320                String load = loadName;
321                String type = (String) typeComboBox.getSelectedItem();
322                log.debug("Selected load ({})", loadName);
323                if (loadAndTypeCheckBox.isSelected()) {
324                    loadName = type + CarLoad.SPLIT_CHAR + loadName;
325                }
326                if (locId[1].equals(RECEIVE)) {
327                    if (cb.isSelected()) {
328                        if (track.getLoadOption().equals(Track.ALL_LOADS)) {
329                            log.debug("All loads selected for track ({})", track.getName());
330                        } else if (track.getLoadOption().equals(Track.INCLUDE_LOADS)) {
331                            track.addLoadName(loadName);
332                        } else if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) {
333                            track.deleteLoadName(loadName);
334                            if (track.getLoadNames().length == 0) {
335                                track.setLoadOption(Track.ALL_LOADS);
336                            }
337                            // need to check if load configuration is to exclude all car types using this load
338                            if (!track.isLoadNameAccepted(load)) {
339                                JmriJOptionPane.showMessageDialog(this,
340                                        Bundle.getMessage("WarningExcludeTrackLoad", track.getLocation().getName(),
341                                                track.getName(), load),
342                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
343                                needLoadTrackEditFrame = true;
344                            } else if (!track.isLoadNameAndCarTypeAccepted(load, type)) {
345                                JmriJOptionPane.showMessageDialog(this,
346                                        Bundle.getMessage("WarningExcludeTrackTypeAndLoad",
347                                                track.getLocation().getName(), track.getName(), type, load,
348                                                NEW_LINE + type + CarLoad.SPLIT_CHAR + load),
349                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
350                                needLoadTrackEditFrame = true;
351                            }
352                        }
353                    } else {
354                        if (track.getLoadOption().equals(Track.INCLUDE_LOADS)) {
355                            track.deleteLoadName(loadName);
356                            // need to check if load configuration is to accept all car types using this load
357                            if (track.isLoadNameAccepted(load)) {
358                                JmriJOptionPane.showMessageDialog(this,
359                                        Bundle.getMessage("WarningAcceptTrackLoad", track.getLocation().getName(),
360                                                track.getName(), load),
361                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
362                                needLoadTrackEditFrame = true;
363                            } else if (track.isLoadNameAndCarTypeAccepted(load, type)) {
364                                JmriJOptionPane.showMessageDialog(this,
365                                        Bundle.getMessage("WarningAcceptTrackTypeAndLoad",
366                                                track.getLocation().getName(), track.getName(), type, load,
367                                                NEW_LINE + type + CarLoad.SPLIT_CHAR + load),
368                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
369                                needLoadTrackEditFrame = true;
370                            }
371                        } else if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) {
372                            track.addLoadName(loadName);
373                        } else if (track.getLoadOption().equals(Track.ALL_LOADS)) {
374                            // need to exclude this load
375                            track.setLoadOption(Track.EXCLUDE_LOADS);
376                            track.addLoadName(loadName);
377                        }
378                    }
379                }
380                if (locId[1].equals(SHIP)) {
381                    if (cb.isSelected()) {
382                        if (track.getShipLoadOption().equals(Track.ALL_LOADS)) {
383                            log.debug("Ship all loads selected for track ({})", track.getName());
384                        } else if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) {
385                            track.addShipLoadName(loadName);
386                        } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) {
387                            track.deleteShipLoadName(loadName);
388                            if (track.getShipLoadNames().length == 0) {
389                                track.setShipLoadOption(Track.ALL_LOADS);
390                            }
391                            // need to check if load configuration is to exclude all car types using this load
392                            if (!track.isLoadNameShipped(load)) {
393                                JmriJOptionPane.showMessageDialog(this,
394                                        Bundle.getMessage("WarningExcludeTrackShipLoad", track.getLocation().getName(),
395                                                track.getName(), load),
396                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
397                                needLoadTrackEditFrame = true;
398                            } else if (!track.isLoadNameAndCarTypeShipped(load, type)) {
399                                JmriJOptionPane.showMessageDialog(this,
400                                        Bundle.getMessage("WarningExcludeTrackShipTypeAndLoad",
401                                                track.getLocation().getName(), track.getName(), type, load,
402                                                NEW_LINE + type + CarLoad.SPLIT_CHAR + load),
403                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
404                                needLoadTrackEditFrame = true;
405                            }
406                        }
407                    } else {
408                        if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) {
409                            track.deleteShipLoadName(loadName);
410                            // need to check if load configuration is to accept all car types using this load
411                            if (track.isLoadNameShipped(load)) {
412                                JmriJOptionPane.showMessageDialog(this,
413                                        Bundle.getMessage("WarningShipTrackLoad", track.getLocation().getName(),
414                                                track.getName(), load),
415                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
416                                needLoadTrackEditFrame = true;
417                            } else if (track.isLoadNameAndCarTypeShipped(load, type)) {
418                                JmriJOptionPane.showMessageDialog(this,
419                                        Bundle.getMessage("WarningShipTrackTypeAndLoad", track.getLocation().getName(),
420                                                track.getName(), type, load,
421                                                NEW_LINE + type + CarLoad.SPLIT_CHAR + load),
422                                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
423                                needLoadTrackEditFrame = true;
424                            }
425                        } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) {
426                            track.addShipLoadName(loadName);
427                        } else if (track.getShipLoadOption().equals(Track.ALL_LOADS)) {
428                            // need to exclude this load
429                            track.setShipLoadOption(Track.EXCLUDE_LOADS);
430                            track.addShipLoadName(loadName);
431                        }
432                    }
433                }
434                if (needLoadTrackEditFrame) {
435                    if (tlef != null) {
436                        tlef.dispose();
437                    }
438                    tlef = new TrackLoadEditFrame();
439                    tlef.initComponents(track.getLocation(), track);
440                }
441            }
442        }
443    }
444
445    private void removePropertyChangeLocations() {
446        for (Location location : locationManager.getList()) {
447            location.removePropertyChangeListener(this);
448            List<Track> tracks = location.getTracksList();
449            for (Track track : tracks) {
450                track.removePropertyChangeListener(this);
451            }
452        }
453    }
454
455    @Override
456    public void dispose() {
457        locationManager.removePropertyChangeListener(this);
458        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
459        InstanceManager.getDefault(CarLoads.class).removePropertyChangeListener(this);
460        removePropertyChangeLocations();
461        super.dispose();
462    }
463
464    @Override
465    public void propertyChange(java.beans.PropertyChangeEvent e) {
466        log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
467                .getNewValue());
468        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) ||
469                e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) ||
470                e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY) ||
471                e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) ||
472                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY) ||
473                e.getPropertyName().equals(Track.NAME_CHANGED_PROPERTY)) {
474            updateLocations();
475        }
476        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) ||
477                e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) {
478            updateTypeComboBox();
479        }
480        if (e.getPropertyName().equals(CarLoads.LOAD_CHANGED_PROPERTY)) {
481            updateLoadComboBox();
482        }
483        if (e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) && e.getSource().getClass().equals(Track.class)) {
484            Track track = (Track) e.getSource();
485            updateLoadStatusCheckBox(track);
486        }
487    }
488
489    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocationsByCarLoadFrame.class);
490}