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                variable._initialValueType = (InitialValueType) value;
105                break;
106            case COLUMN_DATA:
107                variable._initialValueData = (String) value;
108                break;
109            case COLUMN_MENU:
110                // Do nothing
111                break;
112            default:
113                throw new IllegalArgumentException("Invalid column");
114        }
115    }
116
117    /** {@inheritDoc} */
118    @Override
119    public Object getValueAt(int rowIndex, int columnIndex) {
120        if (rowIndex >= _variables.size()) throw new IllegalArgumentException("Invalid row");
121
122        switch (columnIndex) {
123            case COLUMN_NAME:
124                return _variables.get(rowIndex).getName();
125            case COLUMN_TYPE:
126                return _variables.get(rowIndex).getInitialValueType();
127            case COLUMN_DATA:
128                return _variables.get(rowIndex).getInitialValueData();
129            case COLUMN_MENU:
130                return Menu.Select;
131            default:
132                throw new IllegalArgumentException("Invalid column");
133        }
134    }
135
136    public void setColumnForMenu(JTable table) {
137        JComboBox<Menu> comboBox = new JComboBox<>();
138        table.setRowHeight(comboBox.getPreferredSize().height);
139        table.getColumnModel().getColumn(COLUMN_MENU)
140                .setPreferredWidth((comboBox.getPreferredSize().width) + 4);
141    }
142
143    public void add() {
144        int row = _variables.size();
145        _variables.add(new VariableData("", InitialValueType.None, ""));
146        fireTableRowsInserted(row, row);
147    }
148
149    public List<VariableData> getVariables() {
150        return _variables;
151    }
152
153
154    public static enum Menu {
155        Select(Bundle.getMessage("TableMenuSelect")),
156        Delete(Bundle.getMessage("TableMenuDelete")),
157        MoveUp(Bundle.getMessage("TableMenuMoveUp")),
158        MoveDown(Bundle.getMessage("TableMenuMoveDown"));
159
160        private final String _descr;
161
162        private Menu(String descr) {
163            _descr = descr;
164        }
165
166        @Override
167        public String toString() {
168            return _descr;
169        }
170    }
171
172
173    public static class TypeCellRenderer extends DefaultTableCellRenderer {
174
175        @Override
176        public Component getTableCellRendererComponent(JTable table, Object value,
177                boolean isSelected, boolean hasFocus, int row, int column) {
178
179            if (value == null) value = InitialValueType.None;
180
181            if (! (value instanceof InitialValueType)) {
182                throw new IllegalArgumentException("value is not an InitialValueType: " + value.getClass().getName());
183            }
184            setText(((InitialValueType) value).toString());
185            return this;
186        }
187    }
188
189
190    public static class MenuCellRenderer extends DefaultTableCellRenderer {
191
192        @Override
193        public Component getTableCellRendererComponent(JTable table, Object value,
194                boolean isSelected, boolean hasFocus, int row, int column) {
195
196            if (value == null) value = Menu.Select;
197
198            if (! (value instanceof Menu)) {
199                throw new IllegalArgumentException("value is not an Menu: " + value.getClass().getName());
200            }
201            setText(((Menu) value).toString());
202            return this;
203        }
204    }
205
206
207    public static class TypeCellEditor extends AbstractCellEditor
208            implements TableCellEditor, ActionListener {
209
210        private InitialValueType _type;
211
212        @Override
213        public Object getCellEditorValue() {
214            return this._type;
215        }
216
217        @Override
218        public Component getTableCellEditorComponent(JTable table, Object value,
219                boolean isSelected, int row, int column) {
220
221            if (value == null) value = InitialValueType.None;
222
223            if (! (value instanceof InitialValueType)) {
224                throw new IllegalArgumentException("value is not an InitialValueType: " + value.getClass().getName());
225            }
226
227            JComboBox<InitialValueType> typeComboBox = new JComboBox<>();
228
229            for (InitialValueType type : InitialValueType.values()) {
230                typeComboBox.addItem(type);
231            }
232            JComboBoxUtil.setupComboBoxMaxRows(typeComboBox);
233
234            typeComboBox.setSelectedItem(value);
235            typeComboBox.addActionListener(this);
236
237            return typeComboBox;
238        }
239
240        @Override
241        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<InitialValueType>
242        public void actionPerformed(ActionEvent event) {
243            if (! (event.getSource() instanceof JComboBox)) {
244                throw new IllegalArgumentException("value is not an InitialValueType: " + event.getSource().getClass().getName());
245            }
246            JComboBox<InitialValueType> typeComboBox =
247                    (JComboBox<InitialValueType>) event.getSource();
248            _type = typeComboBox.getItemAt(typeComboBox.getSelectedIndex());
249        }
250
251    }
252
253
254    public static class MenuCellEditor extends AbstractCellEditor
255            implements TableCellEditor, ActionListener {
256
257        JTable _table;
258        LocalVariableTableModel _tableModel;
259
260        public MenuCellEditor(JTable table, LocalVariableTableModel tableModel) {
261            _table = table;
262            _tableModel = tableModel;
263        }
264
265        @Override
266        public Object getCellEditorValue() {
267            return Menu.Select;
268        }
269
270        @Override
271        public Component getTableCellEditorComponent(JTable table, Object value,
272                boolean isSelected, int row, int column) {
273
274            if (value == null) value = Menu.Select;
275
276            if (! (value instanceof Menu)) {
277                throw new IllegalArgumentException("value is not an Menu: " + value.getClass().getName());
278            }
279
280            JComboBox<Menu> menuComboBox = new JComboBox<>();
281
282            for (Menu menu : Menu.values()) {
283                if ((menu == Menu.MoveUp) && (row == 0)) continue;
284                if ((menu == Menu.MoveDown) && (row+1 == _tableModel._variables.size())) continue;
285                menuComboBox.addItem(menu);
286            }
287            JComboBoxUtil.setupComboBoxMaxRows(menuComboBox);
288
289            menuComboBox.setSelectedItem(value);
290            menuComboBox.addActionListener(this);
291
292            return menuComboBox;
293        }
294
295        @Override
296        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<Menu>
297        public void actionPerformed(ActionEvent event) {
298            if (! (event.getSource() instanceof JComboBox)) {
299                throw new IllegalArgumentException("value is not an InitialValueType: " + event.getSource().getClass().getName());
300            }
301            JComboBox<Menu> menuComboBox =
302                    (JComboBox<Menu>) event.getSource();
303            int row = _table.getSelectedRow();
304            Menu menu = menuComboBox.getItemAt(menuComboBox.getSelectedIndex());
305
306            switch (menu) {
307                case Delete:
308                    delete(row);
309                    break;
310                case MoveUp:
311                    if ((row) > 0) moveUp(row);
312                    break;
313                case MoveDown:
314                    if ((row+1) < _tableModel._variables.size()) moveUp(row+1);
315                    break;
316                default:
317                    // Do nothing
318            }
319            // Remove focus from combo box
320            if (_tableModel._variables.size() > 0) _table.editCellAt(row, COLUMN_NAME);
321        }
322
323        private void delete(int row) {
324            _tableModel._variables.remove(row);
325            _tableModel.fireTableRowsDeleted(row, row);
326        }
327
328        private void moveUp(int row) {
329            VariableData temp = _tableModel._variables.get(row-1);
330            _tableModel._variables.set(row-1, _tableModel._variables.get(row));
331            _tableModel._variables.set(row, temp);
332            _tableModel.fireTableRowsUpdated(row-1, row);
333        }
334
335    }
336
337}