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