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.swing.NamedBeanComboBox; 018import jmri.util.JmriJFrame; 019import jmri.util.swing.JmriJOptionPane; 020 021/** 022 * MVC Editor component for PositionablePoint objects. 023 * 024 * @author Bob Jacobsen Copyright (c) 2020 025 * 026 */ 027public class LayoutTurntableEditor extends LayoutTrackEditor { 028 029 /** 030 * constructor method. 031 * @param layoutEditor main layout editor. 032 */ 033 public LayoutTurntableEditor(@Nonnull LayoutEditor layoutEditor) { 034 super(layoutEditor); 035 } 036 037 /*==============*\ 038 | Edit Turntable | 039 \*==============*/ 040 // variables for Edit Turntable pane 041 private LayoutTurntable layoutTurntable = null; 042 private LayoutTurntableView layoutTurntableView = null; 043 044 private JmriJFrame editLayoutTurntableFrame = null; 045 private final JTextField editLayoutTurntableRadiusTextField = new JTextField(8); 046 private final JTextField editLayoutTurntableAngleTextField = new JTextField(8); 047 private final NamedBeanComboBox<Block> editLayoutTurntableBlockNameComboBox = new NamedBeanComboBox<>( 048 InstanceManager.getDefault(BlockManager.class), null, DisplayOptions.DISPLAYNAME); 049 private JButton editLayoutTurntableSegmentEditBlockButton; 050 051 private JPanel editLayoutTurntableRayPanel; 052 private JButton editLayoutTurntableAddRayTrackButton; 053 private JCheckBox editLayoutTurntableDccControlledCheckBox; 054 055 private String editLayoutTurntableOldRadius = ""; 056 private boolean editLayoutTurntableOpen = false; 057 private boolean editLayoutTurntableNeedsRedraw = false; 058 059 private final List<Turnout> turntableTurnouts = new ArrayList<>(); 060 061 /** 062 * Edit a Turntable. 063 */ 064 @Override 065 public void editLayoutTrack(@Nonnull LayoutTrackView layoutTrackView) { 066 if ( layoutTrackView instanceof LayoutTurntableView ) { 067 this.layoutTurntableView = (LayoutTurntableView) layoutTrackView; 068 this.layoutTurntable = this.layoutTurntableView.getTurntable(); 069 } else { 070 log.error("editLayoutTrack called with wrong type {}", layoutTrackView, new Exception("traceback")); 071 } 072 sensorList.clear(); 073 074 if (editLayoutTurntableOpen) { 075 editLayoutTurntableFrame.setVisible(true); 076 } else // Initialize if needed 077 if (editLayoutTurntableFrame == null) { 078 editLayoutTurntableFrame = new JmriJFrame(Bundle.getMessage("EditTurntable"), false, true); // NOI18N 079 editLayoutTurntableFrame.addHelpMenu("package.jmri.jmrit.display.EditTurntable", true); // NOI18N 080 editLayoutTurntableFrame.setLocation(50, 30); 081 082 Container contentPane = editLayoutTurntableFrame.getContentPane(); 083 JPanel headerPane = new JPanel(); 084 JPanel footerPane = new JPanel(); 085 headerPane.setLayout(new BoxLayout(headerPane, BoxLayout.Y_AXIS)); 086 footerPane.setLayout(new BoxLayout(footerPane, BoxLayout.Y_AXIS)); 087 contentPane.setLayout(new BorderLayout()); 088 contentPane.add(headerPane, BorderLayout.NORTH); 089 contentPane.add(footerPane, BorderLayout.SOUTH); 090 091 // setup radius text field 092 JPanel panel1 = new JPanel(); 093 panel1.setLayout(new FlowLayout()); 094 JLabel radiusLabel = new JLabel(Bundle.getMessage("TurntableRadius")); // NOI18N 095 panel1.add(radiusLabel); 096 radiusLabel.setLabelFor(editLayoutTurntableRadiusTextField); 097 panel1.add(editLayoutTurntableRadiusTextField); 098 editLayoutTurntableRadiusTextField.setToolTipText(Bundle.getMessage("TurntableRadiusHint")); // NOI18N 099 headerPane.add(panel1); 100 101 // setup ray track angle text field 102 JPanel panel2 = new JPanel(); 103 panel2.setLayout(new FlowLayout()); 104 JLabel rayAngleLabel = new JLabel(Bundle.getMessage("RayAngle")); // NOI18N 105 panel2.add(rayAngleLabel); 106 rayAngleLabel.setLabelFor(editLayoutTurntableAngleTextField); 107 panel2.add(editLayoutTurntableAngleTextField); 108 editLayoutTurntableAngleTextField.setToolTipText(Bundle.getMessage("RayAngleHint")); // NOI18N 109 headerPane.add(panel2); 110 111 // setup block name 112 JPanel panel2a = new JPanel(); 113 panel2a.setLayout(new FlowLayout()); 114 JLabel blockNameLabel = new JLabel(Bundle.getMessage("BlockID")); // NOI18N 115 panel2a.add(blockNameLabel); 116 blockNameLabel.setLabelFor(editLayoutTurntableBlockNameComboBox); 117 LayoutEditor.setupComboBox(editLayoutTurntableBlockNameComboBox, false, true, true); 118 editLayoutTurntableBlockNameComboBox.setToolTipText(Bundle.getMessage("EditBlockNameHint")); // NOI18N 119 panel2a.add(editLayoutTurntableBlockNameComboBox); 120 121 // Edit Block 122 panel2a.add(editLayoutTurntableSegmentEditBlockButton = new JButton(Bundle.getMessage("EditBlock", ""))); // NOI18N 123 editLayoutTurntableSegmentEditBlockButton.addActionListener(this::editLayoutTurntableEditBlockPressed); 124 editLayoutTurntableSegmentEditBlockButton.setToolTipText(Bundle.getMessage("EditBlockHint", "")); // empty value for block 1 // NOI18N 125 headerPane.add(panel2a); 126 127 // setup add ray track button 128 JPanel panel3 = new JPanel(); 129 panel3.setLayout(new FlowLayout()); 130 panel3.add(editLayoutTurntableAddRayTrackButton = new JButton(Bundle.getMessage("AddRayTrack"))); // NOI18N 131 editLayoutTurntableAddRayTrackButton.setToolTipText(Bundle.getMessage("AddRayTrackHint")); // NOI18N 132 editLayoutTurntableAddRayTrackButton.addActionListener((ActionEvent e) -> { 133 addRayTrackPressed(e); 134 updateRayPanel(); 135 }); 136 137 panel3.add(editLayoutTurntableDccControlledCheckBox = new JCheckBox(Bundle.getMessage("TurntableDCCControlled"))); // NOI18N 138 headerPane.add(panel3); 139 140 // set up Done and Cancel buttons 141 JPanel panel5 = new JPanel(); 142 panel5.setLayout(new FlowLayout()); 143 addDoneCancelButtons(panel5, editLayoutTurntableFrame.getRootPane(), 144 this::editLayoutTurntableDonePressed, this::turntableEditCancelPressed); 145 footerPane.add(panel5); 146 147 editLayoutTurntableRayPanel = new JPanel(); 148 editLayoutTurntableRayPanel.setLayout(new BoxLayout(editLayoutTurntableRayPanel, BoxLayout.Y_AXIS)); 149 JScrollPane rayScrollPane = new JScrollPane(editLayoutTurntableRayPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 150 contentPane.add(rayScrollPane, BorderLayout.CENTER); 151 } 152 153 editLayoutTurntableBlockNameComboBox.setSelectedIndex(-1); 154 LayoutBlock lb = layoutTurntable.getLayoutBlock(); 155 if (lb != null) { 156 Block blk = lb.getBlock(); 157 if (blk != null) { 158 editLayoutTurntableBlockNameComboBox.setSelectedItem(blk); 159 } 160 } 161 162 editLayoutTurntableDccControlledCheckBox.setSelected(layoutTurntable.isTurnoutControlled()); 163 editLayoutTurntableDccControlledCheckBox.addActionListener((ActionEvent e) -> { 164 layoutTurntable.setTurnoutControlled(editLayoutTurntableDccControlledCheckBox.isSelected()); 165 166 for (Component comp : editLayoutTurntableRayPanel.getComponents()) { 167 if (comp instanceof TurntableRayPanel) { 168 TurntableRayPanel trp = (TurntableRayPanel) comp; 169 trp.showTurnoutDetails(); 170 } 171 } 172 editLayoutTurntableFrame.pack(); 173 }); 174 updateRayPanel(); 175 // Set up for Edit 176 editLayoutTurntableRadiusTextField.setText(" " + layoutTurntable.getRadius()); 177 editLayoutTurntableOldRadius = editLayoutTurntableRadiusTextField.getText(); 178 editLayoutTurntableAngleTextField.setText("0"); 179 editLayoutTurntableFrame.addWindowListener(new java.awt.event.WindowAdapter() { 180 @Override 181 public void windowClosing(java.awt.event.WindowEvent e) { 182 turntableEditCancelPressed(null); 183 } 184 }); 185 editLayoutTurntableFrame.pack(); 186 editLayoutTurntableFrame.setVisible(true); 187 editLayoutTurntableOpen = true; 188 } // editLayoutTurntable 189 190 @InvokeOnGuiThread 191 private void editLayoutTurntableEditBlockPressed(ActionEvent a) { 192 // check if a block name has been entered 193 String newName = editLayoutTurntableBlockNameComboBox.getSelectedItemDisplayName(); 194 if (newName == null) { 195 newName = ""; 196 } 197 if ((layoutTurntable.getBlockName().isEmpty()) 198 || !layoutTurntable.getBlockName().equals(newName)) { 199 // get new block, or null if block has been removed 200 layoutTurntable.setLayoutBlock(layoutEditor.provideLayoutBlock(newName)); 201 editLayoutTurntableNeedsRedraw = true; 202 ///layoutEditor.getLEAuxTools().setBlockConnectivityChanged(); 203 ///layoutTurntable.updateBlockInfo(); 204 } 205 // check if a block exists to edit 206 LayoutBlock blockToEdit = layoutTurntable.getLayoutBlock(); 207 if (blockToEdit == null) { 208 JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, 209 Bundle.getMessage("Error1"), // NOI18N 210 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 211 return; 212 } 213 blockToEdit.editLayoutBlock(editLayoutTurntableFrame); 214 layoutEditor.setDirty(); 215 editLayoutTurntableNeedsRedraw = true; 216 } 217 218 // Remove old rays and add them back in 219 private void updateRayPanel() { 220 for (Component comp : editLayoutTurntableRayPanel.getComponents()) { 221 editLayoutTurntableRayPanel.remove(comp); 222 } 223 224 // Create list of turnouts to be retained in the NamedBeanComboBox 225 turntableTurnouts.clear(); 226 for (LayoutTurntable.RayTrack rt : layoutTurntable.getRayTrackList()) { 227 turntableTurnouts.add(rt.getTurnout()); 228 } 229 230 editLayoutTurntableRayPanel.setLayout(new BoxLayout(editLayoutTurntableRayPanel, BoxLayout.Y_AXIS)); 231 for (LayoutTurntable.RayTrack rt : layoutTurntable.getRayTrackList()) { 232 editLayoutTurntableRayPanel.add(new TurntableRayPanel(rt)); 233 } 234 editLayoutTurntableRayPanel.revalidate(); 235 editLayoutTurntableRayPanel.repaint(); 236 editLayoutTurntableFrame.pack(); 237 } 238 239 private void saveRayPanelDetail() { 240 for (Component comp : editLayoutTurntableRayPanel.getComponents()) { 241 if (comp instanceof TurntableRayPanel) { 242 TurntableRayPanel trp = (TurntableRayPanel) comp; 243 trp.updateDetails(); 244 } 245 } 246 } 247 248 private void addRayTrackPressed(ActionEvent a) { 249 double ang = 0.0; 250 try { 251 ang = Float.parseFloat(editLayoutTurntableAngleTextField.getText()); 252 } catch (Exception e) { 253 JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": " 254 + e + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"), 255 JmriJOptionPane.ERROR_MESSAGE); 256 return; 257 } 258 layoutTurntable.addRay(ang); 259 layoutEditor.redrawPanel(); 260 layoutEditor.setDirty(); 261 editLayoutTurntableNeedsRedraw = false; 262 } 263 264 private void editLayoutTurntableDonePressed(ActionEvent a) { 265 // check if Block changed 266 String newName = editLayoutTurntableBlockNameComboBox.getSelectedItemDisplayName(); 267 if (newName == null) { 268 newName = ""; 269 } 270 271 if ((layoutTurntable.getBlockName().isEmpty()) || !layoutTurntable.getBlockName().equals(newName)) { 272 // get new block, or null if block has been removed 273 layoutTurntable.setLayoutBlock(layoutEditor.provideLayoutBlock(newName)); 274 editLayoutTurntableNeedsRedraw = true; 275 ///layoutEditor.getLEAuxTools().setBlockConnectivityChanged(); 276 ///layoutTurntable.updateBlockInfo(); 277 } 278 279 // check if new radius was entered 280 String str = editLayoutTurntableRadiusTextField.getText(); 281 if (!str.equals(editLayoutTurntableOldRadius)) { 282 double rad = 0.0; 283 try { 284 rad = Float.parseFloat(str); 285 } catch (Exception e) { 286 JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": " // NOI18N 287 + e + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"), // NOI18N 288 JmriJOptionPane.ERROR_MESSAGE); 289 return; 290 } 291 layoutTurntable.setRadius(rad); 292 editLayoutTurntableNeedsRedraw = true; 293 } 294 // clean up 295 editLayoutTurntableOpen = false; 296 editLayoutTurntableFrame.setVisible(false); 297 editLayoutTurntableFrame.dispose(); 298 editLayoutTurntableFrame = null; 299 saveRayPanelDetail(); 300 if (editLayoutTurntableNeedsRedraw) { 301 layoutEditor.redrawPanel(); 302 layoutEditor.setDirty(); 303 editLayoutTurntableNeedsRedraw = false; 304 } 305 } 306 307 private void turntableEditCancelPressed(ActionEvent a) { 308 editLayoutTurntableOpen = false; 309 editLayoutTurntableFrame.setVisible(false); 310 editLayoutTurntableFrame.dispose(); 311 editLayoutTurntableFrame = null; 312 if (editLayoutTurntableNeedsRedraw) { 313 layoutEditor.redrawPanel(); 314 layoutEditor.setDirty(); 315 editLayoutTurntableNeedsRedraw = false; 316 } 317 } 318 319 /*===================*\ 320 | Turntable Ray Panel | 321 \*===================*/ 322 public class TurntableRayPanel extends JPanel { 323 324 // variables for Edit Turntable ray pane 325 private LayoutTurntable.RayTrack rayTrack = null; 326 private final JPanel rayTurnoutPanel; 327 private final NamedBeanComboBox<Turnout> turnoutNameComboBox; 328 private final TitledBorder rayTitledBorder; 329 private final JComboBox<String> rayTurnoutStateComboBox; 330 private final JLabel rayTurnoutStateLabel; 331 private final JTextField rayAngleTextField; 332 private final int[] rayTurnoutStateValues = new int[]{Turnout.CLOSED, Turnout.THROWN}; 333 private final DecimalFormat twoDForm = new DecimalFormat("#.00"); 334 335 /** 336 * constructor method. 337 * @param rayTrack the single ray track to edit. 338 */ 339 public TurntableRayPanel(@Nonnull LayoutTurntable.RayTrack rayTrack) { 340 this.rayTrack = rayTrack; 341 342 JPanel top = new JPanel(); 343 344 JLabel rayAngleLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("RayAngle"))); 345 top.add(rayAngleLabel); 346 top.add(rayAngleTextField = new JTextField(5)); 347 rayAngleLabel.setLabelFor(rayAngleTextField); 348 349 rayAngleTextField.addFocusListener(new FocusListener() { 350 @Override 351 public void focusGained(FocusEvent e) { 352 } 353 354 @Override 355 public void focusLost(FocusEvent e) { 356 try { 357 Float.parseFloat(rayAngleTextField.getText()); 358 } catch (Exception ex) { 359 JmriJOptionPane.showMessageDialog(editLayoutTurntableFrame, Bundle.getMessage("EntryError") + ": " // NOI18N 360 + ex + Bundle.getMessage("TryAgain"), Bundle.getMessage("ErrorTitle"), // NOI18N 361 JmriJOptionPane.ERROR_MESSAGE); 362 } 363 } 364 } 365 ); 366 this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 367 this.add(top); 368 369 turnoutNameComboBox = new NamedBeanComboBox<>(InstanceManager.getDefault(TurnoutManager.class)); 370 turnoutNameComboBox.setToolTipText(Bundle.getMessage("EditTurnoutToolTip")); 371 LayoutEditor.setupComboBox(turnoutNameComboBox, false, true, false); 372 turnoutNameComboBox.setSelectedItem(rayTrack.getTurnout()); 373 turnoutNameComboBox.addPopupMenuListener( 374 layoutEditor.newTurnoutComboBoxPopupMenuListener(turnoutNameComboBox, turntableTurnouts)); 375 String turnoutStateThrown = InstanceManager.turnoutManagerInstance().getThrownText(); 376 String turnoutStateClosed = InstanceManager.turnoutManagerInstance().getClosedText(); 377 String[] turnoutStates = new String[]{turnoutStateClosed, turnoutStateThrown}; 378 379 rayTurnoutStateComboBox = new JComboBox<>(turnoutStates); 380 rayTurnoutStateLabel = new JLabel(Bundle.getMessage("TurnoutState")); // NOI18N 381 rayTurnoutPanel = new JPanel(); 382 383 rayTurnoutPanel.setBorder(new EtchedBorder()); 384 rayTurnoutPanel.add(turnoutNameComboBox); 385 rayTurnoutPanel.add(rayTurnoutStateLabel); 386 rayTurnoutStateLabel.setLabelFor(rayTurnoutStateComboBox); 387 rayTurnoutPanel.add(rayTurnoutStateComboBox); 388 if (rayTrack.getTurnoutState() == Turnout.CLOSED) { 389 rayTurnoutStateComboBox.setSelectedItem(turnoutStateClosed); 390 } else { 391 rayTurnoutStateComboBox.setSelectedItem(turnoutStateThrown); 392 } 393 this.add(rayTurnoutPanel); 394 395 JButton deleteRayButton; 396 top.add(deleteRayButton = new JButton(Bundle.getMessage("ButtonDelete"))); // NOI18N 397 deleteRayButton.setToolTipText(Bundle.getMessage("DeleteRayTrack")); // NOI18N 398 deleteRayButton.addActionListener((ActionEvent e) -> { 399 delete(); 400 updateRayPanel(); 401 }); 402 rayTitledBorder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 403 404 this.setBorder(rayTitledBorder); 405 406 showTurnoutDetails(); 407 408 rayAngleTextField.setText(twoDForm.format(rayTrack.getAngle())); 409 rayTitledBorder.setTitle(Bundle.getMessage("Ray") + " : " + rayTrack.getConnectionIndex()); // NOI18N 410 if (rayTrack.getConnect() == null) { 411 rayTitledBorder.setTitle(Bundle.getMessage("MakeLabel", 412 Bundle.getMessage("Unconnected")) + " " 413 + rayTrack.getConnectionIndex()); // NOI18N 414 } else if (rayTrack.getConnect().getLayoutBlock() != null) { 415 rayTitledBorder.setTitle(Bundle.getMessage("MakeLabel", 416 Bundle.getMessage("Connected")) + " " 417 + rayTrack.getConnect().getLayoutBlock().getDisplayName()); // NOI18N 418 } 419 } 420 421 private void delete() { 422 int n = JmriJOptionPane.showConfirmDialog(null, 423 Bundle.getMessage("Question7"), // NOI18N 424 Bundle.getMessage("WarningTitle"), // NOI18N 425 JmriJOptionPane.YES_NO_OPTION); 426 if (n == JmriJOptionPane.YES_OPTION) { 427 layoutTurntable.deleteRay(rayTrack); 428 } 429 } 430 431 private void updateDetails() { 432 if (turnoutNameComboBox == null || rayTurnoutStateComboBox == null) { 433 return; 434 } 435 String turnoutName = turnoutNameComboBox.getSelectedItemDisplayName(); 436 if (turnoutName == null) { 437 turnoutName = ""; 438 } 439 rayTrack.setTurnout(turnoutName, rayTurnoutStateValues[rayTurnoutStateComboBox.getSelectedIndex()]); 440 if (!rayAngleTextField.getText().equals(twoDForm.format(rayTrack.getAngle()))) { 441 try { 442 double ang = Float.parseFloat(rayAngleTextField.getText()); 443 rayTrack.setAngle(ang); 444 } catch (Exception e) { 445 log.error("Angle is not in correct format so will skip {}", rayAngleTextField.getText()); // NOI18N 446 } 447 } 448 } 449 450 private void showTurnoutDetails() { 451 boolean vis = layoutTurntable.isTurnoutControlled(); 452 rayTurnoutPanel.setVisible(vis); 453 turnoutNameComboBox.setVisible(vis); 454 rayTurnoutStateComboBox.setVisible(vis); 455 rayTurnoutStateLabel.setVisible(vis); 456 } 457 } 458 459 460 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTurntableEditor.class); 461}