001package jmri.jmrit.symbolicprog;
002
003import java.awt.Component;
004import java.awt.event.FocusEvent;
005import java.awt.event.FocusListener;
006import java.util.EventObject;
007import java.util.Vector;
008import javax.swing.JComboBox;
009import javax.swing.JLabel;
010import javax.swing.JTable;
011import javax.swing.JTextField;
012import javax.swing.event.CellEditorListener;
013import javax.swing.event.ChangeEvent;
014import javax.swing.table.TableCellEditor;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * JTable editor for cells representing CV values. This is a somewhat
020 * unconventional thing in several ways:
021 * <ul>
022 * <li>The returned value is not the String edited into the cell, but an Integer
023 * value. It is not clear how that arose, and it should probably be changed at
024 * some point.
025 * <li>This is also a focus listener. People are not used to having to hit
026 * return/enter to "set" their value in place, and are rather expecting that the
027 * value they typed will be in effect as soon as they type it. We use a
028 * focusListener to do that.
029 * </ul>
030 *
031 * @author Bob Jacobsen Copyright (C) 2001
032 */
033public class ValueEditor extends JComboBox<Object> implements TableCellEditor, FocusListener {
034
035    protected transient Vector<CellEditorListener> listeners;
036    protected transient String originalValue = null;
037    protected Object mValue;
038
039    public ValueEditor() {
040        super();
041        listeners = new Vector<CellEditorListener>();
042    }
043
044    @Override
045    public Component getTableCellEditorComponent(JTable table, Object value,
046            boolean isSelected,
047            int row, int column) {
048        mValue = value;
049        if (log.isDebugEnabled()) {
050            log.debug("getTableCellEditorComponent {} {} {} {}", row, column, isSelected, value.getClass());
051        }
052        table.setRowSelectionInterval(row, row);
053        table.setColumnSelectionInterval(column, column);
054        if (value instanceof Component) {
055            if (value instanceof JTextField) {
056                JTextField tempField = (JTextField) value;
057                originalValue = tempField.getText();
058                tempField.addFocusListener(this);
059                return tempField;
060            } else {
061                return (Component) value;
062            }
063        } else if (value instanceof String) {
064            return new JLabel((String) value);
065        } else {
066            return new JLabel("Unknown value type!");
067        }
068    }
069
070    /**
071     * FocusListener implementations
072     */
073    @Override
074    public void focusGained(FocusEvent e) {
075        if (log.isDebugEnabled()) {
076            log.debug("focusGained");
077        }
078        if (mValue instanceof JTextField) {
079            JTextField tempField = (JTextField) mValue;
080            originalValue = tempField.getText();
081        }
082    }
083
084    @Override
085    public void focusLost(FocusEvent e) {
086        if (log.isDebugEnabled()) {
087            log.debug("focusLost");
088        }
089        if (!(mValue instanceof JTextField)
090                || !(originalValue.equals(((JTextField) mValue).getText()))) {
091            fireEditingStopped();
092        }
093    }
094
095    void removeFocusListener() {
096        if (mValue instanceof JTextField) {
097            JTextField tempField = (JTextField) mValue;
098            originalValue = null;
099            tempField.removeFocusListener(this);
100        }
101    }
102
103    // CellEditor methods
104    @Override
105    public void cancelCellEditing() {
106        if (log.isDebugEnabled()) {
107            log.debug("cancelCellEditing");
108        }
109        removeFocusListener();
110        fireEditingCanceled();
111    }
112
113    @Override
114    public Object getCellEditorValue() {
115        if (log.isDebugEnabled()) {
116            log.debug("getCellEditorValue with 'value' object of type {}", mValue.getClass());
117        }
118        if (mValue instanceof JTextField) {
119            // extract the string from the JTextField and return it
120            return Integer.valueOf(((JTextField) mValue).getText());
121        } else if (mValue instanceof Component) {
122            // extract the string from the JTextField and return it
123            return mValue;
124        } else {
125            log.error("getCellEditorValue unable to return a value from unknown type {}", mValue.getClass());
126            return null;
127        }
128    }
129
130    @Override
131    public boolean isCellEditable(EventObject eo) {
132        return true;
133    }
134
135    @Override
136    public boolean shouldSelectCell(EventObject eo) {
137        return true;
138    }
139
140    @Override
141    public boolean stopCellEditing() {
142        if (log.isDebugEnabled()) {
143            log.debug("stopCellEditing");
144        }
145        removeFocusListener();
146        fireEditingStopped();
147        return true;
148    }
149
150    @Override
151    public void addCellEditorListener(CellEditorListener cel) {
152        listeners.addElement(cel);
153    }
154
155    @Override
156    public void removeCellEditorListener(CellEditorListener cel) {
157        listeners.removeElement(cel);
158    }
159
160    protected void fireEditingCanceled() {
161        if (log.isDebugEnabled()) {
162            log.debug("fireEditingCancelled, but we are not setting back the old value");
163        }
164        Vector<CellEditorListener> local;
165        synchronized (listeners) {
166            local = new Vector<CellEditorListener>(listeners);
167        }
168        ChangeEvent ce = new ChangeEvent(this);
169        for (int i = local.size() - 1; i >= 0; i--) {
170            local.elementAt(i).editingCanceled(ce);
171        }
172    }
173
174    protected void fireEditingStopped() {
175        if (log.isDebugEnabled()) {
176            log.debug("fireEditingStopped");
177        }
178        Vector<CellEditorListener> local;
179        synchronized (listeners) {
180            local = new Vector<CellEditorListener>(listeners);
181        }
182        ChangeEvent ce = new ChangeEvent(this);
183        for (int i = local.size() - 1; i >= 0; i--) {
184            local.elementAt(i).editingStopped(ce);
185        }
186    }
187
188    // initialize logging
189    private final static Logger log = LoggerFactory.getLogger(ValueEditor.class);
190}