001package jmri.jmrit.entryexit; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.beans.PropertyChangeSupport; 006import java.util.ArrayList; 007import java.util.HashMap; 008import java.util.List; 009 010import javax.annotation.CheckForNull; 011import javax.swing.JMenuItem; 012 013import jmri.NamedBean; 014import jmri.SignalMast; 015import jmri.jmrit.display.layoutEditor.LayoutBlock; 016import jmri.jmrit.display.layoutEditor.LayoutEditor; 017 018public class Source implements PropertyChangeListener { 019 020 JMenuItem clear = null; 021 JMenuItem cancel = null; 022 JMenuItem editCancel = null; 023 JMenuItem editClear = null; 024 JMenuItem editOneClick = null; 025 JMenuItem oneClick = null; 026 027 /** 028 * String constant for the active property. 029 */ 030 public static final String PROPERTY_ACTIVE = "active"; 031 032 NamedBean sourceObject = null; 033 NamedBean sourceSignal = null; 034 //String ref = "Empty"; 035 transient PointDetails pd = null; 036 037 EntryExitPairs manager = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class); 038 039 //Using Object here rather than sourceSensor, working on the basis that it might 040 //one day be possible to have a signal icon selectable on a panel and 041 //generate a propertychange, so hence do not want to tie it down at this stage. 042 transient HashMap<PointDetails, DestinationPoints> pointToDest = new HashMap<PointDetails, DestinationPoints>(); 043 044 public Source(PointDetails point/*, ArrayList<LayoutBlock> protectingBlock*/) { 045 if (point.getSensor() != null) { 046 addSourceObject(point.getSensor()); 047 } else { 048 addSourceObject(point.getSignal()); 049 } 050 //protectingBlocks = protectingBlock; 051 point.setSource(Source.this); 052 sourceSignal = point.getSignal(); 053 pd = point; 054 } 055 056 public boolean isEnabled(Object dest, LayoutEditor panel) { 057 PointDetails lookingFor = manager.getPointDetails(dest, panel); 058 if (pointToDest.containsKey(lookingFor)) { 059 return pointToDest.get(lookingFor).isEnabled(); 060 } 061 return true; 062 } 063 064 public void setEnabled(Object dest, LayoutEditor panel, boolean boo) { 065 PointDetails lookingFor = manager.getPointDetails(dest, panel); 066 if (pointToDest.containsKey(lookingFor)) { 067 pointToDest.get(lookingFor).setEnabled(boo); 068 } 069 } 070 071 /** 072 * Property change support for table in AddEntryExitPairPanel. 073 * Catch when paths go active. 074 * @since 4.17.4 075 */ 076 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 077 078 /** 079 * Add property change listener. 080 * @since 4.17.4 081 * @param listener the pcl to add. 082 */ 083 public void addPropertyChangeListener(PropertyChangeListener listener) { 084 pcs.addPropertyChangeListener(listener); 085 } 086 087 /** 088 * Remove property change listener. 089 * @since 4.17.4 090 * @param listener the pcl to remove. 091 */ 092 public void removePropertyChangeListener(PropertyChangeListener listener) { 093 pcs.removePropertyChangeListener(listener); 094 } 095 096 /** 097 * @since 4.17.4 098 */ 099 @Override 100 public void propertyChange(PropertyChangeEvent evt) { 101 pcs.firePropertyChange(PROPERTY_ACTIVE, evt.getOldValue(), evt.getNewValue()); 102 } 103 104 105 void cancelClearInterlockFromSource(int cancelClear) { 106 for (DestinationPoints dp : pointToDest.values()) { 107 if (dp.isActive()) { 108 dp.cancelClearInterlock(cancelClear); 109 } 110 } 111 } 112 113 void setMenuEnabled(boolean boo) { 114 if (clear != null) { 115 clear.setEnabled(boo); 116 } 117 if (cancel != null) { 118 cancel.setEnabled(boo); 119 } 120 if (editClear != null) { 121 editClear.setEnabled(boo); 122 } 123 if (editCancel != null) { 124 editCancel.setEnabled(boo); 125 } 126 if (oneClick != null) { 127 oneClick.setEnabled(!boo); 128 } 129 if (editOneClick != null) { 130 editOneClick.setEnabled(!boo); 131 } 132 } 133 134 /** 135 * @since 4.17.4 136 * Making the source object available for scripting in Jython. 137 * @return the point details. 138 */ 139 public PointDetails getPoint() { 140 return pd; 141 } 142 143 LayoutBlock getStart() { 144 return pd.getFacing(); 145 } 146 147 List<LayoutBlock> getSourceProtecting() { 148 return pd.getProtecting(); 149 } 150 151 NamedBean getSourceSignal() { 152 if (sourceSignal == null) { 153 pd.getSignal(); 154 } 155 return sourceSignal; 156 } 157 158 /** 159 * @since 4.17.4 160 * Add Property Change Listener. 161 * @param dest the points details to add. 162 * @param id the points details id. 163 */ 164 public void addDestination(PointDetails dest, String id) { 165 if (pointToDest.containsKey(dest)) { 166 return; 167 } 168 169 DestinationPoints dstPoint = new DestinationPoints(dest, id, this); 170 dest.setDestination(dstPoint, this); 171 pointToDest.put(dest, dstPoint); 172 dstPoint.addPropertyChangeListener(this); 173 } 174 175 /** 176 * @since 4.17.4 177 * Remove Property Change Listener. 178 * @param dest the point details location to remove. 179 */ 180 public void removeDestination(PointDetails dest) { 181 pointToDest.get(dest).dispose(); 182 pointToDest.remove(dest); 183 dest.removePropertyChangeListener(this); 184 if (pointToDest.isEmpty()) { 185 getPoint().removeSource(this); 186 } 187 } 188 189 private void addSourceObject(NamedBean source) { 190 if (sourceObject == source) { 191 return; 192 } 193 sourceObject = source; 194 } 195 196 Object getSourceObject() { 197 return sourceObject; 198 } 199 200 public ArrayList<PointDetails> getDestinationPoints() { 201 return new ArrayList<>(pointToDest.keySet()); 202 } 203 204 public boolean isDestinationValid(PointDetails destPoint) { 205 return pointToDest.containsKey(destPoint); 206 } 207 208 public boolean getUniDirection(Object dest, LayoutEditor panel) { 209 //Work on the principle that if the source is uniDirection, then the destination has to be. 210 PointDetails lookingFor = manager.getPointDetails(dest, panel); 211 if (pointToDest.containsKey(lookingFor)) { 212 return pointToDest.get(lookingFor).getUniDirection(); 213 } 214 return true; 215 } 216 217 public void setUniDirection(Object dest, LayoutEditor panel, boolean set) { 218 219 PointDetails lookingFor = manager.getPointDetails(dest, panel); 220 if (pointToDest.containsKey(lookingFor)) { 221 pointToDest.get(lookingFor).setUniDirection(set); 222 } 223 } 224 225 public boolean canBeBiDirection(Object dest, LayoutEditor panel) { 226 if (getSourceSignal() == null) { 227 return true; 228 } 229 //Work on the pinciple that if the source is uniDirection, then the destination has to be. 230 PointDetails lookingFor = manager.getPointDetails(dest, panel); 231 if (pointToDest.containsKey(lookingFor)) { 232 return pointToDest.get(lookingFor).getSignal() == null; 233 } 234 return false; 235 } 236 237 public boolean isRouteActive(PointDetails endpoint) { 238 if (pointToDest.containsKey(endpoint)) { 239 return pointToDest.get(endpoint).isActive(); 240 } 241 return false; 242 } 243 244 public void activeBean(DestinationPoints dest, boolean reverseDirection) { 245 if (dest != null) { 246 dest.activeBean(reverseDirection); 247 } 248 } 249 250 public DestinationPoints getDestForPoint(PointDetails dp) { 251 return pointToDest.get(dp); 252 } 253 254 public int getNumberOfDestinations() { 255 return pointToDest.size(); 256 } 257 258 public void setEntryExitType(Object dest, LayoutEditor panel, int type) { 259 PointDetails lookingFor = manager.getPointDetails(dest, panel); 260 if (pointToDest.containsKey(lookingFor)) { 261 pointToDest.get(lookingFor).setEntryExitType(type); 262 } 263 if (type == EntryExitPairs.FULLINTERLOCK) { 264 if (sourceSignal instanceof SignalMast) { 265 if (!manager.isAbsSignalMode()) { 266 ((SignalMast) sourceSignal).setHeld(true); 267 } 268 } 269 } 270 } 271 272 public int getEntryExitType(Object dest, LayoutEditor panel) { 273 PointDetails lookingFor = manager.getPointDetails(dest, panel); 274 if (pointToDest.containsKey(lookingFor)) { 275 return pointToDest.get(lookingFor).getEntryExitType(); 276 } 277 278 return 0x00; 279 } 280 281 public void cancelInterlock(Object dest, LayoutEditor panel) { 282 PointDetails lookingFor = manager.getPointDetails(dest, panel); 283 if (pointToDest.containsKey(lookingFor)) { 284 pointToDest.get(lookingFor).cancelClearInterlock(EntryExitPairs.CANCELROUTE); 285 } 286 } 287 288 @CheckForNull 289 public String getUniqueId(Object dest, LayoutEditor panel) { 290 PointDetails lookingFor = manager.getPointDetails(dest, panel); 291 if (pointToDest.containsKey(lookingFor)) { 292 return pointToDest.get(lookingFor).getUniqueId(); 293 } 294 return null; 295 } 296 297 public ArrayList<String> getDestinationUniqueId() { 298 ArrayList<String> rList = new ArrayList<>(); 299 for (DestinationPoints d : pointToDest.values()) { 300 rList.add(d.getUniqueId()); 301 } 302 return rList; 303 } 304 305 @CheckForNull 306 public DestinationPoints getByUniqueId(String id) { 307 for (DestinationPoints d : pointToDest.values()) { 308 if (d.getUniqueId().equals(id)) { 309 return d; 310 } 311 } 312 return null; 313 } 314 315 @CheckForNull 316 public DestinationPoints getByUserName(String id) { 317 for (DestinationPoints d : pointToDest.values()) { 318 String uname = d.getUserName(); 319 if (uname != null && uname.equals(id)) { 320 return d; 321 } 322 } 323 return null; 324 } 325 326}