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