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