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                    setAttributes(getPopupUtility(), this);
103                    updateSize();
104                } else if (val instanceof jmri.IdTag){
105                    // most IdTags are Reportable objects, so
106                    // this needs to be before Reportable
107                    setText(((jmri.IdTag)val).getDisplayName());
108                    setIcon(null);
109                    _text = true;
110                    _icon = false;
111                    updateSize();
112                } else if (val instanceof Reportable) {
113                    setText(((Reportable)val).toReportString());
114                    setIcon(null);
115                    _text = true;
116                    _icon = false;
117                    updateSize();
118                } else {
119                    log.warn("can't display current value of {}, val= {} of Class {}", getNamedMemory().getName(), val, val.getClass().getName());
120                }
121            } else {
122                // map exists, use it
123                NamedIcon newicon = map.get(key.toString());
124                if (newicon != null) {
125
126                    setText(null);
127                    super.setIcon(newicon);
128                    _text = false;
129                    _icon = true;
130                    updateSize();
131                } else {
132                    // no match, use default
133                    setIcon(getDefaultIcon());
134
135                    setText(null);
136                    _text = false;
137                    _icon = true;
138                    updateSize();
139                }
140            }
141        } else {
142            setIcon(null);
143            setText(defaultText);
144            _text = true;
145            _icon = false;
146            updateSize();
147        }
148    }
149
150    private final JCheckBoxMenuItem updateBlockItem = new JCheckBoxMenuItem("Update Block Details");
151
152    // force a redisplay when content changes
153    @Override
154    public void propertyChange(java.beans.PropertyChangeEvent e) {
155        super.propertyChange(e);
156        panel.redrawPanel();
157    }
158
159    @Override
160    public boolean showPopUp(JPopupMenu popup) {
161        if (isEditable()) {
162            popup.add(updateBlockItem);
163            updateBlockItem.setSelected(updateBlockValueOnChange());
164            updateBlockItem.addActionListener((java.awt.event.ActionEvent e) -> updateBlockValueOnChange(updateBlockItem.isSelected()));
165        }  // end of selectable
166        return super.showPopUp(popup);
167    }
168
169    @Override
170    public void setMemory(String pName) {
171        super.setMemory(pName);
172        lBlock = jmri.InstanceManager.getDefault(LayoutBlockManager.class).getBlockWithMemoryAssigned(getMemory());
173    }
174
175    @Override
176    protected void setValue(Object obj) {
177        if (updateBlockValue && lBlock != null) {
178            lBlock.getBlock().setValue(obj);
179        } else {
180            getMemory().setValue(obj);
181            updateSize();
182        }
183    }
184
185    @Override
186    protected void addRosterToIcon(RosterEntry roster) {
187        if (!jmri.InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled() || lBlock == null) {
188            super.addRosterToIcon(roster);
189            return;
190        }
191
192        int paths = lBlock.getNumberOfThroughPaths();
193        jmri.Block srcBlock = null;
194        jmri.Block desBlock = null;
195        for (int i = 0; i < paths; i++) {
196            if (lBlock.isThroughPathActive(i)) {
197                srcBlock = lBlock.getThroughPathSource(i);
198                desBlock = lBlock.getThroughPathDestination(i);
199                break;
200            }
201        }
202        int dirA;
203        int dirB;
204        if (srcBlock != null && desBlock != null) {
205            dirA = lBlock.getNeighbourDirection(srcBlock);
206            dirB = lBlock.getNeighbourDirection(desBlock);
207        } else {
208            dirA = jmri.Path.EAST;
209            dirB = jmri.Path.WEST;
210        }
211
212        Object[] options = {"Facing " + jmri.Path.decodeDirection(dirB),
213            "Facing " + jmri.Path.decodeDirection(dirA),
214            "Do Not Add"};
215        int n = JmriJOptionPane.showOptionDialog(this,
216                "Would you like to assign loco "
217                + roster.titleString() + " to this location",
218                "Assign Loco",
219                JmriJOptionPane.DEFAULT_OPTION,
220                JmriJOptionPane.QUESTION_MESSAGE,
221                null,
222                options,
223                options[2]);
224        if (n == 2 || n==JmriJOptionPane.CLOSED_OPTION ) { // array position 2, Do Not Add
225            return;
226        }
227        if (n == 0) { // array position 0, facing DirB
228            flipRosterIcon = true;
229            if (updateBlockValue) {
230                lBlock.getBlock().setDirection(dirB);
231            }
232        } else {
233            flipRosterIcon = false;
234            if (updateBlockValue) {
235                lBlock.getBlock().setDirection(dirA);
236            }
237        }
238        if (getMemory().getValue() == roster) {
239            //No change in the loco but a change in direction facing might have occurred
240            updateIconFromRosterVal(roster);
241        } else {
242            setValue(roster);
243        }
244    }
245
246    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MemoryIcon.class);
247}