001package jmri.jmrit.display.controlPanelEditor; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Dimension; 006import java.awt.FlowLayout; 007import java.awt.Rectangle; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.util.ArrayList; 011import java.util.HashMap; 012import java.util.Iterator; 013import java.util.List; 014import java.util.Map; 015import java.util.SortedSet; 016 017import javax.annotation.Nonnull; 018import javax.swing.*; 019import javax.swing.event.ListSelectionListener; 020import javax.swing.event.ListSelectionEvent; 021 022import jmri.InstanceManager; 023import jmri.NamedBean; 024import jmri.NamedBeanHandle; 025import jmri.NamedBeanHandleManager; 026import jmri.SignalHeadManager; 027import jmri.SignalMastManager; 028import jmri.jmrit.catalog.NamedIcon; 029import jmri.jmrit.display.Editor.TargetPane; 030import jmri.jmrit.display.palette.ItemPalette; 031import jmri.jmrit.display.IndicatorTrack; 032import jmri.jmrit.display.Positionable; 033import jmri.jmrit.display.PositionableLabel; 034import jmri.jmrit.display.PositionableIcon; 035import jmri.jmrit.display.SignalHeadIcon; 036import jmri.jmrit.display.SignalMastIcon; 037import jmri.jmrit.display.TurnoutIcon; 038import jmri.jmrit.logix.OBlock; 039import jmri.jmrit.logix.OBlockManager; 040import jmri.jmrit.logix.Portal; 041import jmri.jmrit.logix.PortalManager; 042import jmri.jmrit.logix.WarrantTableAction; 043import jmri.jmrit.picker.PickListModel; 044import jmri.util.HelpUtil; 045import jmri.util.swing.JmriJOptionPane; 046import jmri.util.swing.JmriMouseEvent; 047 048/** 049 * ControlPanelEditor CircuitBuilder tools. 050 * 051 * @author Pete Cressman Copyright: Copyright (c) 2011 052 */ 053public class CircuitBuilder { 054 055 static int STRUT_SIZE = 10; 056 057 private JMenu _circuitMenu; 058 private JMenu _todoMenu; // error checking items 059 060 // map OBlock to List of icons (track, portal, signal that represent it 061 private final HashMap<OBlock, ArrayList<Positionable>> _circuitMap = new HashMap<>(); 062 063 // list of track icons not belonging to an OBlock 064 private final ArrayList<IndicatorTrack> _darkTrack = new ArrayList<>(); 065 066 // list of OBlocks with no track icons 067 private final ArrayList<OBlock> _bareBlock = new ArrayList<>(); 068 069 // list of OBlocks with 0 length 070 private final ArrayList<OBlock> _zeroBlock = new ArrayList<>(); 071 072 // list of circuit icons needing converting 073 private final ArrayList<Positionable> _unconvertedTrack = new ArrayList<>(); 074 075 // list of OBlocks whose track icons need converting 076 private final ArrayList<OBlock> _convertBlock = new ArrayList<>(); 077 078 // list of Portals with no PortalIcon 079 private final ArrayList<Portal> _noPortalIcon = new ArrayList<>(); 080 081 // list of OBlocks with no Portal 082 private final ArrayList<OBlock> _noPortals = new ArrayList<>(); 083 084 // list of OBlocks with no Path 085 private final ArrayList<OBlock> _noPaths = new ArrayList<>(); 086 087 // list of misplaced PortalIcons 088 private final ArrayList<PortalIcon> _misplacedPortalIcon = new ArrayList<>(); 089 090 // map of PortalIcons by portal. A Portal may have 2 icons to connect non-adjacent blocks 091 private final HashMap<Portal, ArrayList<PortalIcon>> _portalIconMap = new HashMap<>(); 092 093 // map of SignalMastIcons or SignalHeadicons by Signal. A Signal may have several icons 094 private final HashMap<NamedBean, ArrayList<PositionableIcon>> _signalIconMap = new HashMap<>(); 095 096 // list of SignalMastIcon and SignalHeadicon not protecting a block 097 private final ArrayList<PositionableIcon> _unattachedMastIcon = new ArrayList<>(); 098 099 // list of SignalMast and SignalHead not protecting a block 100 private final ArrayList<NamedBean> _unprotectingMast = new ArrayList<>(); 101 102 // map SignalMasts and SignalHeads to the Portal where it is configured 103 private final HashMap<NamedBean, Portal> _signalMap = new HashMap<>(); 104 105 private boolean _hasIndicatorTrackIcons; 106 private boolean _hasPortalIcons; 107 private boolean _hasMastIcons; 108 109 // OBlock list to open edit frames 110 private PickListModel<OBlock> _oblockModel; 111 private JTable _blockTable; 112 jmri.util.JmriJFrame _cbFrame; 113 114 // "Editing Frames" - Called from menu in Main Frame 115 private EditFrame _editFrame; 116 117 private OBlock _currentBlock; 118 private JDialog _dialog; 119 protected ControlPanelEditor _editor; 120 private Positionable _selection; 121 122 public final static Color _editGroupColor = new Color(100, 200, 255); 123 public final static Color _pathColor = Color.green; 124 public final static Color _highlightColor = new Color(255, 150, 220); 125 126 /** 127 * *************************************************************** 128 */ 129 public CircuitBuilder() { 130 log.error("CircuitBuilder ctor requires an Editor class"); 131 } 132 133 public CircuitBuilder(ControlPanelEditor ed) { 134 _editor = ed; 135 _oblockModel = PickListModel.oBlockPickModelInstance(); 136 _blockTable = _oblockModel.makePickTable(); 137 138 } 139 140 /** 141 * Makes menu for ControlPanelEditor. Called by ControlPanelEditor at init 142 * before contents have been loaded. 143 * 144 * @return the menu, created if needed 145 */ 146 protected JMenu makeMenu() { 147 _circuitMenu = new JMenu(Bundle.getMessage("CircuitBuilder")); 148 OBlockManager manager = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class); 149 SortedSet<OBlock> oblocks = manager.getNamedBeanSet(); 150 for (OBlock block : oblocks) { 151 _circuitMap.put(block, new ArrayList<>()); 152 } 153 checkCircuits(); // need content for this 154 int num = Math.min(manager.getObjectCount(), 20) + 5; 155 _blockTable.setPreferredScrollableViewportSize(new java.awt.Dimension(300, _blockTable.getRowHeight() * num)); 156 return _circuitMenu; 157 } 158 159 protected void openCBWindow() { 160 if (_cbFrame != null) { 161 _cbFrame.toFront(); 162 } else { 163 _cbFrame = new CBFrame(Bundle.getMessage("CircuitBuilder")); 164 } 165 } 166 167 private void makeNoOBlockMenu() { 168 JMenuItem circuitItem = new JMenuItem(Bundle.getMessage("newCircuitItem")); 169 _circuitMenu.add(circuitItem); 170 circuitItem.addActionListener((ActionEvent event) -> newCircuit()); 171 _circuitMenu.add(new JMenuItem(Bundle.getMessage("noCircuitsItem"))); 172 JMenuItem helpItem = new JMenuItem(Bundle.getMessage("AboutCircuitBuilder")); 173 HelpUtil.enableHelpOnButton(helpItem, "package.jmri.jmrit.display.CircuitBuilder"); 174 _circuitMenu.add(helpItem); 175 176 } 177 private void makeCircuitMenu() { 178 JMenuItem editItem = new JMenuItem(Bundle.getMessage("newCircuitItem")); 179 _circuitMenu.add(editItem); 180 editItem.addActionListener((ActionEvent event) -> { 181 closeCBWindow(); 182 newCircuit(); 183 }); 184 editItem = new JMenuItem(Bundle.getMessage("editCircuitItem")); 185 _circuitMenu.add(editItem); 186 editItem.addActionListener((ActionEvent event) -> { 187 closeCBWindow(); 188 editCircuit("editCircuitItem", true); 189 }); 190 editItem = new JMenuItem(Bundle.getMessage("editPortalsItem")); 191 _circuitMenu.add(editItem); 192 editItem.addActionListener((ActionEvent event) -> { 193 closeCBWindow(); 194 editPortals("editPortalsItem", true); 195 }); 196 editItem = new JMenuItem(Bundle.getMessage("editCircuitPathsItem")); 197 _circuitMenu.add(editItem); 198 editItem.addActionListener((ActionEvent event) -> { 199 closeCBWindow(); 200 editCircuitPaths("editCircuitPathsItem", true); 201 }); 202 editItem = new JMenuItem(Bundle.getMessage("editDirectionItem")); 203 _circuitMenu.add(editItem); 204 editItem.addActionListener((ActionEvent event) -> { 205 closeCBWindow(); 206 editPortalDirection("editDirectionItem", true); 207 }); 208 editItem = new JMenuItem(Bundle.getMessage("editSignalItem")); 209 _circuitMenu.add(editItem); 210 editItem.addActionListener((ActionEvent event) -> { 211 closeCBWindow(); 212 editSignalFrame("editSignalItem", true); 213 }); 214 _todoMenu = new JMenu(Bundle.getMessage("circuitErrorsItem")); 215 _circuitMenu.add(_todoMenu); 216 217 editItem = makePortalIconMenu(); 218 _circuitMenu.add(editItem); 219 220 JMenuItem helpItem = new JMenuItem(Bundle.getMessage("AboutCircuitBuilder")); 221 HelpUtil.enableHelpOnButton(helpItem, "package.jmri.jmrit.display.CircuitBuilder"); 222 _circuitMenu.add(helpItem); 223 makeToDoMenu(); 224 } 225 226 /** 227 * Add icon 'pos' to circuit 'block' 228 */ 229 private void addIcon(OBlock block, Positionable pos) { 230 List<Positionable> icons = getCircuitIcons(block); 231 if (pos != null) { 232 if (!icons.contains(pos)) { 233 icons.add(pos); 234 } 235 } 236 _darkTrack.remove(pos); 237 // if (log.isDebugEnabled()) log.debug("addIcon: block "+block.getDisplayName()+" has "+icons.size()+" icons."); 238 } 239 240 private JMenu makePortalIconMenu() { 241 JMenu familyMenu = new JMenu(Bundle.getMessage("portalIconSet")); 242 ButtonGroup familyGroup = new ButtonGroup(); 243 ActionListener portalIconAction = (ActionEvent event) -> { 244 String family = event.getActionCommand(); 245 if (!family.equals(_editor.getPortalIconFamily())) { 246 closeCBWindow(); 247 _editor.setPortalIconFamily(family); 248 for (Positionable pos : _editor.getContents()) { 249 if (pos instanceof PortalIcon) { 250 PortalIcon pIcon = (PortalIcon) pos; 251 pIcon.setMap(_editor.getPortalIconMap()); 252 } 253 } 254 } 255 }; 256 HashMap<String, HashMap<String, NamedIcon>> familyMap = ItemPalette.getFamilyMaps("Portal"); 257 for (String family : familyMap.keySet()) { 258 JCheckBoxMenuItem mi = new JCheckBoxMenuItem(family); 259 familyGroup.add(mi); 260 if (_editor.getPortalIconFamily().equals(family)) { 261 mi.setSelected(true); 262 } 263 mi.setActionCommand(family); 264 mi.addActionListener(portalIconAction); 265 familyMenu.add(mi); 266 } 267 return familyMenu; 268 } 269 270 271 // Rebuild after any edit change 272 private void makeToDoMenu() { 273 if (_todoMenu == null) { 274 _todoMenu = new JMenu(Bundle.getMessage("circuitErrorsItem")); 275 _circuitMenu.add(_todoMenu); 276 } else { 277 _todoMenu.removeAll(); 278 } 279 280 JMenu blockNeeds = new JMenu(Bundle.getMessage("blockNeedsIconsItem")); 281 ActionListener editCircuitAction = (ActionEvent event) -> { 282 String sysName = event.getActionCommand(); 283 editCircuitError(sysName); 284 }; 285 if (_bareBlock.size() > 0) { 286 for (OBlock block : _bareBlock) { 287 JMenuItem mi = new JMenuItem(java.text.MessageFormat.format(Bundle.getMessage("OpenCircuitItem"), block.getDisplayName())); 288 mi.setActionCommand(block.getSystemName()); 289 mi.addActionListener(editCircuitAction); 290 blockNeeds.add(mi); 291 } 292 } else { 293 blockNeeds.add(new JMenuItem(Bundle.getMessage("circuitsHaveIcons"))); 294 } 295 _todoMenu.add(blockNeeds); // #1 296 297 blockNeeds = new JMenu(Bundle.getMessage("blocksNeedConversionItem")); 298 if (_convertBlock.size() > 0) { 299 for (OBlock block : _convertBlock) { 300 JMenuItem mi = new JMenuItem(java.text.MessageFormat.format(Bundle.getMessage("OpenCircuitItem"), block.getDisplayName())); 301 mi.setActionCommand(block.getSystemName()); 302 mi.addActionListener(editCircuitAction); 303 blockNeeds.add(mi); 304 } 305 } else { 306 blockNeeds.add(new JMenuItem(Bundle.getMessage("circuitIconsConverted"))); 307 } 308 _todoMenu.add(blockNeeds); // #2 309 310 JMenuItem iconNeeds = new JMenuItem(Bundle.getMessage("iconsNeedConversionItem")); 311 if (_unconvertedTrack.size() > 0) { 312 iconNeeds.addActionListener((ActionEvent event) -> { 313 if (editingOK()) { 314 hidePortalIcons(true); 315 ArrayList<Positionable> group = new ArrayList<>(); 316 for (Positionable positionable : _unconvertedTrack) { 317 group.add(positionable); 318 } 319 _editor.setSelectionGroup(group); 320 } 321 }); 322 } else { 323 iconNeeds = new JMenuItem(Bundle.getMessage("noneNeedConversion")); 324 } 325 _todoMenu.add(iconNeeds); // #3 326 327 if (_darkTrack.size() > 0) { 328 iconNeeds = new JMenuItem(Bundle.getMessage("iconsNeedsBlocksItem")); 329 iconNeeds.addActionListener((ActionEvent event) -> { 330 if (editingOK()) { 331 hidePortalIcons(true); 332 ArrayList<Positionable> group = new ArrayList<>(); 333 for (Positionable positionable : _darkTrack) { 334 group.add(positionable); 335 } 336 _editor.setSelectionGroup(group); 337 } 338 }); 339 } else { 340 if (_hasIndicatorTrackIcons) { 341 iconNeeds = new JMenuItem(Bundle.getMessage("IconsHaveCircuits")); 342 } else { 343 iconNeeds = new JMenuItem(Bundle.getMessage("noIndicatorTrackIcon")); 344 } 345 } 346 _todoMenu.add(iconNeeds); // #4 347 348 if (!_noPortals.isEmpty()) { 349 blockNeeds = new JMenu(Bundle.getMessage("blockNeedsPortals")); 350 ActionListener editPortalAction = (ActionEvent event) -> { 351 String sysName = event.getActionCommand(); 352 _currentBlock = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(sysName); 353 editPortals(null, false); 354 }; 355 for (OBlock block : _noPortals) { 356 JMenuItem mi = new JMenuItem(java.text.MessageFormat.format( 357 Bundle.getMessage("OpenPortalTitle"), block.getDisplayName())); 358 mi.setActionCommand(block.getSystemName()); 359 mi.addActionListener(editPortalAction); 360 blockNeeds.add(mi); 361 } 362 } else if (_noPaths.size() > 0) { 363 blockNeeds = new JMenu(Bundle.getMessage("blockNeedsPaths")); 364 ActionListener editPortalAction = (ActionEvent event) -> { 365 String sysName = event.getActionCommand(); 366 _currentBlock = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(sysName); 367 editCircuitPaths(null, false); 368 }; 369 for (OBlock block : _noPaths) { 370 JMenuItem mi = new JMenuItem(java.text.MessageFormat.format( 371 Bundle.getMessage("OpenPathTitle"), block.getDisplayName())); 372 mi.setActionCommand(block.getSystemName()); 373 mi.addActionListener(editPortalAction); 374 blockNeeds.add(mi); 375 } 376 } else { 377 blockNeeds = new JMenu(Bundle.getMessage("circuitsHavePortalsPaths")); 378 } 379 _todoMenu.add(blockNeeds); // #5 380 381 if (_zeroBlock.isEmpty()) { 382 blockNeeds = new JMenu(Bundle.getMessage("blocksHaveLength")); 383 } else { 384 blockNeeds = new JMenu(Bundle.getMessage("blockNeedLength")); 385 for (OBlock block : _zeroBlock) { 386 JMenuItem mi = new JMenuItem(java.text.MessageFormat.format(Bundle.getMessage("OpenCircuitItem"), block.getDisplayName())); 387 mi.setActionCommand(block.getSystemName()); 388 mi.addActionListener(editCircuitAction); 389 blockNeeds.add(mi); 390 } 391 } 392 _todoMenu.add(blockNeeds); // #6 393 394 blockNeeds = new JMenu(Bundle.getMessage("portalsMisplaced")); 395 if (_misplacedPortalIcon.size() > 0) { 396 for (PortalIcon icon : _misplacedPortalIcon) { 397 Portal portal = icon.getPortal(); 398 OBlock fromBlock = portal.getFromBlock(); 399 OBlock toBlock = portal.getToBlock(); 400 if (fromBlock != null) { 401 JMenuItem mi = new JMenuItem(Bundle.getMessage("OpenPortalTitle", fromBlock.getDisplayName())); 402 mi.addActionListener((ActionEvent event) -> editPortalError(fromBlock, portal, icon)); 403 blockNeeds.add(mi); 404 } else if (toBlock != null) { 405 JMenuItem mi = new JMenuItem(Bundle.getMessage("OpenPortalTitle", toBlock.getDisplayName())); 406 mi.addActionListener((ActionEvent event) -> editPortalError(toBlock, portal, icon)); 407 blockNeeds.add(mi); 408 } 409 } 410 } else { 411 if (_hasPortalIcons) { 412 blockNeeds.add(new JMenuItem(Bundle.getMessage("portalsInPlace"))); 413 } else { 414 blockNeeds.add(new JMenuItem(Bundle.getMessage("NoPortalIcons"))); 415 } 416 } 417 _todoMenu.add(blockNeeds); //#7 418 419 if (_misplacedPortalIcon.size() > 0) { 420 iconNeeds = new JMenuItem(Bundle.getMessage("iconsNeedPositioning")); 421 iconNeeds.addActionListener((ActionEvent event) -> { 422 if (editingOK()) { 423 ArrayList<Positionable> group = new ArrayList<>(); 424 for (PortalIcon pi : _misplacedPortalIcon) { 425 group.add(pi); 426 pi.setStatus(PortalIcon.VISIBLE); 427 } 428 _editor.setSelectionGroup(group); 429 } 430 }); 431 } else { 432 if (_hasPortalIcons) { 433 iconNeeds = new JMenuItem(Bundle.getMessage("portalsInPlace")); 434 } else { 435 iconNeeds = new JMenuItem(Bundle.getMessage("NoPortalIcons")); 436 } 437 } 438 _todoMenu.add(iconNeeds); // #8 439 440 JMenu mastNeeds = new JMenu(Bundle.getMessage("UnprotectingMasts")); 441 if (!_unprotectingMast.isEmpty()) { 442// mastNeeds.addActionListener((ActionEvent event) -> { 443 for (NamedBean sig : _unprotectingMast) { 444 JMenuItem mi = new JMenuItem(sig.getDisplayName()); 445 mastNeeds.add(mi); 446 } 447// }); 448 } else { 449 mastNeeds.add(new JMenuItem(Bundle.getMessage("mastsInPlace"))); 450 } 451 _todoMenu.add(mastNeeds); // #9 452 453 if (_unattachedMastIcon.size() > 0) { 454 iconNeeds = new JMenuItem(Bundle.getMessage("UnattachedMasts")); 455 iconNeeds.addActionListener((ActionEvent event) -> { 456// if (editingOK()) { 457 ArrayList<Positionable> group = new ArrayList<>(); 458 for (PositionableIcon pi : _unattachedMastIcon) { 459 group.add(pi); 460 } 461 _editor.setSelectionGroup(group); 462// } 463 }); 464 } else { 465 if (_hasMastIcons) { 466 iconNeeds = new JMenuItem(Bundle.getMessage("mastsInPlace")); 467 } else { 468 iconNeeds = new JMenuItem(Bundle.getMessage("NoMastIcons")); 469 } 470 } 471 _todoMenu.add(iconNeeds); // #10 472 473 blockNeeds = new JMenu(Bundle.getMessage("portalNeedsIcon")); 474 ActionListener editPortalAction = (ActionEvent event) -> { 475 String portalName = event.getActionCommand(); 476 editPortalError(portalName); 477 }; 478 if (_noPortalIcon.size() > 0) { 479 for (Portal portal : _noPortalIcon) { 480 JMenuItem mi = new JMenuItem(portal.toString()); 481 mi.setActionCommand(portal.getName()); 482 mi.addActionListener(editPortalAction); 483 blockNeeds.add(mi); 484 } 485 } else { 486 blockNeeds.add(new JMenuItem(Bundle.getMessage("portalsHaveIcons"))); 487 } 488 _todoMenu.add(blockNeeds); // #11 489 490 JMenuItem pError = new JMenuItem(Bundle.getMessage("CheckPortalPaths")); 491 pError.addActionListener((ActionEvent event) -> { 492 if (!WarrantTableAction.getDefault().errorCheck()) { 493 javax.swing.JFrame frame; 494 if (_editFrame != null) { 495 frame = _editFrame; 496 } else if (_cbFrame != null){ 497 frame = _cbFrame; 498 } else { 499 frame = _editor; 500 } 501 JmriJOptionPane.showMessageDialog(frame, 502 Bundle.getMessage("blocksEtcOK"), Bundle.getMessage("ButtonOK"), 503 JmriJOptionPane.INFORMATION_MESSAGE); 504 } 505 }); 506 _todoMenu.add(pError); // #12 507 508 } 509 510 // used for testing only 511 protected EditFrame getEditFrame() { 512 return _editFrame; 513 } 514 515 /** 516 * ************** Set up editing Frames **************** 517 */ 518 protected void newCircuit() { 519 if (editingOK()) { 520 _blockTable.clearSelection(); 521 setUpEditCircuit(); 522 _editFrame = new EditCircuitFrame(Bundle.getMessage("newCircuitItem"), this, null); 523 } 524 } 525 526 protected void editCircuit(String title, boolean fromMenu) { 527 if (editingOK()) { 528 if (fromMenu) { 529 editCircuitDialog(title); 530 } 531 if (_currentBlock != null) { 532 setUpEditCircuit(); 533 _editFrame = new EditCircuitFrame(Bundle.getMessage("OpenCircuitItem"), this, _currentBlock); 534 } else if (!fromMenu) { 535 selectPrompt(); 536 } 537 } 538 } 539 540 private void setUpEditCircuit() { 541 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, false)); 542 _editor.disableMenus(); 543 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 544 targetPane.setSelectGroupColor(_editGroupColor); 545 targetPane.setHighlightColor(_highlightColor); 546 } 547 548 protected void editCircuitError(String sysName) { 549 hidePortalIcons(true); 550 if (editingOK()) { 551 _currentBlock = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getBySystemName(sysName); 552 if (_currentBlock != null) { 553 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, false)); 554 _editor.disableMenus(); 555 _editFrame = new EditCircuitFrame(Bundle.getMessage("OpenCircuitItem"), this, _currentBlock); 556 } 557 } 558 } 559 560 protected void editPortals(String title, boolean fromMenu) { 561 if (editingOK()) { 562 if (fromMenu) { 563 editCircuitDialog(title); 564 } 565 if (_currentBlock != null) { 566 // check icons to be indicator type 567 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, true)); 568 _editor.disableMenus(); 569 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 570 targetPane.setSelectGroupColor(_editGroupColor); 571 targetPane.setHighlightColor(_highlightColor); 572 setPortalsPositionable(_currentBlock, true); 573 _editFrame = new EditPortalFrame(Bundle.getMessage("OpenPortalTitle"), this, _currentBlock); 574 _editFrame.canEdit(); // will close _editFrame if editing cannot be done 575 } else if (!fromMenu) { 576 selectPrompt(); 577 } 578 } 579 } 580 581 protected void editPortalError(String name) { 582 if (editingOK()) { 583 Portal portal = InstanceManager.getDefault(jmri.jmrit.logix.PortalManager.class).getPortal(name); 584 _currentBlock = portal.getFromBlock(); 585 if (_currentBlock == null) { 586 _currentBlock = portal.getToBlock(); 587 } 588 editPortals(null, false); 589 } 590 } 591 592 protected void editPortalError(OBlock block, Portal portal, PortalIcon icon) { 593 if (editingOK()) { 594 _currentBlock = block; 595 if (_currentBlock != null) { 596 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, true)); 597 _editor.disableMenus(); 598 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 599 targetPane.setSelectGroupColor(_editGroupColor); 600 targetPane.setHighlightColor(_highlightColor); 601 setPortalsPositionable(_currentBlock, true); 602 _editFrame = new EditPortalFrame(Bundle.getMessage("OpenPortalTitle"), this, 603 _currentBlock, portal, icon); 604 } 605 } 606 } 607 608 protected void editPortalDirection(String title, boolean fromMenu) { 609 if (editingOK()) { 610 if (fromMenu) { 611 editCircuitDialog(title); 612 } 613 if (_currentBlock != null) { 614 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, true)); 615 _editor.disableMenus(); 616 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 617 targetPane.setSelectGroupColor(_editGroupColor); 618 targetPane.setHighlightColor(_highlightColor); 619 setPortalsPositionable(_currentBlock, true); 620 _editFrame = new EditPortalDirection(Bundle.getMessage("OpenDirectionTitle"), this, _currentBlock); 621 _editFrame.canEdit(); // will close _editFrame if editing cannot be done 622 } else if (!fromMenu) { 623 selectPrompt(); 624 } 625 } 626 } 627 628 protected void editSignalFrame(String title, boolean fromMenu) { 629 if (editingOK()) { 630 if (fromMenu) { 631 editCircuitDialog(title); 632 } 633 if (_currentBlock != null) { 634 // check icons to be indicator type 635 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, true)); 636 _editor.disableMenus(); 637 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 638 targetPane.setSelectGroupColor(_editGroupColor); 639 targetPane.setHighlightColor(_highlightColor); 640 _editFrame = new EditSignalFrame(Bundle.getMessage("OpenSignalsTitle"), this, _currentBlock); 641 _editFrame.canEdit(); // will close _editFrame if editing cannot be done 642 } else if (!fromMenu) { 643 selectPrompt(); 644 } 645 } 646 } 647 648 protected void editCircuitPaths(String title, boolean fromMenu) { 649 if (editingOK()) { 650 if (fromMenu) { 651 editCircuitDialog(title); 652 } 653 if (_currentBlock != null) { 654 // check icons to be indicator type 655 // must have converted icons for paths 656 _editor.setSelectionGroup(makeSelectionGroup(_currentBlock, true)); 657 // A temporary path "TEST_PATH" is used to display the icons representing a path 658 _currentBlock.allocatePath(EditCircuitPaths.TEST_PATH); 659 _editor.disableMenus(); 660 TargetPane targetPane = (TargetPane) _editor.getTargetPanel(); 661 targetPane.setSelectGroupColor(_editGroupColor); 662 targetPane.setHighlightColor(_editGroupColor); 663 _currentBlock.setState(OBlock.UNOCCUPIED); 664 _editFrame = new EditCircuitPaths(Bundle.getMessage("OpenPathTitle"), this, _currentBlock); 665 _editFrame.canEdit(); // will close _editFrame if editing cannot be done 666 } else if (!fromMenu) { 667 selectPrompt(); 668 } 669 } 670 } 671 672 protected void setCurrentBlock(OBlock b) { 673 _currentBlock = b; 674 } 675 676 protected void hidePortalIcons(boolean hideAll) { 677 if (_editFrame != null) { 678 _editFrame.clearListSelection(); 679 } else { 680 for (ArrayList<PortalIcon> array : _portalIconMap.values()) { 681 for (PortalIcon pi : array) { 682 if (hideAll || pi.getStatus().equals(PortalIcon.VISIBLE)) { 683 // don't hide warrant arrows 684 pi.setStatus(PortalIcon.HIDDEN); 685 } 686 } 687 } 688 } 689 } 690 691 private boolean editingOK() { 692 if (_editFrame != null) { 693 // Already editing a circuit, ask for completion of that edit 694 JmriJOptionPane.showMessageDialog(_editFrame, 695 Bundle.getMessage("AlreadyEditing"), Bundle.getMessage("ErrorTitle"), 696 JmriJOptionPane.ERROR_MESSAGE); 697 _editFrame.toFront(); 698 _editFrame.setVisible(true); 699 return false; 700 } 701 OBlockManager manager = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class); 702 if (manager.getObjectCount() == 0) { 703 return true; 704 } else { 705 for (OBlock block : manager.getNamedBeanSet()) { 706 if ((block.getState() & OBlock.ALLOCATED) != 0) { 707 JmriJOptionPane.showMessageDialog(_editor, Bundle.getMessage("cannotEditCB", block.getWarrant().getDisplayName()), 708 Bundle.getMessage("editCiruit"), JmriJOptionPane.INFORMATION_MESSAGE); 709 return false; 710 } 711 } 712 } 713 checkCircuits(); 714 return true; 715 } 716 717 /** 718 * Edit existing OBlock. Used by edit to set up _editCircuitFrame. 719 * Sets _currentBlock to chosen OBlock or null if none selected. 720 */ 721 private void editCircuitDialog(String title) { 722 _dialog = new JDialog(_editor, Bundle.getMessage(title), true); 723 JPanel panel = new JPanel(); 724 panel.setLayout(new BorderLayout(10, 10)); 725 JPanel mainPanel = new JPanel(); 726 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); 727 728 mainPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 729 JPanel p = new JPanel(); 730 p.add(new JLabel(Bundle.getMessage("selectOBlock"))); 731 p.setMaximumSize(new Dimension(300, p.getPreferredSize().height)); 732 mainPanel.add(p); 733 734 mainPanel.add(new JScrollPane(_blockTable)); 735 mainPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 736 mainPanel.add(makeDoneButtonPanel()); 737 panel.add(mainPanel); 738 _dialog.getContentPane().add(panel); 739 _dialog.setLocation(_editor.getLocation().x + 100, _editor.getLocation().y + 100); 740 _dialog.pack(); 741 _dialog.setVisible(true); 742 } 743 744 private JPanel makeDoneButtonPanel() { 745 JPanel buttonPanel = new JPanel(); 746 JPanel panel0 = new JPanel(); 747 panel0.setLayout(new FlowLayout()); 748 JButton doneButton; 749 doneButton = new JButton(Bundle.getMessage("ButtonOpenCircuit")); 750 doneButton.addActionListener((ActionEvent a) -> { 751 if (doOpenAction()) { 752 _dialog.dispose(); 753 } 754 }); 755 panel0.add(doneButton); 756 757 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 758 cancelButton.addActionListener((ActionEvent a) -> _dialog.dispose()); 759 panel0.add(cancelButton); 760 buttonPanel.add(panel0); 761 buttonPanel.setMaximumSize(new Dimension(300, buttonPanel.getPreferredSize().height)); 762 return buttonPanel; 763 } 764 765 private boolean doOpenAction() { 766 int row = _blockTable.getSelectedRow(); 767 if (row >= 0) { 768 row = _blockTable.convertRowIndexToModel(row); 769 _currentBlock = _oblockModel.getBeanAt(row); 770 return true; 771 } 772 _currentBlock = null; 773 selectPrompt(); 774 return false; 775 } 776 private void selectPrompt() { 777 JmriJOptionPane.showMessageDialog(_editor, Bundle.getMessage("selectOBlock"), 778 Bundle.getMessage("NeedDataTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 779 } 780 781 /* 782 * ************************ end setup frames ***************************** 783 */ 784 private void setPortalsPositionable(OBlock block, boolean set) { 785 for (Positionable p : getCircuitIcons(block)) { 786 if (p instanceof PortalIcon) { 787 p.setPositionable(set); 788 } 789 } 790 } 791 792 ////////////////////////// Closing Editing Frames ////////////////////////// 793 /** 794 * Edit frame closing, set block's icons to support OBlock's state changes 795 * @param block OBlock to set icon selections into data maps 796 */ 797 protected void setIconGroup(OBlock block) { 798 for (Positionable pos : getCircuitIcons(block)) { 799 if (pos instanceof IndicatorTrack) { 800 ((IndicatorTrack) pos).setOccBlockHandle(null); 801 } 802 } 803 // the selectionGroup for all edit frames is full collection of icons 804 // comprising the block. Gather them and store in the block's hashMap 805 List<Positionable> selections = _editor.getSelectionGroup(); 806 List<Positionable> icons = getCircuitIcons(block); 807 icons.clear(); 808 if (selections != null && !selections.isEmpty()) { 809 NamedBeanHandle<OBlock> handle = 810 InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(block.getSystemName(), block); 811 for (Positionable pos : selections) { 812 if (pos instanceof IndicatorTrack) { 813 ((IndicatorTrack) pos).setOccBlockHandle(handle); 814 } 815 icons.add(pos); 816 } 817 } 818 if (log.isDebugEnabled()) { 819 log.debug("setIconGroup: block \"{}\" has {} icons.", block.getDisplayName(), icons.size()); 820 } 821 } 822 823 protected void closeCircuitBuilder(OBlock block) { 824 _currentBlock = null; 825 _editFrame = null; 826 checkCircuits(); 827 setPortalsPositionable(block, false); 828 hidePortalIcons(true); 829 _editor.resetEditor(); 830 831 } 832 833 /* 834 * ************** end closing frames ******************* 835 */ 836 837 /** 838 * Find the blocks with no icons and the blocks with icons that need 839 * conversion Setup for main Frame - used in both initialization and close 840 * of an editing frame Build Lists that are used to create menu items 841 */ 842 private void checkCircuits() { 843 844 _portalIconMap.clear(); 845 _signalIconMap.clear(); 846 _signalMap.clear(); 847 _unattachedMastIcon.clear(); 848 _darkTrack.clear(); 849 _unconvertedTrack.clear(); 850 _hasIndicatorTrackIcons = false; 851 _hasPortalIcons = false; 852 _hasMastIcons = false; 853 ArrayList<Positionable> removeList = new ArrayList<>(); // avoid comodification 854 PortalManager portalMgr = InstanceManager.getDefault(jmri.jmrit.logix.PortalManager.class); 855 856 for (Positionable pos : _editor.getContents()) { 857 if (pos instanceof IndicatorTrack) { 858 IndicatorTrack iPos = (IndicatorTrack) pos; 859 _hasIndicatorTrackIcons = true; 860 OBlock block = iPos.getOccBlock(); 861 iPos.removePath(EditCircuitPaths.TEST_PATH); 862 if (block != null) { 863 addIcon(block, iPos); 864 } else { 865 _darkTrack.add(iPos); 866 } 867 } else if (pos instanceof PortalIcon) { 868 _hasPortalIcons = true; 869 PortalIcon pIcon = (PortalIcon) pos; 870 Portal portal = pIcon.getPortal(); 871 if (portal == null) { 872 log.error("No Portal for PortalIcon called \"{}\". Discarding icon.", pIcon.getName()); 873 removeList.add(pIcon); 874 } else { 875 List<PortalIcon> piArray = getPortalIcons(portal); 876 piArray.add(pIcon); 877 } 878 } else if (pos instanceof SignalMastIcon) { 879 _hasMastIcons = true; 880 SignalMastIcon sIcon = (SignalMastIcon) pos; 881 NamedBean mast = sIcon.getSignalMast(); 882 if (mast == null) { 883 log.error("No SignalMast for SignalMastIcon called \"{}\".", sIcon.getNameString()); 884 removeList.add(sIcon); 885 } else { 886 List<PositionableIcon> siArray = getSignalIconMap(mast); 887 siArray.add(sIcon); 888 _unattachedMastIcon.add(sIcon); 889 } 890 } else if (pos instanceof SignalHeadIcon) { 891 _hasMastIcons = true; 892 SignalHeadIcon sIcon = (SignalHeadIcon) pos; 893 NamedBean mast = sIcon.getSignalHead(); 894 if (mast == null) { 895 log.error("No SignalHead for SignalHeadIcon called \"{}\".", sIcon.getNameString()); 896 removeList.add(sIcon); 897 } else { 898 List<PositionableIcon> siArray = getSignalIconMap(mast); 899 siArray.add(sIcon); 900 _unattachedMastIcon.add(sIcon); 901 } 902 } else if (isUnconvertedTrack(pos)) { 903 if (!_unconvertedTrack.contains(pos)) { 904 _unconvertedTrack.add(pos); 905 } 906 } 907 } 908 for (Positionable positionable : removeList) { 909 positionable.remove(); 910 } 911 912 _bareBlock.clear(); // blocks with no track icons 913 _zeroBlock.clear(); // blocks with 0 length 914 _convertBlock.clear(); // blocks with at least one unconverted track icon 915 _misplacedPortalIcon.clear(); 916 _noPortalIcon.clear(); 917 _noPortals.clear(); 918 _noPaths.clear(); 919 _unprotectingMast.clear(); 920 // initialize _signalMap 921 Iterator<jmri.SignalMast> iter1 = 922 InstanceManager.getDefault(SignalMastManager.class).getNamedBeanSet().iterator(); 923 while (iter1.hasNext()) { 924 _signalMap.put(iter1.next(), null); 925 } 926 Iterator<jmri.SignalHead> iter2 = 927 InstanceManager.getDefault(SignalHeadManager.class).getNamedBeanSet().iterator(); 928 while (iter2.hasNext()) { 929 _signalMap.put(iter2.next(), null); 930 } 931 932 OBlockManager manager = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class); 933 SortedSet<OBlock> oblocks = manager.getNamedBeanSet(); 934 for (OBlock block : oblocks) { 935 List<Portal> portals = block.getPortals(); 936 if (portals.isEmpty()) { 937 _noPortals.add(block); 938 } else { 939 // first add PortalIcons and SignalIcons to circuitMap 940 for (Portal portal : portals) { 941 List<PortalIcon> piArray = getPortalIcons(portal); 942 for (PortalIcon pi : piArray) { 943 addIcon(block, pi); 944 } 945 NamedBean mast = portal.getSignalProtectingBlock(block); 946 if (mast != null) { 947 List<PositionableIcon> siArray = getSignalIconMap(mast); 948 for (PositionableIcon si : siArray) { 949 addIcon(block, si); 950 _unattachedMastIcon.remove(si); 951 } 952 _signalMap.put(mast, portal); 953 } 954 if (log.isDebugEnabled()) { 955 log.debug("Portal {} in block {} has {} icons", portal.getName(), block.getDisplayName(), piArray.size()); 956 } 957 } 958 } 959 960 List<jmri.Path> paths = block.getPaths(); 961 float blkLen = block.getLengthMm(); 962 if (paths.isEmpty()) { 963 _noPaths.add(block); 964 if (blkLen < .001f) { 965 _zeroBlock.add(block); 966 } 967 } else if (blkLen < .001f) { 968 for (jmri.Path path : paths) { 969 if (path.getLengthMm() < .001f) { 970 _zeroBlock.add(block); 971 break; 972 } // blkLen == 0 OK, if all paths have length 973 } 974 } 975 976 List<Positionable> icons = getCircuitIcons(block); 977 if (log.isDebugEnabled()) { 978 log.debug("checkCircuits: block {} has {} icons.", block.getDisplayName(), icons.size()); 979 } 980 if (icons.isEmpty()) { 981 _bareBlock.add(block); 982 } else { 983 boolean hasTrackIcon = false; 984 boolean iconNeedsConversion = false; 985 for (Positionable pos : icons) { 986 if (!(pos instanceof PortalIcon) && !(pos instanceof SignalMastIcon) && !(pos instanceof SignalHeadIcon)) { 987 hasTrackIcon = true; 988 if (!(pos instanceof IndicatorTrack)) { 989 iconNeedsConversion = true; 990 } 991 } 992 } 993 if (hasTrackIcon) { 994 _bareBlock.remove(block); 995 } else if (!_bareBlock.contains(block)) { 996 _bareBlock.add(block); 997 } 998 if (iconNeedsConversion && !_convertBlock.contains(block)) { 999 _convertBlock.add(block); 1000 } 1001 } 1002 } 1003 1004 // check positioning of portal icons for 'direction arrow' state. 1005 for (Portal portal : portalMgr.getPortalSet()) { 1006 List<PortalIcon> piArray = getPortalIcons(portal); 1007 if (piArray.isEmpty()) { 1008 _noPortalIcon.add(portal); 1009 } else { 1010 PortalIcon icon1 = piArray.get(0); 1011 if (piArray.size() == 1) { 1012 if (!iconIntersectsBlock(icon1, portal.getToBlock()) || 1013 !iconIntersectsBlock(icon1, portal.getFromBlock())) { 1014 _misplacedPortalIcon.add(icon1); 1015 } 1016 } else { 1017 boolean fromOK = false; 1018 boolean toOK = false; 1019 PortalIcon icon = null; 1020 for (PortalIcon ic : piArray) { 1021 if (!toOK && iconIntersectsBlock(ic, portal.getToBlock()) && 1022 !iconIntersectsBlock(ic, portal.getFromBlock())) { 1023 toOK = true; 1024 } else if (!fromOK && !iconIntersectsBlock(ic, portal.getToBlock()) && 1025 iconIntersectsBlock(ic, portal.getFromBlock())) { 1026 fromOK = true; 1027 } else { 1028 icon = ic; 1029 } 1030 } 1031 if (!toOK || !fromOK) { 1032 _misplacedPortalIcon.add(icon); 1033 } 1034 } 1035 } 1036 } 1037 1038 for (Map.Entry<NamedBean, Portal> entry : _signalMap.entrySet()) { 1039 if (entry.getValue() == null) { 1040 _unprotectingMast.add(entry.getKey()); 1041 } 1042 } 1043 1044 if (oblocks.size() > 1) { 1045 if (_circuitMenu.getItemCount() <= 3) { 1046 _circuitMenu.removeAll(); 1047 makeCircuitMenu(); 1048 } else { 1049 makeToDoMenu(); 1050 } 1051 } else { 1052 _circuitMenu.removeAll(); 1053 makeNoOBlockMenu(); 1054 } 1055 } // end checkCircuits 1056 1057 protected boolean iconIntersectsBlock(Positionable icon, OBlock block) { 1058 Rectangle iconRect = icon.getBounds(new Rectangle()); 1059 return rectIntersectsBlock(iconRect, block); 1060 } 1061 1062 protected boolean rectIntersectsBlock(Rectangle iconRect, OBlock block) { 1063 java.util.List<Positionable> list = getCircuitIcons(block); 1064 if (list.isEmpty()) { 1065 return false; 1066 } 1067 Rectangle rect = new Rectangle(); 1068 for (Positionable comp : list) { 1069 if (CircuitBuilder.isTrack(comp)) { 1070 rect = comp.getBounds(rect); 1071 if (iconRect.intersects(rect)) { 1072 return true; 1073 } 1074 } 1075 } 1076 return false; 1077 } 1078 1079 ////////////////////////// Frame Utilities ////////////////////////// 1080 @Nonnull 1081 protected ArrayList<Positionable> getCircuitIcons(OBlock block) { 1082 // return empty array when block == null 1083 return _circuitMap.computeIfAbsent(block, k -> new ArrayList<>()); 1084 } 1085 1086 @Nonnull 1087 protected List<PortalIcon> getPortalIcons(@Nonnull Portal portal) { 1088 return _portalIconMap.computeIfAbsent(portal, k -> new ArrayList<>()); 1089 } 1090 1091 @Nonnull 1092 protected List<PositionableIcon> getSignalIconMap(@Nonnull NamedBean mast) { 1093 return _signalIconMap.computeIfAbsent(mast, k -> new ArrayList<>()); 1094 } 1095 1096 protected HashMap<NamedBean, ArrayList<PositionableIcon>> getSignalIconMap() { 1097 return _signalIconMap; 1098 } 1099 1100 protected Portal getSignalPortal(@Nonnull NamedBean mast) { 1101 return _signalMap.get(mast); 1102 } 1103 1104 protected void putSignalPortal(@Nonnull NamedBean mast, Portal portal) { 1105 if (portal == null) { 1106 _signalMap.remove(mast); 1107 } 1108 _signalMap.put(mast, portal); 1109 } 1110 1111 /** 1112 * Remove block, but keep the track icons. Sets block reference in icon to 1113 * null. 1114 * 1115 * @param block the block to remove 1116 */ 1117 protected void removeBlock(OBlock block) { 1118 java.util.List<Positionable> list = getCircuitIcons(block); 1119 for (Positionable pos : list) { 1120 if (pos instanceof IndicatorTrack) { 1121 ((IndicatorTrack) pos).setOccBlockHandle(null); 1122 _darkTrack.add((IndicatorTrack) pos); 1123 } 1124 } 1125 block.dispose(); 1126 if (jmri.InstanceManager.getDefault(OBlockManager.class).getNamedBeanSet().size() < 2) { 1127 _editor.makeWarrantMenu(true, false); 1128 } 1129 } 1130 1131 protected String checkForPortals(@Nonnull OBlock block, String key) { 1132 StringBuffer sb = new StringBuffer(); 1133 List<Portal> portals = block.getPortals(); 1134 if (portals.isEmpty()) { 1135 sb.append(Bundle.getMessage("needPortal", block.getDisplayName(), Bundle.getMessage(key))); 1136 } else { 1137 for (Portal portal : portals) { 1138 if (portal.getToBlock() == null || portal.getFromBlock() == null) { 1139 if (sb.length() > 0) { 1140 sb.append("\n"); 1141 } 1142 sb.append(Bundle.getMessage("portalNeedsBlock", portal.getName())); 1143 } 1144 } 1145 for (Portal portal : portals) { 1146 if (sb.length() > 0) { 1147 sb.append("\n"); 1148 } 1149 if (!block.equals(portal.getToBlock()) && !block.equals(portal.getFromBlock())) { 1150 sb.append(Bundle.getMessage("portalNotInCircuit", portal.getName(), block.getDisplayName())); 1151 } 1152 } 1153 } 1154 return sb.toString(); 1155 } 1156 1157 /** 1158 * Check that there is at least one PortalIcon. called from various _editFrame's 1159 * @param block check icons of this block 1160 * @param key properties key 1161 * @return true if at least one PortalIcon found 1162 */ 1163 protected String checkForPortalIcons(@Nonnull OBlock block, String key) { 1164 StringBuffer sb = new StringBuffer(); 1165 List<Portal> portals = block.getPortals(); 1166 if (portals.isEmpty()) { 1167 sb.append(Bundle.getMessage("needPortal", block.getDisplayName(), Bundle.getMessage(key))); 1168 } else if (_editFrame instanceof EditPortalFrame) { 1169 for (Portal portal : portals) { 1170 String msg = ((EditPortalFrame)_editFrame).checkPortalIcons(portal, false, key); 1171 if (msg != null ) { 1172 if (sb.length() > 0) { 1173 sb.append("\n"); 1174 } 1175 sb.append(msg); 1176 } 1177 } 1178 } 1179 // block has pPortals 1180 boolean ok = false; 1181 List<Positionable> list = getCircuitIcons(block); 1182 if (!list.isEmpty()) { 1183 for (Positionable pos : list) { 1184 if ((pos instanceof PortalIcon)) { 1185 ok = true; 1186 } 1187 } 1188 } 1189 if (!ok) { 1190 if (sb.length() > 0) { 1191 sb.append("\n"); 1192 } 1193 sb.append(Bundle.getMessage("needPortalIcons", block.getDisplayName(), Bundle.getMessage(key))); 1194 } 1195 return sb.toString(); 1196 } 1197 1198 protected String checkForTrackIcons(@Nonnull OBlock block, String key) { 1199 StringBuilder sb = new StringBuilder(); 1200 List<Positionable> list = getCircuitIcons(block); 1201 if (list.isEmpty()) { 1202 sb.append(Bundle.getMessage("needIcons", block.getDisplayName(), Bundle.getMessage(key))); 1203 } else { 1204 boolean ok = true; 1205 for (Positionable p : list) { 1206 PositionableLabel pos = (PositionableLabel) p; 1207 if (CircuitBuilder.isUnconvertedTrack(pos)) { 1208 ok = false; 1209 break; 1210 } 1211 } 1212 if (!ok) { 1213 sb.append(Bundle.getMessage("cantSaveIcon", block.getDisplayName())); 1214 sb.append("\n"); 1215 sb.append(Bundle.getMessage("needIcons", block.getDisplayName(), Bundle.getMessage(key))); 1216 } 1217 } 1218 return sb.toString(); 1219 } 1220 1221 protected void deletePortalIcon(PortalIcon icon) { 1222 if (log.isDebugEnabled()) { 1223 log.debug("deletePortalIcon: {}", icon.getName()); 1224 } 1225 Portal portal = icon.getPortal(); 1226 if (portal != null) { 1227 getCircuitIcons(portal.getToBlock()).remove(icon); 1228 getCircuitIcons(portal.getFromBlock()).remove(icon); 1229 getPortalIcons(portal).remove(icon); 1230 } 1231 List<Positionable> selections = _editor.getSelectionGroup(); 1232 if (selections != null) { 1233 _editor.getSelectionGroup().remove(icon); 1234 } 1235 _editor.repaint(); 1236 } 1237 1238 /** 1239 * Check if the block being edited has all its track icons converted to indicator icons 1240 * If icons need conversion. ask if user wants to convert them 1241 * @param block OBlock to check 1242 * @param key properties key 1243 * @return true if all track icons are IndicatorTrack icons 1244 */ 1245 protected boolean queryConvertTrackIcons(@Nonnull OBlock block, String key) { 1246 // since iconList will be modified, use a copy to find unconverted icons 1247 ArrayList<Positionable> list = new ArrayList<>(getCircuitIcons(block)); 1248 String msg = null; 1249 if (list.isEmpty()) { 1250 msg = Bundle.getMessage("needIcons", block.getDisplayName(), Bundle.getMessage(key)); 1251 } else { 1252 boolean needConversion = false; 1253 for (Positionable p : list) { 1254 PositionableLabel pos = (PositionableLabel) p; 1255 if (CircuitBuilder.isUnconvertedTrack(pos)) { 1256 _editor.highlight(pos); 1257 needConversion = true; 1258 new ConvertDialog(this, pos, block); 1259 _editor.highlight(null); 1260 } 1261 } 1262 if (!needConversion) { 1263 msg = Bundle.getMessage("noneNeedConversion"); 1264 } 1265 } 1266 if (msg != null) { 1267 JmriJOptionPane.showMessageDialog(_editFrame, msg, 1268 Bundle.getMessage("noIcons"), JmriJOptionPane.INFORMATION_MESSAGE); 1269 return false; 1270 } else { 1271 return true; 1272 } 1273 } 1274 1275 1276 //////////////// select - deselect track icons ////////// 1277 /** 1278 * Select block's track icons for editing. filter for what icon types to show and highlight 1279 */ 1280 private ArrayList<Positionable> makeSelectionGroup(OBlock block, boolean showPortal) { 1281 ArrayList<Positionable> group = new ArrayList<>(); 1282 for (Positionable p : getCircuitIcons(block)) { 1283 if (p instanceof PortalIcon) { 1284 if (showPortal) { 1285 ((PortalIcon) p).setStatus(PortalIcon.VISIBLE); 1286 group.add(p); 1287 } 1288 } else if (!(p instanceof SignalMastIcon) && !(p instanceof SignalHeadIcon)) { 1289 group.add(p); 1290 } 1291 } 1292 return group; 1293 } 1294 1295 protected static boolean isTrack(Positionable pos) { 1296 if (pos instanceof IndicatorTrack) { 1297 return true; 1298 } else if (pos instanceof TurnoutIcon) { 1299 return true; 1300 } else if ((pos instanceof PortalIcon) || 1301 (pos instanceof SignalMastIcon) || (pos instanceof SignalHeadIcon)) { 1302 return false; 1303 } else if (pos instanceof PositionableLabel) { 1304 PositionableLabel pl = (PositionableLabel) pos; 1305 if (pl.isIcon()) { 1306 NamedIcon icon = (NamedIcon) pl.getIcon(); 1307 if (icon != null) { 1308 String fileName = icon.getURL(); 1309 // getURL() returns Unix separatorChar= "/" even on windows 1310 // so don't use java.io.File.separatorChar 1311 if (fileName != null && (fileName.contains("/track/") 1312 || (fileName.contains("/tracksegments/") && !fileName.contains("circuit")))) { 1313 return true; 1314 } 1315 } 1316 } 1317 } 1318 return false; 1319 } 1320 1321 private static boolean isUnconvertedTrack(Positionable pos) { 1322 if (pos instanceof IndicatorTrack || (pos instanceof PortalIcon) || 1323 (pos instanceof SignalMastIcon) || (pos instanceof SignalHeadIcon)) { 1324 return false; 1325 } else if (pos instanceof TurnoutIcon) { 1326 return true; 1327 } else if (pos instanceof PositionableLabel) { 1328 PositionableLabel pl = (PositionableLabel) pos; 1329 if (pl.isIcon()) { 1330 NamedIcon icon = (NamedIcon) pl.getIcon(); 1331 if (icon != null) { 1332 String fileName = icon.getURL(); 1333 if (log.isDebugEnabled()) { 1334 log.debug("isUnconvertedTrack Test: url= {}", fileName); 1335 } 1336 // getURL() returns Unix separatorChar= "/" even on windows 1337 // so don't use java.io.File.separatorChar 1338 if (fileName != null 1339 && (fileName.contains("/track/") || fileName.contains("/tracksegments/")) 1340 && !fileName.contains("circuit")) { 1341 return true; 1342 } 1343 } 1344 } 1345 } 1346 return false; 1347 } 1348 1349 /** 1350 * Can this track icon be added to the circuit? N.B. Be sure Positionable 1351 * pos passes isTrack() call 1352 */ 1353 private boolean okToAdd(Positionable pos, OBlock editBlock) { 1354 if (pos instanceof IndicatorTrack) { 1355 OBlock block = ((IndicatorTrack) pos).getOccBlock(); 1356 if (block != null) { 1357 if (!block.equals(editBlock)) { 1358 int result = JmriJOptionPane.showConfirmDialog(_editor, java.text.MessageFormat.format( 1359 Bundle.getMessage("iconBlockConflict"), 1360 block.getDisplayName(), editBlock.getDisplayName()), 1361 Bundle.getMessage("whichCircuit"), JmriJOptionPane.YES_NO_OPTION, 1362 JmriJOptionPane.QUESTION_MESSAGE); 1363 if (result == JmriJOptionPane.YES_OPTION) { 1364 // move icon from block to editBlock 1365 getCircuitIcons(block).remove(pos); 1366 ((IndicatorTrack) pos).setOccBlockHandle( 1367 InstanceManager.getDefault(NamedBeanHandleManager.class) 1368 .getNamedBeanHandle(editBlock.getSystemName(), editBlock)); 1369 return true; 1370 } 1371 return false; 1372 } 1373 } 1374 } 1375 return true; 1376 } 1377 1378 /** 1379 * 1380 * @param pos PortalIcon attempting a move. allow or disallow 1381 * @param x new x position 1382 * @param y new y position 1383 * @return allow, or not 1384 */ 1385 protected boolean portalIconMove(PortalIcon pos, int x, int y) { 1386 if (_editFrame == null || (_editFrame instanceof EditPortalFrame)) { 1387 return true; 1388 } 1389 Rectangle iconRect = pos.getBounds(new Rectangle()); 1390 iconRect.x = x; 1391 iconRect.y = y; 1392 OBlock block = _editFrame._homeBlock; 1393 if (rectIntersectsBlock(iconRect, block)) { 1394 Portal port = pos.getPortal(); 1395 if (block.equals(port.getToBlock())) { 1396 block = port.getFromBlock(); 1397 } else { 1398 block = port.getToBlock(); 1399 } 1400 if (block == null || rectIntersectsBlock(iconRect, block)) { 1401 return true; 1402 } 1403 } 1404 JmriJOptionPane.showMessageDialog(_editFrame, 1405 Bundle.getMessage("moveOffBlock", block.getDisplayName(), pos.getNameString()), 1406 Bundle.getMessage("editCiruit"), JmriJOptionPane.INFORMATION_MESSAGE); 1407 return false; 1408 } 1409 1410 /** 1411 * ************************** Mouse ************************ 1412 */ 1413 ArrayList<Positionable> _saveSelectionGroup; 1414 1415 /** 1416 * Keep selections when editing. Editor calls at entry to mousePressed(). 1417 * CircuitBuilder keeps is own concept of what is "selected". 1418 * 1419 * @param selectionGroup the selection group to save 1420 * @return true if retaining a reference to a frame 1421 */ 1422 protected boolean saveSelectionGroup(ArrayList<Positionable> selectionGroup) { 1423 _saveSelectionGroup = selectionGroup; 1424 return _editFrame != null; 1425 } 1426 1427 /** 1428 * Make note of selection. 1429 * 1430 * @param event the triggering event 1431 * @param selection the selection 1432 * @return true 1433 */ 1434 protected boolean doMousePressed(JmriMouseEvent event, Positionable selection) { 1435 _selection = selection; 1436 return true; 1437 } 1438 1439 /** 1440 * If CircuitBuilder is in progress, restore what editor nulls. 1441 * 1442 * @param event the triggering event 1443 * @return true if the selection group is restored; false otherwise 1444 */ 1445 protected boolean doMousePressed(JmriMouseEvent event) { 1446 if (_editFrame != null) { 1447 _editFrame.toFront(); 1448 _editor.setSelectionGroup(_saveSelectionGroup); 1449 } else { 1450 return false; 1451 } 1452 return true; 1453 } 1454 1455 public boolean doMouseReleased(Positionable selection, boolean dragging) { 1456 if (_editFrame != null) { 1457 if (_editFrame instanceof EditPortalFrame) { 1458 if (selection instanceof PortalIcon 1459 && getCircuitIcons(_editFrame._homeBlock).contains(selection)) { 1460 PortalIcon icon = (PortalIcon)selection; 1461 if (dragging) { 1462 Portal portal = icon.getPortal(); 1463 ((EditPortalFrame)_editFrame).checkPortalIcons(portal, true, null); 1464 } else { 1465 ((EditPortalFrame)_editFrame).setSelected(icon); 1466 } 1467 } 1468 } 1469 return true; 1470 } else { 1471 if (_selection != null) { 1472 if (_selection instanceof PortalIcon) { 1473 PortalIcon pos = (PortalIcon)_selection; 1474 Portal portal = pos.getPortal(); 1475 if (portal != null) { 1476 OBlock block = portal.getToBlock(); 1477 if (block == null) { 1478 block = portal.getFromBlock(); 1479 } 1480 editPortalError(block, portal, pos); 1481 } 1482 } 1483 } 1484 } 1485 return false; 1486 } 1487 1488 // Return true if CircuitBuilder is editing 1489 protected boolean doMouseClicked(List<Positionable> selections, JmriMouseEvent event) { 1490 if (_editFrame != null) { 1491 if (selections != null && selections.size() > 0) { 1492 ArrayList<Positionable> tracks = new ArrayList<>(); 1493 Iterator<Positionable> iter = selections.iterator(); 1494 if (_editFrame instanceof EditCircuitFrame) { 1495 while (iter.hasNext()) { 1496 Positionable pos = iter.next(); 1497 if (isTrack(pos)) { 1498 tracks.add(pos); 1499 } 1500 } 1501 } else if (_editFrame instanceof EditCircuitPaths) { 1502 while (iter.hasNext()) { 1503 Positionable pos = iter.next(); 1504 if (isTrack(pos) || pos instanceof PortalIcon) { 1505 tracks.add(pos); 1506 } 1507 } 1508 } else if (_editFrame instanceof EditSignalFrame) { 1509 while (iter.hasNext()) { 1510 Positionable pos = iter.next(); 1511 if (pos instanceof PortalIcon || pos instanceof SignalMastIcon || pos instanceof SignalHeadIcon) { 1512 tracks.add(pos); 1513 } 1514 } 1515 } else { 1516 while (iter.hasNext()) { 1517 Positionable pos = iter.next(); 1518 if (pos instanceof PortalIcon) { 1519 tracks.add(pos); 1520 } 1521 } 1522 } 1523 if (tracks.size() > 0) { 1524 Positionable selection; 1525 if (tracks.size() == 1) { 1526 selection = tracks.get(0); 1527 } else { 1528 selection = getSelection(tracks); 1529 } 1530 if (_editFrame instanceof EditCircuitPaths && event.isShiftDown() && !event.isControlDown()) { 1531 selection.doMouseClicked(event); 1532 } 1533 handleSelection(selection, event); 1534 } 1535 _editFrame.toFront(); 1536 } 1537 return true; 1538 } 1539 return false; 1540 } 1541 1542 private Positionable getSelection(List<Positionable> tracks) { 1543 if (tracks.size() > 0) { 1544 if (tracks.size() == 1) { 1545 return tracks.get(0); 1546 } 1547 if (tracks.size() > 1) { 1548 String[] selects = new String[tracks.size()]; 1549 Iterator<Positionable> iter = tracks.iterator(); 1550 int i = 0; 1551 while (iter.hasNext()) { 1552 selects[i++] = iter.next().getNameString(); 1553 } 1554 Object select = JmriJOptionPane.showInputDialog(_editor, Bundle.getMessage("multipleSelections"), 1555 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.QUESTION_MESSAGE, 1556 null, selects, null); 1557 if (select != null) { 1558 iter = tracks.iterator(); 1559 while (iter.hasNext()) { 1560 Positionable pos = iter.next(); 1561 if (((String) select).equals(pos.getNameString())) { 1562 return pos; 1563 } 1564 } 1565 } 1566 } 1567 } 1568 return null; 1569 } 1570 1571 /** 1572 * Prevent dragging when CircuitBuilder is in progress, except for 1573 * PortalIcon. 1574 * 1575 * @param selection the item(s) being dragged 1576 * @param event the triggering event 1577 * @return true to prevent dragging; false otherwise 1578 */ 1579 public boolean doMouseDragged(Positionable selection, JmriMouseEvent event) { 1580 if (_editFrame != null) { 1581 if (selection instanceof PortalIcon) { 1582 if (_editFrame instanceof EditPortalFrame) { 1583 ((EditPortalFrame)_editFrame).setSelected((PortalIcon)selection); 1584 return false; // OK to drag portal icon 1585 } else if (_editFrame instanceof EditPortalDirection) { 1586 return false; // OK to drag portal arrow 1587 } 1588 } if (selection instanceof SignalMastIcon || selection instanceof SignalHeadIcon) { 1589 if (_editFrame instanceof EditSignalFrame) { 1590 return false; // OK to drag signal 1591 } 1592 } 1593 return true; // no dragging when editing 1594 } 1595 return false; 1596 } 1597 1598 /** 1599 * Second call needed to only drag the portal icon and not entire selection 1600 * 1601 * @return true if portal frame is open 1602 */ 1603 public boolean dragPortal() { 1604 return (_editFrame instanceof EditPortalFrame || _editFrame instanceof EditPortalDirection); 1605 } 1606 1607 /* 1608 * For the param, selection, Add to or delete from selectionGroup. 1609 * If not there, add. 1610 * If there, delete. 1611 */ 1612 private void handleSelection(Positionable selection, JmriMouseEvent event) { 1613 if (_editFrame == null) { 1614 return; 1615 } 1616 if (_editFrame instanceof EditCircuitFrame) { 1617 EditCircuitFrame editCircuitFrame = (EditCircuitFrame)_editFrame; 1618 ArrayList<Positionable> selectionGroup = _editor.getSelectionGroup(); 1619 if (selectionGroup == null) { 1620 selectionGroup = new ArrayList<>(); 1621 } 1622 if (selectionGroup.contains(selection)) { 1623 selectionGroup.remove(selection); 1624 } else if (okToAdd(selection, editCircuitFrame._homeBlock)) { 1625 selectionGroup.add(selection); 1626 } 1627 editCircuitFrame.updateIconList(selectionGroup); 1628 _editor.setSelectionGroup(selectionGroup); 1629 } else if (_editFrame instanceof EditCircuitPaths) { 1630 EditCircuitPaths editPathsFrame = (EditCircuitPaths)_editFrame; 1631 editPathsFrame.updateSelections(!event.isShiftDown(), selection); 1632 } else if (_editFrame instanceof EditPortalFrame) { 1633 EditPortalFrame editPortalFrame = (EditPortalFrame)_editFrame; 1634 if (selection instanceof PortalIcon && getCircuitIcons(_editFrame._homeBlock).contains(selection)) { 1635 editPortalFrame.setSelected((PortalIcon)selection); 1636 } 1637 } else if (_editFrame instanceof EditPortalDirection) { 1638 EditPortalDirection editDirectionFrame = (EditPortalDirection)_editFrame; 1639 if (selection instanceof PortalIcon) { 1640 editDirectionFrame.setPortalIcon((PortalIcon)selection, true); 1641 } 1642 } else if (_editFrame instanceof EditSignalFrame) { 1643 EditSignalFrame editSignalFrame = (EditSignalFrame)_editFrame; 1644 editSignalFrame.setSelected((PositionableIcon)selection); 1645 } 1646 } 1647 1648 protected void closeCBWindow() { 1649 if (_cbFrame !=null) { 1650 _cbFrame.dispose(); 1651 } 1652 if (_editFrame != null) { 1653 _editFrame.closingEvent(true, null); 1654 } 1655 } 1656 1657 static int NONE = 0; 1658 static int OBLOCK = 1; 1659 static int PORTAL = 2; 1660 static int OPATH = 3; 1661 static int ARROW = 4; 1662 static int SIGNAL = 5; 1663 class CBFrame extends jmri.util.JmriJFrame implements ListSelectionListener { 1664 1665 ButtonGroup _buttonGroup = new ButtonGroup(); 1666 int _which = 0; 1667 JRadioButton _newCircuitButton = makeButton("newCircuitItem", NONE); 1668 1669 CBFrame(String title) { 1670 super(false, false); 1671 setTitle(title); 1672 addHelpMenu("package.jmri.jmrit.display.CircuitBuilder", true); 1673 1674 _blockTable.getSelectionModel().addListSelectionListener(this); 1675 1676 JPanel contentPane = new JPanel(); 1677 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 1678 javax.swing.border.Border padding = BorderFactory.createEmptyBorder(10, 5, 4, 5); 1679 contentPane.setBorder(padding); 1680 1681 JPanel panel0 = new JPanel(); 1682 panel0.setLayout(new BoxLayout(panel0, BoxLayout.X_AXIS)); 1683 JPanel panel = new JPanel(); 1684 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1685 panel.add(_newCircuitButton); 1686 panel.add(makeButton("editCircuitItem", OBLOCK)); 1687 panel.add(makeButton("editPortalsItem", PORTAL)); 1688 panel.add(makeButton("editCircuitPathsItem", OPATH)); 1689 panel.add(makeButton("editDirectionItem", ARROW)); 1690 panel.add(makeButton("editSignalItem", SIGNAL)); 1691 _newCircuitButton.setSelected(true); 1692 panel0.add(panel); 1693 1694 panel = new JPanel(); 1695 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1696 panel.add(new JScrollPane(_blockTable)); 1697 panel0.add(panel); 1698 contentPane.add(panel0); 1699 1700 panel0 = new JPanel(); 1701 panel0.setLayout(new BoxLayout(panel0, BoxLayout.X_AXIS)); 1702 panel = new JPanel(); 1703 JButton button = new JButton(Bundle.getMessage("ButtonOpen")); 1704 button.addActionListener((ActionEvent event) -> { 1705 if (editingOK()) { 1706 setCurrentBlock(); 1707 if (_which == NONE) { 1708 newCircuit(); 1709 } else if (_which == OBLOCK) { 1710 editCircuit("editCircuitItem", false); 1711 } else if (_which == PORTAL) { 1712 editPortals("editPortalsItem", false); 1713 } else if (_which == OPATH) { 1714 editCircuitPaths("editCircuitPathsItem", false); 1715 } else if (_which == ARROW) { 1716 editPortalDirection("editDirectionItem", false); 1717 } else if (_which == SIGNAL) { 1718 editSignalFrame("editSignalItem", false); 1719 } 1720 } 1721 }); 1722 panel.add(button); 1723 1724 button = new JButton(Bundle.getMessage("ButtonDone")); 1725 button.addActionListener((ActionEvent a) -> { 1726 _currentBlock = null; 1727 this.dispose(); 1728 }); 1729 panel.add(button); 1730 panel.setMaximumSize(new Dimension(300, panel.getPreferredSize().height)); 1731 panel0.add(panel); 1732 contentPane.add(panel0); 1733 1734 setContentPane(contentPane); 1735 _blockTable.clearSelection(); 1736 pack(); 1737 InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(_editor, null, this); 1738 setVisible(true); 1739 } 1740 1741 @Override 1742 public void valueChanged(ListSelectionEvent e) { 1743 setCurrentBlock(); 1744 } 1745 private void setCurrentBlock() { 1746 int row = _blockTable.getSelectedRow(); 1747 if (row >= 0) { 1748 row = _blockTable.convertRowIndexToModel(row); 1749 _currentBlock = _oblockModel.getBeanAt(row); 1750 } else { 1751 _currentBlock = null; 1752 } 1753 } 1754 1755 JRadioButton makeButton(String title, int which) { 1756 JRadioButton button = new JRadioButton(Bundle.getMessage(title)); 1757 button.addActionListener((ActionEvent event) -> _which = which); 1758 _buttonGroup.add(button); 1759 return button; 1760 } 1761 1762 @Override 1763 public void dispose() { 1764 _cbFrame = null; 1765 super.dispose(); 1766 } 1767 } 1768 1769 ////////////////////////// static methods ////////////////////////// 1770 1771 protected static void doSize(JComponent comp, int max, int min) { 1772 Dimension dim = comp.getPreferredSize(); 1773 dim.width = max; 1774 comp.setMaximumSize(dim); 1775 dim.width = min; 1776 comp.setMinimumSize(dim); 1777 } 1778 1779 protected static JPanel makeTextBoxPanel(boolean vertical, JTextField textField, String label, 1780 boolean editable, String tooltip) { 1781 JPanel panel = makeBoxPanel(vertical, textField, label, tooltip); 1782 textField.setEditable(editable); 1783 textField.setBackground(Color.white); 1784 return panel; 1785 } 1786 1787 protected static JPanel makeBoxPanel(boolean vertical, JComponent textField, String label, 1788 String tooltip) { 1789 JPanel panel = new JPanel(); 1790 panel.setLayout(new java.awt.GridBagLayout()); 1791 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 1792 c.gridwidth = 1; 1793 c.gridheight = 1; 1794 c.gridx = 0; 1795 c.gridy = 0; 1796 c.weightx = 1.0; 1797 JLabel l = new JLabel(Bundle.getMessage(label)); 1798 if (vertical) { 1799 c.anchor = java.awt.GridBagConstraints.SOUTH; 1800 l.setAlignmentX(JComponent.CENTER_ALIGNMENT); 1801 textField.setAlignmentX(JComponent.CENTER_ALIGNMENT); 1802 } else { 1803 c.anchor = java.awt.GridBagConstraints.EAST; 1804 l.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1805 textField.setAlignmentX(JComponent.RIGHT_ALIGNMENT); 1806 } 1807 panel.add(l, c); 1808 if (vertical) { 1809 c.anchor = java.awt.GridBagConstraints.NORTH; 1810 c.gridy = 1; 1811 } else { 1812 c.anchor = java.awt.GridBagConstraints.WEST; 1813 c.gridx = 1; 1814 } 1815 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 1816 doSize(textField, 9000, 200); // default 1817 panel.add(textField, c); 1818 if (tooltip != null) { 1819 textField.setToolTipText(Bundle.getMessage(tooltip)); 1820 l.setToolTipText(Bundle.getMessage(tooltip)); 1821 panel.setToolTipText(Bundle.getMessage(tooltip)); 1822 } 1823 return panel; 1824 } 1825 1826 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CircuitBuilder.class); 1827 1828}