001package jmri.jmrit.display.controlPanelEditor;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import java.awt.event.ActionEvent;
005import java.beans.PropertyChangeEvent;
006import java.beans.PropertyChangeListener;
007import java.util.HashMap;
008
009import javax.annotation.Nonnull;
010import javax.swing.AbstractAction;
011import javax.swing.JPopupMenu;
012
013import jmri.jmrit.catalog.NamedIcon;
014import jmri.jmrit.display.CoordinateEdit;
015import jmri.jmrit.display.Editor;
016import jmri.jmrit.display.Positionable;
017import jmri.jmrit.display.PositionableIcon;
018import jmri.jmrit.display.ToolTip;
019import jmri.jmrit.display.palette.ItemPalette;
020import jmri.jmrit.logix.Portal;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024/**
025 * @author Pete Cressman Copyright (C) 2011
026 */
027public class PortalIcon extends PositionableIcon implements PropertyChangeListener {
028
029    public static final String HIDDEN = "hidden";
030    public static final String VISIBLE = "block";
031    public static final String PATH = "path";
032    public static final String TO_ARROW = "toArrow";
033    public static final String FROM_ARROW = "fromArrow";
034
035//    private NamedBeanHandle<Portal> _portalHdl;
036    private Portal _portal;
037    private String _status;
038    private boolean _regular = true; // true when TO_ARROW shows entry into ToBlock
039    private boolean _hide = false; // true when arrow should NOT show entry into ToBlock
040
041    public PortalIcon(Editor editor) {
042        super(editor);
043        _status = PortalIcon.HIDDEN;
044        makeIconMap();
045    }
046
047    public PortalIcon(Editor editor, Portal portal) {
048        this(editor);
049        setPortal(portal);
050    }
051
052    static public HashMap<String, NamedIcon> getPaletteMap() {
053        HashMap<String, HashMap<String, NamedIcon>> families = ItemPalette.getFamilyMaps("Portal");
054        if (families.keySet().isEmpty()) {
055            log.debug("Adding missing PortalIconMap");
056            ItemPalette.loadMissingItemType("Portal");
057            families = ItemPalette.getFamilyMaps("Portal");
058        }
059        HashMap<String, NamedIcon> iconMap = families.get("Standard");
060        if (iconMap == null) {
061            for (HashMap<String, NamedIcon> map : families.values()) {
062                iconMap = map;
063                break;
064            }
065        }
066        // return a copy, not ItemPalette's map!
067        return cloneMap(iconMap, null);
068    }
069
070    public void makeIconMap() {
071        _iconMap = ((ControlPanelEditor)_editor).getPortalIconMap();
072        int deg = getDegrees();
073        if (!_regular) {
074            NamedIcon a = _iconMap.get(TO_ARROW);
075            NamedIcon b = _iconMap.get(FROM_ARROW);
076            _iconMap.put(FROM_ARROW, a);
077            _iconMap.put(TO_ARROW, b);
078        }
079        setScale(getScale());
080        rotate(deg);
081        setIcon(_iconMap.get(HIDDEN));
082    }
083
084    /**
085     * PortalIcon family is managed by editor, ignoring per icon setting if any.
086     * @return the family name as set in editor.
087     */
088    @Override
089    public String getFamily() {
090        return ((ControlPanelEditor)_editor).getPortalIconFamily();
091    }
092
093    protected void setMap(HashMap<String, NamedIcon> map) {
094        _iconMap = map;
095    }
096
097    @Override
098    @Nonnull
099    public Positionable deepClone() {
100        PortalIcon pos = new PortalIcon(_editor, getPortal());
101        return finishClone(pos);
102    }
103
104    protected Positionable finishClone(PortalIcon pos) {
105        pos._regular = _regular;
106        pos._hide = _hide;
107        pos._status = _status;
108        return super.finishClone(pos);
109    }
110
111    public void setIcon(String name, NamedIcon ic) {
112        if (log.isDebugEnabled()) {
113            log.debug("Icon {} put icon key= \"{}\" icon= {}", getPortal().getName(), name, ic);
114        }
115        NamedIcon icon = cloneIcon(ic, this);
116        icon.scale(getScale(), this);
117        icon.rotate(getDegrees(), this);
118        _iconMap.put(name, icon);
119    }
120
121    /**
122     * Set direction of portal icon, renamed in 4.21.5
123     * @param set true for regular, false for opposite (flipped in edit frame)
124     */
125    public void setArrowOrientation(boolean set) {
126        if (log.isDebugEnabled()) {
127            log.debug("Icon {} setArrowOrientation regular={} from {}", getPortal().getName(), set, _regular);
128        }
129        _regular = set;
130    }
131
132    public void setHideArrows(boolean set) {
133        if (log.isDebugEnabled()) {
134            log.debug("Icon {} setHideArrows hide={} from {}", getPortal().getName(), set, _hide);
135        }
136        _hide = set;
137    }
138
139    public boolean getArrowSwitch() {
140        return _regular;
141    }
142
143    public boolean getArrowHide() {
144        return _hide;
145    }
146
147    public Portal getPortal() {
148        return _portal;
149    }
150
151    @SuppressFBWarnings(value="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification="Portals always have userNames")
152    public void setPortal(Portal portal) {
153        if (portal == null) {
154            return;
155        }
156        if (_portal != null) {
157            _portal.removePropertyChangeListener(this);
158        }
159        _portal = portal;
160        _portal.addPropertyChangeListener(this);
161        setToolTip(new ToolTip(_portal.getDescription(), 0, 0, this));
162    }
163
164    public void setStatus(String status) {
165        // if (log.isDebugEnabled()) log.debug("Icon "+getPortal().getName()+" setStatus("+status+") regular="+_regular+" icon= "+_iconMap.get(status));
166        setIcon(_iconMap.get(status));
167        _status = status;
168        updateSize();
169        repaint();
170    }
171
172    public String getStatus() {
173        return _status;
174    }
175
176    @Override
177    public void remove() {
178        ((ControlPanelEditor)_editor).getCircuitBuilder().deletePortalIcon(this);
179        super.remove();
180    }
181
182    @Override
183    public void displayState(int state) {
184        switch (state) {
185            case 0x02:
186                if (_hide) {
187                    setStatus(HIDDEN);
188                } else {
189                    setStatus(TO_ARROW);
190                }
191                break;
192            case 0x04:
193                if (_hide) {
194                    setStatus(HIDDEN);
195                } else {
196                    setStatus(FROM_ARROW);
197                }
198                break;
199            case 0x10:
200                setStatus(VISIBLE);
201                break;
202            case 0x20:
203                setStatus(PATH);
204                break;
205            default:
206                setStatus(HIDDEN);
207                break;
208        }
209    }
210
211    @Override
212    public void propertyChange(PropertyChangeEvent e) {
213        Object source = e.getSource();
214//        if (log.isDebugEnabled()) log.debug("Icon "+getPortal().getName()+" PropertyChange= "+e.getPropertyName()+
215//          " oldValue= "+e.getOldValue().toString()+" newValue= "+e.getNewValue().toString());
216        if (source instanceof Portal) {
217            String propertyName = e.getPropertyName();
218            if ("Direction".equals(propertyName)) {
219                if (_hide) {
220                    setStatus(HIDDEN);
221                    return;
222                }
223                switch (((Integer) e.getNewValue())) {
224                    case Portal.UNKNOWN:
225                        setStatus(HIDDEN);
226                        break;
227                    case Portal.ENTER_TO_BLOCK:
228                        setStatus(TO_ARROW);
229                        break;
230                    case Portal.ENTER_FROM_BLOCK:
231                        setStatus(FROM_ARROW);
232                        break;
233                    default:
234                        log.warn("Unhandled portal value: {}", e.getNewValue() );
235                }
236            } else if ("NameChange".equals(propertyName)) {
237                setName((String) e.getNewValue());
238            } else if ("portalDelete".equals(propertyName)) {
239                remove();
240            }
241        }
242    }
243
244    @Override
245    @Nonnull
246    public String getTypeString() {
247        return Bundle.getMessage("PositionableType_PortalIcon");
248    }
249
250    @Override
251    @Nonnull
252    public String getNameString() {
253        Portal p = getPortal();
254        if (p == null) return "No Portal Defined";
255        return p.getDescription();
256    }
257
258    /**
259     * Disable popup items that apply to whole selection Group.
260     * @see jmri.jmrit.display.PositionableLabel#doViemMenu()
261     */
262    @Override
263    public boolean doViemMenu() {
264        return false;
265    }
266
267    private void setRemoveMenu(JPopupMenu popup) {
268        popup.add(new AbstractAction(Bundle.getMessage("Remove")) {
269            @Override
270            public void actionPerformed(ActionEvent e) {
271                remove();
272            }
273        });
274    }
275
276    /**
277     * Use this call to set actions that will not affect whole selection Group.
278     * @see jmri.jmrit.display.PositionableLabel#setEditItemMenu(javax.swing.JPopupMenu)
279     */
280    @Override
281    public boolean showPopUp(JPopupMenu popup) {
282        popup.add(getNameString());
283        _editor.setPositionableMenu(this, popup);
284        if (isPositionable()) {
285            _editor.setShowCoordinatesMenu(this, popup);
286        }
287        _editor.setDisplayLevelMenu(this, popup);
288        popup.addSeparator();
289        popup.add(CoordinateEdit.getScaleEditAction(this));
290        popup.add(CoordinateEdit.getRotateEditAction(this));
291        popup.addSeparator();
292        setRemoveMenu(popup);
293        return true;
294    }
295
296    @Override
297    public void setLocation(int x, int y ) {
298        ControlPanelEditor cpe = (ControlPanelEditor)_editor;
299        if (cpe.getCircuitBuilder().portalIconMove(this, x, y)) {
300            super.setLocation(x, y);
301        }
302    }
303
304    @Override
305    public boolean setRotateMenu(@Nonnull JPopupMenu popup) {
306        return false;
307    }
308
309    @Override
310    public boolean setScaleMenu(@Nonnull JPopupMenu popup) {
311        return false;
312    }
313
314    @Override
315    public boolean setEditItemMenu(@Nonnull JPopupMenu popup) {
316        return false;
317    }
318
319    private final static Logger log = LoggerFactory.getLogger(PortalIcon.class);
320
321}