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