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