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