001package jmri.jmrit.display; 002 003import java.awt.Color; 004import java.awt.Dimension; 005import java.awt.Font; 006import java.awt.Point; 007import java.util.ArrayList; 008 009import javax.annotation.Nonnull; 010 011import jmri.Sensor; 012import jmri.jmrit.display.controlPanelEditor.shape.LocoLabel; 013import jmri.jmrit.logix.OBlock; 014 015/** 016 * A utility class replacing common methods formerly implementing the 017 * IndicatorTrack interface. 018 * 019 * @author Pete Cressman Copyright (c) 2012 020 */ 021public class IndicatorTrackPaths { 022 023 protected ArrayList<String> _paths; // list of paths that this icon displays 024 private boolean _showTrain; // this track icon should display _loco when occupied 025 private LocoLabel _loco = null; 026 027 protected IndicatorTrackPaths() { 028 } 029 030 protected IndicatorTrackPaths deepClone() { 031 IndicatorTrackPaths p = new IndicatorTrackPaths(); 032 if (_paths != null) { 033 p._paths = new ArrayList<>(); 034 for (int i = 0; i < _paths.size(); i++) { 035 p._paths.add(_paths.get(i)); 036 } 037 } 038 p._showTrain = _showTrain; 039 return p; 040 } 041 042 protected ArrayList<String> getPaths() { 043 return _paths; 044 } 045 046 protected void setPaths(ArrayList<String> paths) { 047 _paths = paths; 048 } 049 050 protected void addPath(String path) { 051 if (_paths == null) { 052 _paths = new ArrayList<>(); 053 } 054 if (path != null && path.length() > 0 && !_paths.contains(path.trim() )) { 055 _paths.add(path.trim()); 056 } 057 log.debug("addPath \"{}\" #paths= {}", path, _paths.size()); 058 } 059 060 protected void removePath(String path) { 061 if ( _paths != null && path != null && path.length() > 0 ) { 062 _paths.remove(path.trim()); 063 } 064 } 065 066 protected void setShowTrain(boolean set) { 067 _showTrain = set; 068 } 069 070 protected boolean showTrain() { 071 return _showTrain; 072 } 073 074 protected synchronized String getStatus(OBlock block, int state) { 075 String pathName = block.getAllocatedPathName(); 076 String status; 077 removeLocoIcon(); 078 if ((state & OBlock.TRACK_ERROR) != 0) { 079 status = "ErrorTrack"; 080 } else if ((state & OBlock.OUT_OF_SERVICE) != 0) { 081 status = "DontUseTrack"; 082 } else if ((state & OBlock.ALLOCATED) != 0) { 083 if (_paths != null && _paths.contains(pathName)) { 084 if ((state & OBlock.RUNNING) != 0) { 085 status = "PositionTrack"; //occupied by train on a warrant 086 } else if ((state & OBlock.OCCUPIED) != 0) { 087 status = "OccupiedTrack"; // occupied by rouge train 088 } else { 089 status = "AllocatedTrack"; 090 } 091 } else { 092 status = "ClearTrack"; // icon not on path 093 } 094 } else if ((state & OBlock.OCCUPIED) != 0) { 095 status = "OccupiedTrack"; 096// } else if ((state & Sensor.UNKNOWN)!=0) { 097// status = "DontUseTrack"; 098 } else { 099 status = "ClearTrack"; 100 } 101 return status; 102 } 103 104 public void removeLocoIcon() { 105 if (_loco != null) { 106 _loco.remove(); 107 _loco = null; 108 } 109 } 110 111 /** 112 * @param block OBlock occupied by train 113 * @param pt position of track icon 114 * @param size size of track icon 115 * @param ed editor 116 * LocoLabel ctor causes editor to draw a graphic. Must be done on GUI 117 * Called from IndicatorTrackIcon.setStatus and IndicatorTurnoutIcon.setStatus 118 * Each wraps this method with ThreadingUtil.runOnLayoutEventually, so there is 119 * a time lag for when track icon changes and display of the change. 120 */ 121 @SuppressWarnings("deprecation") // The method getId() from the type Thread is deprecated since version 19 122 // The replacement Thread.threadId() isn't available before version 19 123 @jmri.InvokeOnLayoutThread 124 protected synchronized void setLocoIcon(@Nonnull OBlock block, Point pt, Dimension size, Editor ed) { 125 if (!_showTrain) { 126 removeLocoIcon(); 127 return; 128 } 129 String trainName = (String) block.getValue(); 130 if (trainName == null || trainName.isEmpty()) { 131 removeLocoIcon(); 132 return; 133 } 134 if ((block.getState() & (OBlock.OCCUPIED | OBlock.RUNNING)) == 0) { 135 // during delay of runOnLayoutEventually, state has changed 136 // don't paint loco icon 137 return; 138 } 139 if (_loco != null || pt == null) { 140 return; 141 } 142 trainName = trainName.trim(); 143 try { 144 _loco = new LocoLabel(ed); 145 } catch (Exception e) { 146 jmri.jmrit.logix.Warrant w = block.getWarrant(); 147 log.error("Exception in setLocoIcon() in thread {} {} for block \"{}\", train \"{}\" \"{}\". state= {} at pt({}, {})", 148 Thread.currentThread().getName(), Thread.currentThread().getId(), block.getDisplayName(), trainName, 149 (w!=null? w.getDisplayName(): "no warrant"), block.getState(), pt.x, pt.y, e); 150 return; 151 } 152 Font font = block.getMarkerFont(); 153 if (font == null) { 154 font = ed.getFont(); 155 } 156 int width = ed.getFontMetrics(font).stringWidth(trainName); 157 int height = ed.getFontMetrics(ed.getFont()).getHeight(); // limit height to locoIcon height 158 _loco.setLineWidth(1); 159 _loco.setLineColor(Color.BLACK); 160 _loco.setFillColor(block.getMarkerBackground()); 161 _loco.setBlock(block); 162 _loco.setWidth(width + height / 2); 163 _loco.setHeight(height + 2); 164 _loco.setCornerRadius(height); 165 _loco.setDisplayLevel(Editor.MARKERS); 166 _loco.updateSize(); 167 pt.x += (size.width - _loco.maxWidth()) / 2; 168 pt.y += (size.height - _loco.maxHeight()) / 2; 169 _loco.setLocation(pt); 170 try { 171 ed.putItem(_loco); 172 } catch (Positionable.DuplicateIdException e) { 173 // This should never happen 174 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 175 } 176 } 177 178 /* 179 * Return track name for known state of occupancy sensor 180 */ 181 protected String getStatus(int state) { 182 String status; 183 switch (state) { 184 case Sensor.ACTIVE: 185 status = "OccupiedTrack"; 186 break; 187 case Sensor.INACTIVE: 188 status = "ClearTrack"; 189 break; 190 case Sensor.UNKNOWN: 191 status = "DontUseTrack"; 192 break; 193 default: 194 status = "ErrorTrack"; 195 break; 196 } 197 return status; 198 } 199 200 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IndicatorTrackPaths.class); 201 202}