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.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}