001package jmri.jmrit.beantable.oblock; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.text.ParseException; 006import java.util.ArrayList; 007import java.util.List; 008import java.util.SortedSet; 009import java.util.TreeSet; 010import javax.annotation.Nonnull; 011import javax.swing.*; 012 013import jmri.*; 014import jmri.implementation.SignalSpeedMap; 015import jmri.jmrit.beantable.RowComboBoxPanel; 016import jmri.jmrit.beantable.block.BlockCurvatureJComboBox; 017import jmri.jmrit.logix.*; 018import jmri.util.IntlUtilities; 019import jmri.util.NamedBeanComparator; 020import jmri.util.ThreadingUtil; 021import jmri.util.gui.GuiLafPreferencesManager; 022import jmri.util.swing.JmriJOptionPane; 023 024/** 025 * GUI to define OBlocks. 026 * <p> 027 * Duplicates the JTable model for BlockTableAction and adds a column for the 028 * occupancy sensor. Configured for use within an internal frame. 029 * <p> 030 * Can be used with two interfaces: 031 * <ul> 032 * <li>original "desktop" InternalFrames (parent class TableFrames, an extended JmriJFrame) 033 * <li>JMRI "standard" Tabbed tables (parent class JPanel) 034 * </ul> 035 * The _tabbed field decides, it is set in prefs (restart required). 036 * 037 * @author Pete Cressman (C) 2010 038 * @author Egbert Broerse (C) 2020 039 */ 040public class OBlockTableModel extends jmri.jmrit.beantable.BeanTableDataModel<OBlock> { 041 042 @SuppressWarnings("hiding") // Field has same name as a field in the super class 043 static public final int SYSNAMECOL = 0; 044 @SuppressWarnings("hiding") // Field has same name as a field in the super class 045 static public final int USERNAMECOL = 1; 046 @SuppressWarnings("hiding") // Field has same name as a field in the super class 047 static public final int COMMENTCOL = 2; 048 static public final int STATECOL = 3; 049 static public final int SENSORCOL = 4; 050 static public final int EDIT_COL = 5; // Paths button 051 static public final int DELETE_COL = 6; 052 static public final int LENGTHCOL = 7; 053 static public final int UNITSCOL = 8; 054 static public final int REPORTERCOL = 9; 055 static public final int REPORT_CURRENTCOL = 10; 056 static public final int PERMISSIONCOL = 11; 057 static public final int SPEEDCOL = 12; 058 static public final int WARRANTCOL = 13; 059 static public final int ERR_SENSORCOL = 14; 060 static public final int CURVECOL = 15; 061 static public final int VALUE = 16; 062 static public final int NUMCOLS = 17; 063 064 static String ZEROS = "000000000"; // 9 bits contain the OBlock state info 065 066 java.text.DecimalFormat twoDigit = new java.text.DecimalFormat("0.00"); 067 068 OBlockManager _manager; 069 private final String[] tempRow = new String[NUMCOLS]; 070 private float _tempLen = 0.0f; // mm for length col of tempRow 071 TableFrames _parent; 072 private final boolean _tabbed; // updated from prefs (restart required) 073 private boolean _isMetric = false; 074 public OBlockTableModel(@Nonnull TableFrames parent) { 075 super(); 076 _parent = parent; 077 _tabbed = InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed(); 078 if (_tabbed) { 079 _manager = InstanceManager.getDefault(OBlockManager.class); // TODO also for _desktop? 080 _manager.addPropertyChangeListener(this); 081 } 082 updateNameList(); 083 if (!_tabbed) { 084 initTempRow(); 085 } 086 } 087 088 /** 089 * Respond to mouse events to show/hide columns. 090 * Has public access to allow setting from OBlockTableAction OBlock Panel. 091 * 092 * @param table the table based on this model 093 */ 094 public void addHeaderListener(JTable table) { 095 addMouseListenerToHeader(table); 096 } 097 098 void initTempRow() { 099 for (int i = 0; i < NUMCOLS; i++) { 100 tempRow[i] = null; 101 } 102 tempRow[LENGTHCOL] = twoDigit.format(0.0); 103 tempRow[UNITSCOL] = Bundle.getMessage("in"); 104 tempRow[CURVECOL] = BlockCurvatureJComboBox.getStringFromCurvature(Block.NONE); 105 tempRow[REPORT_CURRENTCOL] = Bundle.getMessage("Current"); 106 tempRow[PERMISSIONCOL] = Bundle.getMessage("Permissive"); 107 tempRow[SPEEDCOL] = ""; 108 tempRow[DELETE_COL] = Bundle.getMessage("ButtonClear"); 109 } 110 111 @Override 112 public Manager<OBlock> getManager() { 113 _manager = InstanceManager.getDefault(OBlockManager.class); 114 return _manager; 115 } 116 117 @Override 118 public OBlock getBySystemName(@Nonnull String name) { 119 return _manager.getBySystemName(name); 120 } 121 122 @Override 123 public OBlock getByUserName(@Nonnull String name) { 124 return _manager.getByUserName(name); 125 } 126 127 @Override 128 protected String getBeanType() { 129 return "OBlock"; 130 } 131 132 @Override 133 public void clickOn(OBlock t) { 134 } 135 136 protected boolean isMetric() { 137 return _isMetric; 138 } 139 140 protected void changeUnits() { 141 _isMetric = !_isMetric; 142 SortedSet<OBlock> oblockList = _manager.getNamedBeanSet(); 143 for (OBlock block : oblockList) { 144 block.setMetricUnits(_isMetric); 145 } 146 fireTableDataChanged(); 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 @Override 153 protected String getMasterClassName() { 154 return jmri.jmrit.beantable.OBlockTableAction.class.getName(); 155 } 156 157 protected List<OBlock> getBeanList() { 158 TreeSet<OBlock> ts = new TreeSet<>(new NamedBeanComparator<>()); 159 160 for (String s : sysNameList) { 161 ts.add(getBySystemName(s)); 162 } 163 ArrayList<OBlock> list = new ArrayList<>(sysNameList.size()); 164 165 list.addAll(ts); 166 return list; 167 } 168 169 @Override 170 public String getValue(String name) { 171 OBlock bl = _manager.getBySystemName(name); 172 if (bl !=null) { 173 return getValue(bl.getState()); 174 } 175 return ""; 176 } 177 178 static protected String getValue(int state) { 179 StringBuilder sb = new StringBuilder(); 180 if ((state & OBlock.UNDETECTED) != 0) { 181 sb.append(Bundle.getMessage("Dark")); 182 } 183 if ((state & OBlock.OCCUPIED) != 0) { 184 if (sb.length() > 0) { 185 sb.append('-'); 186 } 187 sb.append(Bundle.getMessage("Occupied")); 188 } 189 if ((state & OBlock.UNOCCUPIED) != 0) { 190 if (sb.length() > 0) { 191 sb.append('-'); 192 } 193 sb.append(Bundle.getMessage("Unoccupied")); 194 } 195 if ((state & OBlock.INCONSISTENT) != 0) { 196 if (sb.length() > 0) { 197 sb.append('-'); 198 } 199 sb.append(Bundle.getMessage("BeanStateInconsistent")); 200 } 201 if ((state & OBlock.UNKNOWN) != 0) { 202 if (sb.length() > 0) { 203 sb.append('-'); 204 } 205 sb.append(Bundle.getMessage("BeanStateUnknown")); 206 } 207 if ((state & OBlock.ALLOCATED) != 0) { 208 if (sb.length() > 0) { 209 sb.append('-'); 210 } 211 sb.append(Bundle.getMessage("Allocated")); 212 } 213 if ((state & OBlock.RUNNING) != 0) { 214 if (sb.length() > 0) { 215 sb.append('-'); 216 } 217 sb.append(Bundle.getMessage("Running")); 218 } 219 if ((state & OBlock.OUT_OF_SERVICE) != 0) { 220 if (sb.length() > 0) { 221 sb.append('-'); 222 } 223 sb.append(Bundle.getMessage("OutOfService")); 224 } 225 if ((state & OBlock.TRACK_ERROR) != 0) { 226 if (sb.length() > 0) { 227 sb.append('-'); 228 } 229 sb.append(Bundle.getMessage("TrackError")); 230 } 231 if (sb.length() == 0) { 232 sb.append(Bundle.getMessage("UnDefined")); 233 } 234 return sb.toString(); 235 } 236 237 @Override 238 public int getColumnCount() { 239 return NUMCOLS; 240 } 241 242 @Override 243 public int getRowCount() { 244 return super.getRowCount() + (_tabbed ? 0 : 1); // + 1 row in _desktop to create entry row 245 } 246 247 @Override 248 public Object getValueAt(int row, int col) { 249 if (row > sysNameList.size()) { 250 return ""; 251 } 252 OBlock b = null; 253 if ((_tabbed && row <= sysNameList.size()) || (!_tabbed && row < sysNameList.size())) { 254 String name = sysNameList.get(row); 255 b = _manager.getBySystemName(name); 256 } 257 switch (col) { 258 case SYSNAMECOL: 259 if (b != null) { 260 return b.getSystemName(); 261 } 262 return tempRow[col]; // this must be tempRow 263 case USERNAMECOL: 264 if (b != null) { 265 return b.getUserName(); 266 } 267 return tempRow[col]; 268 case COMMENTCOL: 269 if (b != null) { 270 return b.getComment(); 271 } 272 return tempRow[col]; 273 case STATECOL: 274 if (b != null) { 275 int state = b.getState(); 276 int num = Integer.numberOfLeadingZeros(state) - 23; 277 if (num >= 0) { 278 return ZEROS.substring(0, num) + Integer.toBinaryString(state); 279 } 280 } 281 return ZEROS; 282 case SENSORCOL: 283 if (b != null) { 284 Sensor s = b.getSensor(); 285 if (s == null) { 286 return ""; 287 } 288 return s.getDisplayName(); 289 } 290 return tempRow[col]; 291 case LENGTHCOL: 292 if (b != null) { 293 if (b.isMetric()) { 294 return (twoDigit.format(b.getLengthCm())); 295 } 296 return (twoDigit.format(b.getLengthIn())); 297 } 298 if (tempRow[UNITSCOL].equals(Bundle.getMessage("cm"))) { 299 return (twoDigit.format(_tempLen/10)); 300 } 301 return (twoDigit.format(_tempLen/25.4f)); 302 case UNITSCOL: 303 if (b != null) { 304 if (log.isDebugEnabled()) { 305 log.debug("getValueAt: row= {}, col= {}, \"{}\" isMetric= {}", row, col, b.getDisplayName(), b.isMetric()); 306 } 307 return b.isMetric(); 308 } 309 if (log.isDebugEnabled()) { 310 log.debug("getValueAt: row= {}, col= {}, isMetric= {}", row, col, tempRow[UNITSCOL].equals(Bundle.getMessage("cm"))); 311 } 312 return tempRow[UNITSCOL].equals(Bundle.getMessage("cm")); 313 case CURVECOL: 314 if (b != null) { 315 return BlockCurvatureJComboBox.getStringFromCurvature(b.getCurvature()); 316 } 317 return tempRow[col]; 318 case ERR_SENSORCOL: 319 if (b != null) { 320 Sensor s = b.getErrorSensor(); 321 if (s == null) { 322 return ""; 323 } 324 return s.getDisplayName(); 325 } 326 return tempRow[col]; 327 case REPORTERCOL: 328 if (b != null) { 329 Reporter r = b.getReporter(); 330 if (r == null) { 331 return ""; 332 } 333 return r.getDisplayName(); 334 } 335 return tempRow[col]; 336 case REPORT_CURRENTCOL: 337 if (b != null) { 338 if (b.getReporter() != null) { 339 return b.isReportingCurrent(); 340 } 341 return ""; 342 } 343 return tempRow[REPORT_CURRENTCOL].equals(Bundle.getMessage("Current")); 344 case PERMISSIONCOL: 345 if (b != null) { 346 return b.getPermissiveWorking(); 347 } 348 return tempRow[PERMISSIONCOL].equals(Bundle.getMessage("Permissive")); 349 case SPEEDCOL: 350 if (b != null) { 351 return b.getBlockSpeed(); 352 } 353 return tempRow[col]; 354 case WARRANTCOL: 355 if (b != null) { 356 Warrant w = b.getWarrant(); 357 if (w != null) { 358 return w.getDisplayName(); 359 } 360 } 361 return tempRow[col]; 362 case VALUE: 363 if (b != null) { 364 Object obj = b.getValue(); 365 if (obj != null) { 366 return obj; 367 } else if ((b.getState() & OBlock.OCCUPIED) != 0) { 368 return Bundle.getMessage("BlockUnknown"); 369 } else { 370 return null; 371 } 372 } 373 return tempRow[col]; 374 case EDIT_COL: 375 if (b != null) { 376 if (_tabbed) { 377 return Bundle.getMessage("ButtonEdit"); 378 } else { 379 return Bundle.getMessage("ButtonEditPath"); 380 } 381 } 382 return ""; 383 case DELETE_COL: 384 if (b != null) { 385 return Bundle.getMessage("ButtonDelete"); 386 } 387 return Bundle.getMessage("ButtonClear"); 388 default: 389 // fall through 390 break; 391 } 392 return super.getValueAt(row, col); 393 } 394 395 @Override 396 public void setValueAt(Object value, int row, int col) { 397 log.debug("setValueAt: row= {}, col= {}, value= {}", row, col, value); 398 if (!_tabbed && (super.getRowCount() == row)) { // editing tempRow 399 switch (col) { 400 case SYSNAMECOL: 401 if (!_manager.isValidSystemNameFormat((String) value)) { 402 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BadNameOBlock"), 403 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 404 return; 405 } 406 OBlock block = _manager.createNewOBlock((String) value, tempRow[USERNAMECOL]); 407 if (block == null) { // an OBlock with the same systemName or userName already exists 408 block = _manager.getOBlock(tempRow[USERNAMECOL]); 409 String name = value + " / " + tempRow[USERNAMECOL]; 410 if (block != null) { 411 name = block.getDisplayName(); 412 } else { 413 block = _manager.getOBlock((String)value); 414 if (block != null) { 415 name = block.getDisplayName(); 416 } 417 } 418 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("CreateDuplBlockErr", name), 419 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 420 return; 421 } 422 if (tempRow[SENSORCOL] != null) { 423 if (!sensorExists(tempRow[SENSORCOL])) { 424 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchSensorErr", tempRow[SENSORCOL]), 425 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 426 } 427 } 428 block.setComment(tempRow[COMMENTCOL]); 429 float len = 0.0f; 430 try { 431 len = IntlUtilities.floatValue(tempRow[LENGTHCOL]); 432 } catch (ParseException e) { 433 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BadNumber", tempRow[LENGTHCOL]), 434 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 435 } 436 if (tempRow[UNITSCOL].equals(Bundle.getMessage("cm"))) { 437 block.setLength(len * 10.0f); 438 block.setMetricUnits(true); 439 } else { 440 block.setLength(len * 25.4f); 441 block.setMetricUnits(false); 442 } 443 block.setCurvature(BlockCurvatureJComboBox.getCurvatureFromString(tempRow[CURVECOL])); 444 block.setPermissiveWorking(tempRow[PERMISSIONCOL].equals(Bundle.getMessage("Permissive"))); 445 block.setBlockSpeedName(tempRow[SPEEDCOL]); 446 447 if (tempRow[ERR_SENSORCOL] != null) { 448 if (tempRow[ERR_SENSORCOL].trim().length() > 0) { 449 if (!sensorExists(tempRow[ERR_SENSORCOL])) { 450 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchSensorErr", tempRow[ERR_SENSORCOL]), 451 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 452 } 453 } 454 } 455 if (tempRow[REPORTERCOL] != null) { 456 Reporter rep = null; 457 try { 458 rep = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(tempRow[REPORTERCOL]); 459 if (rep != null) { 460 block.setReporter(rep); 461 block.setReportingCurrent(tempRow[REPORT_CURRENTCOL].equals(Bundle.getMessage("Current"))); 462 } 463 } catch (Exception ex) { 464 log.error("No Reporter named \"{}\" found. threw exception", tempRow[REPORTERCOL], ex); 465 } 466 if (rep == null) { 467 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchReporterErr", tempRow[REPORTERCOL]), 468 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 469 } 470 block.setReporter(rep); 471 } 472 initTempRow(); 473 fireTableDataChanged(); 474 return; 475 case DELETE_COL: // "Clear" 476 initTempRow(); 477 fireTableRowsUpdated(row, row); 478 return; 479 case LENGTHCOL: 480 try { 481 _tempLen = IntlUtilities.floatValue(value.toString()); 482 if (tempRow[UNITSCOL].equals(Bundle.getMessage("cm"))) { 483 _tempLen *= 10f; 484 } else { 485 _tempLen *= 25.4f; 486 } 487 } catch (ParseException e) { 488 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BadNumber", tempRow[LENGTHCOL]), 489 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 490 } 491 return; 492 case UNITSCOL: 493 if (((Boolean) value)) { 494 tempRow[UNITSCOL] = Bundle.getMessage("cm"); 495 } else { 496 tempRow[UNITSCOL] = Bundle.getMessage("in"); 497 } 498 fireTableRowsUpdated(row, row); // recalculates length value as displayed 499 return; 500 case REPORT_CURRENTCOL: 501 if ((Boolean) value) {//toggle 502 tempRow[REPORT_CURRENTCOL] = Bundle.getMessage("Current"); 503 } else { 504 tempRow[REPORT_CURRENTCOL] = Bundle.getMessage("Last"); 505 } 506 return; 507 case PERMISSIONCOL: 508 if ((Boolean) value) {//toggle 509 tempRow[PERMISSIONCOL] = Bundle.getMessage("Permissive"); 510 } else { 511 tempRow[PERMISSIONCOL] = Bundle.getMessage("Absolute"); 512 } 513 return; 514 default: 515 // fall though 516 break; 517 } 518 tempRow[col] = (String) value; 519 return; 520 } 521 522 // Edit an existing row 523 String name = sysNameList.get(row); 524 OBlock block = _manager.getBySystemName(name); 525 if (block == null) { 526 log.error("OBlock named {} not found for OBlockTableModel", name); 527 return; 528 } 529 switch (col) { 530 case USERNAMECOL: 531 OBlock b = _manager.getOBlock((String) value); 532 if (b != null) { 533 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("CreateDuplBlockErr", block.getDisplayName()), 534 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 535 return; 536 } 537 block.setUserName((String) value); 538 fireTableRowsUpdated(row, row); 539 return; 540 case COMMENTCOL: 541 block.setComment((String) value); 542 fireTableRowsUpdated(row, row); 543 return; 544 case SENSORCOL: 545 if (!block.setSensor((String) value)) { 546 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchSensorErr", value), 547 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 548 } 549 fireTableRowsUpdated(row, row); 550 return; 551 case LENGTHCOL: 552 try { 553 float len = IntlUtilities.floatValue(value.toString()); 554 if (block.isMetric()) { 555 block.setLength(len * 10.0f); 556 } else { 557 block.setLength(len * 25.4f); 558 } 559 fireTableRowsUpdated(row, row); 560 } catch (ParseException e) { 561 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BadNumber", value), 562 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 563 } 564 return; 565 case UNITSCOL: 566 block.setMetricUnits((Boolean) value); 567 fireTableRowsUpdated(row, row); 568 return; 569 case CURVECOL: 570 String cName = (String) value; 571 if (cName == null) { 572 return; 573 } 574 block.setCurvature(BlockCurvatureJComboBox.getCurvatureFromString(cName)); 575 fireTableRowsUpdated(row, row); 576 return; 577 case ERR_SENSORCOL: 578 boolean ok = false; 579 try { 580 if (((String) value).trim().length() == 0) { 581 block.setErrorSensor(null); 582 ok = true; 583 } else { 584 ok = block.setErrorSensor((String) value); 585 fireTableRowsUpdated(row, row); 586 } 587 } catch (Exception ex) { 588 log.error("getSensor({}) threw exception", value, ex); 589 } 590 if (!ok) { 591 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchSensorErr", value), 592 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 593 } 594 fireTableRowsUpdated(row, row); 595 return; 596 case REPORTERCOL: 597 Reporter rep = null; 598 try { 599 rep = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter((String) value); 600 if (rep != null) { 601 block.setReporter(rep); 602 fireTableRowsUpdated(row, row); 603 } 604 } catch (Exception ex) { 605 log.error("No Reporter named \"{}\" found. threw exception", value, ex); 606 } 607 if (rep == null) { 608 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoSuchReporterErr", tempRow[REPORTERCOL]), 609 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.WARNING_MESSAGE); 610 } 611 block.setReporter(rep); 612 fireTableRowsUpdated(row, row); 613 return; 614 case REPORT_CURRENTCOL: 615 if (block.getReporter() != null) { 616 block.setReportingCurrent((Boolean) value); 617 fireTableRowsUpdated(row, row); 618 } 619 return; 620 case PERMISSIONCOL: 621 block.setPermissiveWorking((Boolean) value); // compare to REPORT_CURRENTCOL 622 fireTableRowsUpdated(row, row); 623 return; 624 case SPEEDCOL: 625 block.setBlockSpeedName((String) value); 626 fireTableRowsUpdated(row, row); 627 return; 628 case EDIT_COL: 629 _parent.openBlockPathPane(block.getSystemName(), null); // interface is checked in TableFrames 630 return; 631 case DELETE_COL: 632 deleteBean(block); 633 return; 634 default: 635 // fall through 636 break; 637 } 638 super.setValueAt(value, row, col); 639 } 640 641 private static boolean sensorExists(String name) { 642 Sensor sensor = InstanceManager.sensorManagerInstance().getByUserName(name); 643 if (sensor == null) { 644 sensor = InstanceManager.sensorManagerInstance().getBySystemName(name); 645 } 646 return (sensor != null); 647 } 648 649 @Override 650 public String getColumnName(int col) { 651 switch (col) { 652 case COMMENTCOL: 653 return Bundle.getMessage("Comment"); 654 case STATECOL: 655 return Bundle.getMessage("ColumnState"); 656 case SENSORCOL: 657 return Bundle.getMessage("BeanNameSensor"); 658 case CURVECOL: 659 return Bundle.getMessage("BlockCurveColName"); 660 case LENGTHCOL: 661 return Bundle.getMessage("BlockLengthColName"); 662 case UNITSCOL: 663 return Bundle.getMessage("UnitsCol"); 664 case EDIT_COL: 665 return Bundle.getMessage("MenuPaths"); 666 case DELETE_COL: 667 return Bundle.getMessage("ColumnDelete"); 668 case ERR_SENSORCOL: 669 return Bundle.getMessage("ErrorSensorCol"); 670 case REPORTERCOL: 671 return Bundle.getMessage("ReporterCol"); 672 case REPORT_CURRENTCOL: 673 return Bundle.getMessage("RepCurrentCol"); 674 case PERMISSIONCOL: 675 return Bundle.getMessage("PermissionCol"); 676 case WARRANTCOL: 677 return Bundle.getMessage("WarrantCol"); 678 case VALUE: 679 return Bundle.getMessage("ValueCol"); 680 case SPEEDCOL: 681 return Bundle.getMessage("SpeedCol"); 682 default: 683 // fall through 684 break; 685 } 686 return super.getColumnName(col); 687 } 688 689 // Delete in row pressed, remove OBlock from manager etc. Works in both interfaces 690 void deleteBean(OBlock bean) { 691 StringBuilder sb = new StringBuilder(Bundle.getMessage("DeletePrompt", bean.getSystemName())); 692 for (PropertyChangeListener listener : bean.getPropertyChangeListeners()) { 693 if (!(listener instanceof OBlockTableModel) && 694 !(listener instanceof BlockPathTableModel) && 695 !(listener instanceof PathTurnoutTableModel) && 696 !(listener instanceof jmri.jmrit.picker.PickListModel) && 697 !(listener instanceof OBlockManager)) { 698 sb.append("\n"); 699 sb.append(Bundle.getMessage("InUseBy", bean.getDisplayName(), listener.getClass().getName())); 700 } 701 } 702 int val = _parent.verifyWarning(sb.toString()); 703 if (val == 2) { 704 return; // return without deleting 705 } 706 bean.dispose(); 707 } 708 709 @Override 710 public Class<?> getColumnClass(int col) { 711 switch (col) { 712 case CURVECOL: 713 return CurveComboBoxPanel.class; 714 case SPEEDCOL: 715 return SpeedComboBoxPanel.class; // apply real combo renderer 716 case DELETE_COL: 717 case EDIT_COL: 718 return JButton.class; 719 case UNITSCOL: 720 return JToggleButton.class; 721 case REPORT_CURRENTCOL: 722 return JRadioButton.class; 723 case PERMISSIONCOL: 724 return JCheckBox.class; // return Boolean.class; 725 default: 726 return String.class; 727 } 728 } 729 730 @Override 731 public int getPreferredWidth(int col) { 732 switch (col) { 733 case SYSNAMECOL: 734 case USERNAMECOL: 735 return new JTextField(15).getPreferredSize().width; 736 case STATECOL: 737 return new JTextField(ZEROS).getPreferredSize().width; 738 case COMMENTCOL: 739 case SENSORCOL: 740 case ERR_SENSORCOL: 741 case REPORTERCOL: 742 case WARRANTCOL: 743 return new JTextField(12).getPreferredSize().width; 744 case VALUE: 745 case CURVECOL: 746 case REPORT_CURRENTCOL: 747 case PERMISSIONCOL: 748 case SPEEDCOL: 749 return new JTextField(10).getPreferredSize().width; 750 case LENGTHCOL: 751 return new JTextField(6).getPreferredSize().width; 752 case UNITSCOL: 753 return new JTextField(5).getPreferredSize().width; 754 case EDIT_COL: 755 return new JButton(Bundle.getMessage("ButtonEditPath")).getPreferredSize().width+4; 756 case DELETE_COL: 757 return new JButton(Bundle.getMessage("ButtonDelete")).getPreferredSize().width+4; 758 default: 759 // fall through 760 break; 761 } 762 return 5; 763 } 764 765 @Override 766 public boolean isCellEditable(int row, int col) { 767 switch (col) { 768 case SYSNAMECOL: 769 if (super.getRowCount() == row) { 770 return true; // the new entry/bottom row is editable in all cells 771 } else { 772 return false; 773 } 774 case STATECOL: 775 case WARRANTCOL: 776 case VALUE: 777 return false; 778 default: 779 // fall through 780 break; 781 } 782 return true; 783 } 784 785 //*********************** combo box cell editors *********************************/ 786 787 /** 788 * Provide a table cell renderer looking like a JComboBox as an 789 * editor/renderer for the OBlock table SPEED column. 790 * <p> 791 * This is a lightweight version of the 792 * {@link jmri.jmrit.beantable.RowComboBoxPanel} RowComboBox cell editor 793 * class, some of the hashtables not needed here since we only need 794 * identical options for all rows in a column. 795 * 796 * see jmri.jmrit.signalling.SignallingPanel.SignalMastModel.AspectComboBoxPanel for a full application with 797 * row specific comboBox choices. 798 */ 799 public static class SpeedComboBoxPanel extends RowComboBoxPanel { 800 801 @Override 802 protected final void eventEditorMousePressed() { 803 this.editor.add(getEditorBox(table.convertRowIndexToModel(this.currentRow))); // add editorBox to JPanel 804 this.editor.revalidate(); 805 SwingUtilities.invokeLater(this.comboBoxFocusRequester); 806 log.debug("eventEditorMousePressed in row: {})", this.currentRow); // NOI18N 807 } 808 809 /** 810 * Call the method in the surrounding method for the 811 * OBlockTable. 812 * 813 * @param row the user clicked on in the table 814 * @return an appropriate combobox for this signal head 815 */ 816 @Override 817 protected JComboBox<String> getEditorBox(int row) { 818 return getSpeedEditorBox(row); 819 } 820 821 } 822 // end of methods to display SPEED_COLUMN ComboBox 823 824 /** 825 * Provide a static JComboBox element to display inside the JPanel 826 * CellEditor. When not yet present, create, store and return a new one. 827 * 828 * @param row Index number (in TableDataModel) 829 * @return A JCombobox containing the valid curvature names. 830 */ 831 static JComboBox<String> getCurveEditorBox(int row) { 832 // create dummy comboBox, override in extended classes for each bean 833 BlockCurvatureJComboBox j = new BlockCurvatureJComboBox(); 834 j.setJTableCellClientProperties(); 835 return j; 836 } 837 838 /** 839 * Customize the Turnout column to show an appropriate ComboBox of 840 * available options. 841 * 842 * @param table a JTable of beans 843 */ 844 public void configCurveColumn(JTable table) { 845 // have the state column hold a JPanel with a JComboBox for Curvature 846 table.setDefaultEditor(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel()); 847 table.setDefaultRenderer(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel()); // use same class as renderer 848 // Set more things? 849 } 850 851 /** 852 * Provide a table cell renderer looking like a JComboBox as an 853 * editor/renderer for the OBlock table CURVE column. 854 * <p> 855 * This is a lightweight version of the 856 * {@link jmri.jmrit.beantable.RowComboBoxPanel} RowComboBox cell editor 857 * class, some of the hashtables not needed here since we only need 858 * identical options for all rows in a column. 859 * 860 * see jmri.jmrit.signalling.SignallingPanel.SignalMastModel.AspectComboBoxPanel for a full application with 861 * row specific comboBox choices. 862 */ 863 public static class CurveComboBoxPanel extends RowComboBoxPanel { 864 865 @Override 866 protected final void eventEditorMousePressed() { 867 this.editor.add(getEditorBox(table.convertRowIndexToModel(this.currentRow))); // add editorBox to JPanel 868 this.editor.revalidate(); 869 SwingUtilities.invokeLater(this.comboBoxFocusRequester); 870 log.debug("eventEditorMousePressed in row: {})", this.currentRow); // NOI18N 871 } 872 873 /** 874 * Call the method in the surrounding method for the 875 * OBlockTable. 876 * 877 * @param row the user clicked on in the table 878 * @return an appropriate combobox for this signal head 879 */ 880 @Override 881 protected JComboBox<String> getEditorBox(int row) { 882 return getCurveEditorBox(row); 883 } 884 885 } 886 // end of methods to display CURVE_COLUMN ComboBox 887 888 /** 889 * Provide a static JComboBox element to display inside the JPanel 890 * CellEditor. When not yet present, create, store and return a new one. 891 * 892 * @param row Index number (in TableDataModel) 893 * @return A combobox containing the valid aspect names for this mast 894 */ 895 static JComboBox<String> getSpeedEditorBox(int row) { 896 // create dummy comboBox, override in extended classes for each bean 897 JComboBox<String> editCombo = new JComboBox<>(jmri.InstanceManager.getDefault(SignalSpeedMap.class).getValidSpeedNames()); 898 // item to reset speed notch to default, i.e. continue at current speed requirement. 899 javax.swing.DefaultComboBoxModel<String> model = (DefaultComboBoxModel<String>)editCombo.getModel(); 900 model.addElement(""); 901 editCombo.putClientProperty("JComponent.sizeVariant", "small"); 902 editCombo.putClientProperty("JComboBox.buttonType", "square"); 903 return editCombo; 904 } 905 906 /** 907 * Customize the Turnout column to show an appropriate ComboBox of 908 * available options. 909 * 910 * @param table a JTable of beans 911 */ 912 public void configSpeedColumn(JTable table) { 913 // have the state column hold a JPanel with a JComboBox for Speeds 914 table.setDefaultEditor(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel()); 915 table.setDefaultRenderer(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel()); // use same class as renderer 916 // Set more things? 917 } 918 919 @Override 920 public void propertyChange(PropertyChangeEvent e) { 921 super.propertyChange(e); 922 String property = e.getPropertyName(); 923 if (log.isDebugEnabled()) log.debug("PropertyChange = {}", property); 924 if (property.equals("length") || property.equals("UserName") || property.equals("state")) { 925 ThreadingUtil.runOnGUIEventually(()-> { 926 _parent.updateOBlockTablesMenu(); 927 fireTableDataChanged(); 928 }); 929 } 930 _parent.getPortalXRefTableModel().propertyChange(e); 931 _parent.getSignalTableModel().propertyChange(e); 932 _parent.getPortalTableModel().propertyChange(e); 933 934 } 935 936 protected String getClassName() { 937 return jmri.jmrit.beantable.OBlockTableAction.class.getName(); 938 } 939 940 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OBlockTableModel.class); 941 942}