001package jmri.jmrit.display.layoutEditor;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import javax.swing.JCheckBoxMenuItem;
006import javax.swing.JPopupMenu;
007
008import jmri.Reportable;
009import jmri.jmrit.catalog.NamedIcon;
010import jmri.jmrit.roster.RosterEntry;
011import jmri.util.swing.JmriJOptionPane;
012
013/**
014 * An icon to display a status of a Memory.
015 *
016 * This is the same name as display.MemoryIcon, but a very
017 * separate class. That's not good. Unfortunately, it's too
018 * hard to disentangle that now because it's resident in the
019 * panel file that have been written out, so we just annotated
020 * the fact, but now we want to leave it on the list to fix.
021 */
022@SuppressFBWarnings(value = "NM_SAME_SIMPLE_NAME_AS_SUPERCLASS", justification="Cannot rename for user data compatiblity reasons.")
023public class MemoryIcon extends jmri.jmrit.display.MemoryIcon {
024
025    private final String defaultText = " ";
026
027    public MemoryIcon(String s, LayoutEditor panel) {
028        super(s, panel);
029        this.panel = panel;
030        log.debug("MemoryIcon ctor= {}", MemoryIcon.class.getName());
031    }
032
033    LayoutEditor panel;
034
035    @Override
036    public void setText(String text) {
037        if (text == null || text.isEmpty()) {
038            super.setText(defaultText);
039        } else {
040            super.setText(text);
041        }
042    }
043
044    private LayoutBlock lBlock = null;
045
046    public LayoutBlock getLayoutBlock() {
047        return lBlock;
048    }
049
050    public void setLayoutBlock(LayoutBlock lb) {
051        lBlock = lb;
052    }
053
054    @Override
055    public void displayState() {
056        log.debug("displayState");
057        if (getMemory() == null) {  // use default if not connected yet
058            setText(defaultText);
059            updateSize();
060            return;
061        }
062        if (re != null) {
063            jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this);
064            re = null;
065        }
066        Object key = getMemory().getValue();
067        if (key != null) {
068            java.util.HashMap<String, NamedIcon> map = getMap();
069            if (map == null) {
070                // no map, attempt to show object directly
071                Object val = key;
072                if (val instanceof jmri.jmrit.roster.RosterEntry) {
073                    jmri.jmrit.roster.RosterEntry roster = (jmri.jmrit.roster.RosterEntry) val;
074                    val = updateIconFromRosterVal(roster);
075                    flipRosterIcon = false;
076                    if (val == null) {
077                        return;
078                    }
079                }
080                if (val instanceof String) {
081                    if (((String)val).isEmpty()) {
082                        setText(defaultText);
083                    } else {
084                        setText((String) val);
085                    }
086                    setIcon(null);
087                    _text = true;
088                    _icon = false;
089                    setAttributes(getPopupUtility(), this);
090                    updateSize();
091                } else if (val instanceof javax.swing.ImageIcon) {
092                    setIcon((javax.swing.ImageIcon) val);
093                    setText(null);
094                    _text = false;
095                    _icon = true;
096                    updateSize();
097                } else if (val instanceof Number) {
098                    setText(val.toString());
099                    setIcon(null);
100                    _text = true;
101                    _icon = false;
102                    updateSize();
103                } else if (val instanceof jmri.IdTag){
104                    // most IdTags are Reportable objects, so
105                    // this needs to be before Reportable
106                    setText(((jmri.IdTag)val).getDisplayName());
107                    setIcon(null);
108                    _text = true;
109                    _icon = false;
110                    updateSize();
111                } else if (val instanceof Reportable) {
112                    setText(((Reportable)val).toReportString());
113                    setIcon(null);
114                    _text = true;
115                    _icon = false;
116                    updateSize();
117                } else {
118                    log.warn("can't display current value of {}, val= {} of Class {}", getNamedMemory().getName(), val, val.getClass().getName());
119                }
120            } else {
121                // map exists, use it
122                NamedIcon newicon = map.get(key.toString());
123                if (newicon != null) {
124
125                    setText(null);
126                    super.setIcon(newicon);
127                    _text = false;
128                    _icon = true;
129                    updateSize();
130                } else {
131                    // no match, use default
132                    setIcon(getDefaultIcon());
133
134                    setText(null);
135                    _text = false;
136                    _icon = true;
137                    updateSize();
138                }
139            }
140        } else {
141            setIcon(null);
142            setText(defaultText);
143            _text = true;
144            _icon = false;
145            updateSize();
146        }
147    }
148
149    private final JCheckBoxMenuItem updateBlockItem = new JCheckBoxMenuItem("Update Block Details");
150
151    // force a redisplay when content changes
152    @Override
153    public void propertyChange(java.beans.PropertyChangeEvent e) {
154        super.propertyChange(e);
155        panel.redrawPanel();
156    }
157
158    @Override
159    public boolean showPopUp(JPopupMenu popup) {
160        if (isEditable()) {
161            popup.add(updateBlockItem);
162            updateBlockItem.setSelected(updateBlockValueOnChange());
163            updateBlockItem.addActionListener((java.awt.event.ActionEvent e) -> updateBlockValueOnChange(updateBlockItem.isSelected()));
164        }  // end of selectable
165        return super.showPopUp(popup);
166    }
167
168    @Override
169    public void setMemory(String pName) {
170        super.setMemory(pName);
171        lBlock = jmri.InstanceManager.getDefault(LayoutBlockManager.class).getBlockWithMemoryAssigned(getMemory());
172    }
173
174    @Override
175    protected void setValue(Object obj) {
176        if (updateBlockValue && lBlock != null) {
177            lBlock.getBlock().setValue(obj);
178        } else {
179            getMemory().setValue(obj);
180            updateSize();
181        }
182    }
183
184    @Override
185    protected void addRosterToIcon(RosterEntry roster) {
186        if (!jmri.InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled() || lBlock == null) {
187            super.addRosterToIcon(roster);
188            return;
189        }
190
191        int paths = lBlock.getNumberOfThroughPaths();
192        jmri.Block srcBlock = null;
193        jmri.Block desBlock = null;
194        for (int i = 0; i < paths; i++) {
195            if (lBlock.isThroughPathActive(i)) {
196                srcBlock = lBlock.getThroughPathSource(i);
197                desBlock = lBlock.getThroughPathDestination(i);
198                break;
199            }
200        }
201        int dirA;
202        int dirB;
203        if (srcBlock != null && desBlock != null) {
204            dirA = lBlock.getNeighbourDirection(srcBlock);
205            dirB = lBlock.getNeighbourDirection(desBlock);
206        } else {
207            dirA = jmri.Path.EAST;
208            dirB = jmri.Path.WEST;
209        }
210
211        Object[] options = {"Facing " + jmri.Path.decodeDirection(dirB),
212            "Facing " + jmri.Path.decodeDirection(dirA),
213            "Do Not Add"};
214        int n = JmriJOptionPane.showOptionDialog(this,
215                "Would you like to assign loco "
216                + roster.titleString() + " to this location",
217                "Assign Loco",
218                JmriJOptionPane.DEFAULT_OPTION,
219                JmriJOptionPane.QUESTION_MESSAGE,
220                null,
221                options,
222                options[2]);
223        if (n == 2 || n==JmriJOptionPane.CLOSED_OPTION ) { // array position 2, Do Not Add
224            return;
225        }
226        if (n == 0) { // array position 0, facing DirB
227            flipRosterIcon = true;
228            if (updateBlockValue) {
229                lBlock.getBlock().setDirection(dirB);
230            }
231        } else {
232            flipRosterIcon = false;
233            if (updateBlockValue) {
234                lBlock.getBlock().setDirection(dirA);
235            }
236        }
237        if (getMemory().getValue() == roster) {
238            //No change in the loco but a change in direction facing might have occurred
239            updateIconFromRosterVal(roster);
240        } else {
241            setValue(roster);
242        }
243    }
244
245    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MemoryIcon.class);
246}