001package jmri.jmrit.operations.locations.tools;
002
003import java.awt.*;
004
005import javax.swing.*;
006
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
011import jmri.InstanceManager;
012import jmri.jmrit.operations.OperationsFrame;
013import jmri.jmrit.operations.OperationsXml;
014import jmri.jmrit.operations.locations.Location;
015import jmri.jmrit.operations.locations.Track;
016import jmri.jmrit.operations.rollingstock.cars.*;
017import jmri.jmrit.operations.setup.Control;
018import jmri.jmrit.operations.setup.Setup;
019
020/**
021 * Frame for user edit of track loads
022 *
023 * @author Dan Boudreau Copyright (C) 2013, 2014, 2015
024 * 
025 */
026public class TrackLoadEditFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
027
028    private static boolean loadAndType = false;
029    private static boolean shipLoadAndType = false;
030
031    Location _location = null;
032    Track _track = null;
033    String _type = "";
034    JMenu _toolMenu = null;
035
036    // panels
037    JPanel pLoadControls = new JPanel();
038    JPanel panelLoads = new JPanel();
039    JScrollPane paneLoads = new JScrollPane(panelLoads);
040
041    JPanel pShipLoadControls = new JPanel();
042    JPanel panelShipLoads = new JPanel();
043    JScrollPane paneShipLoadControls;
044    JScrollPane paneShipLoads = new JScrollPane(panelShipLoads);
045
046    // major buttons
047    JButton saveTrackButton = new JButton(Bundle.getMessage("SaveTrack"));
048
049    JButton addLoadButton = new JButton(Bundle.getMessage("AddLoad"));
050    JButton deleteLoadButton = new JButton(Bundle.getMessage("DeleteLoad"));
051    JButton deleteAllLoadsButton = new JButton(Bundle.getMessage("DeleteAll"));
052
053    JButton addShipLoadButton = new JButton(Bundle.getMessage("AddLoad"));
054    JButton deleteShipLoadButton = new JButton(Bundle.getMessage("DeleteLoad"));
055    JButton deleteAllShipLoadsButton = new JButton(Bundle.getMessage("DeleteAll"));
056
057    // check boxes
058    JCheckBox loadAndTypeCheckBox = new JCheckBox(Bundle.getMessage("TypeAndLoad"));
059    JCheckBox shipLoadAndTypeCheckBox = new JCheckBox(Bundle.getMessage("TypeAndLoad"));
060    JCheckBox holdCars = new JCheckBox(Bundle.getMessage("HoldCarsWithCustomLoads"));
061
062    // radio buttons
063    JRadioButton loadNameAll = new JRadioButton(Bundle.getMessage("AcceptAll"));
064    JRadioButton loadNameInclude = new JRadioButton(Bundle.getMessage("AcceptOnly"));
065    JRadioButton loadNameExclude = new JRadioButton(Bundle.getMessage("Exclude"));
066
067    JRadioButton shipLoadNameAll = new JRadioButton(Bundle.getMessage("ShipAll"));
068    JRadioButton shipLoadNameInclude = new JRadioButton(Bundle.getMessage("ShipOnly"));
069    JRadioButton shipLoadNameExclude = new JRadioButton(Bundle.getMessage("Exclude"));
070
071    // combo box
072    JComboBox<String> comboBoxLoads = InstanceManager.getDefault(CarLoads.class).getComboBox(null);
073    JComboBox<String> comboBoxShipLoads = InstanceManager.getDefault(CarLoads.class).getComboBox(null);
074    JComboBox<String> comboBoxTypes = InstanceManager.getDefault(CarTypes.class).getComboBox();
075    JComboBox<String> comboBoxShipTypes = InstanceManager.getDefault(CarTypes.class).getComboBox();
076
077    // labels
078    JLabel trackName = new JLabel();
079
080    public static final String DISPOSE = "dispose"; // NOI18N
081    public static final int MAX_NAME_LENGTH = Control.max_len_string_track_name;
082
083    public TrackLoadEditFrame() {
084        super(Bundle.getMessage("TitleEditTrackLoads"));
085    }
086
087    public void initComponents(Location location, Track track) {
088        _location = location;
089        _track = track;
090
091        // property changes
092        _location.addPropertyChangeListener(this);
093        // listen for car load name and type changes
094        InstanceManager.getDefault(CarLoads.class).addPropertyChangeListener(this);
095        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
096
097        // the following code sets the frame's initial state
098        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
099
100        // Set up the panels
101        // Layout the panel by rows
102        // row 1
103        JPanel p1 = new JPanel();
104        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
105        p1.setMaximumSize(new Dimension(2000, 250));
106
107        // row 1a
108        JPanel pTrackName = new JPanel();
109        pTrackName.setLayout(new GridBagLayout());
110        pTrackName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Track")));
111        addItem(pTrackName, trackName, 0, 0);
112
113        // row 1b
114        JPanel pLocationName = new JPanel();
115        pLocationName.setLayout(new GridBagLayout());
116        pLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location")));
117        addItem(pLocationName, new JLabel(_location.getName()), 0, 0);
118
119        p1.add(pTrackName);
120        p1.add(pLocationName);
121
122        // row 3
123        JPanel p3 = new JPanel();
124        p3.setLayout(new BoxLayout(p3, BoxLayout.Y_AXIS));
125        JScrollPane pane3 = new JScrollPane(p3);
126        pane3.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LoadsTrack")));
127        pane3.setMaximumSize(new Dimension(2000, 400));
128
129        JPanel pLoadRadioButtons = new JPanel();
130        pLoadRadioButtons.setLayout(new FlowLayout());
131
132        pLoadRadioButtons.add(loadNameAll);
133        pLoadRadioButtons.add(loadNameInclude);
134        pLoadRadioButtons.add(loadNameExclude);
135        pLoadRadioButtons.add(loadAndTypeCheckBox);
136
137        pLoadControls.setLayout(new FlowLayout());
138
139        pLoadControls.add(comboBoxTypes);
140        pLoadControls.add(comboBoxLoads);
141        pLoadControls.add(addLoadButton);
142        pLoadControls.add(deleteLoadButton);
143        pLoadControls.add(deleteAllLoadsButton);
144
145        pLoadControls.setVisible(false);
146
147        p3.add(pLoadRadioButtons);
148        p3.add(pLoadControls);
149
150        // row 4
151        panelLoads.setLayout(new GridBagLayout());
152        paneLoads.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Loads")));
153
154        ButtonGroup loadGroup = new ButtonGroup();
155        loadGroup.add(loadNameAll);
156        loadGroup.add(loadNameInclude);
157        loadGroup.add(loadNameExclude);
158
159        // row 6
160        JPanel p6 = new JPanel();
161        p6.setLayout(new BoxLayout(p6, BoxLayout.Y_AXIS));
162        paneShipLoadControls = new JScrollPane(p6);
163        paneShipLoadControls.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("ShipLoadsTrack")));
164        paneShipLoadControls.setMaximumSize(new Dimension(2000, 400));
165
166        JPanel pShipLoadRadioButtons = new JPanel();
167        pShipLoadRadioButtons.setLayout(new FlowLayout());
168
169        pShipLoadRadioButtons.add(shipLoadNameAll);
170        pShipLoadRadioButtons.add(shipLoadNameInclude);
171        pShipLoadRadioButtons.add(shipLoadNameExclude);
172        pShipLoadRadioButtons.add(shipLoadAndTypeCheckBox);
173
174        pShipLoadControls.setLayout(new FlowLayout());
175
176        pShipLoadControls.add(comboBoxShipTypes);
177        pShipLoadControls.add(comboBoxShipLoads);
178        pShipLoadControls.add(addShipLoadButton);
179        pShipLoadControls.add(deleteShipLoadButton);
180        pShipLoadControls.add(deleteAllShipLoadsButton);
181
182        pShipLoadControls.setVisible(false);
183
184        p6.add(pShipLoadRadioButtons);
185        p6.add(pShipLoadControls);
186
187        // row 7
188        panelShipLoads.setLayout(new GridBagLayout());
189        paneShipLoads.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Loads")));
190
191        ButtonGroup shipLoadGroup = new ButtonGroup();
192        shipLoadGroup.add(shipLoadNameAll);
193        shipLoadGroup.add(shipLoadNameInclude);
194        shipLoadGroup.add(shipLoadNameExclude);
195        
196        JPanel pOptions = new JPanel();
197        pOptions.setLayout(new GridBagLayout());
198        pOptions.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Options")));
199        pOptions.setMaximumSize(new Dimension(2000, 400));
200        addItem(pOptions, holdCars, 0, 0);
201        holdCars.setToolTipText(Bundle.getMessage("HoldCarsWithCustomLoadsTip"));
202
203        // row 12
204        JPanel panelButtons = new JPanel();
205        panelButtons.setLayout(new GridBagLayout());
206        panelButtons.setBorder(BorderFactory.createTitledBorder(""));
207        panelButtons.setMaximumSize(new Dimension(2000, 200));
208
209        // row 13
210        addItem(panelButtons, saveTrackButton, 0, 0);
211
212        getContentPane().add(p1);
213        getContentPane().add(pane3);
214        getContentPane().add(paneLoads);
215        getContentPane().add(paneShipLoadControls);
216        getContentPane().add(paneShipLoads);
217        getContentPane().add(pOptions);
218        getContentPane().add(panelButtons);
219
220        // setup buttons
221        addButtonAction(saveTrackButton);
222
223        addButtonAction(deleteLoadButton);
224        addButtonAction(deleteAllLoadsButton);
225        addButtonAction(addLoadButton);
226
227        addButtonAction(deleteShipLoadButton);
228        addButtonAction(deleteAllShipLoadsButton);
229        addButtonAction(addShipLoadButton);
230
231        addRadioButtonAction(loadNameAll);
232        addRadioButtonAction(loadNameInclude);
233        addRadioButtonAction(loadNameExclude);
234
235        addRadioButtonAction(shipLoadNameAll);
236        addRadioButtonAction(shipLoadNameInclude);
237        addRadioButtonAction(shipLoadNameExclude);
238
239        addComboBoxAction(comboBoxTypes);
240        addComboBoxAction(comboBoxShipTypes);
241
242        paneShipLoadControls.setVisible(false);
243        paneShipLoads.setVisible(false);
244        pOptions.setVisible(false);
245
246        // load fields and enable buttons
247        if (_track != null) {
248            _track.addPropertyChangeListener(this);
249            trackName.setText(_track.getName());
250            // only show ship loads for staging tracks
251            paneShipLoadControls.setVisible(_track.isStaging());
252            paneShipLoads.setVisible(_track.isStaging());
253            pOptions.setVisible(_track.isSpur());
254            holdCars.setSelected(_track.isHoldCarsWithCustomLoadsEnabled());
255            updateButtons(true);
256        } else {
257            updateButtons(false);
258        }
259
260        // build menu
261        // JMenuBar menuBar = new JMenuBar();
262        // _toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
263        // menuBar.add(_toolMenu);
264        // setJMenuBar(menuBar);
265        // load
266        updateTypeComboBoxes();
267        updateLoadComboBoxes();
268        updateLoadNames();
269        updateShipLoadNames();
270
271        loadAndTypeCheckBox.setSelected(loadAndType);
272        shipLoadAndTypeCheckBox.setSelected(shipLoadAndType);
273        
274        // add help menu to window
275        addHelpMenu("package.jmri.jmrit.operations.Operations_LoadOptions", true); // NOI18N
276
277        initMinimumSize(new Dimension(Control.panelWidth600, Control.panelHeight400));
278    }
279
280    // Save, Delete, Add
281    @Override
282    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
283        if (_track == null) {
284            return;
285        }
286        if (ae.getSource() == saveTrackButton) {
287            log.debug("track save button activated");
288            save();
289            if (Setup.isCloseWindowOnSaveEnabled()) {
290                dispose();
291            }
292        }
293        if (ae.getSource() == addLoadButton) {
294            String loadName = (String) comboBoxLoads.getSelectedItem();
295            if (loadAndTypeCheckBox.isSelected()) {
296                loadName = comboBoxTypes.getSelectedItem() + CarLoad.SPLIT_CHAR + loadName;
297            }
298            _track.addLoadName(loadName);
299            selectNextItemComboBox(comboBoxLoads);
300        }
301        if (ae.getSource() == deleteLoadButton) {
302            String loadName = (String) comboBoxLoads.getSelectedItem();
303            if (loadAndTypeCheckBox.isSelected()) {
304                loadName = comboBoxTypes.getSelectedItem() + CarLoad.SPLIT_CHAR + loadName;
305            }
306            _track.deleteLoadName(loadName);
307            selectNextItemComboBox(comboBoxLoads);
308        }
309        if (ae.getSource() == deleteAllLoadsButton) {
310            deleteAllLoads();
311        }
312        if (ae.getSource() == addShipLoadButton) {
313            String loadName = (String) comboBoxShipLoads.getSelectedItem();
314            if (shipLoadAndTypeCheckBox.isSelected()) {
315                loadName = comboBoxShipTypes.getSelectedItem() + CarLoad.SPLIT_CHAR + loadName;
316            }
317            _track.addShipLoadName(loadName);
318            selectNextItemComboBox(comboBoxShipLoads);
319        }
320        if (ae.getSource() == deleteShipLoadButton) {
321            String loadName = (String) comboBoxShipLoads.getSelectedItem();
322            if (shipLoadAndTypeCheckBox.isSelected()) {
323                loadName = comboBoxShipTypes.getSelectedItem() + CarLoad.SPLIT_CHAR + loadName;
324            }
325            _track.deleteShipLoadName(loadName);
326            selectNextItemComboBox(comboBoxShipLoads);
327        }
328        if (ae.getSource() == deleteAllShipLoadsButton) {
329            deleteAllShipLoads();
330        }
331    }
332
333    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
334    protected void save() {
335        checkForErrors();
336        _track.setHoldCarsWithCustomLoadsEnabled(holdCars.isSelected());
337        // save the last state of the "Use car type and load" checkbox
338        loadAndType = loadAndTypeCheckBox.isSelected();
339        shipLoadAndType = shipLoadAndTypeCheckBox.isSelected();
340        // save location file
341        OperationsXml.save();
342    }
343
344    protected void updateButtons(boolean enabled) {
345        saveTrackButton.setEnabled(enabled);
346
347        loadNameAll.setEnabled(enabled);
348        loadNameInclude.setEnabled(enabled);
349        loadNameExclude.setEnabled(enabled);
350        loadAndTypeCheckBox.setEnabled(enabled);
351
352        // enable ship options if any of the three generate loads from staging is selected
353        // or if there are any ship load restrictions for this track
354        boolean en = enabled
355                && (_track.isAddCustomLoadsAnyStagingTrackEnabled() || _track.isAddCustomLoadsAnySpurEnabled() || _track
356                .isAddCustomLoadsEnabled() || !_track.getShipLoadOption().equals(Track.ALL_LOADS));
357
358        shipLoadNameAll.setEnabled(en);
359        shipLoadNameInclude.setEnabled(en);
360        shipLoadNameExclude.setEnabled(en);
361        shipLoadAndTypeCheckBox.setEnabled(en);
362
363        addShipLoadButton.setEnabled(en);
364        deleteShipLoadButton.setEnabled(en);
365        deleteAllShipLoadsButton.setEnabled(en);
366
367        comboBoxShipLoads.setEnabled(en);
368        comboBoxShipTypes.setEnabled(en);
369
370    }
371
372    @Override
373    public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
374        log.debug("radio button activated");
375        if (ae.getSource() == loadNameAll) {
376            _track.setLoadOption(Track.ALL_LOADS);
377        }
378        if (ae.getSource() == loadNameInclude) {
379            _track.setLoadOption(Track.INCLUDE_LOADS);
380        }
381        if (ae.getSource() == loadNameExclude) {
382            _track.setLoadOption(Track.EXCLUDE_LOADS);
383        }
384        if (ae.getSource() == shipLoadNameAll) {
385            _track.setShipLoadOption(Track.ALL_LOADS);
386        }
387        if (ae.getSource() == shipLoadNameInclude) {
388            _track.setShipLoadOption(Track.INCLUDE_LOADS);
389        }
390        if (ae.getSource() == shipLoadNameExclude) {
391            _track.setShipLoadOption(Track.EXCLUDE_LOADS);
392        }
393    }
394
395    // Car type combo box has been changed, show loads associated with this car type
396    @Override
397    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
398        updateLoadComboBoxes();
399    }
400
401    private void updateLoadComboBoxes() {
402        String carType = (String) comboBoxTypes.getSelectedItem();
403        InstanceManager.getDefault(CarLoads.class).updateComboBox(carType, comboBoxLoads);
404        carType = (String) comboBoxShipTypes.getSelectedItem();
405        InstanceManager.getDefault(CarLoads.class).updateComboBox(carType, comboBoxShipLoads);
406    }
407
408    private void updateLoadNames() {
409        log.debug("Update load names");
410        panelLoads.removeAll();
411        if (_track != null) {
412            // set radio button
413            loadNameAll.setSelected(_track.getLoadOption().equals(Track.ALL_LOADS));
414            loadNameInclude.setSelected(_track.getLoadOption().equals(Track.INCLUDE_LOADS));
415            loadNameExclude.setSelected(_track.getLoadOption().equals(Track.EXCLUDE_LOADS));
416
417            pLoadControls.setVisible(!loadNameAll.isSelected());
418
419            if (!loadNameAll.isSelected()) {
420                int x = 0;
421                int y = 0; // vertical position in panel
422
423                int numberOfLoads = getNumberOfCheckboxesPerLine() / 2 + 1;
424                for (String loadName : _track.getLoadNames()) {
425                    JLabel load = new JLabel();
426                    load.setText(loadName);
427                    addItemTop(panelLoads, load, x++, y);
428                    // limit the number of loads per line
429                    if (x > numberOfLoads) {
430                        y++;
431                        x = 0;
432                    }
433                }
434                revalidate();
435            }
436        } else {
437            loadNameAll.setSelected(true);
438        }
439        panelLoads.repaint();
440        panelLoads.revalidate();
441    }
442
443    private void updateShipLoadNames() {
444        log.debug("Update ship load names");
445        panelShipLoads.removeAll();
446        if (_track != null) {
447            // set radio button
448            shipLoadNameAll.setSelected(_track.getShipLoadOption().equals(Track.ALL_LOADS));
449            shipLoadNameInclude.setSelected(_track.getShipLoadOption().equals(Track.INCLUDE_LOADS));
450            shipLoadNameExclude.setSelected(_track.getShipLoadOption().equals(Track.EXCLUDE_LOADS));
451
452            pShipLoadControls.setVisible(!shipLoadNameAll.isSelected());
453
454            if (!shipLoadNameAll.isSelected()) {
455                int x = 0;
456                int y = 0; // vertical position in panel
457
458                int numberOfLoads = getNumberOfCheckboxesPerLine() / 2 + 1;
459                for (String loadName : _track.getShipLoadNames()) {
460                    JLabel load = new JLabel();
461                    load.setText(loadName);
462                    addItemTop(panelShipLoads, load, x++, y);
463                    // limit the number of loads per line
464                    if (x > numberOfLoads) {
465                        y++;
466                        x = 0;
467                    }
468                }
469                revalidate();
470            }
471        } else {
472            shipLoadNameAll.setSelected(true);
473        }
474        panelShipLoads.repaint();
475        panelShipLoads.revalidate();
476    }
477
478    private void deleteAllLoads() {
479        if (_track != null) {
480            for (String loadName : _track.getLoadNames()) {
481                _track.deleteLoadName(loadName);
482            }
483        }
484    }
485
486    private void deleteAllShipLoads() {
487        if (_track != null) {
488            for (String loadName : _track.getShipLoadNames()) {
489                _track.deleteShipLoadName(loadName);
490            }
491        }
492    }
493
494    private void updateTypeComboBoxes() {
495        InstanceManager.getDefault(CarTypes.class).updateComboBox(comboBoxTypes);
496        // remove car types not serviced by this location and track
497        for (int i = comboBoxTypes.getItemCount() - 1; i >= 0; i--) {
498            String type = comboBoxTypes.getItemAt(i);
499            if (_track != null && !_track.isTypeNameAccepted(type)) {
500                comboBoxTypes.removeItem(type);
501            }
502        }
503        InstanceManager.getDefault(CarTypes.class).updateComboBox(comboBoxShipTypes);
504        // remove car types not serviced by this location and track
505        for (int i = comboBoxShipTypes.getItemCount() - 1; i >= 0; i--) {
506            String type = comboBoxShipTypes.getItemAt(i);
507            if (_track != null && !_track.isTypeNameAccepted(type)) {
508                comboBoxShipTypes.removeItem(type);
509            }
510        }
511    }
512
513    private void checkForErrors() {
514        if (_track.getLoadOption().equals(Track.INCLUDE_LOADS) && _track.getLoadNames().length == 0
515                || _track.getShipLoadOption().equals(Track.INCLUDE_LOADS) && _track.getShipLoadNames().length == 0) {
516            JOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNeedLoads"), Bundle.getMessage("ErrorNoLoads"),
517                    JOptionPane.ERROR_MESSAGE);
518        }
519    }
520
521    @Override
522    public void dispose() {
523        if (_track != null) {
524            _track.removePropertyChangeListener(this);
525        }
526        _location.removePropertyChangeListener(this);
527        InstanceManager.getDefault(CarLoads.class).removePropertyChangeListener(this);
528        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
529        super.dispose();
530    }
531
532    @Override
533    public void propertyChange(java.beans.PropertyChangeEvent e) {
534        if (Control.SHOW_PROPERTY) {
535            log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N
536        }
537        if (e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY)
538                || e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY)
539                || e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
540            updateTypeComboBoxes();
541        }
542        if (e.getPropertyName().equals(CarLoads.LOAD_NAME_CHANGED_PROPERTY)
543                || e.getPropertyName().equals(CarLoads.LOAD_CHANGED_PROPERTY)) {
544            updateLoadComboBoxes();
545            updateLoadNames();
546            updateShipLoadNames();
547        }
548        if (e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY)) {
549            updateLoadNames();
550            updateShipLoadNames();
551        }
552        if (_track != null && e.getPropertyName().equals(Track.LOAD_OPTIONS_CHANGED_PROPERTY)) {
553            updateButtons(true);
554        }
555        if (_track != null && e.getPropertyName().equals(Track.HOLD_CARS_CHANGED_PROPERTY)) {
556            holdCars.setSelected(_track.isHoldCarsWithCustomLoadsEnabled());
557        }
558    }
559
560    private final static Logger log = LoggerFactory.getLogger(TrackLoadEditFrame.class);
561}