001package jmri.jmrit.logixng.tools.swing;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.ArrayList;
007import java.util.List;
008
009import javax.swing.*;
010import javax.swing.table.AbstractTableModel;
011import javax.swing.table.DefaultTableCellRenderer;
012import javax.swing.table.TableCellEditor;
013
014import jmri.jmrit.logixng.MaleSocket;
015import jmri.jmrit.logixng.SymbolTable.InitialValueType;
016import jmri.jmrit.logixng.SymbolTable.VariableData;
017import jmri.util.swing.JComboBoxUtil;
018
019/**
020 * Table model for local variables
021 * @author Daniel Bergqvist Copyright 2018
022 */
023public class LocalVariableTableModel extends AbstractTableModel {
024
025    public static final int COLUMN_NAME = 0;
026    public static final int COLUMN_TYPE = 1;
027    public static final int COLUMN_DATA = 2;
028    public static final int COLUMN_MENU = 3;
029
030    private final List<VariableData> _variables = new ArrayList<>();
031
032
033    public LocalVariableTableModel(MaleSocket maleSocket) {
034        if (maleSocket != null) {
035            for (VariableData v : maleSocket.getLocalVariables()) {
036                _variables.add(new VariableData(v));
037            }
038        }
039    }
040
041    /** {@inheritDoc} */
042    @Override
043    public int getRowCount() {
044        return _variables.size();
045    }
046
047    /** {@inheritDoc} */
048    @Override
049    public int getColumnCount() {
050        return 4;
051    }
052
053    /** {@inheritDoc} */
054    @Override
055    public String getColumnName(int col) {
056        switch (col) {
057            case COLUMN_NAME:
058                return Bundle.getMessage("ColumnVariableName");
059            case COLUMN_TYPE:
060                return Bundle.getMessage("ColumnVariableType");
061            case COLUMN_DATA:
062                return Bundle.getMessage("ColumnVariableData");
063            case COLUMN_MENU:
064                return Bundle.getMessage("ColumnVariableMenu");
065            default:
066                throw new IllegalArgumentException("Invalid column");
067        }
068    }
069
070    /** {@inheritDoc} */
071    @Override
072    public Class<?> getColumnClass(int col) {
073        switch (col) {
074            case COLUMN_TYPE:
075                return InitialValueType.class;
076            case COLUMN_MENU:
077                return Menu.class;
078            case COLUMN_NAME:
079            case COLUMN_DATA:
080                return String.class;
081            default:
082                throw new IllegalArgumentException("Invalid column");
083        }
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    public boolean isCellEditable(int row, int col) {
089        return true;
090    }
091
092    /** {@inheritDoc} */
093    @Override
094    public void setValueAt(Object value, int rowIndex, int columnIndex) {
095        if (columnIndex == COLUMN_MENU) return;
096
097        VariableData variable = _variables.get(rowIndex);
098
099        switch (columnIndex) {
100            case COLUMN_NAME:
101                variable._name = (String) value;
102                break;
103            case COLUMN_TYPE:
104                if (value != null) {
105                    variable._initialValueType = (InitialValueType) value;
106                } else {
107                    variable._initialValueType = InitialValueType.None;
108                }
109                break;
110            case COLUMN_DATA:
111                variable._initialValueData = (String) value;
112                break;
113            case COLUMN_MENU:
114                // Do nothing
115                break;
116            default:
117                throw new IllegalArgumentException("Invalid column");
118        }
119    }
120
121    /** {@inheritDoc} */
122    @Override
123    public Object getValueAt(int rowIndex, int columnIndex) {
124        if (rowIndex >= _variables.size()) throw new IllegalArgumentException("Invalid row");
125
126        switch (columnIndex) {
127            case COLUMN_NAME:
128                return _variables.get(rowIndex).getName();
129            case COLUMN_TYPE:
130                return _variables.get(rowIndex).getInitialValueType();
131            case COLUMN_DATA:
132                return _variables.get(rowIndex).getInitialValueData();
133            case COLUMN_MENU:
134                return Menu.Select;
135            default:
136                throw new IllegalArgumentException("Invalid column");
137        }
138    }
139
140    public void setColumnForMenu(JTable table) {
141        JComboBox<Menu> comboBox = new JComboBox<>();
142        table.setRowHeight(comboBox.getPreferredSize().height);
143        table.getColumnModel().getColumn(COLUMN_MENU)
144                .setPreferredWidth((comboBox.getPreferredSize().width) + 4);
145    }
146
147    public void add() {
148        int row = _variables.size();
149        _variables.add(new VariableData("", InitialValueType.None, ""));
150        fireTableRowsInserted(row, row);
151    }
152
153    public List<VariableData> getVariables() {
154        return _variables;
155    }
156
157
158    public static enum Menu {
159        Select(Bundle.getMessage("TableMenuSelect")),
160        Delete(Bundle.getMessage("TableMenuDelete")),
161        MoveUp(Bundle.getMessage("TableMenuMoveUp")),
162        MoveDown(Bundle.getMessage("TableMenuMoveDown"));
163
164        private final String _descr;
165
166        private Menu(String descr) {
167            _descr = descr;
168        }
169
170        @Override
171        public String toString() {
172            return _descr;
173        }
174    }
175
176
177    public static class TypeCellRenderer 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 (value == null) value = InitialValueType.None;
184
185            if (! (value instanceof InitialValueType)) {
186                throw new IllegalArgumentException("value is not an InitialValueType: " + value.getClass().getName());
187            }
188            setText(((InitialValueType) value).toString());
189            return this;
190        }
191    }
192
193
194    public static class MenuCellRenderer extends DefaultTableCellRenderer {
195
196        @Override
197        public Component getTableCellRendererComponent(JTable table, Object value,
198                boolean isSelected, boolean hasFocus, int row, int column) {
199
200            if (value == null) value = Menu.Select;
201
202            if (! (value instanceof Menu)) {
203                throw new IllegalArgumentException("value is not an Menu: " + value.getClass().getName());
204            }
205            setText(((Menu) value).toString());
206            return this;
207        }
208    }
209
210
211    public static class TypeCellEditor extends AbstractCellEditor
212            implements TableCellEditor, ActionListener {
213
214        private InitialValueType _type;
215
216        @Override
217        public Object getCellEditorValue() {
218            return this._type;
219        }
220
221        @Override
222        public Component getTableCellEditorComponent(JTable table, Object value,
223                boolean isSelected, int row, int column) {
224
225            if (value == null) value = InitialValueType.None;
226
227            if (! (value instanceof InitialValueType)) {
228                throw new IllegalArgumentException("value is not an InitialValueType: " + value.getClass().getName());
229            }
230
231            JComboBox<InitialValueType> typeComboBox = new JComboBox<>();
232
233            for (InitialValueType type : InitialValueType.values()) {
234                typeComboBox.addItem(type);
235            }
236            JComboBoxUtil.setupComboBoxMaxRows(typeComboBox);
237
238            typeComboBox.setSelectedItem(value);
239            typeComboBox.addActionListener(this);
240
241            return typeComboBox;
242        }
243
244        @Override
245        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<InitialValueType>
246        public void actionPerformed(ActionEvent event) {
247            if (! (event.getSource() instanceof JComboBox)) {
248                throw new IllegalArgumentException("value is not an InitialValueType: " + event.getSource().getClass().getName());
249            }
250            JComboBox<InitialValueType> typeComboBox =
251                    (JComboBox<InitialValueType>) event.getSource();
252            _type = typeComboBox.getItemAt(typeComboBox.getSelectedIndex());
253        }
254
255    }
256
257
258    public static class MenuCellEditor extends AbstractCellEditor
259            implements TableCellEditor, ActionListener {
260
261        JTable _table;
262        LocalVariableTableModel _tableModel;
263
264        public MenuCellEditor(JTable table, LocalVariableTableModel tableModel) {
265            _table = table;
266            _tableModel = tableModel;
267        }
268
269        @Override
270        public Object getCellEditorValue() {
271            return Menu.Select;
272        }
273
274        @Override
275        public Component getTableCellEditorComponent(JTable table, Object value,
276                boolean isSelected, int row, int column) {
277
278            if (value == null) value = Menu.Select;
279
280            if (! (value instanceof Menu)) {
281                throw new IllegalArgumentException("value is not an Menu: " + value.getClass().getName());
282            }
283
284            JComboBox<Menu> menuComboBox = new JComboBox<>();
285
286            for (Menu menu : Menu.values()) {
287                if ((menu == Menu.MoveUp) && (row == 0)) continue;
288                if ((menu == Menu.MoveDown) && (row+1 == _tableModel._variables.size())) continue;
289                menuComboBox.addItem(menu);
290            }
291            JComboBoxUtil.setupComboBoxMaxRows(menuComboBox);
292
293            menuComboBox.setSelectedItem(value);
294            menuComboBox.addActionListener(this);
295
296            return menuComboBox;
297        }
298
299        @Override
300        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<Menu>
301        public void actionPerformed(ActionEvent event) {
302            if (! (event.getSource() instanceof JComboBox)) {
303                throw new IllegalArgumentException("value is not an InitialValueType: " + event.getSource().getClass().getName());
304            }
305            JComboBox<Menu> menuComboBox =
306                    (JComboBox<Menu>) event.getSource();
307            int row = _table.getSelectedRow();
308            Menu menu = menuComboBox.getItemAt(menuComboBox.getSelectedIndex());
309
310            switch (menu) {
311                case Delete:
312                    delete(row);
313                    break;
314                case MoveUp:
315                    if ((row) > 0) moveUp(row);
316                    break;
317                case MoveDown:
318                    if ((row+1) < _tableModel._variables.size()) moveUp(row+1);
319                    break;
320                default:
321                    // Do nothing
322            }
323            // Remove focus from combo box
324            if (_tableModel._variables.size() > 0) _table.editCellAt(row, COLUMN_NAME);
325        }
326
327        private void delete(int row) {
328            _tableModel._variables.remove(row);
329            _tableModel.fireTableRowsDeleted(row, row);
330        }
331
332        private void moveUp(int row) {
333            VariableData temp = _tableModel._variables.get(row-1);
334            _tableModel._variables.set(row-1, _tableModel._variables.get(row));
335            _tableModel._variables.set(row, temp);
336            _tableModel.fireTableRowsUpdated(row-1, row);
337        }
338
339    }
340
341}