001package jmri.jmrit.logixng.actions.swing;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.*;
007
008import javax.swing.*;
009import javax.swing.table.AbstractTableModel;
010import javax.swing.table.DefaultTableCellRenderer;
011import javax.swing.table.TableCellEditor;
012
013import jmri.Manager;
014import jmri.NamedBean;
015import jmri.jmrit.logixng.actions.NamedBeanType;
016import jmri.jmrit.logixng.actions.ActionListenOnBeans.NamedBeanReference;
017import jmri.swing.NamedBeanComboBox;
018import jmri.util.swing.JComboBoxUtil;
019
020/**
021 * Table model for ListenOnBeans named beans
022 *
023 * @author Daniel Bergqvist Copyright 2020
024 */
025public class ListenOnBeansTableModel extends AbstractTableModel {
026
027    // COLUMN_DUMMY is "hidden" but used when the panel is closed to
028    // ensure that the last edited cell is saved.
029    public static final int COLUMN_BEAN_TYPE = 0;
030    public static final int COLUMN_BEAN_NAME = COLUMN_BEAN_TYPE + 1;
031    public static final int COLUMN_BEAN_ALL = COLUMN_BEAN_NAME + 1;
032    public static final int COLUMN_DELETE = COLUMN_BEAN_ALL + 1;
033    public static final int COLUMN_DUMMY = COLUMN_DELETE + 1;
034
035    private final List<NamedBeanReference> _namedBeanReference = new ArrayList<>();
036
037
038    public ListenOnBeansTableModel(Collection<NamedBeanReference> namedBeanReference) {
039        if (namedBeanReference != null) {
040            for (NamedBeanReference ref : namedBeanReference) {
041                _namedBeanReference.add(new NamedBeanReference(ref));
042            }
043        }
044    }
045
046    /** {@inheritDoc} */
047    @Override
048    public int getRowCount() {
049        return _namedBeanReference.size();
050    }
051
052    /** {@inheritDoc} */
053    @Override
054    public int getColumnCount() {
055        return COLUMN_DUMMY+1;
056    }
057
058    /** {@inheritDoc} */
059    @Override
060    public String getColumnName(int col) {
061        switch (col) {
062            case COLUMN_BEAN_TYPE:
063                return Bundle.getMessage("ActionListenOnBeans_ColumnBeanType");
064            case COLUMN_BEAN_NAME:
065                return Bundle.getMessage("ActionListenOnBeans_ColumnBeanName");
066            case COLUMN_BEAN_ALL:
067                return Bundle.getMessage("ActionListenOnBeans_ColumnBeanAll");
068            case COLUMN_DELETE:
069                return "";  // no label
070            case COLUMN_DUMMY:
071                return "";  // no label
072            default:
073                throw new IllegalArgumentException("Invalid column");
074        }
075    }
076
077    /** {@inheritDoc} */
078    @Override
079    public Class<?> getColumnClass(int col) {
080        switch (col) {
081            case COLUMN_BEAN_TYPE:
082                return NamedBeanType.class;
083            case COLUMN_BEAN_NAME:
084                return NamedBeanReference.class;
085            case COLUMN_BEAN_ALL:
086                return Boolean.class;
087            case COLUMN_DELETE:
088                return JButton.class;
089            case COLUMN_DUMMY:
090                return String.class;
091            default:
092                throw new IllegalArgumentException("Invalid column");
093        }
094    }
095
096    /** {@inheritDoc} */
097    @Override
098    public boolean isCellEditable(int row, int col) {
099        return true;
100    }
101
102    /** {@inheritDoc} */
103    @Override
104    public void setValueAt(Object value, int rowIndex, int columnIndex) {
105        NamedBeanReference ref = _namedBeanReference.get(rowIndex);
106
107        switch (columnIndex) {
108            case COLUMN_BEAN_TYPE:
109                NamedBeanType oldType = ref.getType();
110                ref.setType((NamedBeanType) value);
111                if (oldType != ref.getType()) {
112                    ref.setName("");
113                    // When the type changes, the name is set to null, so the name cell needs to be updated
114                    fireTableCellUpdated(rowIndex, COLUMN_BEAN_NAME);
115                }
116                break;
117            case COLUMN_BEAN_NAME:
118                ref.setName(((NamedBeanReference) value).getHandle());
119                break;
120            case COLUMN_BEAN_ALL:
121                ref.setListenOnAllProperties((boolean) value);
122                break;
123            case COLUMN_DELETE:
124                delete(rowIndex);
125                break;
126            case COLUMN_DUMMY:
127                break;
128            default:
129                throw new IllegalArgumentException("Invalid column");
130        }
131    }
132
133    /** {@inheritDoc} */
134    @Override
135    public Object getValueAt(int rowIndex, int columnIndex) {
136        if (rowIndex >= _namedBeanReference.size()) throw new IllegalArgumentException("Invalid row");
137
138        switch (columnIndex) {
139            case COLUMN_BEAN_TYPE:
140                return _namedBeanReference.get(rowIndex).getType();
141            case COLUMN_BEAN_NAME:
142                return _namedBeanReference.get(rowIndex);
143            case COLUMN_BEAN_ALL:
144                return _namedBeanReference.get(rowIndex).getListenOnAllProperties();
145            case COLUMN_DELETE:
146                return Bundle.getMessage("ButtonDelete");  // NOI18N
147            case COLUMN_DUMMY:
148                return "";
149            default:
150                throw new IllegalArgumentException("Invalid column");
151        }
152    }
153
154    public void setColumnsForComboBoxes(JTable table) {
155        JComboBox<NamedBeanType> beanTypeComboBox = new JComboBox<>();
156        table.setRowHeight(beanTypeComboBox.getPreferredSize().height);
157        table.getColumnModel().getColumn(COLUMN_BEAN_TYPE)
158                .setPreferredWidth((beanTypeComboBox.getPreferredSize().width) + 4);
159    }
160
161    public void add() {
162        int row = _namedBeanReference.size();
163        _namedBeanReference.add(new NamedBeanReference("", NamedBeanType.Turnout, false));
164        fireTableRowsInserted(row, row);
165    }
166
167    private void delete(int row) {
168        _namedBeanReference.remove(row);
169        fireTableRowsDeleted(row, row);
170    }
171
172    public List<NamedBeanReference> getReferences() {
173        return _namedBeanReference;
174    }
175
176
177    public static class CellRenderer extends DefaultTableCellRenderer {
178
179        @Override
180        public Component getTableCellRendererComponent(JTable table, Object value,
181                boolean isSelected, boolean hasFocus, int row, int column) {
182
183            if (column == COLUMN_BEAN_TYPE) {
184                if (value == null) value = NamedBeanType.Turnout;
185
186                if (! (value instanceof NamedBeanType)) {
187                    throw new IllegalArgumentException("value is not an NamedBeanType: " + value.getClass().getName());
188                }
189                setText(((NamedBeanType) value).toString());
190            } else {
191                throw new RuntimeException("Unknown column: "+Integer.toString(column));
192            }
193            return this;
194        }
195    }
196
197
198    public static class NamedBeanTypeCellEditor extends AbstractCellEditor
199            implements TableCellEditor, ActionListener {
200
201        private NamedBeanType _beanType;
202
203        @Override
204        public Object getCellEditorValue() {
205            return this._beanType;
206        }
207
208        @Override
209        public Component getTableCellEditorComponent(JTable table, Object value,
210                boolean isSelected, int row, int column) {
211
212            if (value == null) value = NamedBeanType.Turnout;
213
214            if (! (value instanceof NamedBeanType)) {
215                throw new IllegalArgumentException("value is not an NamedBeanType: " + value.getClass().getName());
216            }
217
218            JComboBox<NamedBeanType> returnValueTypeComboBox = new JComboBox<>();
219
220            for (NamedBeanType type : NamedBeanType.values()) {
221                returnValueTypeComboBox.addItem(type);
222            }
223            JComboBoxUtil.setupComboBoxMaxRows(returnValueTypeComboBox);
224
225            returnValueTypeComboBox.setSelectedItem(value);
226            returnValueTypeComboBox.addActionListener(this);
227
228            return returnValueTypeComboBox;
229        }
230
231        @Override
232        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<NamedBeanType>
233        public void actionPerformed(ActionEvent event) {
234            if (! (event.getSource() instanceof JComboBox)) {
235                throw new IllegalArgumentException("value is not an JComboBox: " + event.getSource().getClass().getName());
236            }
237            JComboBox<NamedBeanType> returnValueTypeComboBox =
238                    (JComboBox<NamedBeanType>) event.getSource();
239            _beanType = returnValueTypeComboBox.getItemAt(returnValueTypeComboBox.getSelectedIndex());
240
241        }
242
243    }
244
245
246    public NamedBeanCellEditor getNamedBeanCellEditor() {
247        return new NamedBeanCellEditor();
248    }
249
250
251    public class NamedBeanCellEditor extends AbstractCellEditor
252            implements TableCellEditor, ActionListener {
253
254        private NamedBeanReference _namedBeanRef;
255
256        @Override
257        public Object getCellEditorValue() {
258            return this._namedBeanRef;
259        }
260
261        @SuppressWarnings({"unchecked", "rawtypes"})    // The actual types are not known by this class.
262        @Override
263        public Component getTableCellEditorComponent(JTable table, Object value,
264                boolean isSelected, int row, int column) {
265
266            if ((value != null) && (! (value instanceof NamedBeanReference))) {
267                throw new IllegalArgumentException("value is not a NamedBeanReference: " + value.getClass().getName());
268            }
269
270            _namedBeanRef = _namedBeanReference.get(row);
271            Manager manager = _namedBeanRef.getType().getManager();
272            NamedBean selection = _namedBeanRef.getHandle() != null ? _namedBeanRef.getHandle().getBean() : null;
273            NamedBeanComboBox<NamedBean> namedBeanComboBox =
274                    new NamedBeanComboBox<>(manager, selection, NamedBean.DisplayOptions.DISPLAYNAME);
275            namedBeanComboBox.setAllowNull(true);
276            JComboBoxUtil.setupComboBoxMaxRows(namedBeanComboBox);
277
278            namedBeanComboBox.addActionListener(this);
279
280            return namedBeanComboBox;
281        }
282
283        @Override
284        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<NamedBeanType>
285        public void actionPerformed(ActionEvent event) {
286            if (! (event.getSource() instanceof JComboBox)) {
287                throw new IllegalArgumentException("value is not an JComboBox: " + event.getSource().getClass().getName());
288            }
289            NamedBeanComboBox<NamedBean> namedBeanComboBox = (NamedBeanComboBox<NamedBean>) event.getSource();
290            int index = namedBeanComboBox.getSelectedIndex();
291            NamedBean namedBean = (index != -1) ? namedBeanComboBox.getItemAt(index) : null;
292            _namedBeanRef.setName(namedBean);
293        }
294
295    }
296
297}