001package jmri.jmrit.display.layoutEditor; 002 003import static java.awt.event.KeyEvent.KEY_PRESSED; 004 005import static jmri.jmrit.display.layoutEditor.LayoutEditor.setupComboBox; 006 007import java.awt.BorderLayout; 008import java.awt.Color; 009import java.awt.Component; 010import java.awt.Dimension; 011import java.awt.FlowLayout; 012import java.awt.event.*; 013import java.awt.geom.Point2D; 014import java.util.LinkedHashMap; 015import java.util.Map; 016 017import javax.annotation.Nonnull; 018import javax.swing.*; 019 020import jmri.*; 021import jmri.jmrit.logixng.GlobalVariable; 022import jmri.jmrit.logixng.GlobalVariableManager; 023import jmri.swing.NamedBeanComboBox; 024import jmri.util.MathUtil; 025import jmri.util.swing.JmriJOptionPane; 026 027import org.apache.commons.lang3.StringUtils; 028 029/** 030 * This is the base class for the horizontal, vertical and floating toolbar 031 * panels 032 * 033 * @author George Warner Copyright: (c) 2017-2019 034 */ 035public class LayoutEditorToolBarPanel extends JPanel implements Disposable { 036 037 protected final LayoutEditor layoutEditor; // initialized in constuctor 038 039 // top row of radio buttons 040 protected JLabel turnoutLabel = new JLabel(); 041 protected JRadioButton turnoutRHButton = new JRadioButton(Bundle.getMessage("RightHandAbbreviation")); 042 protected JRadioButton turnoutLHButton = new JRadioButton(Bundle.getMessage("LeftHandAbbreviation")); 043 protected JRadioButton turnoutWYEButton = new JRadioButton(Bundle.getMessage("WYEAbbreviation")); 044 protected JRadioButton doubleXoverButton = new JRadioButton(Bundle.getMessage("DoubleCrossoverAbbreviation")); 045 protected JRadioButton rhXoverButton = new JRadioButton(Bundle.getMessage("RightCrossover")); //key is also used by Control Panel 046 // Editor, placed in DisplayBundle 047 protected JRadioButton lhXoverButton = new JRadioButton(Bundle.getMessage("LeftCrossover")); //idem 048 protected JRadioButton layoutSingleSlipButton = new JRadioButton(Bundle.getMessage("LayoutSingleSlip")); 049 protected JRadioButton layoutDoubleSlipButton = new JRadioButton(Bundle.getMessage("LayoutDoubleSlip")); 050 051 // Default flow layout definitions for JPanels 052 protected FlowLayout leftRowLayout = new FlowLayout(FlowLayout.LEFT, 5, 0); //5 pixel gap between items, no vertical gap 053 protected FlowLayout centerRowLayout = new FlowLayout(FlowLayout.CENTER, 5, 0); //5 pixel gap between items, no vertical gap 054 protected FlowLayout rightRowLayout = new FlowLayout(FlowLayout.RIGHT, 5, 0); //5 pixel gap between items, no vertical gap 055 056 // top row of check boxes 057 protected NamedBeanComboBox<Turnout> turnoutNameComboBox = new NamedBeanComboBox<>( 058 InstanceManager.turnoutManagerInstance(), null, NamedBean.DisplayOptions.DISPLAYNAME); 059 060 protected JLabel turnoutNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("Name"))); 061 protected JPanel turnoutNamePanel = new JPanel(leftRowLayout); 062 protected JPanel extraTurnoutPanel = new JPanel(leftRowLayout); 063 protected NamedBeanComboBox<Turnout> extraTurnoutNameComboBox = new NamedBeanComboBox<>( 064 InstanceManager.turnoutManagerInstance(), null, NamedBean.DisplayOptions.DISPLAYNAME); 065 protected JComboBox<String> rotationComboBox = null; 066 protected JPanel rotationPanel = new JPanel(leftRowLayout); 067 068 // 2nd row of radio buttons 069 protected JLabel trackLabel = new JLabel(); 070 protected JRadioButton levelXingButton = new JRadioButton(Bundle.getMessage("LevelCrossing")); 071 protected JRadioButton trackButton = new JRadioButton(Bundle.getMessage("TrackSegment")); 072 protected JRadioButton turntableButton = new JRadioButton(Bundle.getMessage("Turntable")); 073 protected JRadioButton traverserButton = new JRadioButton(Bundle.getMessage("Traverser")); 074 075 // 2nd row of check boxes 076 protected JPanel trackSegmentPropertiesPanel = new JPanel(leftRowLayout); 077 protected JCheckBox mainlineTrack = new JCheckBox(Bundle.getMessage("MainlineBox")); 078 protected JCheckBox dashedLine = new JCheckBox(Bundle.getMessage("Dashed")); 079 080 protected JLabel blockLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BlockID"))); 081 protected NamedBeanComboBox<Block> blockIDComboBox = new NamedBeanComboBox<>( 082 InstanceManager.getDefault(BlockManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 083 protected JCheckBox highlightBlockCheckBox = new JCheckBox(Bundle.getMessage("HighlightSelectedBlockTitle")); 084 085 protected JLabel blockSensorLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BlockSensorName"))); 086 protected NamedBeanComboBox<Sensor> blockSensorComboBox = new NamedBeanComboBox<>( 087 InstanceManager.getDefault(SensorManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 088 089 // 3rd row of radio buttons (and any associated text fields) 090 protected JRadioButton endBumperButton = new JRadioButton(Bundle.getMessage("EndBumper")); 091 protected JRadioButton anchorButton = new JRadioButton(Bundle.getMessage("Anchor")); 092 protected JRadioButton edgeButton = new JRadioButton(Bundle.getMessage("EdgeConnector")); 093 094 protected JLabel labelsLabel = new JLabel(); 095 protected JRadioButton textLabelButton = new JRadioButton(Bundle.getMessage("TextLabel")); 096 protected JTextField textLabelTextField = new JTextField(12); 097 098 protected JRadioButton memoryButton = new JRadioButton(Bundle.getMessage("BeanNameMemory")); 099 protected NamedBeanComboBox<Memory> textMemoryComboBox = new NamedBeanComboBox<>( 100 InstanceManager.getDefault(MemoryManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 101 102 protected JRadioButton globalVariableButton = new JRadioButton(Bundle.getMessage("BeanNameGlobalVariable")); 103 protected NamedBeanComboBox<GlobalVariable> textGlobalVariableComboBox = new NamedBeanComboBox<>( 104 InstanceManager.getDefault(GlobalVariableManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 105 106 protected JRadioButton blockContentsButton = new JRadioButton(Bundle.getMessage("BlockContentsLabel")); 107 protected NamedBeanComboBox<Block> blockContentsComboBox = new NamedBeanComboBox<>( 108 InstanceManager.getDefault(BlockManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 109 110 // 4th row of radio buttons (and any associated text fields) 111 protected JRadioButton multiSensorButton = new JRadioButton(Bundle.getMessage("MultiSensor") + "..."); 112 113 protected JRadioButton signalMastButton = new JRadioButton(Bundle.getMessage("SignalMastIcon")); 114 protected NamedBeanComboBox<SignalMast> signalMastComboBox = new NamedBeanComboBox<>( 115 InstanceManager.getDefault(SignalMastManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 116 117 protected JRadioButton sensorButton = new JRadioButton(Bundle.getMessage("SensorIcon")); 118 protected NamedBeanComboBox<Sensor> sensorComboBox = new NamedBeanComboBox<>( 119 InstanceManager.getDefault(SensorManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 120 121 protected JRadioButton turnoutButton = new JRadioButton(Bundle.getMessage("TurnoutIcon")); 122 protected NamedBeanComboBox<Turnout> turnoutComboBox = new NamedBeanComboBox<>( 123 InstanceManager.getDefault(TurnoutManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 124 125 protected JRadioButton signalButton = new JRadioButton(Bundle.getMessage("SignalIcon")); 126 protected NamedBeanComboBox<SignalHead> signalHeadComboBox = new NamedBeanComboBox<>( 127 InstanceManager.getDefault(SignalHeadManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 128 129 protected JRadioButton iconLabelButton = new JRadioButton(Bundle.getMessage("IconLabel")); 130 protected JRadioButton logixngButton = new JRadioButton(Bundle.getMessage("LogixNGIcon")); 131 protected JRadioButton audioButton = new JRadioButton(Bundle.getMessage("AudioIcon")); 132 protected NamedBeanComboBox<Audio> textAudioComboBox = new NamedBeanComboBox<>( 133 InstanceManager.getDefault(AudioSourceManager.class), null, NamedBean.DisplayOptions.DISPLAYNAME); 134 protected JRadioButton shapeButton = new JRadioButton(Bundle.getMessage("LayoutShape")); 135 136 protected JButton changeIconsButton = new JButton(Bundle.getMessage("ChangeIcons") + "..."); 137 138 protected MultiIconEditor sensorIconEditor = null; 139 protected JFrame sensorFrame = null; 140 141 protected MultiIconEditor turnoutIconEditor = null; 142 protected JFrame turnoutFrame = null; 143 144 protected MultiIconEditor signalIconEditor = null; 145 protected JFrame signalFrame = null; 146 147 protected MultiIconEditor iconEditor = null; 148 protected JFrame iconFrame = null; 149 150 protected MultiIconEditor logixngEditor = null; 151 protected JFrame logixngFrame = null; 152 153 protected MultiIconEditor audioEditor = null; 154 protected JFrame audioFrame = null; 155 156 protected MultiSensorIconFrame multiSensorFrame = null; 157 158 protected JPanel zoomPanel = new JPanel(); 159 protected JLabel zoomLabel = new JLabel("x1"); 160 161 protected JPanel locationPanel = new JPanel(); 162 protected JPopupMenu locationPopupMenu = new JPopupMenu(); 163 164 protected JLabel xLabel = new JLabel("00"); 165 protected JLabel yLabel = new JLabel("00"); 166 167 protected JPanel blockPropertiesPanel = null; 168 169 // non-GUI variables 170 protected boolean toolBarIsWide = true; 171 protected ButtonGroup itemGroup = null; 172 173 /** 174 * Constructor for LayoutEditorToolBarPanel. 175 * <p> 176 * Note an unusual design feature: Since this calls the 177 * {@link #setupComponents()} and {@link #layoutComponents()} non-final 178 * methods in the constructor, any subclass reimplementing those must 179 * provide versions that will work before the subclasses own initializers 180 * and constructor is run. 181 * 182 * @param layoutEditor the layout editor that this is for 183 */ 184 public LayoutEditorToolBarPanel(@Nonnull LayoutEditor layoutEditor) { 185 this.layoutEditor = layoutEditor; 186 187 setupComponents(); 188 layoutComponents(); 189 } 190 191 protected void setupComponents() { 192 // setup group for radio buttons selecting items to add and line style 193 itemGroup = new ButtonGroup(); 194 itemGroup.add(turnoutRHButton); 195 itemGroup.add(turnoutLHButton); 196 itemGroup.add(turnoutWYEButton); 197 itemGroup.add(doubleXoverButton); 198 itemGroup.add(rhXoverButton); 199 itemGroup.add(lhXoverButton); 200 itemGroup.add(levelXingButton); 201 itemGroup.add(layoutSingleSlipButton); 202 itemGroup.add(layoutDoubleSlipButton); 203 itemGroup.add(endBumperButton); 204 itemGroup.add(anchorButton); 205 itemGroup.add(edgeButton); 206 itemGroup.add(trackButton); 207 itemGroup.add(turntableButton); 208 itemGroup.add(traverserButton); 209 itemGroup.add(multiSensorButton); 210 itemGroup.add(sensorButton); 211 itemGroup.add(turnoutButton); 212 itemGroup.add(signalButton); 213 itemGroup.add(signalMastButton); 214 itemGroup.add(textLabelButton); 215 itemGroup.add(memoryButton); 216 itemGroup.add(globalVariableButton); 217 itemGroup.add(blockContentsButton); 218 itemGroup.add(iconLabelButton); 219 itemGroup.add(logixngButton); 220 itemGroup.add(audioButton); 221 itemGroup.add(shapeButton); 222 223 // This is used to enable/disable property controls depending on which (radio) button is selected 224 ActionListener selectionListAction = (ActionEvent event) -> { 225 //turnout properties 226 boolean e = (turnoutRHButton.isSelected() 227 || turnoutLHButton.isSelected() 228 || turnoutWYEButton.isSelected() 229 || doubleXoverButton.isSelected() 230 || rhXoverButton.isSelected() 231 || lhXoverButton.isSelected() 232 || layoutSingleSlipButton.isSelected() 233 || layoutDoubleSlipButton.isSelected()); 234 log.debug("turnoutPropertiesPanel is {}", e ? "enabled" : "disabled"); 235 turnoutNamePanel.setEnabled(e); 236 237 for (Component i : turnoutNamePanel.getComponents()) { 238 i.setEnabled(e); 239 } 240 rotationPanel.setEnabled(e); 241 242 for (Component i : rotationPanel.getComponents()) { 243 i.setEnabled(e); 244 } 245 246 //second turnout property 247 e = (layoutSingleSlipButton.isSelected() || layoutDoubleSlipButton.isSelected()); 248 log.debug("extraTurnoutPanel is {}", e ? "enabled" : "disabled"); 249 250 for (Component i : extraTurnoutPanel.getComponents()) { 251 i.setEnabled(e); 252 } 253 254 //track Segment properties 255 boolean isTrack = trackButton.isSelected(); 256 boolean isTT = turntableButton.isSelected() || traverserButton.isSelected(); 257 log.debug("trackSegmentPropertiesPanel is {}", (isTrack || isTT) ? "enabled" : "disabled"); 258 mainlineTrack.setEnabled(isTrack || isTT); 259 dashedLine.setEnabled(isTrack); 260 261 // block properties 262 e = (turnoutRHButton.isSelected() 263 || turnoutLHButton.isSelected() 264 || turnoutWYEButton.isSelected() 265 || doubleXoverButton.isSelected() 266 || rhXoverButton.isSelected() 267 || lhXoverButton.isSelected() 268 || layoutSingleSlipButton.isSelected() 269 || layoutDoubleSlipButton.isSelected() 270 || levelXingButton.isSelected() 271 || trackButton.isSelected() 272 || turntableButton.isSelected() 273 || traverserButton.isSelected()); 274 log.debug("blockPanel is {}", e ? "enabled" : "disabled"); 275 276 if (blockPropertiesPanel != null) { 277 for (Component i : blockPropertiesPanel.getComponents()) { 278 i.setEnabled(e); 279 } 280 281 if (e) { 282 blockPropertiesPanel.setBackground(Color.lightGray); 283 } else { 284 blockPropertiesPanel.setBackground(new Color(238, 238, 238)); 285 } 286 } else { 287 blockLabel.setEnabled(e); 288 blockIDComboBox.setEnabled(e); 289 blockSensorLabel.setEnabled(e); 290 blockSensorComboBox.setEnabled(e); 291 } 292 293 // enable/disable text label, memory, global variable & block contents text fields 294 textLabelTextField.setEnabled(textLabelButton.isSelected()); 295 textMemoryComboBox.setEnabled(memoryButton.isSelected()); 296 textGlobalVariableComboBox.setEnabled(globalVariableButton.isSelected()); 297 blockContentsComboBox.setEnabled(blockContentsButton.isSelected()); 298 textAudioComboBox.setEnabled(audioButton.isSelected()); 299 300 // enable/disable signal mast, sensor, turnout & signal head text fields 301 signalMastComboBox.setEnabled(signalMastButton.isSelected()); 302 sensorComboBox.setEnabled(sensorButton.isSelected()); 303 turnoutComboBox.setEnabled(turnoutButton.isSelected()); 304 signalHeadComboBox.setEnabled(signalButton.isSelected()); 305 306 // changeIconsButton 307 e = (sensorButton.isSelected() 308 || turnoutButton.isSelected() 309 || signalButton.isSelected() 310 || iconLabelButton.isSelected() 311 || logixngButton.isSelected() 312 || audioButton.isSelected()); 313 log.debug("changeIconsButton is {}", e ? "enabled" : "disabled"); 314 changeIconsButton.setEnabled(e); 315 }; 316 317 turnoutRHButton.addActionListener(selectionListAction); 318 turnoutLHButton.addActionListener(selectionListAction); 319 turnoutWYEButton.addActionListener(selectionListAction); 320 doubleXoverButton.addActionListener(selectionListAction); 321 rhXoverButton.addActionListener(selectionListAction); 322 lhXoverButton.addActionListener(selectionListAction); 323 levelXingButton.addActionListener(selectionListAction); 324 layoutSingleSlipButton.addActionListener(selectionListAction); 325 layoutDoubleSlipButton.addActionListener(selectionListAction); 326 endBumperButton.addActionListener(selectionListAction); 327 anchorButton.addActionListener(selectionListAction); 328 edgeButton.addActionListener(selectionListAction); 329 trackButton.addActionListener(selectionListAction); 330 turntableButton.addActionListener(selectionListAction); 331 traverserButton.addActionListener(selectionListAction); 332 multiSensorButton.addActionListener(selectionListAction); 333 sensorButton.addActionListener(selectionListAction); 334 turnoutButton.addActionListener(selectionListAction); 335 signalButton.addActionListener(selectionListAction); 336 signalMastButton.addActionListener(selectionListAction); 337 textLabelButton.addActionListener(selectionListAction); 338 memoryButton.addActionListener(selectionListAction); 339 globalVariableButton.addActionListener(selectionListAction); 340 blockContentsButton.addActionListener(selectionListAction); 341 iconLabelButton.addActionListener(selectionListAction); 342 logixngButton.addActionListener(selectionListAction); 343 audioButton.addActionListener(selectionListAction); 344 shapeButton.addActionListener(selectionListAction); 345 346 // first row of edit tool bar items 347 // turnout items 348 turnoutRHButton.setSelected(true); 349 turnoutRHButton.setToolTipText(Bundle.getMessage("RHToolTip")); 350 turnoutLHButton.setToolTipText(Bundle.getMessage("LHToolTip")); 351 turnoutWYEButton.setToolTipText(Bundle.getMessage("WYEToolTip")); 352 doubleXoverButton.setToolTipText(Bundle.getMessage("DoubleCrossoverToolTip")); 353 rhXoverButton.setToolTipText(Bundle.getMessage("RHCrossoverToolTip")); 354 lhXoverButton.setToolTipText(Bundle.getMessage("LHCrossoverToolTip")); 355 layoutSingleSlipButton.setToolTipText(Bundle.getMessage("SingleSlipToolTip")); 356 layoutDoubleSlipButton.setToolTipText(Bundle.getMessage("DoubleSlipToolTip")); 357 358 turnoutNamePanel.add(turnoutNameLabel); 359 360 setupComboBox(turnoutNameComboBox, false, true, false); 361 turnoutNameComboBox.setToolTipText(Bundle.getMessage("TurnoutNameToolTip")); 362 turnoutNamePanel.add(turnoutNameComboBox); 363 364 // disable turnouts that are already in use 365 turnoutNameComboBox.addPopupMenuListener(layoutEditor.newTurnoutComboBoxPopupMenuListener(turnoutNameComboBox)); 366 // turnoutNameComboBox.setEnabledColor(Color.green.darker().darker()); 367 // turnoutNameComboBox.setDisabledColor(Color.red); 368 369 setupComboBox(extraTurnoutNameComboBox, false, true, false); 370 extraTurnoutNameComboBox.setToolTipText(Bundle.getMessage("SecondTurnoutNameToolTip")); 371 372 extraTurnoutNameComboBox.addPopupMenuListener(layoutEditor.newTurnoutComboBoxPopupMenuListener(extraTurnoutNameComboBox)); 373 // extraTurnoutNameComboBox.setEnabledColor(Color.green.darker().darker()); 374 // extraTurnoutNameComboBox.setDisabledColor(Color.red); 375 376 // this is enabled/disabled via selectionListAction above 377 JLabel extraTurnoutLabel = new JLabel(Bundle.getMessage("SecondName")); 378 extraTurnoutLabel.setEnabled(false); 379 extraTurnoutPanel.add(extraTurnoutLabel); 380 extraTurnoutPanel.add(extraTurnoutNameComboBox); 381 extraTurnoutPanel.setEnabled(false); 382 383 String[] angleStrings = {"-180", "-135", "-90", "-45", "0", "+45", "+90", "+135", "+180"}; 384 rotationComboBox = new JComboBox<>(angleStrings); 385 rotationComboBox.setEditable(true); 386 rotationComboBox.setSelectedIndex(4); 387 rotationComboBox.setMaximumRowCount(9); 388 rotationComboBox.setToolTipText(Bundle.getMessage("RotationToolTip")); 389 390 JLabel rotationLabel = new JLabel(Bundle.getMessage("Rotation")); 391 rotationPanel.add(rotationLabel); 392 rotationPanel.add(rotationComboBox); 393 394 zoomPanel.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("ZoomLabel")))); 395 zoomPanel.add(zoomLabel); 396 397 Dimension coordSize = xLabel.getPreferredSize(); 398 coordSize.width *= 2; 399 xLabel.setPreferredSize(coordSize); 400 yLabel.setPreferredSize(coordSize); 401 402 locationPanel.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("Location")))); 403 locationPanel.add(new JLabel("{x:")); 404 locationPanel.add(xLabel); 405 locationPanel.add(new JLabel(", y:")); 406 locationPanel.add(yLabel); 407 locationPanel.add(new JLabel("} ")); 408 409 locationPanel.addMouseListener(new MouseAdapter() { 410 @Override 411 public void mousePressed(MouseEvent me) { 412 if (me.isPopupTrigger()) { 413 locationPopupMenu.show(locationPanel, me.getX(), me.getY()); 414 } 415 } 416 417 @Override 418 public void mouseReleased(MouseEvent me) { 419 if (me.isPopupTrigger()) { 420 locationPopupMenu.show(locationPanel, me.getX(), me.getY()); 421 } 422 } 423 424 @Override 425 public void mouseClicked(MouseEvent me) { 426 if (me.isPopupTrigger()) { 427 locationPopupMenu.show(locationPanel, me.getX(), me.getY()); 428 } 429 } 430 }); 431 432 // second row of edit tool bar items 433 levelXingButton.setToolTipText(Bundle.getMessage("LevelCrossingToolTip")); 434 trackButton.setToolTipText(Bundle.getMessage("TrackSegmentToolTip")); 435 turntableButton.setToolTipText(Bundle.getMessage("AddTurntable")); 436 traverserButton.setToolTipText(Bundle.getMessage("AddTraverser")); 437 438 // this is enabled/disabled via selectionListAction above 439 trackSegmentPropertiesPanel.add(mainlineTrack); 440 441 mainlineTrack.setSelected(false); 442 mainlineTrack.setEnabled(false); 443 mainlineTrack.setToolTipText(Bundle.getMessage("MainlineCheckBoxTip")); 444 445 trackSegmentPropertiesPanel.add(dashedLine); 446 dashedLine.setSelected(false); 447 dashedLine.setEnabled(false); 448 dashedLine.setToolTipText(Bundle.getMessage("DashedCheckBoxTip")); 449 450 // the blockPanel is enabled/disabled via selectionListAction above 451 setupComboBox(blockIDComboBox, false, true, true); 452 blockIDComboBox.setToolTipText(Bundle.getMessage("BlockIDToolTip")); 453 454 highlightBlockCheckBox.setToolTipText(Bundle.getMessage("HighlightSelectedBlockToolTip")); 455 highlightBlockCheckBox.addActionListener((ActionEvent event) -> layoutEditor.setHighlightSelectedBlock(highlightBlockCheckBox.isSelected())); 456 highlightBlockCheckBox.setSelected(layoutEditor.getHighlightSelectedBlock()); 457 458 // change the block name 459 blockIDComboBox.addActionListener((ActionEvent event) -> { 460 //use the "Extra" color to highlight the selected block 461 if (layoutEditor.getHighlightSelectedBlock()) { 462 layoutEditor.highlightBlockInComboBox(blockIDComboBox); 463 } 464 String newName = blockIDComboBox.getSelectedItemDisplayName(); 465 if (newName == null) { 466 newName = ""; 467 } 468 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(newName); 469 if (lb != null) { 470 //if there is an occupancy sensor assigned already 471 String sensorName = lb.getOccupancySensorName(); 472 473 if (!sensorName.isEmpty()) { 474 //update the block sensor ComboBox 475 blockSensorComboBox.setSelectedItem(lb.getOccupancySensor()); 476 } else { 477 blockSensorComboBox.setSelectedItem(null); 478 } 479 } else { 480 blockSensorComboBox.setSelectedItem(null); 481 } 482 }); 483 484 setupComboBox(blockSensorComboBox, false, true, false); 485 blockSensorComboBox.setToolTipText(Bundle.getMessage("OccupancySensorToolTip")); 486 487 // third row of edit tool bar items 488 endBumperButton.setToolTipText(Bundle.getMessage("EndBumperToolTip")); 489 anchorButton.setToolTipText(Bundle.getMessage("AnchorToolTip")); 490 edgeButton.setToolTipText(Bundle.getMessage("EdgeConnectorToolTip")); 491 textLabelButton.setToolTipText(Bundle.getMessage("TextLabelToolTip")); 492 493 textLabelTextField.setToolTipText(Bundle.getMessage("TextToolTip")); 494 textLabelTextField.setEnabled(false); 495 496 memoryButton.setToolTipText(Bundle.getMessage("MemoryButtonToolTip", Bundle.getMessage("Memory"))); 497 498 setupComboBox(textMemoryComboBox, true, false, false); 499 textMemoryComboBox.setToolTipText(Bundle.getMessage("MemoryToolTip")); 500 501 globalVariableButton.setToolTipText(Bundle.getMessage("GlobalVariableButtonToolTip", Bundle.getMessage("GlobalVariable"))); 502 503 setupComboBox(textGlobalVariableComboBox, true, false, false); 504 textGlobalVariableComboBox.setToolTipText(Bundle.getMessage("GlobalVariableToolTip")); 505 506 setupComboBox(textAudioComboBox, true, false, false); 507 textAudioComboBox.setToolTipText(Bundle.getMessage("AudioToolTip")); 508 509 blockContentsButton.setToolTipText(Bundle.getMessage("BlockContentsButtonToolTip")); 510 511 setupComboBox(blockContentsComboBox, true, false, false); 512 blockContentsComboBox.setToolTipText(Bundle.getMessage("BlockContentsButtonToolTip")); 513 blockContentsComboBox.addActionListener((ActionEvent event) -> { 514 // use the "Extra" color to highlight the selected block 515 if (layoutEditor.getHighlightSelectedBlock()) { 516 layoutEditor.highlightBlockInComboBox(blockContentsComboBox); 517 } 518 }); 519 520 // fourth row of edit tool bar items 521 // multi sensor... 522 multiSensorButton.setToolTipText(Bundle.getMessage("MultiSensorToolTip")); 523 524 // Signal Mast & text 525 signalMastButton.setToolTipText(Bundle.getMessage("SignalMastButtonToolTip")); 526 setupComboBox(signalMastComboBox, true, false, false); 527 528 // sensor icon & text 529 sensorButton.setToolTipText(Bundle.getMessage("SensorButtonToolTip")); 530 531 setupComboBox(sensorComboBox, true, false, false); 532 sensorComboBox.setToolTipText(Bundle.getMessage("SensorIconToolTip")); 533 534 sensorIconEditor = new MultiIconEditor(4); 535 sensorIconEditor.setIcon(0, Bundle.getMessage("MakeLabel", Bundle.getMessage("SensorStateActive")), 536 "resources/icons/smallschematics/tracksegments/circuit-occupied.gif"); 537 sensorIconEditor.setIcon(1, Bundle.getMessage("MakeLabel", Bundle.getMessage("SensorStateInactive")), 538 "resources/icons/smallschematics/tracksegments/circuit-empty.gif"); 539 sensorIconEditor.setIcon(2, Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanStateInconsistent")), 540 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 541 sensorIconEditor.setIcon(3, Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanStateUnknown")), 542 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 543 sensorIconEditor.complete(); 544 545 // turnout icon & text 546 turnoutButton.setToolTipText(Bundle.getMessage("TurnoutButtonToolTip")); 547 548 setupComboBox(turnoutComboBox, true, false, false); 549 turnoutComboBox.setToolTipText(Bundle.getMessage("TurnoutIconToolTip")); 550 551 turnoutIconEditor = new MultiIconEditor(4); 552 turnoutIconEditor.setIcon(0, Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutStateThrown")), 553 "resources/icons/smallschematics/tracksegments/circuit-occupied.gif"); 554 turnoutIconEditor.setIcon(1, Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutStateClosed")), 555 "resources/icons/smallschematics/tracksegments/circuit-empty.gif"); 556 turnoutIconEditor.setIcon(2, Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanStateInconsistent")), 557 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 558 turnoutIconEditor.setIcon(3, Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanStateUnknown")), 559 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 560 turnoutIconEditor.complete(); 561 562 // Signal icon & text 563 signalButton.setToolTipText(Bundle.getMessage("SignalButtonToolTip")); 564 565 setupComboBox(signalHeadComboBox, true, false, false); 566 signalHeadComboBox.setToolTipText(Bundle.getMessage("SignalIconToolTip")); 567 568 signalIconEditor = new MultiIconEditor(10); 569 signalIconEditor.setIcon(0, "Red:", "resources/icons/smallschematics/searchlights/left-red-short.gif"); 570 signalIconEditor.setIcon(1, "Flash red:", "resources/icons/smallschematics/searchlights/left-flashred-short.gif"); 571 signalIconEditor.setIcon(2, "Yellow:", "resources/icons/smallschematics/searchlights/left-yellow-short.gif"); 572 signalIconEditor.setIcon(3, 573 "Flash yellow:", 574 "resources/icons/smallschematics/searchlights/left-flashyellow-short.gif"); 575 signalIconEditor.setIcon(4, "Green:", "resources/icons/smallschematics/searchlights/left-green-short.gif"); 576 signalIconEditor.setIcon(5, "Flash green:", 577 "resources/icons/smallschematics/searchlights/left-flashgreen-short.gif"); 578 signalIconEditor.setIcon(6, "Dark:", "resources/icons/smallschematics/searchlights/left-dark-short.gif"); 579 signalIconEditor.setIcon(7, "Held:", "resources/icons/smallschematics/searchlights/left-held-short.gif"); 580 signalIconEditor.setIcon(8, 581 "Lunar", 582 "resources/icons/smallschematics/searchlights/left-lunar-short-marker.gif"); 583 signalIconEditor.setIcon(9, 584 "Flash Lunar", 585 "resources/icons/smallschematics/searchlights/left-flashlunar-short-marker.gif"); 586 signalIconEditor.complete(); 587 588 sensorFrame = new JFrame(Bundle.getMessage("EditSensorIcons")); 589 sensorFrame.getContentPane().add(new JLabel(Bundle.getMessage("IconChangeInfo")), BorderLayout.NORTH); 590 sensorFrame.getContentPane().add(sensorIconEditor); 591 sensorFrame.pack(); 592 593 turnoutFrame = new JFrame(Bundle.getMessage("EditTurnoutIcons")); 594 turnoutFrame.getContentPane().add(new JLabel(Bundle.getMessage("IconChangeInfo")), BorderLayout.NORTH); 595 turnoutFrame.getContentPane().add(turnoutIconEditor); 596 turnoutFrame.pack(); 597 598 signalFrame = new JFrame(Bundle.getMessage("EditSignalIcons")); 599 signalFrame.getContentPane().add(new JLabel(Bundle.getMessage("IconChangeInfo")), BorderLayout.NORTH); 600 // no spaces around Label as that breaks html formatting 601 signalFrame.getContentPane().add(signalIconEditor); 602 signalFrame.pack(); 603 signalFrame.setVisible(false); 604 605 // icon label 606 iconLabelButton.setToolTipText(Bundle.getMessage("IconLabelToolTip")); 607 logixngButton.setToolTipText(Bundle.getMessage("LogixNGIconToolTip")); 608 audioButton.setToolTipText(Bundle.getMessage("AudioIconToolTip")); 609 shapeButton.setToolTipText(Bundle.getMessage("LayoutShapeToolTip")); 610 611 // change icons... 612 // this is enabled/disabled via selectionListAction above 613 changeIconsButton.addActionListener((ActionEvent event) -> { 614 if (sensorButton.isSelected()) { 615 sensorFrame.setVisible(true); 616 } if (turnoutButton.isSelected()) { 617 turnoutFrame.setVisible(true); 618 } else if (signalButton.isSelected()) { 619 signalFrame.setVisible(true); 620 } else if (iconLabelButton.isSelected()) { 621 iconFrame.setVisible(true); 622 } else if (logixngButton.isSelected()) { 623 logixngFrame.setVisible(true); 624 } else if (audioButton.isSelected()) { 625 audioFrame.setVisible(true); 626 } else { 627 //explain to the user why nothing happens 628 JmriJOptionPane.showMessageDialog(changeIconsButton, Bundle.getMessage("ChangeIconNotApplied"), 629 Bundle.getMessage("ChangeIcons"), JmriJOptionPane.INFORMATION_MESSAGE); 630 } 631 }); 632 633 changeIconsButton.setToolTipText(Bundle.getMessage("ChangeIconToolTip")); 634 changeIconsButton.setEnabled(false); 635 636 // Default icon icon 637 iconEditor = new MultiIconEditor(1); 638 iconEditor.setIcon(0, "", "resources/icons/smallschematics/tracksegments/block.gif"); 639 iconEditor.complete(); 640 iconFrame = new JFrame(Bundle.getMessage("EditIcon")); 641 iconFrame.getContentPane().add(iconEditor); 642 iconFrame.pack(); 643 644 // LogixNG Icon 645 logixngEditor = new MultiIconEditor(1); 646 logixngEditor.setIcon(0, "", "resources/icons/logixng/logixng_icon.gif"); 647 logixngEditor.complete(); 648 logixngFrame = new JFrame(Bundle.getMessage("EditIcon")); 649 logixngFrame.getContentPane().add(logixngEditor); 650 logixngFrame.pack(); 651 652 // Audio Icon 653 audioEditor = new MultiIconEditor(1); 654 audioEditor.setIcon(0, "", "resources/icons/audio_icon.gif"); 655 audioEditor.complete(); 656 audioFrame = new JFrame(Bundle.getMessage("EditIcon")); 657 audioFrame.getContentPane().add(audioEditor); 658 audioFrame.pack(); 659 } 660 661 /*=========================*\ 662 |* toolbar location format *| 663 \*=========================*/ 664 public enum LocationFormat { 665 ePIXELS, 666 eMETRIC_CM, 667 eENGLISH_FEET_INCHES; 668 669 LocationFormat() { 670 } 671 } 672 673 private LocationFormat locationFormat = LocationFormat.ePIXELS; 674 675 public LocationFormat getLocationFormat() { 676 return locationFormat; 677 } 678 679 public void setLocationFormat(LocationFormat locationFormat) { 680 if (this.locationFormat != locationFormat) { 681 switch (locationFormat) { 682 default: 683 case ePIXELS: { 684 Dimension coordSize = new JLabel("10000").getPreferredSize(); 685 xLabel.setPreferredSize(coordSize); 686 yLabel.setPreferredSize(coordSize); 687 break; 688 } 689 case eMETRIC_CM: { 690 Dimension coordSize = new JLabel(getMetricCMText(10005)).getPreferredSize(); 691 xLabel.setPreferredSize(coordSize); 692 yLabel.setPreferredSize(coordSize); 693 694 layoutEditor.gContext.setGridSize(10); 695 layoutEditor.gContext.setGridSize2nd(10); 696 break; 697 } 698 case eENGLISH_FEET_INCHES: { 699 Dimension coordSize = new JLabel(getEnglishFeetInchesText(100008)).getPreferredSize(); 700 xLabel.setPreferredSize(coordSize); 701 yLabel.setPreferredSize(coordSize); 702 703 layoutEditor.gContext.setGridSize(16); 704 layoutEditor.gContext.setGridSize2nd(12); 705 break; 706 } 707 } 708 this.locationFormat = locationFormat; 709 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefsMgr) -> { 710 String windowFrameRef = layoutEditor.getWindowFrameRef(); 711 prefsMgr.setProperty(windowFrameRef, "LocationFormat", locationFormat.name()); 712 }); 713 setLocationText(lastLocation); 714 } 715 } 716 717 private Point2D lastLocation = MathUtil.zeroPoint2D(); 718 719 public void setLocationText(Point2D p) { 720 int x = (int) p.getX(); 721 int y = (int) p.getY(); 722 723 // default behaviour is pixels 724 String xText = Integer.toString(x); 725 String yText = Integer.toString(y); 726 727 if (locationFormat.equals(LocationFormat.eENGLISH_FEET_INCHES)) { 728 xText = getEnglishFeetInchesText(x); 729 yText = getEnglishFeetInchesText(y); 730 } else if (locationFormat.equals(LocationFormat.eMETRIC_CM)) { 731 xText = getMetricCMText(x); 732 yText = getMetricCMText(y); 733 } 734 xLabel.setText(xText); 735 yLabel.setText(yText); 736 lastLocation = p; 737 } 738 739 private String getEnglishFeetInchesText(int v) { 740 String result = ""; 741 742 int denom = 16; // 16 pixels per inch 743 int ipf = 12; // 12 inches per foot 744 745 int feet = v / (ipf * denom); 746 int inches = (v / denom) % ipf; 747 748 int numer = v % denom; 749 int gcd = MathUtil.gcd(numer, denom); 750 751 numer /= gcd; 752 denom /= gcd; 753 754 if (feet > 0) { 755 result = String.format("%d'", feet); 756 } 757 758 boolean inchesFlag = false; 759 if ((v == 0) || (inches > 0)) { 760 result += String.format(" %d", inches); 761 inchesFlag = true; 762 } 763 764 if (numer > 0) { 765 result += String.format(" %d/%d", numer, denom); 766 inchesFlag = true; 767 } 768 if (inchesFlag) { 769 result += "\""; 770 } 771 772 return result; 773 } 774 775 private String getMetricCMText(int v) { 776 return String.format("%d.%d cm", v / 10, v % 10); 777 } 778 779 /** 780 * layout the components in this panel 781 */ 782 protected void layoutComponents() { 783 log.error("layoutComponents called in LayoutEditorToolBarPanel base class"); 784 } 785 786 final Map<JRadioButton, String> quickKeyMap = new LinkedHashMap<JRadioButton, String>() { 787 { // NOTE: These are in the order that the space bar will select thru 788 put(turnoutRHButton, Bundle.getMessage("TurnoutRH_QuickKeys")); 789 put(turnoutLHButton, Bundle.getMessage("TurnoutLH_QuickKeys")); 790 put(turnoutWYEButton, Bundle.getMessage("TurnoutWYE_QuickKeys")); 791 put(doubleXoverButton, Bundle.getMessage("DoubleXover_QuickKeys")); 792 put(rhXoverButton, Bundle.getMessage("RHXover_QuickKeys")); 793 put(lhXoverButton, Bundle.getMessage("LHXover_QuickKeys")); 794 put(layoutSingleSlipButton, Bundle.getMessage("LayoutSingleSlip_QuickKeys")); 795 put(layoutDoubleSlipButton, Bundle.getMessage("LayoutDoubleSlip_QuickKeys")); 796 put(levelXingButton, Bundle.getMessage("LevelXing_QuickKeys")); 797 put(trackButton, Bundle.getMessage("TrackSegment_QuickKeys")); 798 put(endBumperButton, Bundle.getMessage("EndBumper_QuickKeys")); 799 put(anchorButton, Bundle.getMessage("Anchor_QuickKeys")); 800 put(edgeButton, Bundle.getMessage("Edge_QuickKeys")); 801 put(textLabelButton, Bundle.getMessage("TextLabel_QuickKeys")); 802 put(memoryButton, Bundle.getMessage("Memory_QuickKeys")); 803 put(globalVariableButton, Bundle.getMessage("GlobalVariable_QuickKeys")); 804 put(blockContentsButton, Bundle.getMessage("BlockContents_QuickKeys")); 805 put(multiSensorButton, Bundle.getMessage("MultiSensor_QuickKeys")); 806 put(sensorButton, Bundle.getMessage("Sensor_QuickKeys")); 807 put(turnoutButton, Bundle.getMessage("Turnout_QuickKeys")); 808 put(signalMastButton, Bundle.getMessage("SignalMast_QuickKeys")); 809 put(signalButton, Bundle.getMessage("Signal_QuickKeys")); 810 put(iconLabelButton, Bundle.getMessage("IconLabel_QuickKeys")); 811 put(logixngButton, Bundle.getMessage("LogixNGIcon_QuickKeys")); 812 put(audioButton, Bundle.getMessage("AudioIcon_QuickKeys")); 813 put(shapeButton, Bundle.getMessage("Shape_QuickKeys")); 814 } 815 }; 816 817 public void keyPressed(@Nonnull KeyEvent event) { 818 if (layoutEditor.isEditable()) { 819 if (!event.isMetaDown() && !event.isAltDown() && !event.isControlDown()) { 820 if (event.getID() == KEY_PRESSED) { 821 char keyChar = event.getKeyChar(); 822 String keyString = String.valueOf(keyChar); 823 log.trace("KeyEvent.getKeyChar() == {}", KeyEvent.getKeyText(keyChar)); 824 825 // find last radio button 826 JRadioButton lastRadioButton = null; 827 for (Map.Entry<JRadioButton, String> entry : quickKeyMap.entrySet()) { 828 JRadioButton thisRadioButton = entry.getKey(); 829 if (thisRadioButton.isSelected()) { 830 lastRadioButton = thisRadioButton; 831 log.trace("lastRadioButton is {}", lastRadioButton.getText()); 832 break; 833 } 834 } 835 836 JRadioButton firstRadioButton = null; // the first one that matches 837 JRadioButton nextRadioButton = null; // the next one to select 838 boolean foundLast = false; 839 for (Map.Entry<JRadioButton, String> entry : quickKeyMap.entrySet()) { 840 String quickKeys = entry.getValue(); 841 if (keyString.equals(" ") || StringUtils.containsAny(keyString, quickKeys)) { // found keyString 842 JRadioButton thisRadioButton = entry.getKey(); 843 log.trace("Matched keyString to {}", thisRadioButton.getText()); 844 if (foundLast) { 845 nextRadioButton = thisRadioButton; 846 break; 847 } else if (lastRadioButton == thisRadioButton) { 848 foundLast = true; 849 } else if (firstRadioButton == null) { 850 firstRadioButton = thisRadioButton; 851 } 852 } 853 } 854 // if we didn't find the next one... 855 if (nextRadioButton == null) { 856 // ...then use the first one 857 nextRadioButton = firstRadioButton; 858 } 859 // if we found one... 860 if (nextRadioButton != null) { 861 // ...then select it 862 nextRadioButton.setSelected(true); 863 } 864 } // if KEY_PRESSED event 865 } // if no modifier keys pressed 866 } // if is in edit mode 867 } 868 869 @Override 870 public void dispose() { 871 if (sensorFrame != null) { 872 sensorFrame.dispose(); 873 sensorFrame = null; 874 } 875 if (turnoutFrame != null) { 876 turnoutFrame.dispose(); 877 turnoutFrame = null; 878 } 879 if (signalFrame != null) { 880 signalFrame.dispose(); 881 signalFrame = null; 882 } 883 if (iconFrame != null) { 884 iconFrame.dispose(); 885 iconFrame = null; 886 } 887 if (logixngFrame != null) { 888 logixngFrame.dispose(); 889 logixngFrame = null; 890 } 891 if (audioFrame != null) { 892 audioFrame.dispose(); 893 audioFrame = null; 894 } 895 } 896 897 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorToolBarPanel.class); 898 899}