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    public static 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    @Override
152    public jmri.NamedBean getNamedBean() {
153        return null;
154    }
155
156    @SuppressFBWarnings(value="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification="Portals always have userNames")
157    public void setPortal(Portal portal) {
158        if (portal == null) {
159            return;
160        }
161        if (_portal != null) {
162            _portal.removePropertyChangeListener(this);
163        }
164        _portal = portal;
165        _portal.addPropertyChangeListener(this);
166        setToolTip(new ToolTip(_portal.getDescription(), 0, 0, this));
167    }
168
169    public void setStatus(String status) {
170        // if (log.isDebugEnabled()) log.debug("Icon "+getPortal().getName()+" setStatus("+status+") regular="+_regular+" icon= "+_iconMap.get(status));
171        setIcon(_iconMap.get(status));
172        _status = status;
173        updateSize();
174        repaint();
175    }
176
177    public String getStatus() {
178        return _status;
179    }
180
181    @Override
182    public void remove() {
183        ((ControlPanelEditor)_editor).getCircuitBuilder().deletePortalIcon(this);
184        super.remove();
185    }
186
187    @Override
188    public void displayState(int state) {
189        switch (state) {
190            case 0x02:
191                if (_hide) {
192                    setStatus(HIDDEN);
193                } else {
194                    setStatus(TO_ARROW);
195                }
196                break;
197            case 0x04:
198                if (_hide) {
199                    setStatus(HIDDEN);
200                } else {
201                    setStatus(FROM_ARROW);
202                }
203                break;
204            case 0x10:
205                setStatus(VISIBLE);
206                break;
207            case 0x20:
208                setStatus(PATH);
209                break;
210            default:
211                setStatus(HIDDEN);
212                break;
213        }
214    }
215
216    @Override
217    public void propertyChange(PropertyChangeEvent e) {
218        Object source = e.getSource();
219//        if (log.isDebugEnabled()) log.debug("Icon "+getPortal().getName()+" PropertyChange= "+e.getPropertyName()+
220//          " oldValue= "+e.getOldValue().toString()+" newValue= "+e.getNewValue().toString());
221        if (source instanceof Portal) {
222            String propertyName = e.getPropertyName();
223            if ("Direction".equals(propertyName)) {
224                if (_hide) {
225                    setStatus(HIDDEN);
226                    return;
227                }
228                switch (((Integer) e.getNewValue())) {
229                    case Portal.UNKNOWN:
230                        setStatus(HIDDEN);
231                        break;
232                    case Portal.ENTER_TO_BLOCK:
233                        setStatus(TO_ARROW);
234                        break;
235                    case Portal.ENTER_FROM_BLOCK:
236                        setStatus(FROM_ARROW);
237                        break;
238                    default:
239                        log.warn("Unhandled portal value: {}", e.getNewValue() );
240                }
241            } else if ("NameChange".equals(propertyName)) {
242                setName((String) e.getNewValue());
243            } else if ("portalDelete".equals(propertyName)) {
244                remove();
245            }
246        }
247    }
248
249    @Override
250    @Nonnull
251    public String getTypeString() {
252        return Bundle.getMessage("PositionableType_PortalIcon");
253    }
254
255    @Override
256    @Nonnull
257    public String getNameString() {
258        Portal p = getPortal();
259        if (p == null) return "No Portal Defined";
260        return p.getDescription();
261    }
262
263    /**
264     * Disable popup items that apply to whole selection Group.
265     * @see jmri.jmrit.display.PositionableLabel#doViemMenu()
266     */
267    @Override
268    public boolean doViemMenu() {
269        return false;
270    }
271
272    private void setRemoveMenu(JPopupMenu popup) {
273        popup.add(new AbstractAction(Bundle.getMessage("Remove")) {
274            @Override
275            public void actionPerformed(ActionEvent e) {
276                remove();
277            }
278        });
279    }
280
281    /**
282     * Use this call to set actions that will not affect whole selection Group.
283     * @see jmri.jmrit.display.PositionableLabel#setEditItemMenu(javax.swing.JPopupMenu)
284     */
285    @Override
286    public boolean showPopUp(JPopupMenu popup) {
287        popup.add(getNameString());
288        _editor.setPositionableMenu(this, popup);
289        if (isPositionable()) {
290            _editor.setShowCoordinatesMenu(this, popup);
291        }
292        _editor.setDisplayLevelMenu(this, popup);
293        popup.addSeparator();
294        popup.add(CoordinateEdit.getScaleEditAction(this));
295        popup.add(CoordinateEdit.getRotateEditAction(this));
296        popup.addSeparator();
297        setRemoveMenu(popup);
298        return true;
299    }
300
301    @Override
302    public void setLocation(int x, int y ) {
303        ControlPanelEditor cpe = (ControlPanelEditor)_editor;
304        if (cpe.getCircuitBuilder().portalIconMove(this, x, y)) {
305            super.setLocation(x, y);
306        }
307    }
308
309    @Override
310    public boolean setRotateMenu(@Nonnull JPopupMenu popup) {
311        return false;
312    }
313
314    @Override
315    public boolean setScaleMenu(@Nonnull JPopupMenu popup) {
316        return false;
317    }
318
319    @Override
320    public boolean setEditItemMenu(@Nonnull JPopupMenu popup) {
321        return false;
322    }
323
324    private static final Logger log = LoggerFactory.getLogger(PortalIcon.class);
325
326}