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.addItem(newName);
167                        automationBox.setSelectedItem(newName);
168                    }
169                    bean.setTurnoutOperation(null);
170                    bean.setTurnoutOperation(currentOperation);
171                }
172                config.endConfigure();
173                switch (automationBox.getSelectedIndex()) {
174                    case 0:   // Off
175                        bean.setInhibitOperation(true);
176                        bean.setTurnoutOperation(null);
177                        break;
178                    case 1:   // Default
179                        bean.setInhibitOperation(false);
180                        bean.setTurnoutOperation(null);
181                        break;
182                    default:  // named operation
183                        bean.setInhibitOperation(false);
184                        String autoMode = (String) automationBox.getSelectedItem();
185                        if (autoMode != null) {
186                            bean.setTurnoutOperation(InstanceManager.getDefault(TurnoutOperationManager.class).
187                                    getOperation((autoMode)));
188                        }
189                        break;
190                }
191                oldAutomationSelection = bean.getTurnoutOperation();
192                oldModeSelection = bean.getFeedbackModeName();
193                try {
194                    bean.provideFirstFeedbackSensor(sensorFeedBack1ComboBox.getSelectedItemDisplayName());
195                } catch (jmri.JmriException ex) {
196                    JmriJOptionPane.showMessageDialog(turnoutOperation, ex.toString());
197                }
198                try {
199                    bean.provideSecondFeedbackSensor(sensorFeedBack2ComboBox.getSelectedItemDisplayName());
200                } catch (jmri.JmriException ex) {
201                    JmriJOptionPane.showMessageDialog(turnoutOperation, ex.toString());
202                }
203            }
204        });
205
206        feedback.setResetItem(new AbstractAction() {
207            @Override
208            public void actionPerformed(ActionEvent e) {
209                sensorFeedBack1ComboBox.setSelectedItem(bean.getFirstSensor());
210                sensorFeedBack2ComboBox.setSelectedItem(bean.getSecondSensor());
211
212                automationBox.removeActionListener(automationSelectionListener);
213                jmri.jmrit.beantable.TurnoutTableAction.updateAutomationBox(bean, automationBox);
214                automationBox.addActionListener(automationSelectionListener);
215
216                bean.setFeedbackMode(oldModeSelection);
217                updateFeedbackOptions();
218            }
219        });
220        bei.add(feedback);
221        return feedback;
222    }   // feedback()
223
224    private String oldModeSelection;
225    private TurnoutOperation oldAutomationSelection;
226    private TurnoutOperation currentOperation;
227    private JTextField operationsName = new JTextField(10);
228
229    transient ActionListener automationSelectionListener = (ActionEvent e) -> {
230        updateAutomationOptions();
231    };
232
233    private void updateFeedbackOptions() {
234        sensorFeedBack1ComboBox.setEnabled(false);
235        sensorFeedBack2ComboBox.setEnabled(false);
236
237        String mode = (String) modeBox.getSelectedItem();
238        if (mode != null) {
239            if (mode.equals("ONESENSOR")) {
240                sensorFeedBack1ComboBox.setEnabled(true);
241            } else if (mode.equals("TWOSENSOR")) {
242                sensorFeedBack1ComboBox.setEnabled(true);
243                sensorFeedBack2ComboBox.setEnabled(true);
244            }
245            bean.setFeedbackMode(mode);
246        }
247
248        bean.setFeedbackMode((String) modeBox.getSelectedItem());
249        jmri.jmrit.beantable.TurnoutTableAction.updateAutomationBox(bean, automationBox);
250    }
251
252    private void updateAutomationOptions() {
253
254        currentOperation = null;
255        automationBox.removeActionListener(automationSelectionListener);
256        if (automationBox.getSelectedIndex() > 1) {
257            String autoMode = (String) automationBox.getSelectedItem();
258            if (autoMode != null) {
259                currentOperation = InstanceManager.getDefault(TurnoutOperationManager.class).
260                        getOperation((autoMode));
261            }
262        }
263
264        if (currentOperation != null) {
265            turnoutOperation.remove(config);
266            if (!currentOperation.isNonce()) {
267                currentOperation = currentOperation.makeNonce(bean);
268            }
269            config = TurnoutOperationConfig.getConfigPanel(currentOperation);
270            operationsName.setEnabled(true);
271            config.setEnabled(true);
272            turnoutOperation.add(config);
273            feedback.revalidate();
274            feedback.repaint();
275        } else {
276            operationsName.setEnabled(false);
277            config.setEnabled(false);
278            for (Component j : config.getComponents()) {
279                j.setEnabled(false);
280            }
281        }
282        automationBox.addActionListener(automationSelectionListener);
283    }
284
285    @Override
286    protected void cancelButtonAction(ActionEvent e) {
287        bean.setFeedbackMode(oldModeSelection);
288        bean.setTurnoutOperation(oldAutomationSelection);
289        super.cancelButtonAction(e);
290    }
291
292    private final static String bothText = "Both"; // TODO I18N using bundle. Note: check how this property is stored/loaded
293    private final static String cabOnlyText = "Cab only";
294    private final static String pushbutText = "Pushbutton only";
295    private final static String noneText = "None";
296
297    private JComboBox<String> lockBox;
298    protected BeanItemPanel lock() {
299        BeanItemPanel lock = new BeanItemPanel();
300        lock.setName(Bundle.getMessage("Lock"));
301
302        if (bean.getPossibleLockModes() != 0) {
303            // lock operations are available, configure pane for them
304            lock.addItem(new BeanEditItem(null, null, Bundle.getMessage("LockToolTip")));
305
306            // Vector is a JComboBox ctor; List is not
307            java.util.Vector<String> lockOperations = new java.util.Vector<>();
308            int modes = bean.getPossibleLockModes();
309            if (((modes & Turnout.CABLOCKOUT) != 0)
310                    && ((modes & Turnout.PUSHBUTTONLOCKOUT) != 0)) {
311                lockOperations.add(bothText);
312            }
313            if ((modes & Turnout.CABLOCKOUT) != 0) {
314                lockOperations.add(cabOnlyText);
315            }
316            if ((modes & Turnout.PUSHBUTTONLOCKOUT) != 0) {
317                lockOperations.add(pushbutText);
318            }
319            lockOperations.add(noneText);
320            JComboBox<String> lockOperationBox = new JComboBox<>(lockOperations);
321            lockOperationBox.setMaximumRowCount(lockOperationBox.getItemCount());
322
323            lock.addItem(new BeanEditItem(lockOperationBox,
324                    Bundle.getMessage("LockMode"),
325                    Bundle.getMessage("LockModeToolTip")));
326            lockOperationBox.addActionListener((ActionEvent e) -> {
327                String lockOp = (String) lockOperationBox.getSelectedItem();
328                if (lockOp != null) {
329                    if (lockOp.equals(noneText)) {
330                        lockBox.setEnabled(false);
331                    } else {
332                        lockBox.setEnabled(true);
333                    }
334                }
335            });
336
337            if ((bean.getPossibleLockModes() & Turnout.PUSHBUTTONLOCKOUT) != 0) {
338                lockBox = new JComboBox<>(bean.getValidDecoderNames());
339            } else {
340                lockBox = new JComboBox<>(new String[]{bean.getDecoderName()});
341            }
342            lock.addItem(new BeanEditItem(lockBox,
343                    Bundle.getMessage("LockModeDecoder"),
344                    Bundle.getMessage("LockModeDecoderToolTip")));
345
346            lock.setSaveItem(new AbstractAction() {
347                @Override
348                public void actionPerformed(ActionEvent e) {
349                    String lockOpName = (String) lockOperationBox.getSelectedItem();
350                    if (lockOpName != null) {
351                        if (lockOpName.equals(bothText)) {
352                            bean.enableLockOperation(Turnout.CABLOCKOUT
353                                    + Turnout.PUSHBUTTONLOCKOUT, true);
354                        }
355                        if (lockOpName.equals(cabOnlyText)) {
356                            bean.enableLockOperation(Turnout.CABLOCKOUT, true);
357                            bean.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, false);
358                        }
359                        if (lockOpName.equals(pushbutText)) {
360                            bean.enableLockOperation(Turnout.CABLOCKOUT, false);
361                            bean.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, true);
362                        }
363                    }
364                    String decoderName = (String) lockBox.getSelectedItem();
365                    if (decoderName != null) {
366                        bean.setDecoderName(decoderName);
367                    }
368                }
369            });
370
371            lock.setResetItem(new AbstractAction() {
372                @Override
373                public void actionPerformed(ActionEvent e) {
374                    lockBox.setSelectedItem(bean.getDecoderName());
375                    lockBox.setEnabled(true);
376                    if (bean.canLock(Turnout.CABLOCKOUT)
377                            && bean.canLock(Turnout.PUSHBUTTONLOCKOUT)) {
378                        lockOperationBox.setSelectedItem(bothText);
379                    } else if (bean.canLock(Turnout.PUSHBUTTONLOCKOUT)) {
380                        lockOperationBox.setSelectedItem(pushbutText);
381                    } else if (bean.canLock(Turnout.CABLOCKOUT)) {
382                        lockOperationBox.setSelectedItem(cabOnlyText);
383                    } else {
384                        lockOperationBox.setSelectedItem(noneText);
385                        lockBox.setEnabled(false);
386                    }
387                }
388            });
389        } else {
390            // lock operations are not available for this kind of Turnout
391            lock.addItem(new BeanEditItem(null, null, Bundle.getMessage("LockModeUnavailable")));
392        }
393        bei.add(lock);
394        return lock;
395    }   // lock()
396
397    private java.util.Vector<String> speedListClosed = new java.util.Vector<>();
398    private java.util.Vector<String> speedListThrown = new java.util.Vector<>();
399
400    private JComboBox<String> closedSpeedBox;
401    private JComboBox<String> thrownSpeedBox;
402    private String defaultThrownSpeedText;
403    private String defaultClosedSpeedText;
404
405    protected BeanItemPanel speed() {
406        BeanItemPanel speed = new BeanItemPanel();
407        speed.setName(Bundle.getMessage("Speed"));
408
409        speed.addItem(new BeanEditItem(null, null, Bundle.getMessage("SpeedTabToolTip")));
410
411        defaultThrownSpeedText = (Bundle.getMessage("UseGlobal", "Global")
412                + " " + InstanceManager.turnoutManagerInstance().getDefaultThrownSpeed());
413        defaultClosedSpeedText = (Bundle.getMessage("UseGlobal", "Global")
414                + " " + InstanceManager.turnoutManagerInstance().getDefaultClosedSpeed());
415
416      useBlockSpeed = Bundle.getMessage("UseGlobal", "Block Speed");
417
418        speedListClosed.add(defaultClosedSpeedText);
419        speedListThrown.add(defaultThrownSpeedText);
420
421        speedListClosed.add(useBlockSpeed);
422        speedListThrown.add(useBlockSpeed);
423
424        java.util.Vector<String> _speedMap = jmri.InstanceManager.
425                getDefault(SignalSpeedMap.class).getValidSpeedNames();
426        for (String speedMap : _speedMap) {
427            if (!speedListClosed.contains(speedMap)) {
428                speedListClosed.add(speedMap);
429            }
430            if (!speedListThrown.contains(speedMap)) {
431                speedListThrown.add(speedMap);
432            }
433        }
434
435        closedSpeedBox = new JComboBox<>(speedListClosed);
436        closedSpeedBox.setMaximumRowCount(closedSpeedBox.getItemCount());
437        closedSpeedBox.setEditable(true);
438
439        speed.addItem(new BeanEditItem(closedSpeedBox,
440                Bundle.getMessage("ClosedSpeed"),
441                Bundle.getMessage("ClosedSpeedToolTip")));
442        thrownSpeedBox = new JComboBox<>(speedListThrown);
443        thrownSpeedBox.setMaximumRowCount(thrownSpeedBox.getItemCount());
444        thrownSpeedBox.setEditable(true);
445        speed.addItem(new BeanEditItem(thrownSpeedBox,
446                Bundle.getMessage("ThrownSpeed"),
447                Bundle.getMessage("ThrownSpeedToolTip")));
448
449      speed.setSaveItem(new AbstractAction() {
450            @Override
451            public void actionPerformed(ActionEvent e) {
452                String newSpeed = (String) closedSpeedBox.getSelectedItem();
453                if (newSpeed != null) {
454                    try {
455                        bean.setStraightSpeed(newSpeed);
456                        if ((!speedListClosed.contains(newSpeed)) && !newSpeed.contains("Global")) {
457                            speedListClosed.add(newSpeed);
458                        }
459                    } catch (jmri.JmriException ex) {
460                        JmriJOptionPane.showMessageDialog(speed, ex.getMessage() + "\n" + newSpeed);
461                    }
462                }
463                newSpeed = (String) thrownSpeedBox.getSelectedItem();
464                if (newSpeed != null) {
465                    try {
466                        bean.setDivergingSpeed(newSpeed);
467                        if ((!speedListThrown.contains(newSpeed)) && !newSpeed.contains("Global")) {
468                            speedListThrown.add(newSpeed);
469                        }
470                    } catch (jmri.JmriException ex) {
471                        JmriJOptionPane.showMessageDialog(speed, ex.getMessage() + "\n" + newSpeed);
472                    }
473                }
474            }
475        });
476
477        speed.setResetItem(new AbstractAction() {
478            @Override
479            public void actionPerformed(ActionEvent e) {
480                String speed = bean.getDivergingSpeed();
481                speedListThrown.remove(defaultThrownSpeedText);
482                defaultThrownSpeedText = (Bundle.getMessage("UseGlobal", "Global")
483                        + " " + InstanceManager.turnoutManagerInstance().getDefaultThrownSpeed());
484                speedListThrown.add(0, defaultThrownSpeedText);
485                if (!speedListThrown.contains(speed)) {
486                    speedListThrown.add(speed);
487                    thrownSpeedBox.addItem(speed);
488                }
489                thrownSpeedBox.setSelectedItem(speed);
490                speed = bean.getStraightSpeed();
491                speedListClosed.remove(defaultClosedSpeedText);
492                defaultClosedSpeedText = (Bundle.getMessage("UseGlobal", "Global")
493                        + " " + InstanceManager.turnoutManagerInstance().getDefaultClosedSpeed());
494                speedListClosed.add(0, defaultClosedSpeedText);
495                if (!speedListClosed.contains(speed)) {
496                    speedListClosed.add(speed);
497                    closedSpeedBox.addItem(speed);
498                }
499                closedSpeedBox.setSelectedItem(speed);
500            }
501        });
502
503        bei.add(speed);
504        return speed;
505    }
506
507}