001package jmri.jmrit.cabsignals; 002 003import java.beans.PropertyChangeEvent; 004import javax.swing.JTable; 005import javax.swing.JTextField; 006import jmri.Block; 007import jmri.BlockManager; 008import jmri.CabSignalManager; 009import jmri.InstanceManager; 010import jmri.LocoAddress; 011import jmri.Path; 012import jmri.SignalMast; 013 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017 018/** 019 * Table data model for display of Cab Signaling information. 020 * 021 * @author Steve Young (c) 2018 022 * @author Paul Bender (c) 2018 023 * @see CabSignalPane 024 * @since 4.13.4 025 */ 026public class CabSignalTableModel extends javax.swing.table.AbstractTableModel { 027 028 private CabSignalManager cabSignalManager; 029 030 // column order needs to match list in columnToolTips 031 032 static public final int LOCO_ID_COLUMN = 0; 033 static public final int SEND_CABSIG_COLUMN = 1; 034 static public final int CURRENT_BLOCK = 2; 035 static public final int BLOCK_DIR = 3; 036 static public final int REVERSE_BLOCK_DIR_BUTTON_COLUMN = 4; 037 static public final int NEXT_BLOCK = 5; 038 static public final int NEXT_SIGNAL = 6; 039 static public final int NEXT_ASPECT = 7; 040 static public final int NEXT_ASPECT_ICON = 8; 041 042 static public final int MAX_COLUMN = 9; 043 044 static final int[] STARTUPCOLUMNS = {0,1,2,3,4,5,6,7,8}; 045 046 CabSignalTableModel(int row, int column) { 047 cabSignalManager = InstanceManager.getNullableDefault(CabSignalManager.class); 048 if(cabSignalManager == null){ 049 log.info("creating new DefaultCabSignalManager"); 050 InstanceManager.store(new jmri.managers.DefaultCabSignalManager(), CabSignalManager.class); 051 cabSignalManager = InstanceManager.getNullableDefault(CabSignalManager.class); 052 } 053 } 054 055 // order needs to match column list top of dtabledatamodel 056 static final String[] COLUMNTOOLTIPS = { 057 null, // loco id 058 Bundle.getMessage("CabsigCheckboxTip"), 059 Bundle.getMessage("BlockUserName"), 060 Bundle.getMessage("BlockDirectionTip"), 061 null, // block lookup button 062 Bundle.getMessage("NextBlockTip"), 063 Bundle.getMessage("NextSignalTip"), 064 Bundle.getMessage("NextAspectTip"), 065 Bundle.getMessage("NextIconTip"), // aspect icon 066 067 }; // Length = number of items in array should (at least) match number of columns 068 069 /** 070 * {@inheritDoc} 071 */ 072 @Override 073 public int getRowCount() { 074 return cabSignalManager.getCabSignalList().size(); 075 } 076 077 /** 078 * {@inheritDoc} 079 */ 080 @Override 081 public int getColumnCount() { 082 return MAX_COLUMN; 083 } 084 085 /** 086 * Returns String of column name from column int 087 * used in table header. 088 * 089 * @param col int col number 090 */ 091 @Override 092 public String getColumnName(int col) { // not in any order 093 switch (col) { 094 case LOCO_ID_COLUMN: 095 return Bundle.getMessage("LocoID"); 096 case CURRENT_BLOCK: 097 return Bundle.getMessage("Block"); 098 case BLOCK_DIR: 099 return Bundle.getMessage("BlockDirection"); 100 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 101 return Bundle.getMessage("BlockButton"); 102 case NEXT_BLOCK: 103 return Bundle.getMessage("NextBlock"); 104 case NEXT_SIGNAL: 105 return Bundle.getMessage("NextSignal"); 106 case NEXT_ASPECT: 107 return Bundle.getMessage("NextAspect"); 108 case SEND_CABSIG_COLUMN: 109 return Bundle.getMessage("SigDataOn"); 110 case NEXT_ASPECT_ICON: 111 return Bundle.getMessage("AspectIconMenu"); 112 default: 113 return "unknown"; // NOI18N 114 } 115 } 116 117 /** 118 * Returns int of startup column widths. 119 * 120 * @param col int col number 121 * @return initial preferred width 122 */ 123 public static int getPreferredWidth(int col) { 124 switch (col) { 125 case SEND_CABSIG_COLUMN: 126 case NEXT_ASPECT_ICON: 127 return new JTextField(3).getPreferredSize().width; 128 case LOCO_ID_COLUMN: 129 return new JTextField(4).getPreferredSize().width; 130 case BLOCK_DIR: 131 case NEXT_SIGNAL: 132 return new JTextField(6).getPreferredSize().width; 133 case CURRENT_BLOCK: 134 case NEXT_BLOCK: 135 return new JTextField(8).getPreferredSize().width; 136 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 137 case NEXT_ASPECT: 138 return new JTextField(10).getPreferredSize().width; 139 default: 140 log.warn("no width found col {}",col); 141 return new JTextField(" <unknown> ").getPreferredSize().width; // NOI18N 142 } 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 public Class<?> getColumnClass(int col) { 150 switch (col) { 151 case LOCO_ID_COLUMN: 152 return LocoAddress.class; 153 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 154 return javax.swing.JButton.class; 155 case CURRENT_BLOCK: 156 case BLOCK_DIR: 157 case NEXT_BLOCK: 158 case NEXT_SIGNAL: 159 case NEXT_ASPECT: 160 case NEXT_ASPECT_ICON: 161 return String.class; 162 case SEND_CABSIG_COLUMN: 163 return Boolean.class; 164 default: 165 log.error("no column class located"); 166 return null; 167 } 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 @Override 174 public boolean isCellEditable(int row, int col) { 175 switch (col) { 176 case SEND_CABSIG_COLUMN: 177 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 178 return true; 179 default: 180 return false; 181 } 182 } 183 184 /** 185 * Configure a table to have our standard rows and columns. 186 * <p> 187 * This is optional, in that other table formats can use this table model. 188 * But we put it here to help keep it consistent. 189 * @param cmdStatTable Table to be configured 190 */ 191 public void configureTable(JTable cmdStatTable) { 192 // allow reordering of the columns 193 cmdStatTable.getTableHeader().setReorderingAllowed(true); 194 195 // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 196 cmdStatTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 197 198 // resize columns as requested 199 for (int i = 0; i < cmdStatTable.getColumnCount(); i++) { 200 int width = getPreferredWidth(i); 201 cmdStatTable.getColumnModel().getColumn(i).setPreferredWidth(width); 202 } 203 // cmdStatTable.sizeColumnsToFit(-1); 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public Object getValueAt(int row, int col) { 211 SignalMast mast; 212 Block b; 213 switch (col) { 214 case LOCO_ID_COLUMN: 215 cabSignalManager.getCabSignalArray()[row].addPropertyChangeListener( (PropertyChangeEvent e) -> { 216 if(e.getSource() instanceof jmri.CabSignal){ 217 fireTableDataChanged(); 218 } 219 }); 220 return cabSignalManager.getCabSignalArray()[row].getCabSignalAddress(); 221 case CURRENT_BLOCK: 222 b = cabSignalManager.getCabSignalArray()[row].getBlock(); 223 if ( b != null){ 224 return b.getDisplayName(); 225 } else { 226 return ""; 227 } 228 case BLOCK_DIR: 229 b = cabSignalManager.getCabSignalArray()[row].getBlock(); 230 if ( b != null){ 231 return Path.decodeDirection(b.getDirection()); 232 } else { 233 return ""; 234 } 235 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 236 if (cabSignalManager.getCabSignalArray()[row].getBlock()==null){ 237 return Bundle.getMessage("BlockLookup"); 238 } else { 239 return Bundle.getMessage("ChngDirection"); 240 } 241 case NEXT_BLOCK: 242 Block nextBl = cabSignalManager.getCabSignalArray()[row].getNextBlock(); 243 if ( nextBl != null){ 244 return nextBl.getDisplayName(); 245 } else { 246 return ""; 247 } 248 case NEXT_SIGNAL: 249 mast = cabSignalManager.getCabSignalArray()[row].getNextMast(); 250 if (mast!=null) { 251 return mast.getDisplayName(); 252 } 253 return ""; 254 case NEXT_ASPECT: 255 mast = cabSignalManager.getCabSignalArray()[row].getNextMast(); 256 if (mast!=null) { 257 return mast.getAspect(); 258 } 259 return ""; 260 case NEXT_ASPECT_ICON: 261 mast = cabSignalManager.getCabSignalArray()[row].getNextMast(); 262 if (mast!=null) { 263 String imageLink = mast.getAppearanceMap().getProperty(mast.getAspect(), "imagelink"); 264 log.debug("imagelink is {}", imageLink); 265 if ( imageLink != null ) { 266 String newlink = imageLink.replace("../", ""); // replace is immutable 267 // should start at the resources directory 268 return newlink; 269 } 270 } 271 return ""; 272 case SEND_CABSIG_COLUMN: 273 return cabSignalManager.getCabSignalArray()[row].isCabSignalActive(); 274 default: 275 log.error("internal state inconsistent with table request for row {} col {}", row, col); 276 return null; 277 } 278 } 279 280 /** 281 * {@inheritDoc} 282 */ 283 @Override 284 public void setValueAt(Object value, int row, int col) { 285 switch (col) { 286 case REVERSE_BLOCK_DIR_BUTTON_COLUMN: 287 cabSignalManager.getCabSignalArray()[row].setBlock(); 288 chngblockdir(row); 289 break; 290 case NEXT_BLOCK: 291 jmri.util.ThreadingUtil.runOnLayout( ()->{ 292 BlockManager bmgr = jmri.InstanceManager.getDefault(jmri.BlockManager.class); 293 Block b = bmgr.getBlock((String)value); 294 cabSignalManager.getCabSignalArray()[row].setBlock(b); 295 }); 296 break; 297 case SEND_CABSIG_COLUMN: 298 cabSignalManager.getCabSignalArray()[row].setCabSignalActive((Boolean) value); 299 break; 300 default: 301 break; 302 } 303 } 304 305 /** 306 * Reverse the direction on the block associated with 307 * the row. Changes to the cab signal happen when the block's 308 * properties change. 309 */ 310 private void chngblockdir(int row){ 311 log.debug("changing block direction for row {}", row); 312 int olddirection; 313 Block b = cabSignalManager.getCabSignalArray()[row].getBlock(); 314 if (b == null){ 315 cabSignalManager.getCabSignalArray()[row].setBlock(); 316 b = cabSignalManager.getCabSignalArray()[row].getBlock(); 317 if (b==null){ 318 return; 319 } else { 320 cabSignalManager.getCabSignalArray()[row].setBlock(b); 321 olddirection=b.getDirection(); 322 } 323 } 324 else { 325 olddirection=b.getDirection(); 326 log.debug("Block {} set to direction {} ", b.getUserName(), (String.valueOf(olddirection)) ); 327 } 328 329 log.debug(" Direction to reverse :{}", Path.decodeDirection(olddirection) ); 330 331 if (olddirection==0){ 332 log.debug("No direction found, setting to North, East"); 333 b.setDirection(80); 334 } else { 335 log.debug(" direction found, setting reverse."); 336 b.setDirection(Path.reverseDirection(olddirection)); 337 } 338 fireTableDataChanged(); 339 log.debug("block {} now has direction {}", b.getUserName(), b.getDirection()); 340 } 341 342 protected void setPanelPauseButton(boolean isPaused){ 343 for (int i = 0; i < getRowCount(); i++) { 344 cabSignalManager.getCabSignalArray()[i].setMasterCabSigPauseActive(isPaused); 345 } 346 } 347 348 public void dispose() { 349 } 350 351 private final static Logger log = LoggerFactory.getLogger(CabSignalTableModel.class); 352 353}