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