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