001package jmri.jmrit.operations.locations.tools;
002
003import java.awt.Dimension;
004import java.awt.GridBagLayout;
005
006import javax.swing.*;
007
008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
009import jmri.InstanceManager;
010import jmri.jmrit.operations.OperationsFrame;
011import jmri.jmrit.operations.OperationsXml;
012import jmri.jmrit.operations.locations.*;
013import jmri.jmrit.operations.locations.schedules.ScheduleManager;
014import jmri.jmrit.operations.rollingstock.RollingStock;
015import jmri.jmrit.operations.rollingstock.RollingStockManager;
016import jmri.jmrit.operations.rollingstock.cars.CarManager;
017import jmri.jmrit.operations.rollingstock.engines.EngineManager;
018import jmri.jmrit.operations.setup.Control;
019import jmri.jmrit.operations.trains.TrainCommon;
020import jmri.util.swing.JmriJOptionPane;
021
022/**
023 * Frame for copying a track for operations.
024 *
025 * @author Bob Jacobsen Copyright (C) 2001
026 * @author Daniel Boudreau Copyright (C) 2013
027 */
028public class TrackCopyFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
029
030    // text field
031    JTextField trackNameTextField = new javax.swing.JTextField(Control.max_len_string_track_name);
032
033    // major buttons
034    JButton copyButton = new javax.swing.JButton(Bundle.getMessage("ButtonCopy"));
035    JButton saveButton = new javax.swing.JButton(Bundle.getMessage("ButtonSave"));
036
037    // combo boxes
038    JComboBox<Location> locationBox = InstanceManager.getDefault(LocationManager.class).getComboBox();
039    JComboBox<Track> trackBox = new JComboBox<>();
040    JComboBox<Location> destinationBox = InstanceManager.getDefault(LocationManager.class).getComboBox();
041
042    // checkboxes
043    JCheckBox sameNameCheckBox = new JCheckBox(Bundle.getMessage("SameName"));
044    JCheckBox moveRollingStockCheckBox = new JCheckBox(Bundle.getMessage("MoveRollingStock"));
045    JCheckBox deleteTrackCheckBox = new JCheckBox(Bundle.getMessage("DeleteCopiedTrack"));
046
047    Location _location;     // copy from this location
048    Location _destination;  // copy the track to this location
049
050    // remember state of checkboxes during a session
051    static boolean sameName = false;
052    static boolean moveRollingStock = false;
053    static boolean deleteTrack = false;
054
055    /*
056     * Copies track to destination
057     */
058    public TrackCopyFrame(Track track, Location destination) {
059         _destination = destination;
060
061        // general GUI config
062        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
063
064        // Set up the panels
065        // Layout the panel by rows
066        // row 1
067        JPanel pName = new JPanel();
068        pName.setLayout(new GridBagLayout());
069        pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrackName")));
070        addItem(pName, trackNameTextField, 0, 0);
071
072        // row 2
073        JPanel pCopy = new JPanel();
074        pCopy.setLayout(new GridBagLayout());
075        pCopy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SelectTrackToCopy")));
076        addItem(pCopy, locationBox, 0, 0);
077        addItem(pCopy, trackBox, 1, 0);
078        
079        // row 3
080        JPanel pCopyTo = new JPanel();
081        pCopyTo.setLayout(new GridBagLayout());
082        pCopyTo.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SelectCopyToLocation")));
083        addItem(pCopyTo, destinationBox, 0, 0);
084
085        // row 4
086        JPanel pOptions = new JPanel();
087        pOptions.setLayout(new GridBagLayout());
088        pOptions.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Options")));
089        addItemLeft(pOptions, sameNameCheckBox, 0, 0);
090        addItemLeft(pOptions, moveRollingStockCheckBox, 0, 1);
091        addItemLeft(pOptions, deleteTrackCheckBox, 0, 2);
092
093        // row 5
094        JPanel pButton = new JPanel();
095        pButton.setLayout(new GridBagLayout());
096        addItem(pButton, copyButton, 0, 0);
097        addItem(pButton, saveButton, 1, 0);
098
099        getContentPane().add(pName);
100        getContentPane().add(pCopy);
101        getContentPane().add(pCopyTo);
102        getContentPane().add(pOptions);
103        getContentPane().add(pButton);
104
105        addComboBoxAction(locationBox);
106        addComboBoxAction(trackBox);
107        
108        addComboBoxAction(destinationBox);
109
110        // set the checkbox states
111        sameNameCheckBox.setSelected(sameName);
112        moveRollingStockCheckBox.setSelected(moveRollingStock);
113        deleteTrackCheckBox.setSelected(deleteTrack);
114        deleteTrackCheckBox.setEnabled(moveRollingStockCheckBox.isSelected());
115
116        // get notified if combo box gets modified
117        InstanceManager.getDefault(LocationManager.class).addPropertyChangeListener(this);
118
119        // add help menu to window
120        addHelpMenu("package.jmri.jmrit.operations.Operations_CopyTrack", true); // NOI18N
121
122        pack();
123        setMinimumSize(new Dimension(Control.panelWidth400, Control.panelHeight400));
124        
125        if (track != null) {
126            locationBox.setSelectedItem(track.getLocation());
127            trackBox.setSelectedItem(track);
128        }
129        destinationBox.setSelectedItem(destination);
130
131        // setup buttons
132        addButtonAction(copyButton);
133        addButtonAction(saveButton);
134
135        addCheckBoxAction(sameNameCheckBox);
136        addCheckBoxAction(moveRollingStockCheckBox);
137        
138        setTitle(Bundle.getMessage("MenuItemCopyTrack"));
139    }
140
141    // location combo box
142    @Override
143    protected void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
144        if (ae.getSource() == locationBox) {
145            updateTrackComboBox();
146        }
147        if (ae.getSource() == trackBox) {
148            updateTrackName();
149        }
150        if (ae.getSource() == destinationBox) {
151            _destination = (Location) destinationBox.getSelectedItem();
152            copyButton.setEnabled(_destination != null);
153        }
154    }
155
156    protected void updateTrackComboBox() {
157        log.debug("update track combobox");
158        if (_location != null) {
159            _location.removePropertyChangeListener(this);
160        }
161        if (locationBox.getSelectedItem() == null) {
162            trackBox.removeAllItems();
163        } else {
164            log.debug("copy from location: {}", locationBox.getSelectedItem());
165            _location = (Location) locationBox.getSelectedItem();
166            Track track = (Track)trackBox.getSelectedItem();
167            _location.updateComboBox(trackBox);
168            trackBox.setSelectedItem(track);
169            _location.addPropertyChangeListener(this);
170        }
171    }
172
173    protected void updateTrackName() {
174        if (sameNameCheckBox.isSelected() && trackBox.getSelectedItem() != null) {
175            trackNameTextField.setText(trackBox.getSelectedItem().toString());
176        }
177    }
178
179    @Override
180    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
181    protected void buttonActionPerformed(java.awt.event.ActionEvent ae) {
182        if (ae.getSource() == copyButton) {
183            log.debug("copy track button activated");
184            if (!checkName()) {
185                return;
186            }
187            if (trackBox.getSelectedItem() == null || trackBox.getSelectedItem().equals(Location.NONE) || _destination == null) {
188                // tell user that they need to select a track to copy
189                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("SelectLocationAndTrack"), Bundle
190                        .getMessage("SelectTrackToCopy"), JmriJOptionPane.INFORMATION_MESSAGE);
191                return;
192            }
193            Track fromTrack = (Track) trackBox.getSelectedItem();
194            if (moveRollingStockCheckBox.isSelected() && fromTrack.getPickupRS() > 0) {
195                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("FoundRollingStockPickUp",
196                        fromTrack.getPickupRS()), Bundle
197                                .getMessage("TrainsServicingTrack", fromTrack.getName()),
198                        JmriJOptionPane.WARNING_MESSAGE);
199                return; // failed
200            }
201            if (moveRollingStockCheckBox.isSelected() && fromTrack.getDropRS() > 0) {
202                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("FoundRollingStockDrop",
203                        fromTrack.getDropRS()), Bundle
204                                .getMessage("TrainsServicingTrack", fromTrack.getName()),
205                        JmriJOptionPane.WARNING_MESSAGE);
206                return; // failed
207            }
208            // only copy tracks that are okay with the location
209            if (fromTrack.isStaging() ^ _destination.isStaging()) {
210                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("TrackTypeWrong",
211                        fromTrack.getTrackType(), _destination.getName()), Bundle
212                                .getMessage("CanNotCopy", fromTrack.getName()), JmriJOptionPane.ERROR_MESSAGE);
213                return;
214            }
215            Track toTrack = fromTrack.copyTrack(trackNameTextField.getText(), _destination);
216            if (moveRollingStockCheckBox.isSelected()) {
217                // move rolling stock
218                moveRollingStock(fromTrack, toTrack);
219                if (deleteTrackCheckBox.isSelected()) {
220                    InstanceManager.getDefault(ScheduleManager.class).replaceTrack(fromTrack, toTrack);
221                    fromTrack.getLocation().deleteTrack(fromTrack);
222                }
223            }
224        }
225        if (ae.getSource() == saveButton) {
226            log.debug("save track button activated");
227            // save checkbox states
228            sameName = sameNameCheckBox.isSelected();
229            moveRollingStock = moveRollingStockCheckBox.isSelected();
230            deleteTrack = deleteTrackCheckBox.isSelected();
231            // save location file
232            OperationsXml.save();
233        }
234    }
235
236    @Override
237    protected void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
238        if (ae.getSource() == sameNameCheckBox) {
239            updateTrackName();
240        }
241        if (ae.getSource() == moveRollingStockCheckBox) {
242            deleteTrackCheckBox.setEnabled(moveRollingStockCheckBox.isSelected());
243            deleteTrackCheckBox.setSelected(false);
244        }
245    }
246
247    protected void updateComboBoxes() {
248        log.debug("update location combobox");
249        InstanceManager.getDefault(LocationManager.class).updateComboBox(locationBox);
250        InstanceManager.getDefault(LocationManager.class).updateComboBox(destinationBox);
251    }
252
253    /**
254     *
255     * @return true if name entered OK and isn't too long
256     */
257    protected boolean checkName() {
258        if (trackNameTextField.getText().trim().isEmpty()) {
259            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("MustEnterName"), Bundle
260                    .getMessage("CanNotTrack", Bundle.getMessage("ButtonCopy")), JmriJOptionPane.ERROR_MESSAGE);
261            return false;
262        }
263        if (TrainCommon.splitString(trackNameTextField.getText()).length() > Control.max_len_string_track_name) {
264            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("TrackNameLengthMax",
265                    Integer.toString(Control.max_len_string_track_name + 1)), Bundle
266                            .getMessage("CanNotTrack", Bundle.getMessage("ButtonCopy")), JmriJOptionPane.ERROR_MESSAGE);
267            return false;
268        }
269        // check to see if track already exists
270        if (_destination == null) {
271            return false;
272        }
273        Track check = _destination.getTrackByName(trackNameTextField.getText(), null);
274        if (check != null) {
275            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("TrackAlreadyExists"), Bundle
276                    .getMessage("CanNotTrack", Bundle.getMessage("ButtonCopy")), JmriJOptionPane.ERROR_MESSAGE);
277            return false;
278        }
279        return true;
280    }
281
282    protected void moveRollingStock(Track fromTrack, Track toTrack) {
283        moveRollingStock(fromTrack, toTrack, InstanceManager.getDefault(CarManager.class));
284        moveRollingStock(fromTrack, toTrack, InstanceManager.getDefault(EngineManager.class));
285    }
286
287    private void moveRollingStock(Track fromTrack, Track toTrack, RollingStockManager<? extends RollingStock> manager) {
288        for (RollingStock rs : manager.getByIdList()) {
289            if (rs.getTrack() == fromTrack) {
290                rs.setLocation(toTrack.getLocation(), toTrack, RollingStock.FORCE);
291            }
292        }
293    }
294
295    @Override
296    public void dispose() {
297        InstanceManager.getDefault(LocationManager.class).removePropertyChangeListener(this);
298        if (_destination != null) {
299            _destination.removePropertyChangeListener(this);
300        }
301        if (_location != null) {
302            _location.removePropertyChangeListener(this);
303        }
304        super.dispose();
305    }
306
307    @Override
308    public void propertyChange(java.beans.PropertyChangeEvent e) {
309        log.debug("PropertyChange ({}) old ({}) new ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue());
310        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) {
311            updateComboBoxes();
312        }
313        if (e.getSource() == _location) {
314            updateTrackComboBox();
315        }
316        if (e.getSource() == _destination && e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) {
317            dispose();
318        }
319    }
320
321    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrackCopyFrame.class);
322}