001package jmri.jmrit.symbolicprog; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005import java.beans.PropertyChangeEvent; 006import java.beans.PropertyChangeListener; 007import java.util.Collections; 008import java.util.HashMap; 009import java.util.Set; 010import java.util.Vector; 011 012import javax.swing.JButton; 013import javax.swing.JLabel; 014import javax.swing.JTextField; 015import jmri.Programmer; 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * Table data model for display of CvValues in symbolic programmer. 021 * <p> 022 * This represents the contents of a single decoder, so the Programmer used to 023 * access it is a data member. 024 * 025 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2006 026 * @author Howard G. Penny Copyright (C) 2005 027 */ 028public class CvTableModel extends javax.swing.table.AbstractTableModel implements ActionListener, PropertyChangeListener { 029 030 private int _numRows = 0; // must be zero until Vectors are initialized 031 static final int MAXCVNUM = 1024; 032 private Vector<CvValue> _cvDisplayVector = new Vector<CvValue>(); // vector of CvValue objects, in display-row order, for doing row mapping 033 034 private HashMap<String, CvValue> _cvAllMap = new HashMap<String, CvValue>(); 035 036 public HashMap<String, CvValue> allCvMap() { 037 return _cvAllMap; 038 } 039 040 private Vector<JButton> _writeButtons = new Vector<JButton>(); 041 private Vector<JButton> _readButtons = new Vector<JButton>(); 042 private Vector<JButton> _compareButtons = new Vector<JButton>(); 043 private Programmer mProgrammer; 044 045 // Defines the columns 046 public static final int NUMCOLUMN = 0; 047 private static final int VALCOLUMN = 1; 048 private static final int STATECOLUMN = 2; 049 private static final int READCOLUMN = 3; 050 private static final int WRITECOLUMN = 4; 051 private static final int COMPARECOLUMN = 5; 052 053 private static final int HIGHESTCOLUMN = COMPARECOLUMN + 1; 054 private static final int HIGHESTNOPROG = STATECOLUMN + 1; 055 056 private JLabel _status = null; 057 058 public JLabel getStatusLabel() { 059 return _status; 060 } 061 062 public CvTableModel(JLabel status, Programmer pProgrammer) { 063 super(); 064 065 mProgrammer = pProgrammer; 066 // save a place for notification 067 _status = status; 068 069 // define just address CV at start, pending some variables 070 // boudreau: not sure why we need the statement below, 071 // messes up building CV table for CV #1 when in ops mode. 072 //addCV("1", false, false, false); 073 } 074 075 /** 076 * Gives access to the programmer used to reach these CVs, so you can check 077 * on mode, capabilities, etc. 078 * 079 * @return Programmer object for the CVs 080 */ 081 public Programmer getProgrammer() { 082 return mProgrammer; 083 } 084 085 public void setProgrammer(Programmer p) { 086 mProgrammer = p; 087 // tell all variables 088 for (CvValue cv : allCvMap().values()) { 089 if (cv != null) { 090 cv.setProgrammer(p); 091 } 092 } 093 for (CvValue cv : _cvDisplayVector) { 094 if (cv != null) { 095 cv.setProgrammer(p); 096 } 097 } 098 } 099 100 // basic methods for AbstractTableModel implementation 101 @Override 102 public int getRowCount() { 103 return _numRows; 104 } 105 106 @Override 107 public int getColumnCount() { 108 if (getProgrammer() != null) { 109 return HIGHESTCOLUMN; 110 } else { 111 return HIGHESTNOPROG; 112 } 113 } 114 115 @Override 116 public String getColumnName(int col) { 117 switch (col) { 118 case NUMCOLUMN: 119 return Bundle.getMessage("ColumnNameNumber"); 120 case VALCOLUMN: 121 return Bundle.getMessage("ColumnNameValue"); 122 case STATECOLUMN: 123 return Bundle.getMessage("ColumnNameState"); 124 case READCOLUMN: 125 return Bundle.getMessage("ColumnNameRead"); 126 case WRITECOLUMN: 127 return Bundle.getMessage("ColumnNameWrite"); 128 case COMPARECOLUMN: 129 return Bundle.getMessage("ColumnNameCompare"); 130 default: 131 return "unknown"; 132 } 133 } 134 135 @Override 136 public Class<?> getColumnClass(int col) { 137 switch (col) { 138 case NUMCOLUMN: 139 return Integer.class; 140 case VALCOLUMN: 141 return JTextField.class; 142 case STATECOLUMN: 143 return String.class; 144 case READCOLUMN: 145 return JButton.class; 146 case WRITECOLUMN: 147 return JButton.class; 148 case COMPARECOLUMN: 149 return JButton.class; 150 default: 151 return null; 152 } 153 } 154 155 @Override 156 public boolean isCellEditable(int row, int col) { 157 switch (col) { 158 case NUMCOLUMN: 159 return false; 160 case VALCOLUMN: 161 if (_cvDisplayVector.elementAt(row).getReadOnly() 162 || _cvDisplayVector.elementAt(row).getInfoOnly()) { 163 return false; 164 } else { 165 return true; 166 } 167 case STATECOLUMN: 168 return false; 169 case READCOLUMN: 170 return true; 171 case WRITECOLUMN: 172 return true; 173 case COMPARECOLUMN: 174 return true; 175 default: 176 return false; 177 } 178 } 179 180 public String getName(int row) { // name is text number 181 return "" + _cvDisplayVector.elementAt(row).number(); 182 } 183 184 public String getValString(int row) { 185 return "" + _cvDisplayVector.elementAt(row).getValue(); 186 } 187 188 public CvValue getCvByRow(int row) { 189 return _cvDisplayVector.elementAt(row); 190 } 191 192 public CvValue getCvByNumber(String number) { 193 return _cvAllMap.get(number); 194 } 195 196 @Override 197 public Object getValueAt(int row, int col) { 198 switch (col) { 199 case NUMCOLUMN: 200 return _cvDisplayVector.elementAt(row).number(); 201 case VALCOLUMN: 202 return _cvDisplayVector.elementAt(row).getTableEntry(); 203 case STATECOLUMN: 204 AbstractValue.ValueState state = _cvDisplayVector.elementAt(row).getState(); 205 switch (state) { 206 case UNKNOWN: 207 return Bundle.getMessage("CvStateUnknown"); 208 case READ: 209 return Bundle.getMessage("CvStateRead"); 210 case EDITED: 211 return Bundle.getMessage("CvStateEdited"); 212 case STORED: 213 return Bundle.getMessage("CvStateStored"); 214 case FROMFILE: 215 return Bundle.getMessage("CvStateFromFile"); 216 case SAME: 217 return Bundle.getMessage("CvStateSame"); 218 case DIFFERENT: 219 return Bundle.getMessage("CvStateDiff") + " " 220 + _cvDisplayVector.elementAt(row).getDecoderValue(); 221 default: 222 return "inconsistent"; 223 } 224 case READCOLUMN: 225 return _readButtons.elementAt(row); 226 case WRITECOLUMN: 227 return _writeButtons.elementAt(row); 228 case COMPARECOLUMN: 229 return _compareButtons.elementAt(row); 230 default: 231 return "unknown"; 232 } 233 } 234 235 @Override 236 public void setValueAt(Object value, int row, int col) { 237 switch (col) { 238 case VALCOLUMN: // Object is actually an Integer 239 if (_cvDisplayVector.elementAt(row).getValue() != ((Integer) value).intValue()) { 240 _cvDisplayVector.elementAt(row).setValue(((Integer) value).intValue()); 241 } 242 break; 243 default: 244 break; 245 } 246 } 247 248 @Override 249 public void actionPerformed(ActionEvent e) { 250 if (log.isDebugEnabled()) { 251 log.debug("action command: {}", e.getActionCommand()); 252 } 253 char b = e.getActionCommand().charAt(0); 254 int row = Integer.parseInt(e.getActionCommand().substring(1)); 255 if (log.isDebugEnabled()) { 256 log.debug("event on {} row {}", b, row); 257 } 258 if (b == 'R') { 259 // read command 260 _cvDisplayVector.elementAt(row).read(_status); 261 } else if (b == 'C') { 262 // compare command 263 _cvDisplayVector.elementAt(row).confirm(_status); 264 } else { 265 // write command 266 _cvDisplayVector.elementAt(row).write(_status); 267 } 268 } 269 270 @Override 271 public void propertyChange(PropertyChangeEvent e) { 272 // don't need to forward Busy, do need to forward Value 273 // not sure about any others 274 if (!e.getPropertyName().equals("Busy")) { 275 fireTableDataChanged(); 276 } 277 } 278 279 public void addCV(String s, boolean readOnly, boolean infoOnly, boolean writeOnly) { 280 if (_cvAllMap.get(s) == null) { 281 CvValue cv = new CvValue(s, mProgrammer); 282 cv.setReadOnly(readOnly); 283 _cvAllMap.put(s, cv); 284 _cvDisplayVector.addElement(cv); 285 // connect to this CV to ensure the table display updates 286 cv.addPropertyChangeListener(this); 287 JButton bw = new JButton(Bundle.getMessage("ButtonWrite")); 288 _writeButtons.addElement(bw); 289 JButton br = new JButton(Bundle.getMessage("ButtonRead")); 290 _readButtons.addElement(br); 291 JButton bc = new JButton(Bundle.getMessage("ButtonCompare")); 292 _compareButtons.addElement(bc); 293 if (infoOnly || readOnly) { 294 if (writeOnly) { 295 bw.setEnabled(true); 296 bw.setActionCommand("W" + _numRows); 297 bw.addActionListener(this); 298 } else { 299 bw.setEnabled(false); 300 } 301 if (infoOnly) { 302 br.setEnabled(false); 303 bc.setEnabled(false); 304 } else { 305 br.setEnabled(true); 306 br.setActionCommand("R" + _numRows); 307 br.addActionListener(this); 308 bc.setEnabled(true); 309 bc.setActionCommand("C" + _numRows); 310 bc.addActionListener(this); 311 } 312 } else { 313 bw.setEnabled(true); 314 bw.setActionCommand("W" + _numRows); 315 bw.addActionListener(this); 316 if (writeOnly) { 317 br.setEnabled(false); 318 bc.setEnabled(false); 319 } else { 320 br.setEnabled(true); 321 br.setActionCommand("R" + _numRows); 322 br.addActionListener(this); 323 bc.setEnabled(true); 324 bc.setActionCommand("C" + _numRows); 325 bc.addActionListener(this); 326 } 327 } 328 _numRows++; 329 fireTableDataChanged(); 330 } 331 // make sure readonly set true if required 332 CvValue cv = _cvAllMap.get(s); 333 if (readOnly) { 334 cv.setReadOnly(readOnly); 335 } 336 if (infoOnly) { 337 cv.setReadOnly(!infoOnly); 338 cv.setWriteOnly(!infoOnly); 339 cv.setInfoOnly(infoOnly); 340 } 341 if (writeOnly) { 342 cv.setWriteOnly(writeOnly); 343 } 344 } 345 346 public boolean decoderDirty() { 347 int len = _cvDisplayVector.size(); 348 for (int i = 0; i < len; i++) { 349 if (_cvDisplayVector.elementAt(i).getState() == AbstractValue.ValueState.EDITED) { 350 if (log.isDebugEnabled()) { 351 log.debug("CV decoder dirty due to {}", _cvDisplayVector.elementAt(i).number()); 352 } 353 return true; 354 } 355 } 356 return false; 357 } 358 359 /** 360 * Register a VariableValue in a common store mapping CV numbers to 361 * variable names. This is for use by e.g. a CVTable to show tooltips 362 * efficiently. 363 * @param cv specific CV number that the variable references 364 * @param variableName from the variable being defined 365 */ 366 public void registerCvToVariableMapping(String cv, String variableName) { 367 // is there already a Set for these? 368 if ( ! cvToVarMap.containsKey(cv)) { 369 // no, create one 370 cvToVarMap.put(cv, Collections.newSetFromMap(new HashMap<String, Boolean>())); 371 } 372 // add the String 373 cvToVarMap.get(cv).add(variableName); 374 } 375 376 public Set<String> getCvToVariableMapping(String cv) { return cvToVarMap.get(cv); } 377 378 private HashMap<String, Set<String>> cvToVarMap = new HashMap<>(); 379 380 public void dispose() { 381 if (log.isDebugEnabled()) { 382 log.debug("dispose"); 383 } 384 385 // remove buttons 386 for (int i = 0; i < _writeButtons.size(); i++) { 387 _writeButtons.elementAt(i).removeActionListener(this); 388 } 389 for (int i = 0; i < _readButtons.size(); i++) { 390 _readButtons.elementAt(i).removeActionListener(this); 391 } 392 for (int i = 0; i < _compareButtons.size(); i++) { 393 _compareButtons.elementAt(i).removeActionListener(this); 394 } 395 396 // remove CV listeners 397 for (int i = 0; i < _cvDisplayVector.size(); i++) { 398 _cvDisplayVector.elementAt(i).removePropertyChangeListener(this); 399 } 400 401 // null references, so that they can be gc'd even if this isn't. 402 cvToVarMap = null; 403 404 _cvDisplayVector.removeAllElements(); 405 _cvDisplayVector = null; 406 407 _writeButtons.removeAllElements(); 408 _writeButtons = null; 409 410 _readButtons.removeAllElements(); 411 _readButtons = null; 412 413 _compareButtons.removeAllElements(); 414 _compareButtons = null; 415 416 _cvAllMap.clear(); 417 _cvAllMap = null; 418 419 _status = null; 420 421 } 422 423 int holdsAddress() { 424 int shortAddr = getCvByNumber("1").getValue(); 425 int longAddr = ((getCvByNumber("17").getValue()-192)<<8)+getCvByNumber("18").getValue(); 426 int addr = holdsLongAddress() ? longAddr : shortAddr; 427 return addr; 428 } 429 430 boolean holdsLongAddress() { 431 return (getCvByNumber("29").getValue() & 0x20) != 0; 432 } 433 434 private final static Logger log = LoggerFactory.getLogger(CvTableModel.class); 435}