001package jmri.jmrix.sprog.sprogslotmon; 002 003import javax.swing.JLabel; 004import javax.swing.JTable; 005import javax.swing.JTextField; 006import jmri.jmrix.sprog.SprogConstants; 007import jmri.jmrix.sprog.SprogSlot; 008import jmri.jmrix.sprog.SprogSlotListener; 009import jmri.jmrix.sprog.SprogSystemConnectionMemo; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Table data model for display of slot manager contents. 015 * 016 * @author Bob Jacobsen Copyright (C) 2001 017 * @author Andrew Crosland (C) 2006 ported to SPROG 018 */ 019public class SprogSlotMonDataModel extends javax.swing.table.AbstractTableModel implements SprogSlotListener { 020 021 static public final int SLOTCOLUMN = 0; 022 static public final int ADDRCOLUMN = 1; 023 static public final int SPDCOLUMN = 2; 024 static public final int STATCOLUMN = 3; // status: free, common, etc 025 static public final int DIRCOLUMN = 4; 026 027 static public final int NUMCOLUMN = 5; 028 029 private SprogSystemConnectionMemo _memo = null; 030 031 SprogSlotMonDataModel(int row, int column,SprogSystemConnectionMemo memo) { 032 _memo = memo; 033 // connect to SprogSlotManager for updates 034 _memo.getCommandStation().addSlotListener(this); 035 } 036 037 /** 038 * Return the number of rows to be displayed. This can vary depending on 039 * whether only active rows are displayed. 040 * <p> 041 * This should probably use a local cache instead of counting/searching each 042 * time. 043 */ 044 @Override 045 public int getRowCount() { 046 int nMax = _memo.getNumSlots(); 047 if (_allSlots) { 048 // will show the entire set, so don't bother counting 049 return nMax; 050 } 051 int n = 0; 052 int nMin = 0; 053 for (int i = nMin; i < nMax; i++) { 054 SprogSlot s = _memo.getCommandStation().slot(i); 055 if (s.isFree() != true) { 056 n++; 057 } 058 } 059 return n; 060 } 061 062 @Override 063 public int getColumnCount() { 064 return NUMCOLUMN; 065 } 066 067 @Override 068 public String getColumnName(int col) { 069 switch (col) { 070 case SLOTCOLUMN: 071 return Bundle.getMessage("SlotCol"); 072// case ESTOPCOLUMN: return ""; // no heading, as button is clear 073 case ADDRCOLUMN: 074 return Bundle.getMessage("AddressCol"); 075 case SPDCOLUMN: 076 return Bundle.getMessage("SpeedCol"); 077 case STATCOLUMN: 078 return Bundle.getMessage("StatusCol"); 079// case CONSCOLUMN: return "Consisted"; 080 case DIRCOLUMN: 081 return Bundle.getMessage("DirectionCol"); 082// case DISPCOLUMN: return ""; // no heading, as button is clear 083 default: 084 return "unknown"; // NOI18N 085 } 086 } 087 088 @Override 089 public Class<?> getColumnClass(int col) { 090 switch (col) { 091 case SLOTCOLUMN: 092 return Integer.class; 093 case ADDRCOLUMN: 094 case SPDCOLUMN: 095 case STATCOLUMN: 096// case CONSCOLUMN: 097 case DIRCOLUMN: 098 return String.class; 099// case ESTOPCOLUMN: 100// case DISPCOLUMN: 101// return JButton.class; 102 default: 103 return null; 104 } 105 } 106 107 @Override 108 public boolean isCellEditable(int row, int col) { 109 switch (col) { 110// case ESTOPCOLUMN: 111// case DISPCOLUMN: 112// return true; 113 default: 114 return false; 115 } 116 } 117 118 static final Boolean True = Boolean.valueOf("True"); 119 static final Boolean False = Boolean.valueOf("False"); 120 121 @SuppressWarnings("null") 122 @Override 123 public Object getValueAt(int row, int col) { 124 SprogSlot s = _memo.getCommandStation().slot(slotNum(row)); 125 if (s == null) { 126 log.error("slot pointer was null for slot row: {} col: {}", row, col); 127 } 128 129 switch (col) { 130 case SLOTCOLUMN: // slot number 131 return Integer.valueOf(slotNum(row)); 132// case ESTOPCOLUMN: // 133// return "E Stop"; // will be name of button in default GUI 134 case ADDRCOLUMN: // 135 switch (s.slotStatus()) { 136 case SprogConstants.SLOT_IN_USE: 137 return Integer.toString(s.getAddr()) + "("+ (s.getIsLong() ? Bundle.getMessage("LongAddressChar") : Bundle.getMessage("ShortAddressChar")) + ")"; 138 case SprogConstants.SLOT_FREE: 139 return "-"; 140 default: 141 return Bundle.getMessage("StateError"); 142 } 143 case SPDCOLUMN: // 144 switch (s.slotStatus()) { 145 case SprogConstants.SLOT_IN_USE: 146 if (s.isF0to4Packet()) { 147 return "F0to4Pkt"; 148 } else if (s.isF5to8Packet()) { 149 return "F5to8Pkt"; 150 } else if (s.isF9to12Packet()) { 151 return "F9to12Pkt"; 152 } else if (s.isF13to20Packet()) { 153 return "F13to20Pkt"; 154 } else if (s.isF21to28Packet()) { 155 return "F21to28Pkt"; 156 } else if (s.isOpsPkt()) { 157 return "OpsPkt"; 158 } else if (s.isSpeedPacket()) { 159 String t; 160 if (s.speed() == 1) { 161 t = "(estop) 1"; 162 } else { 163 t = " " + s.speed(); 164 } 165 return t.substring(t.length() - 9, t.length()); // 9 comes from (estop) 166 } else { 167 return Bundle.getMessage("StateError"); 168 } 169 case SprogConstants.SLOT_FREE: 170 return "-"; 171 default: 172 return Bundle.getMessage("StateError"); 173 } 174 case STATCOLUMN: // 175 switch (s.slotStatus()) { 176 case SprogConstants.SLOT_IN_USE: 177 return Bundle.getMessage("StateInUse"); 178 case SprogConstants.SLOT_FREE: 179 return Bundle.getMessage("StateFree"); 180 default: 181 return Bundle.getMessage("StateError"); 182 } 183// case CONSCOLUMN: // 184// return "<n/a>"; 185// case DISPCOLUMN: // 186// return Bundle.getMessage("ButtonRelease"); // will be name of button in default GUI 187 case DIRCOLUMN: // 188 switch (s.slotStatus()) { 189 case SprogConstants.SLOT_IN_USE: 190 if (s.isSpeedPacket()) { 191 return (s.isForward() ? Bundle.getMessage("DirColForward") : Bundle.getMessage("DirColReverse")); 192 } else { 193 return "-"; 194 } 195 case SprogConstants.SLOT_FREE: 196 return "-"; 197 default: 198 return Bundle.getMessage("StateError"); 199 } 200 201 default: 202 log.error("internal state inconsistent with table request for row {}, col {}", row, col); 203 return null; 204 } 205 } 206 207 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES", 208 justification="better to keep cases in column order rather than to combine") 209 public int getPreferredWidth(int col) { 210 switch (col) { 211 case SLOTCOLUMN: 212 return new JTextField(3).getPreferredSize().width; 213// case ESTOPCOLUMN: 214// return new JButton("E Stop").getPreferredSize().width; 215 case ADDRCOLUMN: 216 return new JTextField(5).getPreferredSize().width; 217 case SPDCOLUMN: 218 return new JTextField(6).getPreferredSize().width; 219 case STATCOLUMN: 220 return new JTextField(6).getPreferredSize().width; 221// case CONSCOLUMN: 222// return new JTextField(4).getPreferredSize().width; 223 case DIRCOLUMN: 224 return new JTextField(3).getPreferredSize().width; 225// case DISPCOLUMN: 226// return new JButton(Bundle.getMessage("ButtonRelease")).getPreferredSize().width; 227 default: 228 return new JLabel(" <unknown> ").getPreferredSize().width; // NOI18N 229 } 230 } 231 232 @Override 233 public void setValueAt(Object value, int row, int col) { 234 // check for in use 235 SprogSlot s = _memo.getCommandStation().slot(slotNum(row)); 236 if (s == null) { 237 log.error("slot pointer was null for slot row: {} col: {}", row, col); 238 return; 239 } 240// if (col == ESTOPCOLUMN) { 241// log.debug("Start eStop in slot "+row); 242// _memo.getSlotManager().estopSlot(row); 243// } 244// else if (col == DISPCOLUMN) { 245// log.debug("Start freeing slot {}", row); 246// fireTableRowsUpdated(row,row); 247// } 248 } 249 250 /** 251 * Configure a table to have our standard rows and columns. 252 * This is optional, in that other table formats can use this table model. 253 * But we put it here to help keep it consistent. 254 * 255 * @param slotTable the slot table to configure. 256 */ 257 public void configureTable(JTable slotTable) { 258 // allow reordering of the columns 259 slotTable.getTableHeader().setReorderingAllowed(true); 260 261 // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 262 slotTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 263 264 // resize columns as requested 265 for (int i = 0; i < slotTable.getColumnCount(); i++) { 266 int width = getPreferredWidth(i); 267 slotTable.getColumnModel().getColumn(i).setPreferredWidth(width); 268 } 269 slotTable.sizeColumnsToFit(-1); 270 271// // install a button renderer & editor in the "DISP" column for freeing a slot 272// setColumnToHoldButton(slotTable, SprogSlotMonDataModel.DISPCOLUMN); 273// 274// // install a button renderer & editor in the "ESTOP" column for stopping a loco 275// setColumnToHoldEStopButton(slotTable, SprogSlotMonDataModel.ESTOPCOLUMN); 276 } 277 278// void setColumnToHoldButton(JTable slotTable, int column) { 279// TableColumnModel tcm = slotTable.getColumnModel(); 280// // install the button renderers & editors in this column 281// ButtonRenderer buttonRenderer = new ButtonRenderer(); 282// tcm.getColumn(column).setCellRenderer(buttonRenderer); 283// TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 284// tcm.getColumn(column).setCellEditor(buttonEditor); 285// // ensure the table rows, columns have enough room for buttons 286// slotTable.setRowHeight(new JButton(" "+getValueAt(1, column)).getPreferredSize().height); 287// slotTable.getColumnModel().getColumn(column) 288// .setPreferredWidth(new JButton(" "+getValueAt(1, column)).getPreferredSize().width); 289// } 290// 291// void setColumnToHoldEStopButton(JTable slotTable, int column) { 292// TableColumnModel tcm = slotTable.getColumnModel(); 293// // install the button renderers & editors in this column 294// ButtonRenderer buttonRenderer = new ButtonRenderer(); 295// tcm.getColumn(column).setCellRenderer(buttonRenderer); 296// TableCellEditor buttonEditor = new ButtonEditor(new JButton()){ 297// public void mousePressed(MouseEvent e) { 298// stopCellEditing(); 299// } 300// }; 301// tcm.getColumn(column).setCellEditor(buttonEditor); 302// // ensure the table rows, columns have enough room for buttons 303// slotTable.setRowHeight(new JButton(" "+getValueAt(1, column)).getPreferredSize().height); 304// slotTable.getColumnModel().getColumn(column) 305// .setPreferredWidth(new JButton(" "+getValueAt(1, column)).getPreferredSize().width); 306// } 307 // methods to communicate with SprogSlotManager 308 @Override 309 public synchronized void notifyChangedSlot(SprogSlot s) { 310 // update model from this slot 311 312 int slotNum = -1; 313 if (_allSlots) { // this will be row until we show only active slots 314 slotNum = s.getSlotNumber(); // and we are displaying the System slots 315 } 316 log.debug("Received notification of changed slot: {}", slotNum); 317 // notify the JTable object that a row has changed; do that in the Swing thread! 318 Runnable r = new Notify(slotNum, this); // -1 in first arg means all 319 javax.swing.SwingUtilities.invokeLater(r); 320 } 321 322 static class Notify implements Runnable { 323 324 private int _row; 325 javax.swing.table.AbstractTableModel _model; 326 327 public Notify(int row, javax.swing.table.AbstractTableModel model) { 328 _row = row; 329 _model = model; 330 } 331 332 @Override 333 public void run() { 334 if (-1 == _row) { // notify about entire table 335 _model.fireTableDataChanged(); // just that row 336 } else { 337 // notify that _row has changed 338 _model.fireTableRowsUpdated(_row, _row); // just that row 339 } 340 } 341 } 342 343 // methods for control of "all slots" vs "only active slots" 344 private boolean _allSlots = true; 345 346 public void showAllSlots(boolean val) { 347 _allSlots = val; 348 } 349 350 /** 351 * Return slot number for a specific row. 352 * <p> 353 * This should probably use a local cache instead of counting/searching each 354 * time. 355 * 356 * @param row Row number in the displayed table 357 * @return Matching slot number 358 */ 359 protected int slotNum(int row) { 360 // ??? Can't this just return row ??? 361 int slotNum; 362 int n = -1; // need to find a used slot to have the 0th one! 363 int nMin = 0; 364 int nMax = _memo.getNumSlots(); 365 for (slotNum = nMin; slotNum < nMax; slotNum++) { 366 SprogSlot s = _memo.getCommandStation().slot(slotNum); 367 if (_allSlots || s.slotStatus() != SprogConstants.SLOT_FREE) { 368 n++; 369 } 370 if (n == row) { 371 break; 372 } 373 } 374 return slotNum; 375 } 376 377 public void dispose() { 378 _memo.getCommandStation().removeSlotListener(this); 379 // table.removeAllElements(); 380 // table = null; 381 } 382 383 private final static Logger log = LoggerFactory.getLogger(SprogSlotMonDataModel.class); 384 385}