001package jmri.jmrix.can.cbus.node; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005 006import javax.annotation.CheckForNull; 007import javax.swing.JTable; 008import javax.swing.JTextField; 009 010import jmri.jmrix.can.CanSystemConnectionMemo; 011import jmri.jmrix.can.cbus.swing.nodeconfig.CbusNodeEditEventFrame; 012import jmri.util.ThreadingUtil; 013 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017/** 018 * Table data model for display of CBUS Node Single Events 019 * 020 * @author Steve Young (c) 2019 021 * 022 */ 023public class CbusNodeSingleEventTableDataModel extends javax.swing.table.AbstractTableModel { 024 025 public int[] newEVs; 026 private final CbusNodeEvent _ndEv; 027 private final CanSystemConnectionMemo _memo; 028 029 // column order needs to match list in column tooltips 030 static public final int EV_NUMBER_COLUMN = 0; 031 static public final int EV_CURRENT_VAL_COLUMN = 1; 032 static public final int EV_CURRENT_HEX_COLUMN = 2; 033 static public final int EV_CURRENT_BIT_COLUMN = 3; 034 static public final int EV_SELECT_COLUMN = 4; 035 static public final int EV_SELECT_HEX_COLUMN = 5; 036 static public final int EV_SELECT_BIT_COLUMN = 6; 037 static public final int MAX_COLUMN = 7; 038 039 public CbusNodeSingleEventTableDataModel(CanSystemConnectionMemo memo, int row, int column , CbusNodeEvent ndEv) { 040 041 log.debug("Starting a Single Node Event Variable Model"); 042 _memo = memo; 043 _ndEv = ndEv; 044 if ( _ndEv.getEvVarArray() == null ) { 045 newEVs = new int[0]; 046 } 047 else { 048 newEVs = new int[ ( _ndEv.getEvVarArray().length ) ]; 049 log.debug(" set node newEVs length {} ",newEVs.length); 050 051 newEVs = Arrays.copyOf( 052 _ndEv.getEvVarArray(), 053 _ndEv.getEvVarArray().length); 054 log.debug(" set ev var arr length {} data {}",newEVs.length, newEVs); 055 } 056 setTableModel(); 057 } 058 059 public final void setTableModel(){ 060 _ndEv.setEditTableModel(this); 061 } 062 063 /** 064 * {@inheritDoc} 065 */ 066 @Override 067 public int getRowCount() { 068 return _ndEv.getNumEvVars(); 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public int getColumnCount() { 076 return MAX_COLUMN; 077 } 078 079 /** 080 * Configure a table to have our standard rows and columns. 081 * <p> 082 * This is optional, in that other table formats can use this table model. 083 * But we put it here to help keep it consistent. 084 * @param eventTable Table to configure 085 */ 086 public void configureTable(JTable eventTable) { 087 // allow reordering of the columns 088 eventTable.getTableHeader().setReorderingAllowed(true); 089 090 // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 091 eventTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 092 093 // resize columns as requested 094 for (int i = 0; i < eventTable.getColumnCount(); i++) { 095 int width = getPreferredWidth(i); 096 eventTable.getColumnModel().getColumn(i).setPreferredWidth(width); 097 } 098 eventTable.sizeColumnsToFit(-1); 099 } 100 101 /** 102 * Returns String of column name from column int 103 * used in table header 104 * @param col int col number 105 */ 106 @Override 107 public String getColumnName(int col) { // not in any order 108 switch (col) { 109 case EV_NUMBER_COLUMN: 110 return ("EV Var"); 111 case EV_CURRENT_VAL_COLUMN: 112 return ("Dec."); 113 case EV_CURRENT_HEX_COLUMN: 114 return ("Hex."); 115 case EV_CURRENT_BIT_COLUMN: 116 return ("Bin."); 117 case EV_SELECT_COLUMN: 118 return ("New Dec."); 119 case EV_SELECT_HEX_COLUMN: 120 return ("New Hex."); 121 case EV_SELECT_BIT_COLUMN: 122 return("New Bin."); 123 default: 124 return "unknown " + col; // NOI18N 125 } 126 } 127 128 /** 129 * Returns int of startup column widths 130 * @param col int col number 131 * @return preferred initial width 132 */ 133 public static int getPreferredWidth(int col) { 134 switch (col) { 135 case EV_NUMBER_COLUMN: 136 case EV_CURRENT_BIT_COLUMN: 137 case EV_SELECT_COLUMN: 138 case EV_SELECT_HEX_COLUMN: 139 case EV_SELECT_BIT_COLUMN: 140 return new JTextField(6).getPreferredSize().width; 141 case EV_CURRENT_VAL_COLUMN: 142 case EV_CURRENT_HEX_COLUMN: 143 return new JTextField(4).getPreferredSize().width; 144 default: 145 return new JTextField(" <unknown> ").getPreferredSize().width; // NOI18N 146 } 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 @Override 153 public Class<?> getColumnClass(int col) { 154 switch (col) { 155 case EV_SELECT_HEX_COLUMN: 156 case EV_SELECT_BIT_COLUMN: 157 case EV_CURRENT_HEX_COLUMN: 158 case EV_CURRENT_BIT_COLUMN: 159 return String.class; 160 default: 161 return Integer.class; 162 } 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public boolean isCellEditable(int row, int col) { 170 switch (col) { 171 case EV_SELECT_COLUMN: 172 case EV_SELECT_HEX_COLUMN: 173 case EV_SELECT_BIT_COLUMN: 174 return true; 175 default: 176 return false; 177 } 178 } 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override 184 public Object getValueAt(int row, int col) { 185 186 int currEvVal = _ndEv.getEvVar(row+1); 187 188 switch (col) { 189 case EV_NUMBER_COLUMN: 190 return (row +1); 191 case EV_CURRENT_VAL_COLUMN: 192 if ( ( newEVs[(row)] < 0 ) && ( currEvVal > -1 )){ 193 newEVs[(row)] = currEvVal; 194 } 195 return currEvVal; 196 case EV_CURRENT_HEX_COLUMN: 197 if ( currEvVal > -1 ) { 198 return jmri.util.StringUtil.twoHexFromInt(currEvVal); 199 } 200 else { 201 return currEvVal; 202 } 203 case EV_CURRENT_BIT_COLUMN: 204 if ( currEvVal > -1 ) { 205 return (String.format("%8s", Integer.toBinaryString(currEvVal)).replace(' ', '0')).substring(0,4) + " " + 206 (String.format("%8s", Integer.toBinaryString(currEvVal)).replace(' ', '0')).substring(4,8); 207 } 208 else { 209 return currEvVal; 210 } 211 case EV_SELECT_COLUMN: 212 if ( newEVs[(row)] > -1 ) { 213 return newEVs[(row)]; 214 } else { 215 return currEvVal; 216 } 217 case EV_SELECT_HEX_COLUMN: 218 if ( newEVs[(row)] != currEvVal ) { 219 return jmri.util.StringUtil.twoHexFromInt(newEVs[(row)]); 220 } else { 221 return ""; 222 } 223 case EV_SELECT_BIT_COLUMN: 224 if ( newEVs[(row)] != currEvVal ) { 225 return (String.format("%8s", Integer.toBinaryString(newEVs[(row)])).replace(' ', '0')).substring(0,4) + " " + 226 (String.format("%8s", Integer.toBinaryString(newEVs[(row)])).replace(' ', '0')).substring(4,8); 227 } 228 else { 229 return ""; 230 } 231 default: 232 return null; 233 } 234 } 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override 240 public void setValueAt(Object value, int row, int col) { 241 log.debug("set value {} row {} col {}",value,row,col); 242 switch (col) { 243 case EV_SELECT_COLUMN: 244 newEVs[(row)] = (int) value; 245 updateFromNode(row,col); 246 break; 247 case EV_SELECT_HEX_COLUMN: 248 newEVs[(row)] = jmri.util.StringUtil.getByte(0, ((String) value).trim()); 249 ThreadingUtil.runOnGUIEventually(() -> fireTableRowsUpdated(row,row)); 250 break; 251 case EV_SELECT_BIT_COLUMN: 252 try { 253 int newInt = Integer.parseInt(((String) value).replaceAll("\\s+",""), 2); 254 if (newInt > -1 && newInt < 256) { 255 newEVs[(row)] = newInt; 256 ThreadingUtil.runOnGUIEventually(() -> fireTableRowsUpdated(row,row)); 257 } 258 } 259 catch ( NumberFormatException e ){} 260 break; 261 default: 262 break; 263 } 264 } 265 266 public void updateFromNode( int arrayid, int col){ 267 fireTableDataChanged(); 268 } 269 270 public boolean isTableLoaded(){ 271 if ( getRowCount() < 1 ) { 272 return false; 273 } 274 try { 275 for (int i = 0; i < getRowCount(); i++) { 276 if ( ( (int) getValueAt(i,EV_CURRENT_VAL_COLUMN) ) < 0 ) { 277 return false; 278 } 279 } 280 return true; 281 } 282 catch ( NullPointerException e ){ 283 return false; 284 } 285 } 286 287 public boolean isSingleEvDirty( int evToCheck ) { 288 return ( (int) getValueAt(evToCheck,EV_CURRENT_VAL_COLUMN) ) != ( 289 (int) getValueAt(evToCheck,EV_SELECT_COLUMN) ); 290 } 291 292 public boolean isTableDirty() { 293 try { 294 for (int i = 0; i < getRowCount(); i++) { 295 if ( isSingleEvDirty(i) ) { 296 return true; 297 } 298 } 299 return false; 300 } 301 catch ( NullPointerException e ){ 302 return false; 303 } 304 } 305 306 public int getCountDirty() { 307 int count = 0; 308 for (int i = 0; i < getRowCount(); i++) { 309 if ( isSingleEvDirty(i) ) { 310 count++; 311 } 312 } 313 return count; 314 } 315 316 public void resetnewEVs() { 317 for (int i = 0; i < getRowCount(); i++) { 318 setValueAt( getValueAt(i,EV_CURRENT_VAL_COLUMN), i, EV_SELECT_COLUMN); 319 } 320 } 321 322 @CheckForNull 323 private CbusNode getEventNode(){ 324 CbusNodeTableDataModel nodeModel = _memo.get(CbusNodeTableDataModel.class); 325 return nodeModel.getNodeByNodeNum( _ndEv.getParentNn() ); 326 } 327 328 public void passNewEvToNode ( CbusNodeEditEventFrame frame ) { 329 CbusNodeEvent newevent = new CbusNodeEvent( _memo, 330 frame.getNodeVal(), 331 frame.getEventVal(), 332 _ndEv.getParentNn(), 333 -1, 334 _ndEv.getNumEvVars() ); 335 336 newevent.setEvArr(newEVs); 337 338 ArrayList<CbusNodeEvent> eventArray = new ArrayList<>(1); 339 eventArray.add(newevent); 340 log.debug(" pass changes arr length {} ",newEVs.length); 341 342 CbusNode tmpNode = getEventNode(); 343 if (tmpNode!=null) { 344 tmpNode.getNodeEventManager().sendNewEvSToNode( eventArray ); 345 } 346 } 347 348 public void passEditEvToNode( CbusNodeEditEventFrame frame ) { 349 if ( frame.spinnersDirty() ) { 350 CbusNode tmpNode = getEventNode(); 351 if (tmpNode!=null) { 352 tmpNode.getNodeEventManager().deleteEvOnNode(_ndEv.getNn(), _ndEv.getEn() ); 353 } 354 355 // learn mode - to reset after unlearn, timeout, no feedback from node 356 // teach new event ( as brand new event ) 357 // notify frame 358 ThreadingUtil.runOnLayoutDelayed( () -> { 359 passNewEvToNode(frame); 360 }, 200 ); 361 362 } else { 363 // loop through each ev var and send 364 passNewEvToNode(frame); 365 } 366 } 367 368 public void dispose(){ 369 _ndEv.setEditTableModel(null); 370 } 371 372 private final static Logger log = LoggerFactory.getLogger(CbusNodeSingleEventTableDataModel.class); 373}