001package jmri.jmrit.display.layoutEditor.LayoutEditorDialogs;
002
003import java.awt.*;
004import java.awt.event.*;
005import java.text.DecimalFormat;
006import java.util.ArrayList;
007import java.util.List;
008
009import javax.annotation.Nonnull;
010import javax.swing.*;
011import javax.swing.border.EtchedBorder;
012import javax.swing.border.TitledBorder;
013
014import jmri.NamedBean.DisplayOptions;
015import jmri.*;
016import jmri.jmrit.display.layoutEditor.*;
017import jmri.jmrit.display.Positionable;
018import jmri.jmrit.display.SignalMastIcon;
019import jmri.swing.NamedBeanComboBox;
020import jmri.util.JmriJFrame;
021import jmri.util.swing.JmriJOptionPane;
022
023/**
024 * MVC Editor component for PositionablePoint objects.
025 *
026 * @author Bob Jacobsen  Copyright (c) 2020
027 *
028 */
029public class LayoutTurntableEditor extends LayoutTrackEditor {
030
031    /**
032     * constructor method.
033     * @param layoutEditor main layout editor.
034     */
035    public LayoutTurntableEditor(@Nonnull LayoutEditor layoutEditor) {
036        super(layoutEditor);
037    }
038
039    /*==============*\
040    | Edit Turntable |
041    \*==============*/
042    // variables for Edit Turntable pane
043    private LayoutTurntable layoutTurntable = null;
044    private LayoutTurntableView layoutTurntableView = null;
045
046    private JmriJFrame editLayoutTurntableFrame = null;
047    private final JTextField editLayoutTurntableRadiusTextField = new JTextField(8);
048    private final JTextField editLayoutTurntableAngleTextField = new JTextField(8);
049    private final NamedBeanComboBox<Block> editLayoutTurntableBlockNameComboBox = new NamedBeanComboBox<>(
050             InstanceManager.getDefault(BlockManager.class), null, DisplayOptions.DISPLAYNAME);
051    private JButton editLayoutTurntableSegmentEditBlockButton;
052
053    private JPanel editLayoutTurntableRayPanel;
054    private JButton editLayoutTurntableAddRayTrackButton;
055    private JCheckBox editLayoutTurntableDccControlledCheckBox;
056    private JCheckBox editLayoutTurntableUseSignalMastsCheckBox;
057    private JPanel signalMastParametersPanel;
058    private NamedBeanComboBox<SignalMast> exitMastComboBox;
059    private NamedBeanComboBox<SignalMast> bufferMastComboBox;
060
061    private String editLayoutTurntableOldRadius = "";
062    private final List<NamedBeanComboBox<SignalMast>> approachMastComboBoxes = new ArrayList<>();
063    private final java.util.Set<SignalMast> mastsUsedElsewhere = new java.util.HashSet<>();
064    private boolean editLayoutTurntableOpen = false;
065    private boolean editLayoutTurntableNeedsRedraw = false;
066
067    private JRadioButton doNotPlaceIcons;
068    private JRadioButton placeIconsLeft;
069    private JRadioButton placeIconsRight;
070
071    private final List<Turnout> turntableTurnouts = new ArrayList<>();
072
073    /**
074     * Edit a Turntable.
075     */
076    @Override
077    public void editLayoutTrack(@Nonnull LayoutTrackView layoutTrackView) {
078        if ( layoutTrackView instanceof LayoutTurntableView ) {
079            this.layoutTurntableView = (LayoutTurntableView) layoutTrackView;
080            this.layoutTurntable = this.layoutTurntableView.getTurntable();
081        } else {
082            log.error("editLayoutTrack called with wrong type {}", layoutTrackView, new Exception("traceback"));
083        }
084        sensorList.clear();
085
086        if (editLayoutTurntableOpen) {
087            editLayoutTurntableFrame.setVisible(true);
088        } else // Initialize if needed
089        if (editLayoutTurntableFrame == null) {
090            editLayoutTurntableFrame = new JmriJFrame(Bundle.getMessage("EditTurntable"), false, true);  // NOI18N
091            editLayoutTurntableFrame.addHelpMenu("package.jmri.jmrit.display.EditTurntable", true);  // NOI18N
092            editLayoutTurntableFrame.setLocation(50, 30);
093
094            Container contentPane = editLayoutTurntableFrame.getContentPane();
095            JPanel headerPane = new JPanel();
096            JPanel footerPane = new JPanel();
097            headerPane.setLayout(new BoxLayout(headerPane, BoxLayout.Y_AXIS));
098            footerPane.setLayout(new BoxLayout(footerPane, BoxLayout.Y_AXIS));
099            contentPane.setLayout(new BorderLayout());
100            contentPane.add(headerPane, BorderLayout.NORTH);
101            contentPane.add(footerPane, BorderLayout.SOUTH);
102
103            // setup radius text field
104            JPanel panel1 = new JPanel();
105            panel1.setLayout(new FlowLayout());
106            JLabel radiusLabel = new JLabel(Bundle.getMessage("TurntableRadius"));  // NOI18N
107            panel1.add(radiusLabel);
108            radiusLabel.setLabelFor(editLayoutTurntableRadiusTextField);
109            panel1.add(editLayoutTurntableRadiusTextField);
110            editLayoutTurntableRadiusTextField.setToolTipText(Bundle.getMessage("TurntableRadiusHint"));  // NOI18N
111            headerPane.add(panel1);
112
113            // setup ray track angle text field
114            JPanel panel2 = new JPanel();
115            panel2.setLayout(new FlowLayout());
116            JLabel rayAngleLabel = new JLabel(Bundle.getMessage("RayAngle"));  // NOI18N
117            panel2.add(rayAngleLabel);
118            rayAngleLabel.setLabelFor(editLayoutTurntableAngleTextField);
119            panel2.add(editLayoutTurntableAngleTextField);
120            editLayoutTurntableAngleTextField.setToolTipText(Bundle.getMessage("RayAngleHint"));  // NOI18N
121            headerPane.add(panel2);
122
123            // setup block name
124            JPanel panel2a = new JPanel();
125            panel2a.setLayout(new FlowLayout());
126            JLabel blockNameLabel = new JLabel(Bundle.getMessage("BlockID"));  // NOI18N
127            panel2a.add(blockNameLabel);
128            blockNameLabel.setLabelFor(editLayoutTurntableBlockNameComboBox);
129            LayoutEditor.setupComboBox(editLayoutTurntableBlockNameComboBox, false, true, true);
130            editLayoutTurntableBlockNameComboBox.setToolTipText(Bundle.getMessage("EditBlockNameHint"));  // NOI18N
131            panel2a.add(editLayoutTurntableBlockNameComboBox);
132
133            // Edit Block
134            panel2a.add(editLayoutTurntableSegmentEditBlockButton = new JButton(Bundle.getMessage("EditBlock", "")));  // NOI18N
135            editLayoutTurntableSegmentEditBlockButton.addActionListener(this::editLayoutTurntableEditBlockPressed);
136            editLayoutTurntableSegmentEditBlockButton.setToolTipText(Bundle.getMessage("EditBlockHint", "")); // empty value for block 1  // NOI18N
137            headerPane.add(panel2a);
138
139            // setup add ray track button
140            JPanel panel3 = new JPanel();
141            panel3.setLayout(new FlowLayout());
142            panel3.add(editLayoutTurntableAddRayTrackButton = new JButton(Bundle.getMessage("AddRayTrack")));  // NOI18N
143            editLayoutTurntableAddRayTrackButton.setToolTipText(Bundle.getMessage("AddRayTrackHint"));  // NOI18N
144            editLayoutTurntableAddRayTrackButton.addActionListener((ActionEvent e) -> {
145                addRayTrackPressed(e);
146                updateRayPanel();
147            });
148
149            panel3.add(editLayoutTurntableDccControlledCheckBox = new JCheckBox(Bundle.getMessage("TurntableDCCControlled")));  // NOI18N
150            headerPane.add(panel3);
151
152            JPanel panel4 = new JPanel();
153            panel4.setLayout(new FlowLayout());
154            panel4.add(editLayoutTurntableUseSignalMastsCheckBox = new JCheckBox(Bundle.getMessage("TurntableUseSignalMasts"))); // NOI18N
155            headerPane.add(panel4);
156
157            // setup signal mast parameters panel
158            signalMastParametersPanel = new JPanel();
159            signalMastParametersPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TurntableSignalMastAssignmentsTitle"))); // NOI18N
160            exitMastComboBox = new NamedBeanComboBox<>(InstanceManager.getDefault(SignalMastManager.class), null, DisplayOptions.DISPLAYNAME);
161            LayoutEditor.setupComboBox(exitMastComboBox, false, true, true);
162            exitMastComboBox.setEditable(false);
163            exitMastComboBox.setAllowNull(true);
164            bufferMastComboBox = new NamedBeanComboBox<>(InstanceManager.getDefault(SignalMastManager.class), null, DisplayOptions.DISPLAYNAME);
165            LayoutEditor.setupComboBox(bufferMastComboBox, false, true, true);
166            bufferMastComboBox.setEditable(false);
167            bufferMastComboBox.setAllowNull(true);
168
169            // set up Done and Cancel buttons
170            JPanel panel5 = new JPanel();
171            panel5.setLayout(new FlowLayout());
172            addDoneCancelButtons(panel5, editLayoutTurntableFrame.getRootPane(),
173                    this::editLayoutTurntableDonePressed, this::turntableEditCancelPressed);
174            footerPane.add(panel5);
175
176            editLayoutTurntableRayPanel = new JPanel();
177            editLayoutTurntableRayPanel.setLayout(new BoxLayout(editLayoutTurntableRayPanel, BoxLayout.Y_AXIS));
178            JScrollPane rayScrollPane = new JScrollPane(editLayoutTurntableRayPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
179            contentPane.add(rayScrollPane, BorderLayout.CENTER);
180        }
181
182        editLayoutTurntableBlockNameComboBox.setSelectedIndex(-1);
183        LayoutBlock lb = layoutTurntable.getLayoutBlock();
184        if (lb != null) {
185            Block blk = lb.getBlock();
186            if (blk != null) {
187                editLayoutTurntableBlockNameComboBox.setSelectedItem(blk);
188            }
189        }
190
191        editLayoutTurntableDccControlledCheckBox.setSelected(layoutTurntable.isTurnoutControlled());
192        editLayoutTurntableUseSignalMastsCheckBox.setSelected(layoutTurntable.isDispatcherManaged());
193        editLayoutTurntableDccControlledCheckBox.addActionListener((ActionEvent e) -> {
194            layoutTurntable.setTurnoutControlled(editLayoutTurntableDccControlledCheckBox.isSelected());
195
196            for (Component comp : editLayoutTurntableRayPanel.getComponents()) {
197                if (comp instanceof TurntableRayPanel) {
198                    TurntableRayPanel trp = (TurntableRayPanel) comp;
199                    trp.showTurnoutDetails();
200                }
201            }
202            editLayoutTurntableFrame.pack();
203        });
204
205        signalMastParametersPanel.setVisible(layoutTurntable.isDispatcherManaged());
206        editLayoutTurntableUseSignalMastsCheckBox.addActionListener((ActionEvent e) -> {
207            boolean isSelected = editLayoutTurntableUseSignalMastsCheckBox.isSelected();
208            layoutTurntable.setDispatcherManaged(isSelected); // Update the model
209            signalMastParametersPanel.setVisible(layoutTurntable.isDispatcherManaged());
210            updateRayPanel(); // Rebuild to show/hide mast details, which also handles visibility
211            editLayoutTurntableFrame.pack();
212        });
213
214        // Cache the list of masts used on other parts of the layout.
215        // This is the performance-critical change, preventing a full layout scan on every click.
216        mastsUsedElsewhere.clear();
217        layoutEditor.getLETools().createListUsedSignalMasts();
218        mastsUsedElsewhere.addAll(layoutEditor.getLETools().usedMasts);
219
220        // Remove masts assigned to the current turntable from the "used elsewhere" list
221        if (layoutTurntable.getBufferMast() != null) {
222            mastsUsedElsewhere.remove(layoutTurntable.getBufferMast());
223        }
224        if (layoutTurntable.getExitSignalMast() != null) {
225            mastsUsedElsewhere.remove(layoutTurntable.getExitSignalMast());
226        }
227        for (LayoutTurntable.RayTrack ray : layoutTurntable.getRayTrackList()) {
228            if (ray.getApproachMast() != null) {
229                mastsUsedElsewhere.remove(ray.getApproachMast());
230            }
231        }
232
233        exitMastComboBox.setExcludedItems(mastsUsedElsewhere);
234        exitMastComboBox.addActionListener(e -> {
235            SignalMast newMast = exitMastComboBox.getSelectedItem();
236            layoutTurntable.setExitSignalMast( (newMast != null) ? newMast.getSystemName() : null );
237        });
238
239        bufferMastComboBox.setExcludedItems(mastsUsedElsewhere);
240        bufferMastComboBox.addActionListener(e -> {
241            SignalMast newMast = bufferMastComboBox.getSelectedItem();
242            layoutTurntable.setBufferSignalMast( (newMast != null) ? newMast.getSystemName() : null );
243        });
244
245        // Set up for Edit
246        editLayoutTurntableRadiusTextField.setText(" " + layoutTurntable.getRadius());
247        editLayoutTurntableOldRadius = editLayoutTurntableRadiusTextField.getText();
248        editLayoutTurntableAngleTextField.setText("0");
249        editLayoutTurntableFrame.addWindowListener(new java.awt.event.WindowAdapter() {
250            @Override
251            public void windowClosing(java.awt.event.WindowEvent e) {
252                turntableEditCancelPressed(null);
253            }
254        });
255        updateRayPanel();
256        SignalMast exitMast = layoutTurntable.getExitSignalMast();
257        SignalMast bufferMast = layoutTurntable.getBufferMast();
258        if (exitMast != null) {
259            exitMastComboBox.setSelectedItem(exitMast);
260        }
261        if (bufferMast != null) {
262            bufferMastComboBox.setSelectedItem(bufferMast);
263        }
264
265        editLayoutTurntableFrame.pack();
266        editLayoutTurntableFrame.setVisible(true);
267        editLayoutTurntableOpen = true;
268    }   // editLayoutTurntable
269
270    @InvokeOnGuiThread
271    private void editLayoutTurntableEditBlockPressed(ActionEvent a) {
272         // check if a block name has been entered
273         String newName = editLayoutTurntableBlockNameComboBox.getSelectedItemDisplayName();
274         if (newName == null) {
275             newName = "";
276         }
277         if ((layoutTurntable.getBlockName().isEmpty())
278                 || !layoutTurntable.getBlockName().equals(newName)) {
279             // get new block, or null if block has been removed
280             layoutTurntable.setLayoutBlock(layoutEditor.provideLayoutBlock(newName));
281             editLayoutTurntableNeedsRedraw = true;
282             ///layoutEditor.getLEAuxTools().setBlockConnectivityChanged();
283             ///layoutTurntable.updateBlockInfo();
284         }
285         // check if a block exists to edit
286         LayoutBlock blockToEdit = layoutTurntable.getLayoutBlock();
287         if (blockToEdit == null) {
288             JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame,
289                     Bundle.getMessage("Error1"), // NOI18N
290                     Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
291             return;
292         }
293         blockToEdit.editLayoutBlock(editLayoutTurntableFrame);
294         layoutEditor.setDirty();
295         editLayoutTurntableNeedsRedraw = true;
296     }
297
298    // Remove old rays and add them back in
299    private void updateRayPanel() {
300        // Create list of turnouts to be retained in the NamedBeanComboBox
301        turntableTurnouts.clear();
302        layoutTurntable.getRayTrackList().forEach(rt -> turntableTurnouts.add(rt.getTurnout()));
303
304        editLayoutTurntableRayPanel.removeAll();
305        editLayoutTurntableRayPanel.setLayout(new BoxLayout(editLayoutTurntableRayPanel, BoxLayout.Y_AXIS));
306        for (LayoutTurntable.RayTrack rt : layoutTurntable.getRayTrackList()) {
307            editLayoutTurntableRayPanel.add(new TurntableRayPanel(rt));
308        }
309
310        // Rebuild signal mast panel
311        signalMastParametersPanel.removeAll();
312        approachMastComboBoxes.clear();
313
314        if (layoutTurntable.isDispatcherManaged()) {
315            signalMastParametersPanel.setLayout(new BoxLayout(signalMastParametersPanel, BoxLayout.Y_AXIS));
316
317            // Add approach masts for each ray
318            for (LayoutTurntable.RayTrack rt : layoutTurntable.getRayTrackList()) {
319                JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
320                p.add(new JLabel(Bundle.getMessage("ApproachMastRay", rt.getConnectionIndex() + 1)));
321                NamedBeanComboBox<SignalMast> combo = new NamedBeanComboBox<>(
322                        InstanceManager.getDefault(SignalMastManager.class), rt.getApproachMast(), DisplayOptions.DISPLAYNAME);
323                LayoutEditor.setupComboBox(combo, false, true, true);
324                combo.setEditable(false);
325                combo.setAllowNull(true);
326                p.add(combo);
327                signalMastParametersPanel.add(p);
328                approachMastComboBoxes.add(combo);
329            }
330
331            // Add action listeners now that the list is complete
332            for (int i = 0; i < approachMastComboBoxes.size(); i++) {
333                final int index = i; // final variable for use in lambda
334                approachMastComboBoxes.get(i).setExcludedItems(mastsUsedElsewhere);
335                approachMastComboBoxes.get(i).addActionListener(e -> {
336                    SignalMast newMast = approachMastComboBoxes.get(index).getSelectedItem();
337                    layoutTurntable.getRayTrackList().get(index).setApproachMast( (newMast != null) ? newMast.getSystemName() : null );
338                });
339            }
340            if (!approachMastComboBoxes.isEmpty()) {
341                signalMastParametersPanel.add(new JSeparator());
342            }
343
344            // Add shared icon placement controls
345            JPanel placementPanel = new JPanel();
346            placementPanel.setLayout(new BoxLayout(placementPanel, BoxLayout.Y_AXIS)); // NOI18N
347            placementPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TurntableAddMastIconsTitle")));
348
349            doNotPlaceIcons = new JRadioButton(Bundle.getMessage("DoNotPlace")); // NOI18N
350            placeIconsLeft = new JRadioButton(Bundle.getMessage("LeftHandSide")); // NOI18N
351            placeIconsRight = new JRadioButton(Bundle.getMessage("RightHandSide")); // NOI18N
352            ButtonGroup bg = new ButtonGroup();
353            bg.add(doNotPlaceIcons);
354            bg.add(placeIconsLeft);
355            bg.add(placeIconsRight);
356            switch (layoutTurntable.getSignalIconPlacement()) {
357                case 1:
358                    placeIconsLeft.setSelected(true);
359                    break;
360                case 2:
361                    placeIconsRight.setSelected(true);
362                    break;
363                default: doNotPlaceIcons.setSelected(true);
364            }
365
366            JPanel radioPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
367            radioPanel.add(doNotPlaceIcons);
368            radioPanel.add(placeIconsLeft);
369            radioPanel.add(placeIconsRight);
370            placementPanel.add(radioPanel);
371            signalMastParametersPanel.add(placementPanel);
372
373            signalMastParametersPanel.add(new JSeparator());
374
375            JPanel mastPanel = new JPanel(new GridBagLayout());
376            GridBagConstraints c = new GridBagConstraints();
377            c.gridx = 0;
378            c.gridy = 0;
379            c.anchor = GridBagConstraints.LINE_START;
380            c.insets = new Insets(2, 2, 2, 2); // Default insets
381            mastPanel.add(new JLabel(Bundle.getMessage("TurntableExitMastLabel")), c);
382            c.gridx = 1;
383            c.insets = new Insets(2, 5, 2, 2); // Add left padding
384            mastPanel.add(exitMastComboBox, c);
385
386            c.gridx = 0;
387            c.gridy = 1;
388            c.insets = new Insets(2, 2, 2, 2); // Reset for label
389            mastPanel.add(new JLabel(Bundle.getMessage("TurntableBufferMastLabel")), c);
390            c.gridx = 1;
391            c.insets = new Insets(2, 5, 2, 2); // Add left padding
392            mastPanel.add(bufferMastComboBox, c);
393
394            signalMastParametersPanel.add(mastPanel);
395            editLayoutTurntableRayPanel.add(signalMastParametersPanel);
396        }
397        editLayoutTurntableRayPanel.revalidate();
398        editLayoutTurntableRayPanel.repaint();
399        editLayoutTurntableFrame.pack();
400    }
401
402    private void saveRayPanelDetail() {
403        for (Component comp : editLayoutTurntableRayPanel.getComponents()) {
404            if (comp instanceof TurntableRayPanel) {
405                TurntableRayPanel trp = (TurntableRayPanel) comp;
406                trp.updateDetails();
407            }
408        }
409    }
410
411    private void addRayTrackPressed(ActionEvent a) {
412        double ang = 0.0;
413        try {
414            ang = Float.parseFloat(editLayoutTurntableAngleTextField.getText());
415        } catch (Exception e) {
416            JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": "
417                    + e + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"),
418                    JmriJOptionPane.ERROR_MESSAGE);
419            return;
420        }
421        layoutTurntable.addRay(ang);
422        layoutEditor.redrawPanel();
423        layoutEditor.setDirty();
424        editLayoutTurntableNeedsRedraw = false;
425    }
426
427    private void editLayoutTurntableDonePressed(ActionEvent a) {
428        // check if Block changed
429        String newName = editLayoutTurntableBlockNameComboBox.getSelectedItemDisplayName();
430        if (newName == null) {
431            newName = "";
432        }
433
434        if ((layoutTurntable.getBlockName().isEmpty()) || !layoutTurntable.getBlockName().equals(newName)) {
435            // get new block, or null if block has been removed
436            layoutTurntable.setLayoutBlock(layoutEditor.provideLayoutBlock(newName));
437            editLayoutTurntableNeedsRedraw = true;
438            ///layoutEditor.getLEAuxTools().setBlockConnectivityChanged();
439            ///layoutTurntable.updateBlockInfo();
440        }
441
442        layoutTurntable.setDispatcherManaged(editLayoutTurntableUseSignalMastsCheckBox.isSelected());
443        if (editLayoutTurntableUseSignalMastsCheckBox.isSelected()) {
444            String exitMastName = exitMastComboBox.getSelectedItemDisplayName();
445            String bufferMastName = bufferMastComboBox.getSelectedItemDisplayName();
446            layoutTurntable.setExitSignalMast(exitMastName);
447            layoutTurntable.setBufferSignalMast(bufferMastName);
448
449            for (int i = 0; i < approachMastComboBoxes.size(); i++) {
450                LayoutTurntable.RayTrack ray = layoutTurntable.getRayTrackList().get(i);
451                NamedBeanComboBox<SignalMast> combo = approachMastComboBoxes.get(i);
452                ray.setApproachMast(combo.getSelectedItemDisplayName());
453            }
454
455            // Always remove any existing icons for this turntable's approach masts first.
456            // This ensures that moving icons from right-to-left works correctly.
457            List<SignalMastIcon> iconsToRemove = new ArrayList<>();
458            for (Positionable p : layoutEditor.getContents()) {
459                if (p instanceof SignalMastIcon) {
460                    SignalMastIcon icon = (SignalMastIcon) p;
461                    if (layoutTurntable.isApproachMast(icon.getSignalMast())) {
462                        iconsToRemove.add(icon);
463                    }
464                }
465            }
466            for (SignalMastIcon icon : iconsToRemove) {
467                icon.remove();
468                editLayoutTurntableNeedsRedraw = true;
469            }
470
471            // Now, if requested, place the new icons.
472            if (!doNotPlaceIcons.isSelected()) { // placeIconsLeft or placeIconsRight is selected
473                for (int i = 0; i < approachMastComboBoxes.size(); i++) {
474                    LayoutTurntable.RayTrack ray = layoutTurntable.getRayTrackList().get(i);
475                    SignalMast mast = approachMastComboBoxes.get(i).getSelectedItem();
476                    if (mast != null) {
477                        if (ray.getConnect() != null) {
478                            SignalMastIcon icon = new SignalMastIcon(layoutEditor);
479                            icon.setSignalMast(mast.getDisplayName());
480                            log.debug("Placing mast for turntable ray, connected to track segment: {}", ray.getConnect().getName()); // NOI18N
481                            layoutEditor.getLETools().placingBlockForTurntable(icon, placeIconsRight.isSelected(), 0.0,
482                                    ray.getConnect(), layoutTurntableView.getRayCoordsIndexed(ray.getConnectionIndex()));
483                            editLayoutTurntableNeedsRedraw = true;
484                        }
485                    }
486                }
487            }
488
489            // Save placement selection
490            int placementSelection;
491            if (placeIconsLeft.isSelected()) {
492                placementSelection = 1;
493            } else if (placeIconsRight.isSelected()) {
494                placementSelection = 2;
495            } else {
496                placementSelection = 0;
497            }
498            layoutTurntable.setSignalIconPlacement(placementSelection);
499        }
500
501        // check if new radius was entered
502        String str = editLayoutTurntableRadiusTextField.getText();
503        if (!str.equals(editLayoutTurntableOldRadius)) {
504            double rad = 0.0;
505            try {
506                rad = Float.parseFloat(str);
507            } catch (Exception e) {
508                JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": " // NOI18N
509                        + e + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"), // NOI18N
510                        JmriJOptionPane.ERROR_MESSAGE);
511                return;
512            }
513            layoutTurntable.setRadius(rad);
514            editLayoutTurntableNeedsRedraw = true;
515        }
516        // clean up
517        saveRayPanelDetail();
518        editLayoutTurntableOpen = false;
519        editLayoutTurntableFrame.setVisible(false);
520        editLayoutTurntableFrame.dispose();
521        editLayoutTurntableFrame = null;
522        if (editLayoutTurntableNeedsRedraw) {
523            layoutEditor.redrawPanel();
524            layoutEditor.setDirty();
525            editLayoutTurntableNeedsRedraw = false;
526        }
527    }
528
529    private void turntableEditCancelPressed(ActionEvent a) {
530        editLayoutTurntableOpen = false;
531        editLayoutTurntableFrame.setVisible(false);
532        editLayoutTurntableFrame.dispose();
533        editLayoutTurntableFrame = null;
534        if (editLayoutTurntableNeedsRedraw) {
535            layoutEditor.redrawPanel();
536            layoutEditor.setDirty();
537            editLayoutTurntableNeedsRedraw = false;
538        }
539    }
540
541    /*===================*\
542    | Turntable Ray Panel |
543    \*===================*/
544    public class TurntableRayPanel extends JPanel {
545
546        // variables for Edit Turntable ray pane
547        private LayoutTurntable.RayTrack rayTrack = null;
548        private final JPanel rayTurnoutPanel;
549        private final NamedBeanComboBox<Turnout> turnoutNameComboBox;
550        private final TitledBorder rayTitledBorder;
551        private final JComboBox<String> rayTurnoutStateComboBox;
552        private final JLabel rayTurnoutStateLabel;
553        private final JTextField rayAngleTextField;
554        private final int[] rayTurnoutStateValues = new int[]{Turnout.CLOSED, Turnout.THROWN};
555        private final DecimalFormat twoDForm = new DecimalFormat("#.00");
556
557        /**
558         * constructor method.
559         * @param rayTrack the single ray track to edit.
560         */
561        public TurntableRayPanel(@Nonnull LayoutTurntable.RayTrack rayTrack) {
562            this.rayTrack = rayTrack;
563
564            JPanel top = new JPanel();
565
566            JLabel rayAngleLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("RayAngle")));
567            top.add(rayAngleLabel);
568            top.add(rayAngleTextField = new JTextField(5));
569            rayAngleLabel.setLabelFor(rayAngleTextField);
570
571            rayAngleTextField.addFocusListener(new FocusListener() {
572                @Override
573                public void focusGained(FocusEvent e) {
574                }
575
576                @Override
577                public void focusLost(FocusEvent e) {
578                    try {
579                        Float.parseFloat(rayAngleTextField.getText());
580                    } catch (Exception ex) {
581                        JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": " // NOI18N
582                                + ex + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"), // NOI18N
583                                JmriJOptionPane.ERROR_MESSAGE);
584                    }
585                }
586            }
587            );
588            this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
589            this.add(top);
590
591            turnoutNameComboBox = new NamedBeanComboBox<>(InstanceManager.getDefault(TurnoutManager.class));
592            turnoutNameComboBox.setToolTipText(Bundle.getMessage("EditTurnoutToolTip"));
593            LayoutEditor.setupComboBox(turnoutNameComboBox, false, true, false);
594            turnoutNameComboBox.setSelectedItem(rayTrack.getTurnout());
595            turnoutNameComboBox.addPopupMenuListener(
596                    layoutEditor.newTurnoutComboBoxPopupMenuListener(turnoutNameComboBox, turntableTurnouts));
597            String turnoutStateThrown = InstanceManager.turnoutManagerInstance().getThrownText();
598            String turnoutStateClosed = InstanceManager.turnoutManagerInstance().getClosedText();
599            String[] turnoutStates = new String[]{turnoutStateClosed, turnoutStateThrown};
600
601            rayTurnoutStateComboBox = new JComboBox<>(turnoutStates);
602            rayTurnoutStateLabel = new JLabel(Bundle.getMessage("TurnoutState"));  // NOI18N
603            rayTurnoutPanel = new JPanel();
604
605            rayTurnoutPanel.setBorder(new EtchedBorder());
606            JLabel turnoutLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout"))); // NOI18N
607            rayTurnoutPanel.add(turnoutLabel);
608            turnoutLabel.setLabelFor(turnoutNameComboBox);
609            rayTurnoutPanel.add(turnoutNameComboBox);
610            rayTurnoutPanel.add(rayTurnoutStateLabel);
611            rayTurnoutStateLabel.setLabelFor(rayTurnoutStateComboBox);
612            rayTurnoutPanel.add(rayTurnoutStateComboBox);
613            if (rayTrack.getTurnoutState() == Turnout.CLOSED) {
614                rayTurnoutStateComboBox.setSelectedItem(turnoutStateClosed);
615            } else {
616                rayTurnoutStateComboBox.setSelectedItem(turnoutStateThrown);
617            }
618            this.add(rayTurnoutPanel);
619
620            JButton deleteRayButton;
621            top.add(deleteRayButton = new JButton(Bundle.getMessage("ButtonDelete")));  // NOI18N
622            deleteRayButton.setToolTipText(Bundle.getMessage("DeleteRayTrack"));  // NOI18N
623            deleteRayButton.addActionListener((ActionEvent e) -> {
624                delete();
625                updateRayPanel();
626            });
627            rayTitledBorder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black));
628
629            this.setBorder(rayTitledBorder);
630
631            showTurnoutDetails();
632
633            rayAngleTextField.setText(twoDForm.format(rayTrack.getAngle()));
634            int rayNumber = rayTrack.getConnectionIndex() + 1;
635            rayTitledBorder.setTitle(Bundle.getMessage("Ray") + " : " + rayNumber);  // NOI18N
636            if (rayTrack.getConnect() == null) {
637                rayTitledBorder.setTitle(Bundle.getMessage("MakeLabel",
638                        Bundle.getMessage("Unconnected")) + " "
639                        + rayNumber);  // NOI18N
640            } else if (rayTrack.getConnect().getLayoutBlock() != null) {
641                rayTitledBorder.setTitle(Bundle.getMessage("MakeLabel",
642                        Bundle.getMessage("Connected")) + " "
643                        + rayTrack.getConnect().getLayoutBlock().getDisplayName()
644                        + " (" + Bundle.getMessage("Ray") + " " + rayNumber + ")");  // NOI18N
645            }
646        }
647
648        private void delete() {
649            int n = JmriJOptionPane.showConfirmDialog(null,
650                    Bundle.getMessage("Question7"), // NOI18N
651                    Bundle.getMessage("WarningTitle"), // NOI18N
652                    JmriJOptionPane.YES_NO_OPTION);
653            if (n == JmriJOptionPane.YES_OPTION) {
654                layoutTurntable.deleteRay(rayTrack);
655            }
656        }
657
658        private void updateDetails() {
659            if (turnoutNameComboBox == null || rayTurnoutStateComboBox == null) {
660                return;
661            }
662            String turnoutName = turnoutNameComboBox.getSelectedItemDisplayName();
663            if (turnoutName == null) {
664                turnoutName = "";
665            }
666            rayTrack.setTurnout(turnoutName, rayTurnoutStateValues[rayTurnoutStateComboBox.getSelectedIndex()]);
667            if (!rayAngleTextField.getText().equals(twoDForm.format(rayTrack.getAngle()))) {
668                try {
669                    double ang = Float.parseFloat(rayAngleTextField.getText());
670                    rayTrack.setAngle(ang);
671                } catch (Exception e) {
672                    log.error("Angle is not in correct format so will skip {}", rayAngleTextField.getText());  // NOI18N
673                }
674            }
675        }
676
677        private void showTurnoutDetails() {
678            boolean vis = layoutTurntable.isTurnoutControlled();
679            rayTurnoutPanel.setVisible(vis);
680            turnoutNameComboBox.setVisible(vis);
681            rayTurnoutStateComboBox.setVisible(vis);
682            rayTurnoutStateLabel.setVisible(vis);
683        }
684    }
685
686
687    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTurntableEditor.class);
688}