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