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