001package jmri.jmrit.operations.locations.tools;
002
003import java.awt.*;
004
005import javax.swing.*;
006
007import jmri.jmrit.operations.*;
008import jmri.jmrit.operations.locations.*;
009import jmri.jmrit.operations.setup.Control;
010import jmri.jmrit.operations.setup.Setup;
011import jmri.util.swing.JmriJOptionPane;
012
013/**
014 *
015 * @author Daniel Boudreau Copyright (C) 2011, 2025
016 * @author Gregory Madsen Copyright (C) 2012
017 */
018class PoolTrackFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
019
020    // labels
021    JLabel name = new JLabel(Bundle.getMessage("Name"));
022    JLabel minimum = new JLabel(Bundle.getMessage("Minimum"));
023    JLabel maximum = new JLabel(Bundle.getMessage("Maximum"));
024    JLabel maxUser = new JLabel(Bundle.getMessage("Maximum") + "*");
025    JLabel length = new JLabel(Bundle.getMessage("Length"));
026
027    // text field
028    JTextField trackPoolNameTextField = new JTextField(20);
029    JTextField trackMinLengthTextField = new JTextField(5);
030    JTextField trackMaxLengthTextField = new JTextField(5);
031
032    // checkbox
033    JCheckBox maxLengthCheckBox = new JCheckBox(Bundle.getMessage("EnableMax"));
034
035    // combo box
036    JComboBox<Pool> comboBoxPools = new JComboBox<>();
037    
038    // train departure order out of staging
039    JRadioButton orderNormal = new JRadioButton(Bundle.getMessage("Normal"));
040    JRadioButton orderFIFO = new JRadioButton(Bundle.getMessage("DescriptiveFIFO"));
041    JRadioButton orderLIFO = new JRadioButton(Bundle.getMessage("DescriptiveLIFO"));
042
043    // major buttons
044    JButton addButton = new JButton(Bundle.getMessage("Add"));
045    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
046
047    // pool status
048    JPanel poolStatus = new JPanel();
049
050    protected Track _track;
051    protected Pool _pool;
052
053    public PoolTrackFrame(Track track) {
054        super();
055        _track = track;
056    }
057
058    @Override
059    public void initComponents() {
060        if (_track == null) {
061            log.debug("track is null, pools can not be created");
062            return;
063        }
064        // the following code sets the frame's initial state
065        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
066
067        _track.addPropertyChangeListener(this);
068        _track.getLocation().addPropertyChangeListener(this);
069
070        _pool = _track.getPool();
071
072        // load the panel
073        JPanel p1 = new JPanel();
074        p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS));
075        JScrollPane p1Pane = new JScrollPane(p1);
076        p1Pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
077        p1Pane.setBorder(BorderFactory.createTitledBorder(""));
078        
079        // row 1
080        JPanel pt = new JPanel();
081        pt.setLayout(new BoxLayout(pt, BoxLayout.X_AXIS));
082        pt.setMaximumSize(new Dimension(2000, 250));
083
084        // row 1a
085        JPanel pTrackName = new JPanel();
086        pTrackName.setLayout(new GridBagLayout());
087        pTrackName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Track")));
088        addItem(pTrackName, new JLabel(_track.getName()), 0, 0);
089
090        // row 1b
091        JPanel pLocationName = new JPanel();
092        pLocationName.setLayout(new GridBagLayout());
093        pLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location")));
094        addItem(pLocationName, new JLabel(_track.getLocation().getName()), 0, 0);
095
096        pt.add(pTrackName);
097        pt.add(pLocationName);
098
099        JPanel poolName = new JPanel();
100        poolName.setLayout(new GridBagLayout());
101        poolName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PoolName")));
102        addItem(poolName, trackPoolNameTextField, 0, 0);
103        addItem(poolName, addButton, 1, 0);
104
105        JPanel selectPool = new JPanel();
106        selectPool.setLayout(new GridBagLayout());
107        selectPool.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PoolSelect")));
108        addItem(selectPool, comboBoxPools, 0, 0);
109        OperationsPanel.padComboBox(comboBoxPools);
110
111        JPanel minLengthTrack = new JPanel();
112        minLengthTrack.setLayout(new GridBagLayout());
113        minLengthTrack
114                .setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PoolTrackMinimum", _track.getName())));
115        addItem(minLengthTrack, trackMinLengthTextField, 0, 0);
116
117        trackMinLengthTextField.setText(Integer.toString(_track.getPoolMinimumLength()));
118        
119        JPanel maxLengthTrack = new JPanel();
120        maxLengthTrack.setLayout(new GridBagLayout());
121        maxLengthTrack
122                .setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PoolTrackMaximum", _track.getName())));
123        addItemLeft(maxLengthTrack, maxLengthCheckBox, 0, 0);
124        addItemLeft(maxLengthTrack, trackMaxLengthTextField, 1, 0);
125
126        maxLengthCheckBox.setToolTipText(Bundle.getMessage("EnableMaxTrackTip"));
127        updateMaxLengthFields();
128
129        // row 4, train service order panel
130        JPanel panelOrder = new JPanel();
131        panelOrder.setLayout(new GridBagLayout());
132        panelOrder.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainPickupOrder")));
133        panelOrder.add(orderNormal);
134        panelOrder.add(orderFIFO);
135        panelOrder.add(orderLIFO);
136        
137        ButtonGroup orderGroup = new ButtonGroup();
138        orderGroup.add(orderNormal);
139        orderGroup.add(orderFIFO);
140        orderGroup.add(orderLIFO);
141        
142        updateRadioButtons();
143        
144        addRadioButtonAction(orderNormal);
145        addRadioButtonAction(orderFIFO);
146        addRadioButtonAction(orderLIFO);
147
148        JPanel savePool = new JPanel();
149        savePool.setLayout(new GridBagLayout());
150        savePool.setBorder(BorderFactory.createTitledBorder(""));
151        addItem(savePool, saveButton, 0, 0);
152
153        p1.add(pt);
154        p1.add(poolName);
155        p1.add(selectPool);
156        p1.add(minLengthTrack);
157        p1.add(maxLengthTrack);
158        if (_track.isStaging()) {
159            p1.add(panelOrder);
160        }
161
162        // pool status panel
163        poolStatus.setLayout(new GridBagLayout());
164
165        p1.add(poolStatus);
166        p1.add(savePool);
167
168        getContentPane().add(p1Pane);
169        setTitle(Bundle.getMessage("MenuItemPoolTrack"));
170
171        // load comboBox
172        updatePoolsComboBox();
173        updatePoolStatus();
174
175        addCheckBoxAction(maxLengthCheckBox);
176        addButtonAction(addButton);
177        addButtonAction(saveButton);
178        
179        addPropertyChangeListeners();
180
181        // add help menu to window
182        addHelpMenu("package.jmri.jmrit.operations.Operations_Pools", true); // NOI18N
183        
184        initMinimumSize(new Dimension(Control.panelWidth600, Control.panelHeight400));
185    }
186
187    private void updatePoolsComboBox() {
188        _track.getLocation().updatePoolComboBox(comboBoxPools);
189        comboBoxPools.setSelectedItem(_track.getPool());
190    }
191
192    private void updateMaxLengthFields() {
193        maxLengthCheckBox.setSelected(false);
194        if (_track != null && _track.getPool() != null) {
195            boolean enable = _track.getPool().isMaxLengthOptionEnabled();
196            maxLengthCheckBox.setEnabled(enable);
197            if (enable) {
198                if (_track.getPoolMaximumLength() != Integer.MAX_VALUE) {
199                    maxLengthCheckBox.setSelected(true);
200                    trackMaxLengthTextField.setText(Integer.toString(_track.getPoolMaximumLength()));
201                } else {
202                    trackMaxLengthTextField.setText(Integer.toString(_track.getPool().getMaxLengthTrack(_track)));
203                }
204            }
205
206        } else {
207            maxLengthCheckBox.setEnabled(false);
208            trackMaxLengthTextField.setText("");
209        }
210        trackMaxLengthTextField.setEnabled(maxLengthCheckBox.isSelected());
211    }
212
213    private void updatePoolStatus() {
214        // This shows the details of the current member tracks in the Pool.
215        poolStatus.removeAll();
216
217        addItemLeft(poolStatus, name, 0, 0);
218        addItem(poolStatus, maximum, 1, 0);
219        addItem(poolStatus, minimum, 3, 0);
220        addItem(poolStatus, length, 4, 0);
221
222        String poolName = "";
223        if (_track.getPool() != null) {
224            Pool pool = _track.getPool();
225            if (pool.isThereMaxLengthRestrictions()) {
226                addItem(poolStatus, maxUser, 2, 0);
227            }
228            poolName = pool.getName();
229
230            int totalMinLength = 0;
231            int totalLength = 0;
232            int i = 0;
233            for (Track track : pool.getTracks()) {
234                i++;
235
236                JLabel name = new JLabel();
237                name.setText(track.getName());
238                
239                JLabel maximum = new JLabel();
240                maximum.setText(Integer.toString(pool.getMaxLengthTrack(track)));
241
242                JLabel maxUser = new JLabel();
243                if (track.getPoolMaximumLength() == Integer.MAX_VALUE) {
244                    maxUser.setText("");
245                } else {
246                    maxUser.setText(Integer.toString(track.getPoolMaximumLength()));
247                }
248
249                JLabel minimum = new JLabel();
250                minimum.setText(Integer.toString(track.getPoolMinimumLength()));
251                totalMinLength = totalMinLength + track.getPoolMinimumLength();
252
253                JLabel length = new JLabel();
254                length.setText(Integer.toString(track.getLength()));
255                totalLength = totalLength + track.getLength();
256
257                addItemLeft(poolStatus, name, 0, i);
258                addItem(poolStatus, maximum, 1, i);
259                addItem(poolStatus, minimum, 3, i);
260                addItem(poolStatus, length, 4, i);
261
262                if (pool.isThereMaxLengthRestrictions()) {
263                    addItem(poolStatus, maxUser, 2, i);
264                }
265            }
266            i++;
267            // Summary
268            JLabel total = new JLabel(Bundle.getMessage("Totals"));
269            addItem(poolStatus, total, 0, i);
270            if (totalMinLength > totalLength) {
271                JLabel error = new JLabel(Bundle.getMessage("ErrorMinLen"));
272                error.setForeground(Color.RED);
273                addItem(poolStatus, error, 1, i);
274            }
275            JLabel totalMin = new JLabel();
276            totalMin.setText(Integer.toString(totalMinLength));
277            addItem(poolStatus, totalMin, 3, i);
278            JLabel totalLen = new JLabel();
279            totalLen.setText(Integer.toString(totalLength));
280            addItem(poolStatus, totalLen, 4, i);
281        }
282        poolStatus.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PoolTracks", poolName)));
283        poolStatus.repaint();
284        poolStatus.revalidate();
285        setPreferredSize(null); // kill JMRI window size
286        pack();
287    }
288
289    @Override
290    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
291        if (ae.getSource() == addButton) {
292            Location location = _track.getLocation();
293            Pool pool = location.addPool(trackPoolNameTextField.getText().trim());
294            if (_pool == null) {
295                comboBoxPools.setSelectedItem(pool);
296            }
297        }
298        if (ae.getSource() == saveButton) {
299            save();
300            // save location file
301            OperationsXml.save();
302            if (Setup.isCloseWindowOnSaveEnabled()) {
303                dispose();
304            }
305        }
306    }
307    
308    @Override
309    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
310        trackMaxLengthTextField.setEnabled(maxLengthCheckBox.isSelected());
311    }
312
313    private void save() {
314        Pool pool = (Pool) comboBoxPools.getSelectedItem();
315        if (pool != _pool) {
316            maxLengthCheckBox.setSelected(false);
317            removePropertyChangeListeners();
318            _pool = pool;
319            addPropertyChangeListeners();
320        }
321        try {
322            _track.setPoolMinimumLength(Integer.parseInt(trackMinLengthTextField.getText()));
323            if (maxLengthCheckBox.isSelected() && _pool != null && _pool.isMaxLengthOptionEnabled()) {
324                _track.setPoolMaximumLength(Integer.parseInt(trackMaxLengthTextField.getText()));
325            } else {
326                _track.setPoolMaximumLength(Integer.MAX_VALUE); // default
327            }
328        } catch (NumberFormatException e) {
329            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("TrackMustBeNumber"), Bundle
330                    .getMessage("ErrorTrackLength"), JmriJOptionPane.ERROR_MESSAGE);
331            return;
332        }
333        _track.setPool(_pool); // this causes a property change to this frame
334        updateServiceOrder();
335    }
336
337    @Override
338    public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
339        log.debug("radio button activated");
340        if (_track.getPool() != null) {
341            for (Track track : _track.getPool().getTracks()) {
342                if (ae.getSource() == orderNormal) {
343                    track.setServiceOrder(Track.NORMAL);
344                }
345                if (ae.getSource() == orderFIFO) {
346                    track.setServiceOrder(Track.FIFO);
347                }
348                if (ae.getSource() == orderLIFO) {
349                    track.setServiceOrder(Track.LIFO);
350                }
351            }
352        }
353    }
354    
355    private void updateServiceOrder() {
356        if (_track.isStaging() && _track.getPool() != null) {
357            for (Track track : _track.getPool().getTracks()) {
358                if (track != _track) {
359                    _track.setServiceOrder(track.getServiceOrder());
360                    updateRadioButtons();
361                    break;
362                }
363            }
364        }
365    }
366    
367    private void updateRadioButtons() {
368        orderNormal.setSelected(_track.getServiceOrder().equals(Track.NORMAL));
369        orderFIFO.setSelected(_track.getServiceOrder().equals(Track.FIFO));
370        orderLIFO.setSelected(_track.getServiceOrder().equals(Track.LIFO));
371    }
372
373    private void addPropertyChangeListeners() {
374        if (_pool != null) {
375            _pool.addPropertyChangeListener(this);
376            for (Track t : _pool.getTracks()) {
377                if (t != _track) {
378                    t.addPropertyChangeListener(this);
379                }
380            }
381        }
382    }
383
384    private void removePropertyChangeListeners() {
385        if (_pool != null) {
386            _pool.removePropertyChangeListener(this);
387            for (Track t : _pool.getTracks()) {
388                if (t != _track) {
389                    t.removePropertyChangeListener(this);
390                }
391            }
392        }
393    }
394
395    @Override
396    public void dispose() {
397        if (_track != null) {
398            _track.removePropertyChangeListener(this);
399            _track.getLocation().removePropertyChangeListener(this);
400        }
401        removePropertyChangeListeners();
402        super.dispose();
403    }
404
405    @Override
406    public void propertyChange(java.beans.PropertyChangeEvent e) {
407        if (Control.SHOW_PROPERTY) {
408            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
409                    .getNewValue());
410        }
411        if (e.getPropertyName().equals(Location.POOL_LENGTH_CHANGED_PROPERTY)) {
412            updatePoolsComboBox();
413        }
414        if (e.getPropertyName().equals(Pool.LISTCHANGE_CHANGED_PROPERTY)) {
415            removePropertyChangeListeners();
416            addPropertyChangeListeners();
417        }
418        if (e.getPropertyName().equals(Pool.LISTCHANGE_CHANGED_PROPERTY) ||
419                e.getPropertyName().equals(Location.LENGTH_CHANGED_PROPERTY) ||
420                e.getPropertyName().equals(Track.POOL_CHANGED_PROPERTY) ||
421                e.getPropertyName().equals(Track.MIN_LENGTH_CHANGED_PROPERTY) ||
422                e.getPropertyName().equals(Track.MAX_LENGTH_CHANGED_PROPERTY)) {
423            updatePoolStatus();
424        }
425        if (e.getPropertyName().equals(Track.POOL_CHANGED_PROPERTY) ||
426                e.getPropertyName().equals(Track.MAX_LENGTH_CHANGED_PROPERTY)) {
427            updateMaxLengthFields();
428        }
429    }
430
431    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PoolTrackFrame.class);
432}