001package jmri.jmrit.display;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.util.Map;
006
007import javax.annotation.Nonnull;
008import javax.swing.AbstractAction;
009import javax.swing.JPopupMenu;
010import javax.swing.JSeparator;
011
012import jmri.Block;
013import jmri.InstanceManager;
014import jmri.NamedBeanHandle;
015import jmri.NamedBean.DisplayOptions;
016import jmri.jmrit.catalog.NamedIcon;
017import jmri.jmrit.throttle.ThrottleFrame;
018import jmri.jmrit.throttle.ThrottleFrameManager;
019import jmri.util.swing.JmriJOptionPane;
020import jmri.util.swing.JmriMouseEvent;
021
022/**
023 * An icon to display the value contained within a Block.
024 *
025 * @author Bob Jacobsen Copyright (c) 2004
026 */
027public class BlockContentsIcon extends MemoryIcon {
028
029    private NamedIcon defaultIcon = null;
030    java.util.HashMap<String, NamedIcon> map = null;
031    private NamedBeanHandle<Block> namedBlock;
032
033    public BlockContentsIcon(String s, Editor editor) {
034        super(s, editor);
035        resetDefaultIcon();
036        _namedIcon = defaultIcon;
037        //By default all text objects are left justified
038        _popupUtil.setJustification(LEFT);
039        this.setTransferHandler(new TransferHandler());
040    }
041
042    public BlockContentsIcon(NamedIcon s, Editor editor) {
043        super(s, editor);
044        setDisplayLevel(Editor.LABELS);
045        defaultIcon = s;
046        _popupUtil.setJustification(LEFT);
047        log.debug("BlockContentsIcon ctor= {}", BlockContentsIcon.class.getName());
048        this.setTransferHandler(new TransferHandler());
049    }
050
051    @Override
052    @Nonnull
053    public Positionable deepClone() {
054        BlockContentsIcon pos = new BlockContentsIcon("", _editor);
055        return finishClone(pos);
056    }
057
058    protected Positionable finishClone(BlockContentsIcon pos) {
059        pos.setBlock(namedBlock);
060        pos.setOriginalLocation(getOriginalX(), getOriginalY());
061        if (map != null) {
062            for (Map.Entry<String, NamedIcon> entry : map.entrySet()) {
063                String url = entry.getValue().getName();
064                pos.addKeyAndIcon(NamedIcon.getIconByName(url), entry.getKey());
065            }
066        }
067        return super.finishClone(pos);
068    }
069
070    @Override
071    public void resetDefaultIcon() {
072        defaultIcon = new NamedIcon("resources/icons/misc/X-red.gif",
073                "resources/icons/misc/X-red.gif");
074    }
075
076    /**
077     * Attach a named Block to this display item.
078     *
079     * @param pName Used as a system/user name to lookup the Block object
080     */
081    public void setBlock(String pName) {
082        if (InstanceManager.getNullableDefault(jmri.BlockManager.class) != null) {
083            Block block = InstanceManager.getDefault(jmri.BlockManager.class).
084                    provideBlock(pName);
085            setBlock(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, block));
086        } else {
087            log.error("No Block Manager for this protocol, icon won't see changes");
088        }
089        updateSize();
090    }
091
092    /**
093     * Attach a named Block to this display item.
094     *
095     * @param m The Block object
096     */
097    public void setBlock(NamedBeanHandle<Block> m) {
098        if (namedBlock != null) {
099            getBlock().removePropertyChangeListener(this);
100        }
101        namedBlock = m;
102        if (namedBlock != null) {
103            getBlock().addPropertyChangeListener(this, namedBlock.getName(), "Block Icon");
104            displayState();
105            setName(namedBlock.getName());
106        }
107    }
108
109    public NamedBeanHandle<Block> getNamedBlock() {
110        return namedBlock;
111    }
112
113    public Block getBlock() {
114        if (namedBlock == null) {
115            return null;
116        }
117        return namedBlock.getBean();
118    }
119
120    @Override
121    public jmri.NamedBean getNamedBean() {
122        return getBlock();
123    }
124
125    @Override
126    public java.util.HashMap<String, NamedIcon> getMap() {
127        return map;
128    }
129
130    @Override
131    @Nonnull
132    public String getTypeString() {
133        return Bundle.getMessage("PositionableType_BlockContentsIcon");
134    }
135
136    @Override
137    @Nonnull
138    public String getNameString() {
139        String name;
140        if (namedBlock == null) {
141            name = Bundle.getMessage("NotConnected");
142        } else {
143            name = getBlock().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME);
144        }
145        return name;
146    }
147
148    @Override
149    public boolean showPopUp(JPopupMenu popup) {
150        if (isEditable() && selectable) {
151            popup.add(new JSeparator());
152
153            for (String key : map.keySet()) {
154                //String value = ((NamedIcon)map.get(key)).getName();
155                popup.add(new AbstractAction(key) {
156                    @Override
157                    public void actionPerformed(ActionEvent e) {
158                        String key = e.getActionCommand();
159                        setValue(key);
160                    }
161                });
162            }
163            return true;
164        }  // end of selectable
165        if (re != null) {
166            popup.add(new AbstractAction("Open Throttle") {
167                @Override
168                public void actionPerformed(ActionEvent e) {
169                    ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame();
170                    tf.toFront();
171                    tf.getAddressPanel().setRosterEntry(re);
172                }
173            });
174
175            final jmri.jmrit.dispatcher.DispatcherFrame df = jmri.InstanceManager.getNullableDefault(jmri.jmrit.dispatcher.DispatcherFrame.class);
176            if (df != null) {
177                final jmri.jmrit.dispatcher.ActiveTrain at = df.getActiveTrainForRoster(re);
178                if (at != null) {
179                    popup.add(new AbstractAction(Bundle.getMessage("MenuTerminateTrain")) {
180                        @Override
181                        public void actionPerformed(ActionEvent e) {
182                            df.terminateActiveTrain(at,true,false);
183                        }
184                    });
185                    popup.add(new AbstractAction(Bundle.getMessage("MenuAllocateExtra")) {
186                        @Override
187                        public void actionPerformed(ActionEvent e) {
188                            //Just brings up the standard allocate extra frame, this could be expanded in the future
189                            //As a point and click operation.
190                            df.allocateExtraSection(e, at);
191                        }
192                    });
193                    if (at.getStatus() == jmri.jmrit.dispatcher.ActiveTrain.DONE) {
194                        popup.add(new AbstractAction("Restart") {
195                            @Override
196                            public void actionPerformed(ActionEvent e) {
197                                at.allocateAFresh();
198                            }
199                        });
200                    }
201                } else {
202                    popup.add(new AbstractAction(Bundle.getMessage("MenuNewTrain")) {
203                        @Override
204                        public void actionPerformed(ActionEvent e) {
205                            if (!df.getNewTrainActive()) {
206                                df.getActiveTrainFrame().initiateTrain(e, re, getBlock());
207                                df.setNewTrainActive(true);
208                            } else {
209                                df.getActiveTrainFrame().showActivateFrame(re);
210                            }
211                        }
212
213                    });
214                }
215            }
216            return true;
217        }
218        return false;
219    }
220
221    /**
222     * Text edits cannot be done to Block text - override.
223     */
224    @Override
225    public boolean setTextEditMenu(JPopupMenu popup) {
226        popup.add(new AbstractAction(Bundle.getMessage("EditBlockValue")) {
227            @Override
228            public void actionPerformed(ActionEvent e) {
229                editBlockValue();
230            }
231        });
232        return true;
233    }
234
235    /**
236     * Drive the current state of the display from the state of the Block Value.
237     */
238    @Override
239    public void displayState() {
240        log.debug("displayState");
241        if (namedBlock == null) {  // use default if not connected yet
242            setIcon(defaultIcon);
243            updateSize();
244            return;
245        }
246        if (re != null) {
247            jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this);
248            re = null;
249        }
250        Object key = getBlock().getValue();
251        displayState(key);
252    }
253
254    @Override
255    public boolean setEditIconMenu(JPopupMenu popup) {
256        String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameBlock"));
257        popup.add(new AbstractAction(txt) {
258            @Override
259            public void actionPerformed(ActionEvent e) {
260                edit();
261            }
262        });
263        return true;
264    }
265
266    @Override
267    protected void edit() {
268        makeIconEditorFrame(this, "Block", true, null); // NOI18N
269        _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.blockPickModelInstance());
270        ActionListener addIconAction = a -> editBlock();
271        _iconEditor.complete(addIconAction, false, true, true);
272        _iconEditor.setSelection(getBlock());
273    }
274
275    void editBlock() {
276        setBlock(_iconEditor.getTableSelection().getDisplayName());
277        updateSize();
278        _iconEditorFrame.dispose();
279        _iconEditorFrame = null;
280        _iconEditor = null;
281        invalidate();
282    }
283
284    @Override
285    public void dispose() {
286        if (getBlock() != null) {
287            getBlock().removePropertyChangeListener(this);
288        }
289        namedBlock = null;
290        if (re != null) {
291            jmri.InstanceManager.throttleManagerInstance().removeListener(re.getDccLocoAddress(), this);
292            re = null;
293        }
294        super.dispose();
295    }
296
297    @Override
298    public void doMouseClicked(JmriMouseEvent e) {
299        if (e.getClickCount() == 2) { // double click?
300            editBlockValue();
301        }
302    }
303
304    protected void editBlockValue() {
305
306        String reval = (String)JmriJOptionPane.showInputDialog(this,
307                                     Bundle.getMessage("EditCurrentBlockValue", namedBlock.getName()),
308                                     getBlock().getValue());
309
310        setValue(reval);
311        updateSize();
312    }
313
314    @Override
315    protected Object getValue() {
316        if (getBlock() == null) {
317            return null;
318        }
319        return getBlock().getValue();
320    }
321
322    @Override
323    protected void setValue(Object val) {
324        getBlock().setValue(val);
325    }
326
327    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BlockContentsIcon.class);
328
329}