001package jmri.jmrit.display.layoutEditor; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.awt.geom.*; 006import java.beans.PropertyVetoException; 007import java.util.List; 008import java.util.*; 009 010import javax.annotation.*; 011import javax.swing.*; 012 013import jmri.*; 014import jmri.jmrit.logixng.InlineLogixNG; 015import jmri.jmrit.logixng.LogixNG; 016import jmri.jmrit.logixng.LogixNG_Manager; 017import jmri.jmrit.logixng.tools.swing.DeleteBean; 018import jmri.jmrit.logixng.tools.swing.LogixNGEditor; 019import jmri.util.*; 020import jmri.util.swing.JmriJOptionPane; 021import jmri.util.swing.JmriMouseEvent; 022 023/** 024 * MVC View component abstract base for the LayoutTrack hierarchy. 025 * <p> 026 * This contains the display information, including screen geometry, for a 027 * LayoutEditor panel. The geometry/connectivity information is held in 028 * {@link LayoutTrack} subclasses. 029 * <ul> 030 * <li>Position(s) of the screen icons and its parts, typically the center; 031 * scaling and translation; size and bounds 032 * <li>Line colors 033 * <li>Flipped status; drawing details like bezier curve points 034 * <li>Various decorations: arrows, tunnels, bridges 035 * <li>Hidden status 036 * </ul> 037 * 038 * @author Bob Jacobsen Copyright (c) 2020 039 * 040 */ 041abstract public class LayoutTrackView implements InlineLogixNG { 042 043 /** 044 * Constructor method. 045 * 046 * @param track the layout track to view 047 * @param layoutEditor the panel in which to place the view 048 */ 049 public LayoutTrackView(@Nonnull LayoutTrack track, @Nonnull LayoutEditor layoutEditor) { 050 this.layoutTrack = track; 051 this.layoutEditor = layoutEditor; 052 } 053 054 /** 055 * constructor method 056 * 057 * @param track the track to view 058 * @param c display location 059 * @param layoutEditor for reference to tools 060 */ 061 public LayoutTrackView(@Nonnull LayoutTrack track, @Nonnull Point2D c, @Nonnull LayoutEditor layoutEditor) { 062 this.layoutTrack = track; 063 this.layoutEditor = layoutEditor; 064 this.center = c; 065 } 066 067 final private LayoutTrack layoutTrack; 068 069 final protected LayoutEditor layoutEditor; 070 071 private LogixNG _logixNG; 072 private String _logixNG_SystemName; 073 074 private boolean _inEditInlineLogixNGMode = false; 075 private LogixNGEditor _inlineLogixNGEdit; 076 077 // Accessor Methods 078 079 @Nonnull 080 final public String getId() { // temporary Id vs name; is one for the View? 081 return layoutTrack.getId(); 082 } 083 084 @Nonnull 085 final public String getName() { 086 return layoutTrack.getName(); 087 } 088 089 @Nonnull 090 @CheckReturnValue 091 public LayoutEditor getLayoutEditor() { 092 return layoutEditor; 093 } 094 095 final protected void setIdent(@Nonnull String ident) { 096 layoutTrack.setIdent(ident); 097 } 098 099 // temporary accessor? Or is this a long term thing? 100 // @Nonnull temporary until we gigure out if can be null or not 101 public LayoutTrack getLayoutTrack() { 102 return layoutTrack; 103 } 104 105 /** 106 * Set center coordinates 107 * 108 * @return The center coordinates 109 */ 110 public Point2D getCoordsCenter() { // should be final for efficiency, temporary not to allow redirction overrides. 111 return center; 112 } 113 114 /** 115 * Set center coordinates. 116 * <p> 117 * Some subtypes may reimplement this is "center" is a more complicated 118 * idea, i.e. for Bezier curves 119 * 120 * @param p the coordinates to set 121 */ 122 public void setCoordsCenter(@Nonnull Point2D p) { // temporary = want to make protected after migration 123 center = p; 124 } 125 126 private Point2D center = new Point2D.Double(50.0, 50.0); 127 128 /** 129 * @return true if this track segment has decorations 130 */ 131 public boolean hasDecorations() { 132 return false; 133 } 134 135 /** 136 * Get current decorations 137 * 138 * @return the decorations 139 */ 140 public Map<String, String> getDecorations() { 141 return decorations; 142 } 143 144 /** 145 * Set new decorations 146 * 147 * This is a complete replacement of the decorations, not an appending. 148 * 149 * @param decorations A map from strings ("arrow", "bridge", "bumper",..) to 150 * specific value strings ("single", "entry;right", ), 151 * perhaps including multiple values separated by 152 * semicolons. 153 */ 154 public void setDecorations(Map<String, String> decorations) { 155 this.decorations = decorations; 156 } 157 protected Map<String, String> decorations = null; 158 159 /** 160 * convenience method for accessing... 161 * 162 * @return the layout editor's toolbar panel 163 */ 164 @Nonnull 165 final public LayoutEditorToolBarPanel getLayoutEditorToolBarPanel() { 166 return layoutEditor.getLayoutEditorToolBarPanel(); 167 } 168 169 // these are convenience methods to return circles & rectangle used to draw onscreen 170 // 171 // compute the control point rect at inPoint; use the turnout circle size 172 final public Ellipse2D trackEditControlCircleAt(@Nonnull Point2D inPoint) { 173 return trackControlCircleAt(inPoint); 174 } 175 176 // compute the turnout circle at inPoint (used for drawing) 177 final public Ellipse2D trackControlCircleAt(@Nonnull Point2D inPoint) { 178 return new Ellipse2D.Double(inPoint.getX() - layoutEditor.circleRadius, 179 inPoint.getY() - layoutEditor.circleRadius, 180 layoutEditor.circleDiameter, layoutEditor.circleDiameter); 181 } 182 183 // compute the turnout circle control rect at inPoint 184 final public Rectangle2D trackControlCircleRectAt(@Nonnull Point2D inPoint) { 185 return new Rectangle2D.Double(inPoint.getX() - layoutEditor.circleRadius, 186 inPoint.getY() - layoutEditor.circleRadius, 187 layoutEditor.circleDiameter, layoutEditor.circleDiameter); 188 } 189 190 final protected Color getColorForTrackBlock( 191 @CheckForNull LayoutBlock layoutBlock, boolean forceBlockTrackColor) { 192 Color result = ColorUtil.CLEAR; // transparent 193 if (layoutBlock != null) { 194 if (forceBlockTrackColor) { 195 result = layoutBlock.getBlockTrackColor(); 196 } else { 197 result = layoutBlock.getBlockColor(); 198 } 199 } 200 return result; 201 } 202 203 // optional parameter forceTrack = false 204 final protected Color getColorForTrackBlock(@CheckForNull LayoutBlock lb) { 205 return getColorForTrackBlock(lb, false); 206 } 207 208 final protected Color setColorForTrackBlock(Graphics2D g2, 209 @CheckForNull LayoutBlock layoutBlock, boolean forceBlockTrackColor) { 210 Color result = getColorForTrackBlock(layoutBlock, forceBlockTrackColor); 211 g2.setColor(result); 212 return result; 213 } 214 215 // optional parameter forceTrack = false 216 final protected Color setColorForTrackBlock(Graphics2D g2, @CheckForNull LayoutBlock lb) { 217 return setColorForTrackBlock(g2, lb, false); 218 } 219 220 /** 221 * draw one line (Ballast, ties, center or 3rd rail, block lines) 222 * 223 * @param g2 the graphics context 224 * @param isMain true if drawing mainlines 225 * @param isBlock true if drawing block lines 226 */ 227 abstract protected void draw1(Graphics2D g2, boolean isMain, boolean isBlock); 228 229 /** 230 * draw two lines (rails) 231 * 232 * @param g2 the graphics context 233 * @param isMain true if drawing mainlines 234 * @param railDisplacement the offset from center to draw the lines 235 */ 236 abstract protected void draw2(Graphics2D g2, boolean isMain, float railDisplacement); 237 238 /** 239 * draw hidden track 240 * 241 * @param g2 the graphics context 242 */ 243 // abstract protected void drawHidden(Graphics2D g2); 244 // note: placeholder until I get this implemented in all sub-classes 245 // TODO: replace with abstract declaration (above) 246 final protected void drawHidden(Graphics2D g2) { 247 // nothing to do here... move along... 248 } 249 250 /** 251 * draw the text for this layout track 252 * @param g 253 * note: currently can't override (final); change this if you need to 254 */ 255 final protected void drawLayoutTrackText(Graphics2D g) { 256 // get the center coordinates 257 int x = (int) center.getX(), y = (int) center.getY(); 258 259 // get the name of this track 260 String name = getName(); 261 262 // get the FontMetrics 263 FontMetrics metrics = g.getFontMetrics(g.getFont()); 264 265 // determine the X coordinate for the text 266 x -= metrics.stringWidth(name) / 2; 267 268 // determine the Y coordinate for the text 269 y += metrics.getHeight() / 2; 270 271 // (note we add the ascent, as in java 2d 0 is top of the screen) 272 //y += (int) metrics.getAscent(); 273 274 g.drawString(name, x, y); 275 } 276 277 /** 278 * Load a file for a specific arrow ending. 279 * 280 * @param n The arrow type as a number 281 * @param arrowsCountMenu menu containing the arrows to set visible 282 * selection 283 * @return An item for the arrow menu 284 */ 285 public JCheckBoxMenuItem loadArrowImageToJCBItem(int n, JMenu arrowsCountMenu) { 286 ImageIcon imageIcon = new ImageIcon(FileUtil.findURL("program:resources/icons/decorations/ArrowStyle" + n + ".png")); 287 JCheckBoxMenuItem jcbmi = new JCheckBoxMenuItem(imageIcon); 288 arrowsCountMenu.add(jcbmi); 289 jcbmi.setToolTipText(Bundle.getMessage("DecorationStyleMenuToolTip")); 290 // can't set selected here because the ActionListener has to be set first 291 return jcbmi; 292 } 293 protected static final int NUM_ARROW_TYPES = 6; 294 295 /** 296 * highlight unconnected connections 297 * 298 * @param g2 the graphics context 299 * @param specificType the specific connection to draw (or NONE for all) 300 */ 301 abstract protected void highlightUnconnected(Graphics2D g2, HitPointType specificType); 302 303 // optional parameter specificType = NONE 304 final protected void highlightUnconnected(Graphics2D g2) { 305 highlightUnconnected(g2, HitPointType.NONE); 306 } 307 308 /** 309 * draw the edit controls 310 * 311 * @param g2 the graphics context 312 */ 313 abstract protected void drawEditControls(Graphics2D g2); 314 315 /** 316 * Draw the turnout controls 317 * 318 * @param g2 the graphics context 319 */ 320 abstract protected void drawTurnoutControls(Graphics2D g2); 321 322 /** 323 * Draw track decorations 324 * 325 * @param g2 the graphics context 326 */ 327 abstract protected void drawDecorations(Graphics2D g2); 328 329 /** 330 * Get the hidden state of the track element. 331 * 332 * @return true if hidden; false otherwise 333 */ 334 final public boolean isHidden() { 335 return hidden; 336 } 337 338 final public void setHidden(boolean hide) { 339 if (hidden != hide) { 340 hidden = hide; 341 if (layoutEditor != null) { 342 layoutEditor.redrawPanel(); 343 } 344 } 345 } 346 347 private boolean hidden = false; 348 349 /* 350 * non-accessor methods 351 */ 352 /** 353 * get turnout state string 354 * 355 * @param turnoutState of the turnout 356 * @return the turnout state string 357 */ 358 final public String getTurnoutStateString(int turnoutState) { 359 String result = ""; 360 if (turnoutState == Turnout.CLOSED) { 361 result = Bundle.getMessage("TurnoutStateClosed"); 362 } else if (turnoutState == Turnout.THROWN) { 363 result = Bundle.getMessage("TurnoutStateThrown"); 364 } else { 365 result = Bundle.getMessage("BeanStateUnknown"); 366 } 367 return result; 368 } 369 370 /** 371 * Check for active block boundaries. 372 * <p> 373 * If any connection point of a layout track object has attached objects, 374 * such as signal masts, signal heads or NX sensors, the layout track object 375 * cannot be deleted. 376 * 377 * @return true if the layout track object can be deleted. 378 */ 379 abstract public boolean canRemove(); 380 381 /** 382 * Display the attached items that prevent removing the layout track item. 383 * 384 * @param itemList A list of the attached heads, masts and/or sensors. 385 * @param typeKey The object type such as Turnout, Level Crossing, etc. 386 */ 387 final public void displayRemoveWarningDialog(List<String> itemList, String typeKey) { 388 itemList.sort(null); 389 StringBuilder msg = new StringBuilder(Bundle.getMessage("MakeLabel", // NOI18N 390 Bundle.getMessage("DeleteTrackItem", Bundle.getMessage(typeKey)))); // NOI18N 391 for (String item : itemList) { 392 msg.append("\n " + item); // NOI18N 393 } 394 JmriJOptionPane.showMessageDialog(layoutEditor, 395 msg.toString(), 396 Bundle.getMessage("WarningTitle"), // NOI18N 397 JmriJOptionPane.WARNING_MESSAGE); 398 } 399 400 /** 401 * scale this LayoutTrack's coordinates by the x and y factors 402 * 403 * @param xFactor the amount to scale X coordinates 404 * @param yFactor the amount to scale Y coordinates 405 */ 406 abstract public void scaleCoords(double xFactor, double yFactor); 407 408 /** 409 * translate this LayoutTrack's coordinates by the x and y factors 410 * 411 * @param xFactor the amount to translate X coordinates 412 * @param yFactor the amount to translate Y coordinates 413 */ 414 abstract public void translateCoords(double xFactor, double yFactor); 415 416 /** 417 * rotate this LayoutTrack's coordinates by angleDEG's 418 * 419 * @param angleDEG the amount to rotate in degrees 420 */ 421 abstract public void rotateCoords(double angleDEG); 422 423 final protected Point2D rotatePoint(@Nonnull Point2D p, double sineRot, double cosineRot) { 424 double cX = center.getX(); 425 double cY = center.getY(); 426 427 double deltaX = p.getX() - cX; 428 double deltaY = p.getY() - cY; 429 430 double x = cX + cosineRot * deltaX - sineRot * deltaY; 431 double y = cY + sineRot * deltaX + cosineRot * deltaY; 432 433 return new Point2D.Double(x, y); 434 } 435 436 /** 437 * find the hit (location) type for a point 438 * 439 * @param hitPoint the point 440 * @param useRectangles whether to use (larger) rectangles or (smaller) 441 * circles for hit testing 442 * @param requireUnconnected whether to only return hit types for free 443 * connections 444 * @return the location type for the point (or NONE) 445 * @since 7.4.3 446 */ 447 abstract protected HitPointType findHitPointType(@Nonnull Point2D hitPoint, 448 boolean useRectangles, 449 boolean requireUnconnected); 450 451 // optional useRectangles & requireUnconnected parameters default to false 452 final protected HitPointType findHitPointType(@Nonnull Point2D p) { 453 return findHitPointType(p, false, false); 454 } 455 456 // optional requireUnconnected parameter defaults to false 457 final protected HitPointType findHitPointType(@Nonnull Point2D p, boolean useRectangles) { 458 return findHitPointType(p, useRectangles, false); 459 } 460 461 /** 462 * return the coordinates for a specified connection type (abstract: should 463 * be overridden by ALL subclasses) 464 * 465 * @param connectionType the connection type 466 * @return the coordinates for the specified connection type 467 */ 468 abstract public Point2D getCoordsForConnectionType(HitPointType connectionType); 469 470 /** 471 * @return the bounds of this track 472 */ 473 abstract public Rectangle2D getBounds(); 474 475 /** 476 * show the popup menu for this layout track 477 * 478 * @param mouseEvent the mouse down event that triggered this popup 479 * @return the popup menu for this layout track 480 */ 481 @Nonnull 482 abstract protected JPopupMenu showPopup(@Nonnull JmriMouseEvent mouseEvent); 483 484 /** 485 * Att items to the popup menu that's common to all implementing classes. 486 * @param mouseEvent the mouse down event that triggered this popup 487 * @param popup the popup menu 488 */ 489 protected void addCommonPopupItems(@Nonnull JmriMouseEvent mouseEvent, @Nonnull JPopupMenu popup) { 490 popup.addSeparator(); 491 setLogixNGPositionableMenu(popup); 492 layoutEditor.addPopupItems(popup, mouseEvent); 493 } 494 495 @Override 496 public String getNameString() { 497 return getName(); 498 } 499 500 @Override 501 public String getEditorName() { 502 return getLayoutEditor().getName(); 503 } 504 505 @Override 506 public int getX() { 507 return (int) center.getX(); 508 } 509 510 @Override 511 public int getY() { 512 return (int) center.getY(); 513 } 514 515 @Override 516 public String getTypeName() { 517 return layoutTrack.getTypeName(); 518 } 519 520 /** 521 * Check if edit of a conditional is in progress. 522 * 523 * @return true if this is the case, after showing dialog to user 524 */ 525 private boolean checkEditConditionalNG() { 526 if (_inEditInlineLogixNGMode) { 527 // Already editing a LogixNG, ask for completion of that edit 528 JmriJOptionPane.showMessageDialog(null, 529 Bundle.getMessage("Error_InlineLogixNGInEditMode"), // NOI18N 530 Bundle.getMessage("ErrorTitle"), // NOI18N 531 JmriJOptionPane.ERROR_MESSAGE); 532 _inlineLogixNGEdit.bringToFront(); 533 return true; 534 } 535 return false; 536 } 537 538 /** 539 * Add a menu entry to edit Id of the Positionable item 540 * 541 * @param popup the menu to add the entry to 542 */ 543 public void setLogixNGPositionableMenu(JPopupMenu popup) { 544 JMenu logixNG_Menu = new JMenu("LogixNG"); 545 popup.add(logixNG_Menu); 546 547 logixNG_Menu.add(new AbstractAction(Bundle.getMessage("LogixNG_Inline")) { 548 @Override 549 public void actionPerformed(ActionEvent e) { 550 if (checkEditConditionalNG()) return; 551 552 if (getLogixNG() == null) { 553 LogixNG logixNG = InstanceManager.getDefault(LogixNG_Manager.class) 554 .createLogixNG(null, true); 555 logixNG.setInlineLogixNG(LayoutTrackView.this); 556 logixNG.activate(); 557 logixNG.setEnabled(true); 558 logixNG.clearStartup(); 559 setLogixNG(logixNG); 560 } 561 LogixNGEditor logixNGEditor = new LogixNGEditor(null, getLogixNG().getSystemName()); 562 logixNGEditor.addEditorEventListener((HashMap<String, String> data) -> { 563 _inEditInlineLogixNGMode = false; 564 data.forEach((key, value) -> { 565 if (key.equals("Finish")) { // NOI18N 566 _inlineLogixNGEdit = null; 567 _inEditInlineLogixNGMode = false; 568 } else if (key.equals("Delete")) { // NOI18N 569 _inEditInlineLogixNGMode = false; 570 deleteLogixNG(getLogixNG()); 571 } else if (key.equals("chgUname")) { // NOI18N 572 getLogixNG().setUserName(value); 573 } 574 }); 575 if (getLogixNG() != null && getLogixNG().getNumConditionalNGs() == 0) { 576 deleteLogixNG_Internal(getLogixNG()); 577 } 578 }); 579 logixNGEditor.bringToFront(); 580 _inEditInlineLogixNGMode = true; 581 _inlineLogixNGEdit = logixNGEditor; 582 } 583 }); 584 } 585 586 private void deleteLogixNG(LogixNG logixNG) { 587 DeleteBean<LogixNG> deleteBean = new DeleteBean<>( 588 InstanceManager.getDefault(LogixNG_Manager.class)); 589 590 boolean hasChildren = logixNG.getNumConditionalNGs() > 0; 591 592 deleteBean.delete(logixNG, hasChildren, (t)->{deleteLogixNG_Internal(t);}, 593 (t,list)->{logixNG.getListenerRefsIncludingChildren(list);}, 594 jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName()); 595 } 596 597 private void deleteLogixNG_Internal(LogixNG logixNG) { 598 logixNG.setEnabled(false); 599 try { 600 InstanceManager.getDefault(LogixNG_Manager.class).deleteBean(logixNG, "DoDelete"); 601 logixNG.getInlineLogixNG().setLogixNG(null); 602 } catch (PropertyVetoException e) { 603 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 604 log.error("{} : Could not Delete.", e.getMessage()); 605 } 606 } 607 608 /** 609 * Get the LogixNG of this Positionable. 610 * @return the LogixNG or null if it has no LogixNG 611 */ 612 @Override 613 public LogixNG getLogixNG() { 614 return _logixNG; 615 } 616 617 /** 618 * Set the LogixNG of this Positionable. 619 * @param logixNG the LogixNG or null if remove the LogixNG from the Positionable 620 */ 621 @Override 622 public void setLogixNG(LogixNG logixNG) { 623 this._logixNG = logixNG; 624 } 625 626 @Override 627 public void setLogixNG_SystemName(String systemName) { 628 this._logixNG_SystemName = systemName; 629 } 630 631 @Override 632 public void setupLogixNG() { 633 _logixNG = InstanceManager.getDefault(LogixNG_Manager.class) 634 .getBySystemName(_logixNG_SystemName); 635 if (_logixNG == null) { 636 throw new RuntimeException(String.format( 637 "LogixNG %s is not found for LayoutTrackView %s in panel %s", 638 _logixNG_SystemName, getName(), layoutEditor.getName())); 639 } 640 _logixNG.setInlineLogixNG(this); 641 } 642 643 /** 644 * show the popup menu for this layout track 645 * 646 * @param where to show the popup 647 * @return the popup menu for this layout track 648 */ 649 @Nonnull 650 final protected JPopupMenu showPopup(Point2D where) { 651 return this.showPopup(new JmriMouseEvent( 652 layoutEditor.getTargetPanel(), // source 653 JmriMouseEvent.MOUSE_CLICKED, // id 654 System.currentTimeMillis(), // when 655 0, // modifiers 656 (int) where.getX(), (int) where.getY(), // where 657 0, // click count 658 true)); // popup trigger 659 660 } 661 662 /** 663 * show the popup menu for this layout track 664 * 665 * @return the popup menu for this layout track 666 */ 667 @Nonnull 668 final protected JPopupMenu showPopup() { 669 Point2D where = MathUtil.multiply(getCoordsCenter(), 670 layoutEditor.getZoom()); 671 return this.showPopup(where); 672 } 673 674 /** 675 * get the LayoutTrack connected at the specified connection type 676 * 677 * @param connectionType where on us to get the connection 678 * @return the LayoutTrack connected at the specified connection type 679 * @throws JmriException - if the connectionType is invalid 680 */ 681 abstract public LayoutTrack getConnection(HitPointType connectionType) throws JmriException; 682 683 /** 684 * set the LayoutTrack connected at the specified connection type 685 * 686 * @param connectionType where on us to set the connection 687 * @param o the LayoutTrack that is to be connected 688 * @param type where on the LayoutTrack we are connected 689 * @throws JmriException - if connectionType or type are invalid 690 */ 691 abstract public void setConnection(HitPointType connectionType, LayoutTrack o, HitPointType type) throws JmriException; 692 693 /** 694 * abstract method... subclasses should implement _IF_ they need to recheck 695 * their block boundaries 696 */ 697 abstract protected void reCheckBlockBoundary(); 698 699 /** 700 * get the layout connectivity for this track 701 * 702 * @return the list of Layout Connectivity objects 703 */ 704 abstract protected List<LayoutConnectivity> getLayoutConnectivity(); 705 706 /** 707 * return true if this connection type is disconnected 708 * 709 * @param connectionType the connection type to test 710 * @return true if the connection for this connection type is free 711 */ 712 public boolean isDisconnected(HitPointType connectionType) { 713 throw new IllegalArgumentException("should have called in Object instead of View (temporary)"); 714 } 715 716 /** 717 * return a list of the available connections for this layout track 718 * 719 * @return the list of available connections 720 */ 721 // note: used by LayoutEditorChecks.setupCheckUnConnectedTracksMenu() 722 // 723 // This could have just returned a boolean but I thought a list might be 724 // more useful (eventually... not currently being used; we just check to see 725 // if it's not empty.) 726 @Nonnull 727 abstract public List<HitPointType> checkForFreeConnections(); 728 729 /** 730 * determine if all the appropriate blocks have been assigned to this track 731 * 732 * @return true if all appropriate blocks have been assigned 733 */ 734 // note: used by LayoutEditorChecks.setupCheckUnBlockedTracksMenu() 735 // 736 abstract public boolean checkForUnAssignedBlocks(); 737 738 /** 739 * check this track and its neighbors for non-contiguous blocks 740 * <p> 741 * For each (non-null) blocks of this track do: #1) If it's got an entry in 742 * the blockNamesToTrackNameSetMap then #2) If this track is not in one of 743 * the TrackNameSets for this block #3) add a new set (with this 744 * block/track) to blockNamesToTrackNameSetMap and #4) check all the 745 * connections in this block (by calling the 2nd method below) 746 * <p> 747 * Basically, we're maintaining contiguous track sets for each block found 748 * (in blockNamesToTrackNameSetMap) 749 * 750 * @param blockNamesToTrackNameSetMaps hashmap of key:block names to lists 751 * of track name sets for those blocks 752 */ 753 // note: used by LayoutEditorChecks.setupCheckNonContiguousBlocksMenu() 754 // 755 abstract public void checkForNonContiguousBlocks( 756 @Nonnull HashMap<String, List<Set<String>>> blockNamesToTrackNameSetMaps); 757 758 /** 759 * recursive routine to check for all contiguous tracks in this blockName 760 * 761 * @param blockName the block that we're checking for 762 * @param TrackNameSet the set of track names in this block 763 */ 764 abstract public void collectContiguousTracksNamesInBlockNamed( 765 @Nonnull String blockName, 766 @Nonnull Set<String> TrackNameSet); 767 768 /** 769 * Assign all the layout blocks in this track 770 * 771 * @param layoutBlock to this layout block (used by the Tools menu's "Assign 772 * block to selection" item) 773 */ 774 abstract public void setAllLayoutBlocks(LayoutBlock layoutBlock); 775 776 protected boolean removeInlineLogixNG() { 777 LogixNG logixNG = getLogixNG(); 778 779 if (logixNG == null) return true; 780 781 DeleteBean<LogixNG> deleteBean = new DeleteBean<>( 782 InstanceManager.getDefault(LogixNG_Manager.class)); 783 784 boolean hasChildren = logixNG.getNumConditionalNGs() > 0; 785 786 return deleteBean.delete(logixNG, hasChildren, (t)->{deleteLogixNG_Internal(t);}, 787 (t,list)->{logixNG.getListenerRefsIncludingChildren(list);}, 788 jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName(), 789 true); 790 } 791 792 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTrackView.class); 793}