001package jmri.jmrit.display;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005
006import javax.swing.AbstractButton;
007import javax.swing.BoxLayout;
008import javax.swing.DefaultComboBoxModel;
009import javax.swing.DefaultListModel;
010import javax.swing.JButton;
011import javax.swing.JComboBox;
012import javax.swing.JLabel;
013import javax.swing.JList;
014import javax.swing.JPanel;
015import javax.swing.JScrollPane;
016import javax.swing.JTextField;
017import javax.swing.SwingConstants;
018
019import jmri.InstanceManager;
020import jmri.Memory;
021import jmri.NamedBeanHandle;
022import jmri.NamedBean.DisplayOptions;
023import jmri.util.swing.*;
024
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028/**
029 * An icon to display and input a Memory value in a TextField.
030 * <p>
031 * Handles the case of either a String or an Integer in the Memory, preserving
032 * what it finds.
033 *
034 * @author Pete Cressman Copyright (c) 2012
035 * @since 2.7.2
036 */
037public class MemoryComboIcon extends MemoryOrGVComboIcon
038        implements java.beans.PropertyChangeListener, ActionListener {
039
040    private final JComboBox<String> _comboBox;
041    private final ComboModel _model;
042
043    // the associated Memory object
044    private NamedBeanHandle<Memory> namedMemory;
045
046    private final java.awt.event.MouseListener _mouseListener = JmriMouseListener.adapt(this);
047    private final java.awt.event.MouseMotionListener _mouseMotionListener = JmriMouseMotionListener.adapt(this);
048
049    public MemoryComboIcon(Editor editor, String[] list) {
050        super(editor);
051        if (list != null) {
052            _model = new ComboModel(list);
053        } else {
054            _model = new ComboModel();
055        }
056        _comboBox = new JComboBox<>(_model);
057        _comboBox.addActionListener(this);
058        setDisplayLevel(Editor.LABELS);
059
060        setLayout(new java.awt.GridBagLayout());
061        add(_comboBox);
062        _comboBox.addMouseListener(JmriMouseListener.adapt(this));
063
064        for (int i = 0; i < _comboBox.getComponentCount(); i++) {
065            java.awt.Component component = _comboBox.getComponent(i);
066            if (component instanceof AbstractButton) {
067                component.addMouseListener(_mouseListener);
068                component.addMouseMotionListener(_mouseMotionListener);
069            }
070        }
071        setPopupUtility(new PositionablePopupUtil(this, _comboBox));
072    }
073
074    @Override
075    public JComboBox<String> getTextComponent() {
076        return _comboBox;
077    }
078
079    @Override
080    public Positionable deepClone() {
081        String[] list = new String[_model.getSize()];
082        for (int i = 0; i < _model.getSize(); i++) {
083            list[i] = _model.getElementAt(i);
084        }
085        MemoryComboIcon pos = new MemoryComboIcon(_editor, list);
086        return finishClone(pos);
087    }
088
089    protected Positionable finishClone(MemoryComboIcon pos) {
090        pos.setMemory(namedMemory.getName());
091        return super.finishClone(pos);
092    }
093
094    /**
095     * Attach a named Memory to this display item.
096     *
097     * @param pName used as a system/user name to look up the Memory object
098     */
099    public void setMemory(String pName) {
100        log.debug("setMemory for memory= {}", pName);
101        if (InstanceManager.getNullableDefault(jmri.MemoryManager.class) != null) {
102            try {
103                Memory memory = InstanceManager.memoryManagerInstance().provideMemory(pName);
104                setMemory(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, memory));
105            } catch (IllegalArgumentException e) {
106                log.error("No MemoryManager for this protocol, icon won't see changes");
107            }
108        }
109        updateSize();
110    }
111
112    /**
113     * Attach a named Memory to this display item.
114     *
115     * @param m The Memory object
116     */
117    public void setMemory(NamedBeanHandle<Memory> m) {
118        if (namedMemory != null) {
119            getMemory().removePropertyChangeListener(this);
120        }
121        namedMemory = m;
122        if (namedMemory != null) {
123            getMemory().addPropertyChangeListener(this, namedMemory.getName(), "Memory Input Icon");
124            displayState();
125            setName(namedMemory.getName());
126        }
127    }
128
129    public NamedBeanHandle<Memory> getNamedMemory() {
130        return namedMemory;
131    }
132
133    public Memory getMemory() {
134        if (namedMemory == null) {
135            return null;
136        }
137        return namedMemory.getBean();
138    }
139
140    @Override
141    public ComboModel getComboModel() {
142        return _model;
143    }
144
145    /**
146     * Display
147     */
148    @Override
149    public void actionPerformed(ActionEvent e) {
150        update();
151    }
152
153    // update icon as state of Memory changes
154    @Override
155    public void propertyChange(java.beans.PropertyChangeEvent e) {
156        if (e.getPropertyName().equals("value")) {
157            displayState();
158        }
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    protected void update() {
174        if (namedMemory == null) {
175            return;
176        }
177        getMemory().setValue(_comboBox.getSelectedItem());
178    }
179
180    @Override
181    public boolean setEditIconMenu(javax.swing.JPopupMenu popup) {
182        String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameMemory"));
183        popup.add(new javax.swing.AbstractAction(txt) {
184            @Override
185            public void actionPerformed(ActionEvent e) {
186                edit();
187            }
188        });
189        return true;
190    }
191
192    /**
193     * Popup menu iconEditor's ActionListener
194     */
195    private DefaultListModel<String> _listModel;
196
197    @Override
198    protected void edit() {
199        _iconEditor = new IconAdder("Memory") {
200            JList<String> list;
201            final JButton bDel = new JButton(Bundle.getMessage("deleteSelection"));
202            final JButton bAdd = new JButton(Bundle.getMessage("addItem"));
203            final JTextField textfield = new JTextField(30);
204            int idx;
205
206            @Override
207            protected void addAdditionalButtons(JPanel p) {
208                _listModel = new DefaultListModel<>();
209                bDel.addActionListener(a -> {
210                    if ( list == null ){ return; }
211                    idx = list.getSelectedIndex();
212                    if (idx >= 0) {
213                        _listModel.removeElementAt(idx);
214                    }
215                });
216                bAdd.addActionListener(a -> {
217                    String text = textfield.getText();
218                    if (text == null || list == null || text.length() == 0 || _listModel.indexOf(text) >= 0) {
219                        return;
220                    }
221                    idx = list.getSelectedIndex();
222                    if (idx < 0) {
223                        idx = _listModel.getSize();
224                    }
225                    _listModel.add(idx, text);
226                });
227                for (int i = 0; i < _model.getSize(); i++) {
228                    _listModel.add(i, _model.getElementAt(i));
229                }
230                list = new JList<>(_listModel);
231                JScrollPane scrollPane = new JScrollPane(list);
232                JPanel p1 = new JPanel();
233                p1.add(new JLabel(Bundle.getMessage("comboList")));
234                p.add(p1);
235                p.add(scrollPane);
236                p1 = new JPanel();
237                JPanel pInner1 = new JPanel();
238                pInner1.setLayout(new BoxLayout(pInner1, BoxLayout.X_AXIS));
239                pInner1.add(new JLabel(Bundle.getMessage("newItem"), SwingConstants.RIGHT));
240                pInner1.add(textfield);
241                p1.add(pInner1);
242                p.add(p1);
243                JPanel p2 = new JPanel();
244                //p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
245                //p2.setLayout(new FlowLayout(FlowLayout.TRAILING));
246                p2.add(bDel);
247                p2.add(bAdd);
248                p.add(p2);
249                p.setVisible(true);
250            }
251        };
252
253        makeIconEditorFrame(this, "Memory", true, _iconEditor);
254        _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.memoryPickModelInstance());
255        ActionListener addIconAction = a -> editMemory();
256
257        _iconEditor.makeIconPanel(false);
258        _iconEditor.complete(addIconAction, false, true, true);
259        _iconEditor.setSelection(getMemory());
260    }
261
262    void editMemory() {
263        jmri.NamedBean bean = _iconEditor.getTableSelection();
264        setMemory(bean.getDisplayName());
265        _model.removeAllElements();
266        for (int i = 0; i < _listModel.size(); i++) {
267            _model.addElement(_listModel.getElementAt(i));
268        }
269        setSize(getPreferredSize().width + 1, getPreferredSize().height);
270        _iconEditorFrame.dispose();
271        _iconEditorFrame = null;
272        _iconEditor = null;
273        validate();
274    }
275
276    /**
277     * Drive the current state of the display from the state of the Memory.
278     */
279    public void displayState() {
280        log.debug("displayState");
281        if (namedMemory == null) {  // leave alone if not connected yet
282            return;
283        }
284        _model.setSelectedItem(getMemory().getValue());
285    }
286
287    @Override
288    public void mouseExited(JmriMouseEvent e) {
289        _comboBox.setFocusable(false);
290        _comboBox.transferFocus();
291        super.mouseExited(e);
292    }
293
294    @Override
295    void cleanup() {
296        if (namedMemory != null) {
297            getMemory().removePropertyChangeListener(this);
298        }
299        if (_comboBox != null) {
300            for (int i = 0; i < _comboBox.getComponentCount(); i++) {
301                java.awt.Component component = _comboBox.getComponent(i);
302                if (component instanceof AbstractButton) {
303                    component.removeMouseListener(_mouseListener);
304                    component.removeMouseMotionListener(_mouseMotionListener);
305                }
306            }
307            _comboBox.removeMouseListener(_mouseListener);
308        }
309        namedMemory = null;
310    }
311
312    private final static Logger log = LoggerFactory.getLogger(MemoryComboIcon.class);
313
314}