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