001package jmri.jmrit.display;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.awt.event.KeyAdapter;
006import java.awt.event.KeyEvent;
007
008import javax.annotation.Nonnull;
009import javax.swing.JComponent;
010import javax.swing.JLabel;
011import javax.swing.JPanel;
012import javax.swing.JSpinner;
013import javax.swing.JTextField;
014import javax.swing.SpinnerNumberModel;
015
016import jmri.InstanceManager;
017import jmri.NamedBeanHandle;
018import jmri.NamedBean.DisplayOptions;
019import jmri.jmrit.logixng.GlobalVariable;
020import jmri.jmrit.logixng.GlobalVariableManager;
021import jmri.util.swing.*;
022
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 * An icon to display and input a GlobalVariable value in a TextField.
028 * <p>
029 * Handles the case of either a String or an Integer in the GlobalVariable, preserving
030 * what it finds.
031 *
032 * @author Pete Cressman    Copyright (c) 2009
033 * @author Daniel Bergqvist Copyright (C) 2022
034 * @since 2.7.2
035 */
036public class GlobalVariableInputIcon extends PositionableJPanel implements java.beans.PropertyChangeListener {
037
038    JTextField _textBox = new JTextField();
039    int _nCols;
040
041    // the associated GlobalVariable object
042    private NamedBeanHandle<GlobalVariable> namedGlobalVariable;
043
044    private final java.awt.event.MouseListener _mouseListener = JmriMouseListener.adapt(this);
045    private final java.awt.event.MouseMotionListener _mouseMotionListener = JmriMouseMotionListener.adapt(this);
046
047    public GlobalVariableInputIcon(int nCols, Editor editor) {
048        super(editor);
049        _nCols = nCols;
050        setDisplayLevel(Editor.LABELS);
051
052        setLayout(new java.awt.GridBagLayout());
053        add(_textBox, new java.awt.GridBagConstraints());
054        _textBox.addKeyListener(new KeyAdapter() {
055            @Override
056            public void keyReleased(KeyEvent e) {
057                int key = e.getKeyCode();
058                if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_TAB) {
059                    updateGlobalVariable();
060                }
061            }
062        });
063        _textBox.setColumns(_nCols);
064        _textBox.addMouseMotionListener(_mouseMotionListener);
065        _textBox.addMouseListener(_mouseListener);
066        setPopupUtility(new PositionablePopupUtil(this, _textBox));
067    }
068
069    @Override
070    public Positionable deepClone() {
071        GlobalVariableInputIcon pos = new GlobalVariableInputIcon(_nCols, _editor);
072        return finishClone(pos);
073    }
074
075    protected Positionable finishClone(GlobalVariableInputIcon pos) {
076        pos.setGlobalVariable(namedGlobalVariable.getName());
077        return super.finishClone(pos);
078    }
079
080    @Override
081    public JComponent getTextComponent() {
082        return _textBox;
083    }
084
085    @Override
086    public void mouseExited(JmriMouseEvent e) {
087        updateGlobalVariable();
088        super.mouseExited(e);
089    }
090
091    /**
092     * Attached a named GlobalVariable to this display item
093     *
094     * @param pName Used as a system/user name to lookup the GlobalVariable object
095     */
096    public void setGlobalVariable(String pName) {
097        log.debug("setGlobalVariable for memory= {}", pName);
098        if (InstanceManager.getNullableDefault(GlobalVariableManager.class) != null) {
099            try {
100                GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getGlobalVariable(pName);
101                setGlobalVariable(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, globalVariable));
102            } catch (IllegalArgumentException e) {
103                log.error("GlobalVariable '{}' not available, icon won't see changes", pName);
104            }
105        } else {
106            log.error("No GlobalVariableManager for this protocol, icon won't see changes");
107        }
108        updateSize();
109    }
110
111    /**
112     * Attached a named GlobalVariable to this display item
113     *
114     * @param m The GlobalVariable object
115     */
116    public void setGlobalVariable(NamedBeanHandle<GlobalVariable> m) {
117        if (namedGlobalVariable != null) {
118            getGlobalVariable().removePropertyChangeListener(this);
119        }
120        namedGlobalVariable = m;
121        if (namedGlobalVariable != null) {
122            getGlobalVariable().addPropertyChangeListener(this, namedGlobalVariable.getName(), "GlobalVariable Input Icon");
123            displayState();
124            setName(namedGlobalVariable.getName());
125        }
126    }
127
128    public void setNumColumns(int nCols) {
129        _textBox.setColumns(nCols);
130        _nCols = nCols;
131    }
132
133    public NamedBeanHandle<GlobalVariable> getNamedGlobalVariable() {
134        return namedGlobalVariable;
135    }
136
137    public GlobalVariable getGlobalVariable() {
138        if (namedGlobalVariable == null) {
139            return null;
140        }
141        return namedGlobalVariable.getBean();
142    }
143
144    public int getNumColumns() {
145        return _nCols;
146    }
147
148    // update icon as state of GlobalVariable changes
149    @Override
150    public void propertyChange(java.beans.PropertyChangeEvent e) {
151        if (e.getPropertyName().equals("value")) {
152            displayState();
153        }
154    }
155
156    @Override
157    @Nonnull
158    public String getTypeString() {
159        return Bundle.getMessage("PositionableType_GlobalVariableInputIcon");
160    }
161
162    @Override
163    public String getNameString() {
164        String name;
165        if (namedGlobalVariable == null) {
166            name = Bundle.getMessage("NotConnected");
167        } else {
168            name = getGlobalVariable().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME);
169        }
170        return name;
171    }
172
173    @Override
174    public void mouseMoved(JmriMouseEvent e) {
175        updateGlobalVariable();
176    }
177
178    private void updateGlobalVariable() {
179        if (namedGlobalVariable == null) {
180            return;
181        }
182        String str = _textBox.getText();
183        getGlobalVariable().setValue(str);
184    }
185
186    @Override
187    public boolean setEditIconMenu(javax.swing.JPopupMenu popup) {
188        String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameGlobalVariable"));
189        popup.add(new javax.swing.AbstractAction(txt) {
190            @Override
191            public void actionPerformed(ActionEvent e) {
192                edit();
193            }
194        });
195        return true;
196    }
197
198    /**
199     * Popup menu iconEditor's ActionListener
200     */
201    SpinnerNumberModel _spinModel = new SpinnerNumberModel(3, 1, 100, 1);
202
203    @Override
204    protected void edit() {
205        _iconEditor = new IconAdder("GlobalVariable") {
206            final JSpinner spinner = new JSpinner(_spinModel);
207
208            @Override
209            protected void addAdditionalButtons(JPanel p) {
210                ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(2);
211                spinner.setMaximumSize(spinner.getPreferredSize());
212                spinner.setValue(_textBox.getColumns());
213                JPanel p2 = new JPanel();
214                //p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
215                //p2.setLayout(new FlowLayout(FlowLayout.TRAILING));
216                p2.add(new JLabel(Bundle.getMessage("NumColsLabel")));
217                p2.add(spinner);
218                p.add(p2);
219                p.setVisible(true);
220            }
221        };
222
223        makeIconEditorFrame(this, "GlobalVariable", true, _iconEditor);
224        _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.globalVariablePickModelInstance());
225        ActionListener addIconAction = a -> editGlobalVariable();
226        _iconEditor.makeIconPanel(false);
227        _iconEditor.complete(addIconAction, false, false, true);
228        _iconEditor.setSelection(getGlobalVariable());
229    }
230
231    void editGlobalVariable() {
232        setGlobalVariable(_iconEditor.getTableSelection().getDisplayName());
233        _nCols = _spinModel.getNumber().intValue();
234        _textBox.setColumns(_nCols);
235        setSize(getPreferredSize().width + 1, getPreferredSize().height);
236        _iconEditorFrame.dispose();
237        _iconEditorFrame = null;
238        _iconEditor = null;
239        validate();
240    }
241
242    /**
243     * Drive the current state of the display from the state of the GlobalVariable.
244     */
245    public void displayState() {
246        log.debug("displayState");
247        if (namedGlobalVariable == null) {  // leave alone if not connected yet
248            return;
249        }
250        Object show = getGlobalVariable().getValue();
251        if (show != null) {
252            _textBox.setText(show.toString());
253        } else {
254            _textBox.setText("");
255        }
256    }
257
258    @Override
259    void cleanup() {
260        if (namedGlobalVariable != null) {
261            getGlobalVariable().removePropertyChangeListener(this);
262        }
263        if (_textBox != null) {
264            _textBox.removeMouseMotionListener(_mouseMotionListener);
265            _textBox.removeMouseListener(_mouseListener);
266        }
267        namedGlobalVariable = null;
268    }
269
270    private final static Logger log = LoggerFactory.getLogger(GlobalVariableInputIcon.class);
271}