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