001package jmri.jmrit.beantable.signalmast; 002 003import java.util.*; 004 005import javax.annotation.CheckForNull; 006import javax.annotation.Nonnull; 007import javax.swing.*; 008import javax.swing.table.TableColumn; 009 010import jmri.*; 011import jmri.jmrit.beantable.BeanTableDataModel; 012import jmri.jmrit.beantable.SignalMastLogicTableAction; 013import jmri.util.ThreadingUtil; 014import jmri.util.swing.JmriMouseEvent; 015import jmri.util.swing.XTableColumnModel; 016 017/** 018 * Create a SignalMastLogic Table Data Model. 019 * Code originally from SignalMastLogicTableAction 020 * @author Kevin Dickerson Copyright(C) 2011 021 * @author Steve Young (C) 2023 022 */ 023public class SignalMastLogicTableDataModel extends BeanTableDataModel<SignalMastLogic>{ 024 025 public static final int SOURCECOL = 0; 026 public static final int SOURCEAPPCOL = 1; 027 public static final int DESTCOL = 2; 028 public static final int DESTAPPCOL = 3; 029 public static final int COMCOL = 4; 030 public static final int DELCOL = 5; 031 public static final int ENABLECOL = 6; 032 public static final int EDITLOGICCOL = 7; 033 public static final int MAXSPEEDCOL = 8; 034 public static final int COLUMNCOUNT = 9; 035 036 037 private boolean suppressUpdate = false; // does not update table model changelistener during auto create pairs 038 039 public SignalMastLogicTableDataModel(){ 040 super(); 041 SignalMastLogicTableDataModel.this.updateNameList(); 042 } 043 044 //We have to set a manager first off, but this gets replaced. 045 @Override 046 protected SignalMastLogicManager getManager() { 047 return InstanceManager.getDefault(SignalMastLogicManager.class); 048 } 049 050 @Override 051 public String getValue(String s) { 052 return "Set"; 053 } 054 055 @Override 056 protected String getMasterClassName() { 057 return SignalMastLogicTableAction.class.getName(); 058 } 059 060 @Override 061 public void clickOn(SignalMastLogic t) { 062 } 063 064 private ArrayList<Hashtable<SignalMastLogic, SignalMast>> signalMastLogicList = null; 065 066 @Nonnull 067 private List<Hashtable<SignalMastLogic, SignalMast>> getSMLList(){ 068 if ( signalMastLogicList == null) { 069 signalMastLogicList = new ArrayList<>(); 070 } 071 return signalMastLogicList; 072 } 073 074 @Override 075 protected synchronized void updateNameList() { 076 // first, remove listeners from the individual objects 077 078 for (int i = 0; i < getSMLList().size(); i++) { 079 // if object has been deleted, it's not here; ignore it 080 Hashtable<SignalMastLogic, SignalMast> b = getSMLList().get(i); 081 Enumeration<SignalMastLogic> en = b.keys(); 082 while (en.hasMoreElements()) { 083 SignalMastLogic sm = en.nextElement(); 084 SignalMast dest = b.get(sm); 085 sm.removePropertyChangeListener(this); 086 sm.getSourceMast().removePropertyChangeListener(this); 087 dest.removePropertyChangeListener(this); 088 } 089 } 090 List<SignalMastLogic> source = getManager().getSignalMastLogicList(); 091 signalMastLogicList.clear(); 092 for (int i = 0; i < source.size(); i++) { 093 List<SignalMast> destList = source.get(i).getDestinationList(); 094 source.get(i).addPropertyChangeListener(this); 095 source.get(i).getSourceMast().addPropertyChangeListener(this); 096 for (int j = 0; j < destList.size(); j++) { 097 Hashtable<SignalMastLogic, SignalMast> hash = new Hashtable<>(1); 098 hash.put(source.get(i), destList.get(j)); 099 destList.get(j).addPropertyChangeListener(this); 100 getSMLList().add(hash); 101 } 102 } 103 } 104 105 public void setSuppressUpdate(boolean newVal) { 106 suppressUpdate = newVal; 107 } 108 109 //Will need to redo this so that we work out the row number from looking in the signalmastlogiclist. 110 @Override 111 public void propertyChange(java.beans.PropertyChangeEvent e) { 112 if (suppressUpdate) { 113 return; 114 } 115 // updateNameList(); 116 if (SignalMastLogicManager.PROPERTY_LENGTH.equals(e.getPropertyName()) 117 || SignalMastLogic.PROPERTY_UPDATED_DESTINATION.equals(e.getPropertyName()) 118 || SignalMastLogic.PROPERTY_UPDATED_SOURCE.equals(e.getPropertyName())) { 119 updateNameList(); 120 log.debug("Table changed length to {}", getSMLList().size()); 121 fireTableDataChanged(); 122 } else if (e.getSource() instanceof SignalMastLogic) { 123 SignalMastLogic logic = (SignalMastLogic) e.getSource(); 124 if (matchPropertyName(e)) { 125 for (int i = 0; i < getSMLList().size(); i++) { 126 Hashtable<SignalMastLogic, SignalMast> b = getSMLList().get(i); 127 Enumeration<SignalMastLogic> en = b.keys(); 128 while (en.hasMoreElements()) { 129 SignalMastLogic sm = en.nextElement(); 130 if (sm == logic) { 131 fireTableRowsUpdated(i, i); 132 } 133 } 134 } 135 } 136 } else if (e.getSource() instanceof SignalMast) { 137 SignalMast sigMast = (SignalMast) e.getSource(); 138 for (int i = 0; i < getSMLList().size(); i++) { 139 Hashtable<SignalMastLogic, SignalMast> b = getSMLList().get(i); 140 Enumeration<SignalMastLogic> en = b.keys(); 141 while (en.hasMoreElements()) { 142 SignalMastLogic sm = en.nextElement(); 143 //SignalMast dest = b.get(sm); 144 if (sm.getSourceMast() == sigMast) { 145 fireTableRowsUpdated(i, i); 146 } 147 } 148 } 149 } 150 } 151 152 /** 153 * Is this property event announcing a change this table should 154 * display? 155 * <p> 156 * Note that events will come both from the NamedBeans and also from 157 * the manager 158 */ 159 @Override 160 protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) { 161 switch (e.getPropertyName()) { 162 case SignalMastLogic.PROPERTY_ENABLED: 163 case SignalMastLogic.PROPERTY_UPDATED_DESTINATION: 164 return true; 165 default: 166 return super.matchPropertyName(e); 167 } 168 } 169 170 @Override 171 public int getColumnCount() { 172 return COLUMNCOUNT; 173 } 174 175 @Override 176 public int getRowCount() { 177 return getSMLList().size(); 178 } 179 180 @Override 181 public Object getValueAt(int row, int col) { 182 // some error checking 183 if (row >= getSMLList().size()) { 184 log.debug("row index is greater than signalMastLogicList size"); 185 return null; 186 } 187 SignalMastLogic b = getLogicFromRow(row); 188 if (b==null){ 189 return null; 190 } 191 SignalMast destMast; 192 switch (col) { 193 case SOURCECOL: 194 return b.getSourceMast().getDisplayName(); 195 case DESTCOL: // return user name 196 // sometimes, the TableSorter invokes this on rows that no longer exist, so we check 197 destMast = getDestMastFromRow(row); 198 return ( destMast != null ? destMast.getDisplayName() : null); 199 case SOURCEAPPCOL: // 200 return b.getSourceMast().getAspect(); 201 case DESTAPPCOL: // 202 destMast = getDestMastFromRow(row); 203 return ( destMast != null ? destMast.getAspect() : null); 204 case COMCOL: 205 return b.getComment(getDestMastFromRow(row)); 206 case DELCOL: 207 return Bundle.getMessage("ButtonDelete"); 208 case EDITLOGICCOL: 209 return Bundle.getMessage("ButtonEdit"); 210 case ENABLECOL: 211 return b.isEnabled(getDestMastFromRow(row)); 212 case MAXSPEEDCOL: 213 return b.getMaximumSpeed(getDestMastFromRow(row)); 214 default: 215 return super.getValueAt(row, col); 216 } 217 } 218 219 @Override 220 public void setValueAt(Object value, int row, int col) { 221 SignalMastLogic rowLogic = getLogicFromRow(row); 222 if ( rowLogic == null ){ 223 return; 224 } 225 switch (col) { 226 case COMCOL: 227 rowLogic.setComment((String) value, getDestMastFromRow(row)); 228 break; 229 case EDITLOGICCOL: 230 ThreadingUtil.runOnGUIEventually( () -> editLogic(row)); 231 break; 232 case DELCOL: 233 // button fired, delete Bean 234 deleteLogic(row); 235 break; 236 case ENABLECOL: 237 SignalMast destMast = getDestMastFromRow(row); 238 if (destMast==null){ 239 break; 240 } 241 if ((Boolean) value) { 242 rowLogic.setEnabled(destMast); 243 } else { 244 rowLogic.setDisabled(destMast); 245 } 246 break; 247 default: 248 super.setValueAt(value, row, col); 249 } 250 } 251 252 @Override 253 public String getColumnName(int col) { 254 switch (col) { 255 case SOURCECOL: 256 return Bundle.getMessage("Source"); 257 case DESTCOL: 258 return Bundle.getMessage("Destination"); 259 case SOURCEAPPCOL: 260 return Bundle.getMessage("LabelAspectType"); 261 case DESTAPPCOL: 262 return Bundle.getMessage("LabelAspectType"); 263 case COMCOL: 264 return Bundle.getMessage("Comment"); 265 case DELCOL: 266 return ""; // override default, no title for Delete column 267 case EDITLOGICCOL: 268 return ""; // override default, no title for Edit column 269 case ENABLECOL: 270 return Bundle.getMessage("ColumnHeadEnabled"); 271 case MAXSPEEDCOL: 272 return Bundle.getMessage("LabelMaxSpeed"); 273 default: 274 return super.getColumnName(col); 275 } 276 } 277 278 @Override 279 public Class<?> getColumnClass(int col) { 280 switch (col) { 281 case SOURCECOL: 282 case DESTCOL: 283 case SOURCEAPPCOL: 284 case COMCOL: 285 case DESTAPPCOL: 286 return String.class; 287 case ENABLECOL: 288 return Boolean.class; 289 case EDITLOGICCOL: 290 case DELCOL: 291 return JButton.class; 292 case MAXSPEEDCOL: 293 return Float.class; 294 default: 295 return super.getColumnClass(col); 296 } 297 } 298 299 @Override 300 public boolean isCellEditable(int row, int col) { 301 switch (col) { 302 case COMCOL: 303 case EDITLOGICCOL: 304 case DELCOL: 305 case ENABLECOL: 306 return true; 307 default: 308 return false; 309 } 310 } 311 312 final jmri.jmrit.signalling.SignallingAction sigLog = new jmri.jmrit.signalling.SignallingAction(); 313 314 void editLogic(int row) { 315 SignalMastLogic sml = getLogicFromRow(row); 316 if ( sml != null ) { 317 318 sigLog.setMast(sml.getSourceMast(), getDestMastFromRow(row)); 319 sigLog.actionPerformed(null); 320 } 321 } 322 323 void deleteLogic(int row) { 324 //This needs to be looked at 325 SignalMastLogic sml = getLogicFromRow(row); 326 SignalMast destMast = getDestMastFromRow(row); 327 if ( sml != null && destMast !=null ) { 328 InstanceManager.getDefault(SignalMastLogicManager.class).removeSignalMastLogic(sml, destMast); 329 } 330 } 331 332 @CheckForNull 333 public SignalMast getDestMastFromRow(int row) { 334 // if object has been deleted, it's not here; ignore it 335 Hashtable<SignalMastLogic, SignalMast> b = getSMLList().get(row); 336 Enumeration<SignalMastLogic> en = b.keys(); 337 while (en.hasMoreElements()) { 338 return b.get(en.nextElement()); 339 } 340 return null; 341 } 342 343 @CheckForNull 344 public SignalMastLogic getLogicFromRow(int row) { 345 Hashtable<SignalMastLogic, SignalMast> b = getSMLList().get(row); 346 Enumeration<SignalMastLogic> en = b.keys(); 347 while (en.hasMoreElements()) { 348 return en.nextElement(); 349 } 350 return null; 351 } 352 353 @Override 354 public int getPreferredWidth(int col) { 355 switch (col) { 356 case SOURCECOL: 357 case DESTCOL: 358 case DESTAPPCOL: 359 case SOURCEAPPCOL: 360 case MAXSPEEDCOL: 361 return new JTextField(10).getPreferredSize().width; 362 case COMCOL: 363 return 75; 364 case EDITLOGICCOL: // not actually used due to the configureTable, setColumnToHoldButton, configureButton 365 return new JTextField(6).getPreferredSize().width; 366 case DELCOL: // not actually used due to the configureTable, setColumnToHoldButton, configureButton 367 case ENABLECOL: 368 return new JTextField(5).getPreferredSize().width; 369 default: 370 return super.getPreferredWidth(col); 371 } 372 } 373 374 @Override 375 public void configureTable(JTable table) { 376 setColumnToHoldButton(table, EDITLOGICCOL, 377 new JButton(Bundle.getMessage("ButtonEdit"))); 378 table.getTableHeader().setReorderingAllowed(true); 379 380 // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 381 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 382 383 // resize columns as requested 384 for (int i = 0; i < table.getColumnCount(); i++) { 385 int width = getPreferredWidth(i); 386 table.getColumnModel().getColumn(i).setPreferredWidth(width); 387 } 388 table.sizeColumnsToFit(-1); 389 390 // configValueColumn(table); 391 configDeleteColumn(table); 392 } 393 394 @Override 395 public SignalMastLogic getBySystemName(@Nonnull String name) { 396 return null; 397 } 398 399 @Override 400 public SignalMastLogic getByUserName(@Nonnull String name) { 401 return null; 402 } 403 404 @Override 405 synchronized public void dispose() { 406 407 getManager().removePropertyChangeListener(this); 408 409 for (int i = 0; i < getSMLList().size(); i++) { 410 SignalMastLogic b = getLogicFromRow(i); 411 if (b != null) { 412 b.removePropertyChangeListener(this); 413 } 414 } 415 416 super.dispose(); 417 } 418 419 @Override 420 protected void configDeleteColumn(JTable table) { 421 // have the delete column hold a button 422 setColumnToHoldButton(table, DELCOL, 423 new JButton(Bundle.getMessage("ButtonDelete"))); 424 } 425 426 @Override 427 protected String getBeanType() { 428 return "Signal Mast Logic"; 429 } 430 431 @Override 432 protected void showPopup(JmriMouseEvent e) { 433 } 434 435 @Override 436 protected void setColumnIdentities(JTable table) { 437 super.setColumnIdentities(table); 438 Enumeration<TableColumn> columns; 439 if (table.getColumnModel() instanceof XTableColumnModel) { 440 columns = ((XTableColumnModel) table.getColumnModel()).getColumns(false); 441 } else { 442 columns = table.getColumnModel().getColumns(); 443 } 444 while (columns.hasMoreElements()) { 445 TableColumn column = columns.nextElement(); 446 switch (column.getModelIndex()) { 447 case SOURCEAPPCOL: 448 column.setIdentifier("SrcAspect"); 449 break; 450 case DESTAPPCOL: 451 column.setIdentifier("DstAspect"); 452 break; 453 case DELCOL: 454 column.setIdentifier("Delete"); 455 break; 456 case EDITLOGICCOL: 457 column.setIdentifier("Edit"); 458 break; 459 default: 460 // use existing value 461 } 462 } 463 } 464 465 @Override 466 public String getCellToolTip(JTable table, int row, int col) { 467 468 SignalMastLogic sml = getLogicFromRow(row); 469 if ( sml == null ) { 470 return null; 471 } 472 473 String tip = null; 474 SignalMast dest; 475 switch (col) { 476 case SOURCECOL: 477 tip = formatToolTip(sml.getSourceMast().getComment()); 478 break; 479 case DESTCOL: 480 dest = this.getDestMastFromRow(row); 481 if ( dest != null) { 482 tip = formatToolTip(dest.getComment()); 483 } 484 break; 485 case COMCOL: 486 dest = this.getDestMastFromRow(row); 487 if ( dest != null) { 488 tip = formatToolTip(sml.getComment(dest)); 489 } 490 break; 491 default: 492 break; 493 } 494 return tip; 495 } 496 497 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignalMastLogicTableDataModel.class); 498}