001package jmri.jmrit.beantable.beanedit;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import javax.swing.AbstractAction;
007import javax.swing.JCheckBox;
008import javax.swing.JComboBox;
009import javax.swing.JPanel;
010import javax.swing.JTextField;
011import jmri.InstanceManager;
012import jmri.Sensor;
013import jmri.Turnout;
014import jmri.TurnoutOperation;
015import jmri.TurnoutOperationManager;
016import jmri.NamedBean.DisplayOptions;
017import jmri.implementation.SignalSpeedMap;
018import jmri.util.swing.JComboBoxUtil;
019import jmri.jmrit.turnoutoperations.TurnoutOperationConfig;
020import jmri.swing.NamedBeanComboBox;
021import jmri.util.swing.JmriJOptionPane;
022
023/**
024 * Provides an edit panel for a turnout object.
025 *
026 * @author Kevin Dickerson Copyright (C) 2011
027 */
028public class TurnoutEditAction extends BeanEditAction<Turnout> {
029    @Override
030    public String helpTarget() {
031        return "package.jmri.jmrit.beantable.TurnoutAddEdit";
032    } // NOI18N
033
034    @Override
035    public void actionPerformed(ActionEvent e) {
036        oldAutomationSelection = bean.getTurnoutOperation();
037        oldModeSelection = bean.getFeedbackModeName();
038        super.actionPerformed(e);
039    }
040
041    @Override
042    protected void initPanels() {
043        super.initPanels();
044        feedback();
045        lock();
046        speed();
047    }
048
049    @Override
050    public Turnout getByUserName(String name) {
051        return InstanceManager.turnoutManagerInstance().getByUserName(name);
052    }
053
054    private JCheckBox inverted = new JCheckBox();
055
056    @Override
057    protected BeanItemPanel basicDetails() {
058        BeanItemPanel basic = super.basicDetails();
059        basic.addItem(new BeanEditItem(inverted, Bundle.getMessage("Inverted"),
060                Bundle.getMessage("InvertedToolTip")));
061        return basic;
062    }
063
064    @Override
065    protected void saveBasicItems(ActionEvent e) {
066        super.saveBasicItems(e);
067        Turnout turn = bean;
068        turn.setInverted(inverted.isSelected());
069    }
070
071    @Override
072    protected void resetBasicItems(ActionEvent e) {
073        super.resetBasicItems(e);
074        Turnout turn = bean;
075        if (turn.canInvert()) {
076            inverted.setSelected(turn.getInverted());
077        }
078        inverted.setEnabled(turn.canInvert());
079    }
080
081    private NamedBeanComboBox<Sensor> sensorFeedBack1ComboBox;
082    private NamedBeanComboBox<Sensor> sensorFeedBack2ComboBox;
083    private JComboBox<String> modeBox;
084    private JComboBox<String> automationBox;
085    private String useBlockSpeed = Bundle.getMessage("UseGlobal", "Block Speed");
086    private TurnoutOperationConfig config;
087    private BeanItemPanel feedback;
088    private final JPanel turnoutOperation = new JPanel();
089
090    private BeanItemPanel feedback() {
091        feedback = new BeanItemPanel();
092        feedback.setName(Bundle.getMessage("Feedback"));
093
094        modeBox = new JComboBox<>(bean.getValidFeedbackNames());
095        modeBox.setMaximumRowCount(modeBox.getItemCount());
096        oldModeSelection = bean.getFeedbackModeName();
097        modeBox.setSelectedItem(oldModeSelection);
098        modeBox.addActionListener((ActionEvent e) -> {
099            updateFeedbackOptions();
100        });
101
102        feedback.addItem(new BeanEditItem(null, null, Bundle.getMessage("FeedbackToolTip")));
103        feedback.addItem(new BeanEditItem(modeBox, Bundle.getMessage("FeedbackMode"),
104                Bundle.getMessage("FeedbackModeToolTip")));
105
106        sensorFeedBack1ComboBox = new NamedBeanComboBox<>(
107                InstanceManager.sensorManagerInstance(),
108                bean.getFirstSensor(),
109                DisplayOptions.DISPLAYNAME);
110        sensorFeedBack1ComboBox.setAllowNull(true);
111        JComboBoxUtil.setupComboBoxMaxRows(sensorFeedBack1ComboBox);
112        feedback.addItem(new BeanEditItem(sensorFeedBack1ComboBox,
113                Bundle.getMessage("FeedbackSensor1"),
114                Bundle.getMessage("FeedbackSensorToolTip")));
115
116        sensorFeedBack2ComboBox = new NamedBeanComboBox<>(
117                InstanceManager.sensorManagerInstance(),
118                bean.getSecondSensor(),
119                DisplayOptions.DISPLAYNAME);
120        sensorFeedBack2ComboBox.setAllowNull(true);
121        JComboBoxUtil.setupComboBoxMaxRows(sensorFeedBack2ComboBox);
122        feedback.addItem(new BeanEditItem(sensorFeedBack2ComboBox,
123                Bundle.getMessage("FeedbackSensor2"),
124                Bundle.getMessage("FeedbackSensorToolTip")));
125
126        String[] str = new String[]{"empty"};
127        automationBox = new JComboBox<>(str);
128        feedback.addItem(new BeanEditItem(automationBox,
129                Bundle.getMessage("TurnoutAutomation"),
130                Bundle.getMessage("TurnoutAutomationToolTip")));
131
132        oldAutomationSelection = bean.getTurnoutOperation();
133        if (oldAutomationSelection != null) {
134            config = TurnoutOperationConfig.getConfigPanel(oldAutomationSelection);
135        } else {
136            config = TurnoutOperationConfig.getConfigPanel(new jmri.RawTurnoutOperation());
137            config.setEnabled(false);
138            for (Component j : config.getComponents()) {
139                j.setEnabled(false);
140            }
141        }
142
143        turnoutOperation.add(config);
144        feedback.addItem(new BeanEditItem(turnoutOperation,
145                Bundle.getMessage("FeedbackOperation"),
146                Bundle.getMessage("FeedbackOperationToolTip")));
147        feedback.addItem(new BeanEditItem(operationsName,
148                Bundle.getMessage("FeedbackNameSet"),
149                Bundle.getMessage("FeedbackNameSetToolTip")));
150
151        feedback.setSaveItem(new AbstractAction() {
152            @Override
153            public void actionPerformed(ActionEvent e) {
154                String modeName = (String) modeBox.getSelectedItem();
155                if (modeName != null) {
156                    bean.setFeedbackMode(modeName);
157                }
158                String newName = operationsName.getText();
159                if ((currentOperation != null) && (newName != null) && !newName.isEmpty()) {
160                    if (!currentOperation.rename(newName)) {
161                        JmriJOptionPane.showMessageDialog(turnoutOperation,
162                                Bundle.getMessage("ErrorDuplicateUserName", newName),
163                                Bundle.getMessage("ErrorTitle"),
164                                JmriJOptionPane.ERROR_MESSAGE);
165                    } else {
166                        automationBox.removeActionListener(automationSelectionListener);
167                        automationBox.addItem(newName);
168                        automationBox.setSelectedItem(newName);
169                        automationBox.addActionListener(automationSelectionListener);
170                    }
171                    bean.setTurnoutOperation(null);
172                    bean.setTurnoutOperation(currentOperation);
173                }
174                config.endConfigure();
175                switch (automationBox.getSelectedIndex()) {
176                    case 0:   // Off
177                        bean.setInhibitOperation(true);
178                        bean.setTurnoutOperation(null);
179                        break;
180                    case 1:   // Default
181                        bean.setInhibitOperation(false);
182                        bean.setTurnoutOperation(null);
183                        break;
184                    default:  // named operation
185                        bean.setInhibitOperation(false);
186                        String autoMode = (String) automationBox.getSelectedItem();
187                        if (autoMode != null) {
188                            TurnoutOperation selectedOp = InstanceManager.getDefault(TurnoutOperationManager.class).
189                                    getOperation((autoMode));
190                            if (currentOperation != null && currentOperation.isNonce() && !currentOperation.equivalentTo(selectedOp)) {
191                                // attach the private copy holding the edited values
192                                bean.setTurnoutOperation(currentOperation);
193                            } else {
194                                bean.setTurnoutOperation(selectedOp);
195                            }
196                        }
197                        break;
198                }
199                oldAutomationSelection = bean.getTurnoutOperation();
200                oldModeSelection = bean.getFeedbackModeName();
201                try {
202                    bean.provideFirstFeedbackSensor(sensorFeedBack1ComboBox.getSelectedItemDisplayName());
203                } catch (jmri.JmriException ex) {
204                    JmriJOptionPane.showMessageDialog(turnoutOperation, ex.toString());
205                }
206                try {
207                    bean.provideSecondFeedbackSensor(sensorFeedBack2ComboBox.getSelectedItemDisplayName());
208                } catch (jmri.JmriException ex) {
209                    JmriJOptionPane.showMessageDialog(turnoutOperation, ex.toString());
210                }
211            }
212        });
213
214        feedback.setResetItem(new AbstractAction() {
215            @Override
216            public void actionPerformed(ActionEvent e) {
217                sensorFeedBack1ComboBox.setSelectedItem(bean.getFirstSensor());
218                sensorFeedBack2ComboBox.setSelectedItem(bean.getSecondSensor());
219
220                automationBox.removeActionListener(automationSelectionListener);
221                jmri.jmrit.beantable.TurnoutTableAction.updateAutomationBox(bean, automationBox);
222                automationBox.addActionListener(automationSelectionListener);
223
224                bean.setFeedbackMode(oldModeSelection);
225                updateFeedbackOptions();
226            }
227        });
228        bei.add(feedback);
229        return feedback;
230    }   // feedback()
231
232    private String oldModeSelection;
233    private TurnoutOperation oldAutomationSelection;
234    private TurnoutOperation currentOperation;
235    private JTextField operationsName = new JTextField(10);
236
237    transient ActionListener automationSelectionListener = (ActionEvent e) -> {
238        updateAutomationOptions();
239    };
240
241    private void updateFeedbackOptions() {
242        sensorFeedBack1ComboBox.setEnabled(false);
243        sensorFeedBack2ComboBox.setEnabled(false);
244
245        String mode = (String) modeBox.getSelectedItem();
246        if (mode != null) {
247            if (mode.equals("ONESENSOR")) {
248                sensorFeedBack1ComboBox.setEnabled(true);
249            } else if (mode.equals("TWOSENSOR")) {
250                sensorFeedBack1ComboBox.setEnabled(true);
251                sensorFeedBack2ComboBox.setEnabled(true);
252            }
253            bean.setFeedbackMode(mode);
254        }
255
256        bean.setFeedbackMode((String) modeBox.getSelectedItem());
257        jmri.jmrit.beantable.TurnoutTableAction.updateAutomationBox(bean, automationBox);
258    }
259
260    private void updateAutomationOptions() {
261
262        currentOperation = null;
263        automationBox.removeActionListener(automationSelectionListener);
264        if (automationBox.getSelectedIndex() > 1) {
265            String autoMode = (String) automationBox.getSelectedItem();
266            if (autoMode != null) {
267                currentOperation = InstanceManager.getDefault(TurnoutOperationManager.class).
268                        getOperation((autoMode));
269            }
270        }
271
272        if (currentOperation != null) {
273            turnoutOperation.remove(config);
274            TurnoutOperation existing = bean.getTurnoutOperation();
275            if (existing != null && existing.isNonce() && currentOperation.equals(existing.getDefinitive())) {
276                // keep editing the turnout's own copy so its saved values show
277                currentOperation = existing;
278            } else if (!currentOperation.isNonce()) {
279                currentOperation = currentOperation.makeNonce(bean);
280            }
281            config = TurnoutOperationConfig.getConfigPanel(currentOperation);
282            operationsName.setEnabled(true);
283            config.setEnabled(true);
284            turnoutOperation.add(config);
285            feedback.revalidate();
286            feedback.repaint();
287        } else {
288            operationsName.setEnabled(false);
289            config.setEnabled(false);
290            for (Component j : config.getComponents()) {
291                j.setEnabled(false);
292            }
293        }
294        automationBox.addActionListener(automationSelectionListener);
295    }
296
297    @Override
298    protected void cancelButtonAction(ActionEvent e) {
299        bean.setFeedbackMode(oldModeSelection);
300        bean.setTurnoutOperation(oldAutomationSelection);
301        super.cancelButtonAction(e);
302    }
303
304    private static final String bothText = "Both"; // TODO I18N using bundle. Note: check how this property is stored/loaded
305    private static final String cabOnlyText = "Cab only";
306    private static final String pushbutText = "Pushbutton only";
307    private static final String noneText = "None";
308
309    private JComboBox<String> lockBox;
310    protected BeanItemPanel lock() {
311        BeanItemPanel lock = new BeanItemPanel();
312        lock.setName(Bundle.getMessage("Lock"));
313
314        if (bean.getPossibleLockModes() != 0) {
315            // lock operations are available, configure pane for them
316            lock.addItem(new BeanEditItem(null, null, Bundle.getMessage("LockToolTip")));
317
318            // Vector is a JComboBox ctor; List is not
319            java.util.Vector<String> lockOperations = new java.util.Vector<>();
320            int modes = bean.getPossibleLockModes();
321            if (((modes & Turnout.CABLOCKOUT) != 0)
322                    && ((modes & Turnout.PUSHBUTTONLOCKOUT) != 0)) {
323                lockOperations.add(bothText);
324            }
325            if ((modes & Turnout.CABLOCKOUT) != 0) {
326                lockOperations.add(cabOnlyText);
327            }
328            if ((modes & Turnout.PUSHBUTTONLOCKOUT) != 0) {
329                lockOperations.add(pushbutText);
330            }
331            lockOperations.add(noneText);
332            JComboBox<String> lockOperationBox = new JComboBox<>(lockOperations);
333            lockOperationBox.setMaximumRowCount(lockOperationBox.getItemCount());
334
335            lock.addItem(new BeanEditItem(lockOperationBox,
336                    Bundle.getMessage("LockMode"),
337                    Bundle.getMessage("LockModeToolTip")));
338            lockOperationBox.addActionListener((ActionEvent e) -> {
339                String lockOp = (String) lockOperationBox.getSelectedItem();
340                if (lockOp != null) {
341                    if (lockOp.equals(noneText)) {
342                        lockBox.setEnabled(false);
343                    } else {
344                        lockBox.setEnabled(true);
345                    }
346                }
347            });
348
349            if ((bean.getPossibleLockModes() & Turnout.PUSHBUTTONLOCKOUT) != 0) {
350                lockBox = new JComboBox<>(bean.getValidDecoderNames());
351            } else {
352                lockBox = new JComboBox<>(new String[]{bean.getDecoderName()});
353            }
354            lock.addItem(new BeanEditItem(lockBox,
355                    Bundle.getMessage("LockModeDecoder"),
356                    Bundle.getMessage("LockModeDecoderToolTip")));
357
358            lock.setSaveItem(new AbstractAction() {
359                @Override
360                public void actionPerformed(ActionEvent e) {
361                    String lockOpName = (String) lockOperationBox.getSelectedItem();
362                    if (lockOpName != null) {
363                        if (lockOpName.equals(bothText)) {
364                            bean.enableLockOperation(Turnout.CABLOCKOUT
365                                    + Turnout.PUSHBUTTONLOCKOUT, true);
366                        }
367                        if (lockOpName.equals(cabOnlyText)) {
368                            bean.enableLockOperation(Turnout.CABLOCKOUT, true);
369                            bean.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, false);
370                        }
371                        if (lockOpName.equals(pushbutText)) {
372                            bean.enableLockOperation(Turnout.CABLOCKOUT, false);
373                            bean.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, true);
374                        }
375                    }
376                    String decoderName = (String) lockBox.getSelectedItem();
377                    if (decoderName != null) {
378                        bean.setDecoderName(decoderName);
379                    }
380                }
381            });
382
383            lock.setResetItem(new AbstractAction() {
384                @Override
385                public void actionPerformed(ActionEvent e) {
386                    lockBox.setSelectedItem(bean.getDecoderName());
387                    lockBox.setEnabled(true);
388                    if (bean.canLock(Turnout.CABLOCKOUT)
389                            && bean.canLock(Turnout.PUSHBUTTONLOCKOUT)) {
390                        lockOperationBox.setSelectedItem(bothText);
391                    } else if (bean.canLock(Turnout.PUSHBUTTONLOCKOUT)) {
392                        lockOperationBox.setSelectedItem(pushbutText);
393                    } else if (bean.canLock(Turnout.CABLOCKOUT)) {
394                        lockOperationBox.setSelectedItem(cabOnlyText);
395                    } else {
396                        lockOperationBox.setSelectedItem(noneText);
397                        lockBox.setEnabled(false);
398                    }
399                }
400            });
401        } else {
402            // lock operations are not available for this kind of Turnout
403            lock.addItem(new BeanEditItem(null, null, Bundle.getMessage("LockModeUnavailable")));
404        }
405        bei.add(lock);
406        return lock;
407    }   // lock()
408
409    private java.util.Vector<String> speedListClosed = new java.util.Vector<>();
410    private java.util.Vector<String> speedListThrown = new java.util.Vector<>();
411
412    private JComboBox<String> closedSpeedBox;
413    private JComboBox<String> thrownSpeedBox;
414    private String defaultThrownSpeedText;
415    private String defaultClosedSpeedText;
416
417    protected BeanItemPanel speed() {
418        BeanItemPanel speed = new BeanItemPanel();
419        speed.setName(Bundle.getMessage("Speed"));
420
421        speed.addItem(new BeanEditItem(null, null, Bundle.getMessage("SpeedTabToolTip")));
422
423        defaultThrownSpeedText = (Bundle.getMessage("UseGlobal", "Global")
424                + " " + InstanceManager.turnoutManagerInstance().getDefaultThrownSpeed());
425        defaultClosedSpeedText = (Bundle.getMessage("UseGlobal", "Global")
426                + " " + InstanceManager.turnoutManagerInstance().getDefaultClosedSpeed());
427
428      useBlockSpeed = Bundle.getMessage("UseGlobal", "Block Speed");
429
430        speedListClosed.add(defaultClosedSpeedText);
431        speedListThrown.add(defaultThrownSpeedText);
432
433        speedListClosed.add(useBlockSpeed);
434        speedListThrown.add(useBlockSpeed);
435
436        java.util.Vector<String> _speedMap = jmri.InstanceManager.
437                getDefault(SignalSpeedMap.class).getValidSpeedNames();
438        for (String speedMap : _speedMap) {
439            if (!speedListClosed.contains(speedMap)) {
440                speedListClosed.add(speedMap);
441            }
442            if (!speedListThrown.contains(speedMap)) {
443                speedListThrown.add(speedMap);
444            }
445        }
446
447        closedSpeedBox = new JComboBox<>(speedListClosed);
448        closedSpeedBox.setMaximumRowCount(closedSpeedBox.getItemCount());
449        closedSpeedBox.setEditable(true);
450
451        speed.addItem(new BeanEditItem(closedSpeedBox,
452                Bundle.getMessage("ClosedSpeed"),
453                Bundle.getMessage("ClosedSpeedToolTip")));
454        thrownSpeedBox = new JComboBox<>(speedListThrown);
455        thrownSpeedBox.setMaximumRowCount(thrownSpeedBox.getItemCount());
456        thrownSpeedBox.setEditable(true);
457        speed.addItem(new BeanEditItem(thrownSpeedBox,
458                Bundle.getMessage("ThrownSpeed"),
459                Bundle.getMessage("ThrownSpeedToolTip")));
460
461      speed.setSaveItem(new AbstractAction() {
462            @Override
463            public void actionPerformed(ActionEvent e) {
464                String newSpeed = (String) closedSpeedBox.getSelectedItem();
465                if (newSpeed != null) {
466                    try {
467                        bean.setStraightSpeed(newSpeed);
468                        if ((!speedListClosed.contains(newSpeed)) && !newSpeed.contains("Global")) {
469                            speedListClosed.add(newSpeed);
470                        }
471                    } catch (jmri.JmriException ex) {
472                        JmriJOptionPane.showMessageDialog(speed, ex.getMessage() + "\n" + newSpeed);
473                    }
474                }
475                newSpeed = (String) thrownSpeedBox.getSelectedItem();
476                if (newSpeed != null) {
477                    try {
478                        bean.setDivergingSpeed(newSpeed);
479                        if ((!speedListThrown.contains(newSpeed)) && !newSpeed.contains("Global")) {
480                            speedListThrown.add(newSpeed);
481                        }
482                    } catch (jmri.JmriException ex) {
483                        JmriJOptionPane.showMessageDialog(speed, ex.getMessage() + "\n" + newSpeed);
484                    }
485                }
486            }
487        });
488
489        speed.setResetItem(new AbstractAction() {
490            @Override
491            public void actionPerformed(ActionEvent e) {
492                String speed = bean.getDivergingSpeed();
493                speedListThrown.remove(defaultThrownSpeedText);
494                defaultThrownSpeedText = (Bundle.getMessage("UseGlobal", "Global")
495                        + " " + InstanceManager.turnoutManagerInstance().getDefaultThrownSpeed());
496                speedListThrown.add(0, defaultThrownSpeedText);
497                if (!speedListThrown.contains(speed)) {
498                    speedListThrown.add(speed);
499                    thrownSpeedBox.addItem(speed);
500                }
501                thrownSpeedBox.setSelectedItem(speed);
502                speed = bean.getStraightSpeed();
503                speedListClosed.remove(defaultClosedSpeedText);
504                defaultClosedSpeedText = (Bundle.getMessage("UseGlobal", "Global")
505                        + " " + InstanceManager.turnoutManagerInstance().getDefaultClosedSpeed());
506                speedListClosed.add(0, defaultClosedSpeedText);
507                if (!speedListClosed.contains(speed)) {
508                    speedListClosed.add(speed);
509                    closedSpeedBox.addItem(speed);
510                }
511                closedSpeedBox.setSelectedItem(speed);
512            }
513        });
514
515        bei.add(speed);
516        return speed;
517    }
518
519}