001package jmri.jmrit.display;
002
003import java.awt.Dimension;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.beans.PropertyChangeListener;
007
008import javax.annotation.Nonnull;
009import javax.swing.AbstractAction;
010import javax.swing.JSpinner;
011import javax.swing.SpinnerNumberModel;
012import javax.swing.event.ChangeEvent;
013import javax.swing.event.ChangeListener;
014
015import jmri.InstanceManager;
016import jmri.NamedBeanHandle;
017import jmri.NamedBean.DisplayOptions;
018import jmri.jmrit.logixng.GlobalVariable;
019import jmri.jmrit.logixng.GlobalVariableManager;
020import jmri.util.swing.*;
021
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024
025/**
026 * An icon to display a status of a GlobalVariable in a JSpinner.
027 * <p>
028 * Handles the case of either a String or an Integer in the GlobalVariable, preserving
029 * what it finds.
030 *
031 * @author Bob Jacobsen     Copyright (c) 2009
032 * @author Daniel Bergqvist Copyright (C) 2022
033 * @since 2.7.2
034 */
035public class GlobalVariableSpinnerIcon extends PositionableJPanel implements ChangeListener, PropertyChangeListener {
036
037    int _min = 0;
038    int _max = 100;
039    JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, _min, _max, 1));
040    // the associated GlobalVariable object
041    //GlobalVariable globalVariable = null;
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 GlobalVariableSpinnerIcon(Editor editor) {
048        super(editor);
049        setDisplayLevel(Editor.LABELS);
050
051        setLayout(new java.awt.GridBagLayout());
052        add(spinner, new java.awt.GridBagConstraints());
053        spinner.addChangeListener(this);
054        javax.swing.JTextField textBox = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
055        textBox.addMouseMotionListener(_mouseMotionListener);
056        textBox.addMouseListener(_mouseListener);
057        setPopupUtility(new PositionablePopupUtil(this, textBox));
058    }
059
060    @Override
061    public Positionable deepClone() {
062        GlobalVariableSpinnerIcon pos = new GlobalVariableSpinnerIcon(_editor);
063        return finishClone(pos);
064    }
065
066    protected Positionable finishClone(GlobalVariableSpinnerIcon pos) {
067        pos.setGlobalVariable(namedGlobalVariable.getName());
068        return super.finishClone(pos);
069    }
070
071    @Override
072    public javax.swing.JComponent getTextComponent() {
073        return ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
074    }
075
076    @Override
077    public Dimension getSize() {
078        if (log.isDebugEnabled()) {
079            Dimension d = spinner.getSize();
080            log.debug("spinner width= {}, height= {}", d.width, d.height);
081            java.awt.Rectangle rect = getBounds(null);
082            log.debug("Bounds rect= ({},{}) width= {}, height= {}",
083                    rect.x, rect.y, rect.width, rect.height);
084            d = super.getSize();
085            log.debug("Panel width= {}, height= {}", d.width, d.height);
086        }
087        return super.getSize();
088    }
089
090    /**
091     * Attached a named GlobalVariable to this display item
092     *
093     * @param pName Used as a system/user name to lookup the GlobalVariable object
094     */
095    public void setGlobalVariable(String pName) {
096        log.debug("setGlobalVariable for memory= {}", pName);
097        if (InstanceManager.getNullableDefault(GlobalVariableManager.class) != null) {
098            try {
099                GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getGlobalVariable(pName);
100                setGlobalVariable(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, globalVariable));
101            } catch (IllegalArgumentException e) {
102                log.error("GlobalVariable '{}' not available, icon won't see changes", pName);
103            }
104        } else {
105            log.error("No GlobalVariableManager for this protocol, icon won't see changes");
106        }
107        updateSize();
108    }
109
110    /**
111     * Attached a named GlobalVariable to this display item
112     *
113     * @param m The GlobalVariable object
114     */
115    public void setGlobalVariable(NamedBeanHandle<GlobalVariable> m) {
116        if (namedGlobalVariable != null) {
117            getGlobalVariable().removePropertyChangeListener(this);
118        }
119        namedGlobalVariable = m;
120        if (namedGlobalVariable != null) {
121            getGlobalVariable().addPropertyChangeListener(this, namedGlobalVariable.getName(), "GlobalVariable Spinner Icon");
122            displayState();
123            setName(namedGlobalVariable.getName());
124        }
125    }
126
127    public NamedBeanHandle<GlobalVariable> getNamedGlobalVariable() {
128        return namedGlobalVariable;
129    }
130
131    // update icon as state of GlobalVariable changes
132    @Override
133    public void propertyChange(java.beans.PropertyChangeEvent e) {
134        if (e.getPropertyName().equals("value")) {
135            displayState();
136        }
137    }
138
139    public GlobalVariable getGlobalVariable() {
140        if (namedGlobalVariable == null) {
141            return null;
142        }
143        return namedGlobalVariable.getBean();
144    }
145
146    @Override
147    public jmri.NamedBean getNamedBean() {
148        return getGlobalVariable();
149    }
150
151    @Override
152    public void stateChanged(ChangeEvent e) {
153        spinnerUpdated();
154    }
155
156    @Override
157    @Nonnull
158    public String getTypeString() {
159        return Bundle.getMessage("PositionableType_GlobalVariableSpinnerIcon");
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    /*
174     public void setSelectable(boolean b) {selectable = b;}
175     public boolean isSelectable() { return selectable;}
176     boolean selectable = false;
177     */
178    @Override
179    public boolean setEditIconMenu(javax.swing.JPopupMenu popup) {
180        String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameGlobalVariable"));
181        popup.add(new AbstractAction(txt) {
182            @Override
183            public void actionPerformed(ActionEvent e) {
184                edit();
185            }
186        });
187        return true;
188    }
189
190    @Override
191    protected void edit() {
192        makeIconEditorFrame(this, "GlobalVariable", true, null);
193        _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.globalVariablePickModelInstance());
194        ActionListener addIconAction = a -> editGlobalVariable();
195        _iconEditor.complete(addIconAction, false, false, true);
196        _iconEditor.setSelection(getGlobalVariable());
197    }
198
199    void editGlobalVariable() {
200        setGlobalVariable(_iconEditor.getTableSelection().getDisplayName());
201        setSize(getPreferredSize().width, getPreferredSize().height);
202        _iconEditorFrame.dispose();
203        _iconEditorFrame = null;
204        _iconEditor = null;
205        invalidate();
206    }
207
208    /**
209     * Drive the current state of the display from the state of the GlobalVariable.
210     */
211    public void displayState() {
212        log.debug("displayState");
213        if (namedGlobalVariable == null) {  // leave alone if not connected yet
214            return;
215        }
216        if (getGlobalVariable().getValue() == null) {
217            return;
218        }
219        Integer num = null;
220        if (getGlobalVariable().getValue().getClass() == String.class) {
221            try {
222                num = Integer.valueOf((String) getGlobalVariable().getValue());
223            } catch (NumberFormatException e) {
224                return;
225            }
226        } else if (getGlobalVariable().getValue().getClass() == Integer.class) {
227            num = ((Number) getGlobalVariable().getValue()).intValue();
228        } else if (getGlobalVariable().getValue().getClass() == Float.class) {
229            num = Math.round((Float) getGlobalVariable().getValue());
230            log.debug("num= {}", num);
231        } else {
232            //spinner.setValue(getGlobalVariable().getValue());
233            return;
234        }
235        int n = num;
236        if (n > _max) {
237            num = _max;
238        } else if (n < _min) {
239            num = _min;
240        }
241        spinner.setValue(num);
242    }
243
244    @Override
245    public void mouseExited(JmriMouseEvent e) {
246        spinnerUpdated();
247        super.mouseExited(e);
248    }
249
250    protected void spinnerUpdated() {
251        if (namedGlobalVariable == null) {
252            return;
253        }
254        if (getGlobalVariable().getValue() == null) {
255            getGlobalVariable().setValue(spinner.getValue());
256            return;
257        }
258        // Spinner is always an Integer, but memory can contain Integer or String
259        if (getGlobalVariable().getValue().getClass() == String.class) {
260            String newValue = "" + spinner.getValue();
261            if (!getGlobalVariable().getValue().equals(newValue)) {
262                getGlobalVariable().setValue(newValue);
263            }
264        } else {
265            getGlobalVariable().setValue(spinner.getValue());
266        }
267    }
268
269    public String getValue() {
270        return "" + spinner.getValue();
271    }
272
273    @Override
274    void cleanup() {
275        if (namedGlobalVariable != null) {
276            getGlobalVariable().removePropertyChangeListener(this);
277        }
278        if (spinner != null) {
279            spinner.removeChangeListener(this);
280            ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseMotionListener(_mouseMotionListener);
281            ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseListener(_mouseListener);
282        }
283        namedGlobalVariable = null;
284    }
285
286    private static final Logger log = LoggerFactory.getLogger(GlobalVariableSpinnerIcon.class);
287}