001package jmri.jmrit.beantable.sensor; 002 003import jmri.util.gui.GuiLafPreferencesManager; 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.Image; 007import java.awt.Rectangle; 008import java.awt.event.MouseAdapter; 009import java.awt.event.MouseEvent; 010import java.awt.image.BufferedImage; 011import java.beans.PropertyChangeEvent; 012import java.io.File; 013import java.io.IOException; 014import java.util.Enumeration; 015 016import javax.annotation.Nonnull; 017import javax.imageio.ImageIO; 018import javax.swing.*; 019import javax.swing.table.TableCellEditor; 020import javax.swing.table.TableCellRenderer; 021import javax.swing.table.TableColumn; 022import jmri.InstanceManager; 023import jmri.JmriException; 024import jmri.Manager; 025import jmri.NamedBean; 026import jmri.Sensor; 027import jmri.SensorManager; 028import jmri.managers.ProxySensorManager; 029import jmri.jmrit.beantable.BeanTableDataModel; 030import jmri.util.swing.XTableColumnModel; 031import jmri.util.swing.JmriJOptionPane; 032 033/** 034 * Data model for a SensorTable. 035 * 036 * @author Bob Jacobsen Copyright (C) 2003, 2009 037 * @author Egbert Broerse Copyright (C) 2017 038 */ 039public class SensorTableDataModel extends BeanTableDataModel<Sensor> { 040 041 public static final int INVERTCOL = BeanTableDataModel.NUMCOLUMN; 042 public static final int EDITCOL = INVERTCOL + 1; 043 public static final int USEGLOBALDELAY = EDITCOL + 1; 044 public static final int ACTIVEDELAY = USEGLOBALDELAY + 1; 045 public static final int INACTIVEDELAY = ACTIVEDELAY + 1; 046 public static final int PULLUPCOL = INACTIVEDELAY + 1; 047 public static final int FORGETCOL = PULLUPCOL + 1; 048 public static final int QUERYCOL = FORGETCOL + 1; 049 050 private Manager<Sensor> senManager = null; 051 protected boolean _graphicState = false; // icon state col updated from prefs 052 053 /** 054 * Create a new Sensor Table Data Model. 055 * The default Manager for the bean type will be a Proxy Manager. 056 */ 057 public SensorTableDataModel() { 058 super(); 059 _graphicState = InstanceManager.getDefault(GuiLafPreferencesManager.class).isGraphicTableState(); 060 } 061 062 /** 063 * Create a new Sensor Table Data Model. 064 * The default Manager for the bean type will be a Proxy Manager unless 065 * one is specified here. 066 * @param manager Bean Manager. 067 */ 068 public SensorTableDataModel(Manager<Sensor> manager) { 069 super(); 070 setManager(manager); // updates name list 071 // load graphic state column display preference 072 _graphicState = InstanceManager.getDefault(GuiLafPreferencesManager.class).isGraphicTableState(); 073 } 074 075 /** 076 * {@inheritDoc} 077 */ 078 @Override 079 public String getValue(String name) { 080 Sensor sen = getManager().getBySystemName(name); 081 if (sen == null) { 082 return "Failed to get sensor " + name; 083 } 084 return sen.describeState(sen.getKnownState()); 085 } 086 087 /** 088 * {@inheritDoc} 089 */ 090 @Override 091 protected final void setManager(@Nonnull Manager<Sensor> manager) { 092 if (!(manager instanceof SensorManager)) { 093 return; 094 } 095 getManager().removePropertyChangeListener(this); 096 if (sysNameList != null) { 097 for (int i = 0; i < sysNameList.size(); i++) { 098 // if object has been deleted, it's not here; ignore it 099 NamedBean b = getBySystemName(sysNameList.get(i)); 100 if (b != null) { 101 b.removePropertyChangeListener(this); 102 } 103 } 104 } 105 senManager = manager; 106 getManager().addPropertyChangeListener(this); 107 updateNameList(); 108 } 109 110 /** 111 * {@inheritDoc} 112 */ 113 @Override 114 protected Manager<Sensor> getManager() { 115 if (senManager == null) { 116 senManager = InstanceManager.sensorManagerInstance(); 117 } 118 return senManager; 119 } 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override 125 protected Sensor getBySystemName(@Nonnull String name) { 126 return getManager().getBySystemName(name); 127 } 128 129 /** 130 * {@inheritDoc} 131 */ 132 @Override 133 protected Sensor getByUserName(@Nonnull String name) { 134 return InstanceManager.getDefault(SensorManager.class).getByUserName(name); 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 protected String getMasterClassName() { 142 return getClassName(); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 protected void clickOn(Sensor t) { 150 try { 151 t.setKnownState(t.getKnownState() == Sensor.INACTIVE ? Sensor.ACTIVE : Sensor.INACTIVE ); 152 } catch (JmriException e) { 153 log.warn("Error setting state", e); 154 } 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public int getColumnCount() { 162 return QUERYCOL + getPropertyColumnCount() + 1; 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public String getColumnName(int col) { 170 switch (col) { 171 case INVERTCOL: 172 return Bundle.getMessage("Inverted"); 173 case EDITCOL: 174 return ""; 175 case USEGLOBALDELAY: 176 return Bundle.getMessage("SensorUseGlobalDebounce"); 177 case ACTIVEDELAY: 178 return Bundle.getMessage("SensorActiveDebounce"); 179 case INACTIVEDELAY: 180 return Bundle.getMessage("SensorInActiveDebounce"); 181 case PULLUPCOL: 182 return Bundle.getMessage("SensorPullUp"); 183 case FORGETCOL: 184 return Bundle.getMessage("StateForgetHeader"); 185 case QUERYCOL: 186 return Bundle.getMessage("StateQueryHeader"); 187 default: 188 return super.getColumnName(col); 189 } 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override 196 public Class<?> getColumnClass(int col) { 197 switch (col) { 198 case INVERTCOL: 199 case USEGLOBALDELAY: 200 return Boolean.class; 201 case ACTIVEDELAY: 202 case INACTIVEDELAY: 203 return Long.class; // if long.class (lowercase) is returned here, cell is NOT editable. 204 case PULLUPCOL: 205 return JComboBox.class; 206 case EDITCOL: 207 case FORGETCOL: 208 case QUERYCOL: 209 return JButton.class; 210 case VALUECOL: 211 if (_graphicState) { 212 return JLabel.class; // use an image to show sensor state 213 } else { 214 return super.getColumnClass(col); 215 } 216 default: 217 return super.getColumnClass(col); 218 } 219 } 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override 225 public int getPreferredWidth(int col) { 226 switch (col) { 227 case INVERTCOL: 228 return new JTextField(4).getPreferredSize().width; 229 case USEGLOBALDELAY: 230 case ACTIVEDELAY: 231 case INACTIVEDELAY: 232 case PULLUPCOL: 233 return new JTextField(8).getPreferredSize().width; 234 case EDITCOL: 235 return new JButton(Bundle.getMessage("ButtonEdit")).getPreferredSize().width+4; 236 case FORGETCOL: 237 return new JButton(Bundle.getMessage("StateForgetButton")) 238 .getPreferredSize().width+4; 239 case QUERYCOL: 240 return new JButton(Bundle.getMessage("StateQueryButton")) 241 .getPreferredSize().width+4; 242 default: 243 return super.getPreferredWidth(col); 244 } 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 public boolean isCellEditable(int row, int col) { 252 String name = sysNameList.get(row); 253 Sensor sen = getManager().getBySystemName(name); 254 if (sen == null) { 255 return false; 256 } 257 switch (col) { 258 case EDITCOL: 259 case USEGLOBALDELAY: 260 case FORGETCOL: 261 case QUERYCOL: 262 return true; 263 case INVERTCOL: 264 return sen.canInvert(); 265 case ACTIVEDELAY: 266 case INACTIVEDELAY: 267 return !sen.getUseDefaultTimerSettings(); 268 case PULLUPCOL: 269 if ( getManager() instanceof ProxySensorManager ) { 270 return ((ProxySensorManager)getManager()).isPullResistanceConfigurable(name); 271 } 272 return (((SensorManager) getManager()).isPullResistanceConfigurable()); // proxymanager always false 273 274 default: 275 return super.isCellEditable(row, col); 276 } 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 @Override 283 public Object getValueAt(int row, int col) { 284 if (row >= sysNameList.size()) { 285 log.debug("row is greater than name list"); 286 return ""; 287 } 288 String name = sysNameList.get(row); 289 Sensor s = senManager.getBySystemName(name); 290 if (s == null) { 291 log.debug("error null sensor!"); 292 return "error"; 293 } 294 switch (col) { 295 case INVERTCOL: 296 return s.getInverted(); 297 case USEGLOBALDELAY: 298 return s.getUseDefaultTimerSettings(); 299 case ACTIVEDELAY: 300 return s.getSensorDebounceGoingActiveTimer(); 301 case INACTIVEDELAY: 302 return s.getSensorDebounceGoingInActiveTimer(); 303 case EDITCOL: 304 return Bundle.getMessage("ButtonEdit"); 305 case PULLUPCOL: 306 PullResistanceComboBox c = new PullResistanceComboBox(Sensor.PullResistance.values()); 307 c.setSelectedItem(s.getPullResistance()); 308 return c; 309 case FORGETCOL: 310 return Bundle.getMessage("StateForgetButton"); 311 case QUERYCOL: 312 return Bundle.getMessage("StateQueryButton"); 313 default: 314 return super.getValueAt(row, col); 315 } 316 } 317 318 /** 319 * Small class to ensure type-safety of references otherwise lost to type erasure 320 */ 321 private static class PullResistanceComboBox extends JComboBox<Sensor.PullResistance> { 322 PullResistanceComboBox(Sensor.PullResistance[] values) { super(values); } 323 } 324 325 /** 326 * {@inheritDoc} 327 */ 328 @Override 329 public void setValueAt(Object value, int row, int col) { 330 if (row >= sysNameList.size()) { 331 log.debug("row is greater than name list"); 332 return; 333 } 334 String name = sysNameList.get(row); 335 Sensor s = senManager.getBySystemName(name); 336 if (s == null) { 337 log.debug("error null sensor!"); 338 return; 339 } 340 switch (col) { 341 case INVERTCOL: 342 s.setInverted(((boolean) value)); 343 break; 344 case USEGLOBALDELAY: 345 s.setUseDefaultTimerSettings(((boolean) value)); 346 break; 347 case ACTIVEDELAY: 348 try { 349 long activeDeBounce = (long) value; 350 if (activeDeBounce < 0 || activeDeBounce > Sensor.MAX_DEBOUNCE) { 351 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SensorDebounceActOutOfRange") 352 + "\n\"" + Sensor.MAX_DEBOUNCE + "\"", Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 353 } else { 354 s.setSensorDebounceGoingActiveTimer(activeDeBounce); 355 } 356 } catch (NumberFormatException exActiveDeBounce) { 357 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SensorDebounceActError") 358 + "\n\"" + value + "\"" + exActiveDeBounce.getLocalizedMessage(), Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 359 } 360 break; 361 case INACTIVEDELAY: 362 try { 363 long inactiveDeBounce = (long) value; 364 if (inactiveDeBounce < 0 || inactiveDeBounce > Sensor.MAX_DEBOUNCE) { 365 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SensorDebounceInActOutOfRange") 366 + "\n\"" + Sensor.MAX_DEBOUNCE + "\"", Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 367 } else { 368 s.setSensorDebounceGoingInActiveTimer(inactiveDeBounce); 369 } 370 } catch (NumberFormatException exActiveDeBounce) { 371 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SensorDebounceInActError") 372 + "\n\"" + value + "\"" + exActiveDeBounce.getLocalizedMessage(), Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 373 } 374 break; 375 case EDITCOL: 376 javax.swing.SwingUtilities.invokeLater(() -> { 377 editButton(s); 378 }); 379 break; 380 case PULLUPCOL: 381 PullResistanceComboBox cb = (PullResistanceComboBox) value; 382 s.setPullResistance((Sensor.PullResistance) cb.getSelectedItem()); 383 break; 384 case FORGETCOL: 385 try { 386 s.setKnownState(Sensor.UNKNOWN); 387 } catch (JmriException e) { 388 log.warn("Failed to set state to UNKNOWN: ", e); 389 } 390 break; 391 case QUERYCOL: 392 try { 393 s.setKnownState(Sensor.UNKNOWN); 394 } catch (JmriException e) { 395 log.warn("Failed to set state to UNKNOWN: ", e); 396 } 397 s.requestUpdateFromLayout(); 398 break; 399 case VALUECOL: 400 if (_graphicState) { // respond to clicking on ImageIconRenderer CellEditor 401 clickOn(s); 402 fireTableRowsUpdated(row, row); 403 } else { 404 super.setValueAt(value, row, col); 405 } 406 break; 407 default: 408 super.setValueAt(value, row, col); 409 break; 410 } 411 } 412 413 /** 414 * {@inheritDoc} 415 */ 416 @Override 417 protected boolean matchPropertyName(PropertyChangeEvent e) { 418 switch (e.getPropertyName()) { 419 case Sensor.PROPERTY_SENSOR_INVERTED: 420 case Sensor.PROPERTY_GLOBAL_TIMER: 421 case Sensor.PROPERTY_ACTIVE_TIMER: 422 case Sensor.PROPERTY_INACTIVE_TIMER: 423 return true; 424 default: 425 return super.matchPropertyName(e); 426 } 427 } 428 429 /** 430 * Customize the sensor table Value (State) column to show an appropriate 431 * graphic for the sensor state if _graphicState = true, or (default) just 432 * show the localized state text when the TableDataModel is being called 433 * from ListedTableAction. 434 * 435 * @param table a JTable of Sensors 436 */ 437 @Override 438 protected void configValueColumn(JTable table) { 439 // have the value column hold a JPanel (icon) 440 //setColumnToHoldButton(table, VALUECOL, new JLabel("1234")); // for small round icon, but cannot be converted to JButton 441 // add extras, override BeanTableDataModel 442 log.debug("Sensor configValueColumn (I am {})", this); 443 if (_graphicState) { // load icons, only once 444 table.setDefaultEditor(JLabel.class, new ImageIconRenderer()); // editor 445 table.setDefaultRenderer(JLabel.class, new ImageIconRenderer()); // item class copied from SwitchboardEditor panel 446 } else { 447 super.configValueColumn(table); // classic text style state indication 448 } 449 } 450 451 /** 452 * Visualize state in table as a graphic, customized for Sensors (2 states). 453 * Renderer and Editor are identical, as the cell contents are not actually 454 * edited, only used to toggle state using {@link #clickOn}. 455 * <p> 456 * A single label is reused for every cell and the icons are loaded once 457 * per JVM, so rendering a cell allocates nothing. The label ignores 458 * revalidate/repaint requests and the row height is set once in 459 * {@link #configureTable}: painting a cell must never schedule more 460 * painting, or the table repaints itself forever. 461 */ 462 static class ImageIconRenderer extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { 463 464 private static final String ROOT_PATH = "resources/icons/misc/switchboard/"; // also used in display.switchboardEditor 465 private static final char BEAN_TYPE_CHAR = 'S'; // for Sensor 466 private static final String ON_ICON_PATH = ROOT_PATH + BEAN_TYPE_CHAR + "-on-s.png"; 467 private static final String OFF_ICON_PATH = ROOT_PATH + BEAN_TYPE_CHAR + "-off-s.png"; 468 private static ImageIcon onIcon = null; 469 private static ImageIcon offIcon = null; 470 private static int iconHeight = -1; 471 472 private final StateLabel label = new StateLabel(); 473 private final Color defaultForeground = label.getForeground(); 474 private final String activeText = Bundle.getMessage("SensorStateActive"); 475 private final String inactiveText = Bundle.getMessage("SensorStateInactive"); 476 private final String unknownText = Bundle.getMessage("BeanStateUnknown"); 477 private final String inconsistentText = Bundle.getMessage("BeanStateInconsistent"); 478 private int row = -1; // row of the cell last rendered or edited 479 480 ImageIconRenderer() { 481 label.setHorizontalAlignment(JLabel.CENTER); 482 // must stay the only anonymous class in ImageIconRenderer, see jmri.ArchitectureTest 483 label.addMouseListener(new MouseAdapter() { 484 @Override 485 public final void mousePressed(MouseEvent evt) { 486 log.debug("Clicked on icon in row {}", row); 487 stopCellEditing(); 488 } 489 }); 490 } 491 492 /** 493 * {@inheritDoc} 494 */ 495 @Override 496 public Component getTableCellRendererComponent( 497 JTable table, Object value, boolean isSelected, 498 boolean hasFocus, int row, int column) { 499 log.debug("Renderer Item = {}, State = {}", row, value); 500 return updateLabel((String) value, row); 501 } 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override 507 public Component getTableCellEditorComponent( 508 JTable table, Object value, boolean isSelected, 509 int row, int column) { 510 log.debug("Editor Item = {}, State = {}", row, value); 511 return updateLabel((String) value, row); 512 } 513 514 protected JLabel updateLabel(String value, int row) { 515 this.row = row; 516 if (iconHeight < 0) { // load resources only first time, either for renderer or editor 517 loadIcons(); 518 log.debug("icons loaded"); 519 } 520 label.setForeground(defaultForeground); // reset a foreground left over from an INCONSISTENT cell 521 if (value.equals(inactiveText) && offIcon != null) { 522 label.setIcon(offIcon); 523 label.setText(null); 524 label.setVerticalAlignment(JLabel.BOTTOM); 525 log.debug("offIcon set"); 526 } else if (value.equals(activeText) && onIcon != null) { 527 label.setIcon(onIcon); 528 label.setText(null); 529 label.setVerticalAlignment(JLabel.BOTTOM); 530 log.debug("onIcon set"); 531 } else if (value.equals(inconsistentText)) { 532 label.setIcon(null); 533 label.setText("X"); 534 label.setForeground(Color.red); 535 label.setVerticalAlignment(JLabel.CENTER); 536 log.debug("Sensor state inconsistent"); 537 } else if (value.equals(unknownText)) { 538 label.setIcon(null); 539 label.setText("?"); 540 label.setVerticalAlignment(JLabel.CENTER); 541 log.debug("Sensor state unknown"); 542 } else { // failed to load icon 543 label.setIcon(null); 544 label.setText(value); 545 label.setVerticalAlignment(JLabel.CENTER); 546 log.warn("Error reading icons for SensorTable"); 547 } 548 label.setToolTipText(value); 549 return label; 550 } 551 552 /** 553 * {@inheritDoc} 554 */ 555 @Override 556 public Object getCellEditorValue() { 557 log.debug("getCellEditorValue, me = {})", this); 558 return this.toString(); 559 } 560 561 /** 562 * Get the row height needed to fit the state icons, loading them if 563 * required. Used by {@link #configureTable} to size the rows once, 564 * outside of the render path. 565 * 566 * @return required row height in pixels, 0 if the icons could not be read 567 */ 568 static int getIconRowHeight() { 569 if (iconHeight < 0) { 570 loadIcons(); 571 } 572 return Math.max(iconHeight - 5, 0); 573 } 574 575 /** 576 * Read and buffer graphics. Only called once per JVM, the icons are 577 * shared by all instances of this renderer. 578 */ 579 private static synchronized void loadIcons() { 580 if (iconHeight >= 0) { // another instance already loaded the icons 581 return; 582 } 583 BufferedImage onImage = null; 584 BufferedImage offImage = null; 585 try { 586 onImage = ImageIO.read(new File(ON_ICON_PATH)); 587 offImage = ImageIO.read(new File(OFF_ICON_PATH)); 588 } catch (IOException ex) { 589 log.error("error reading image from {} or {}", ON_ICON_PATH, OFF_ICON_PATH, ex); 590 } 591 if (onImage == null || offImage == null) { // ImageIO.read returns null for an unreadable file 592 log.error("error reading image from {} or {}", ON_ICON_PATH, OFF_ICON_PATH); 593 iconHeight = 0; // give up: render states as text, don't retry for every cell 594 return; 595 } 596 log.debug("Success reading images"); 597 int imageWidth = onImage.getWidth(); 598 int imageHeight = onImage.getHeight(); 599 // scale icons 50% to fit in table rows 600 Image smallOnImage = onImage.getScaledInstance(imageWidth / 2, imageHeight / 2, Image.SCALE_DEFAULT); 601 Image smallOffImage = offImage.getScaledInstance(imageWidth / 2, imageHeight / 2, Image.SCALE_DEFAULT); 602 onIcon = new ImageIcon(smallOnImage); 603 offIcon = new ImageIcon(smallOffImage); 604 iconHeight = onIcon.getIconHeight(); 605 } 606 607 /** 608 * Label that ignores revalidate and repaint requests. 609 * <p> 610 * The rendered component stays a child of the table's 611 * CellRendererPane after painting, so a plain JLabel schedules a new 612 * repaint of the whole table each time its icon or text changes 613 * during a paint cycle. Same overrides as DefaultTableCellRenderer, 614 * except firePropertyChange, which BasicLabelUI needs to keep its 615 * view up to date. 616 */ 617 private static class StateLabel extends JLabel { 618 619 @Override 620 public void revalidate() { 621 // ignored, see class comment 622 } 623 624 @Override 625 public void repaint() { 626 // ignored, see class comment 627 } 628 629 @Override 630 public void repaint(long tm) { 631 // ignored, see class comment 632 } 633 634 @Override 635 public void repaint(int x, int y, int width, int height) { 636 // ignored, see class comment 637 } 638 639 @Override 640 public void repaint(long tm, int x, int y, int width, int height) { 641 // ignored, see class comment 642 } 643 644 @Override 645 public void repaint(Rectangle r) { 646 // ignored, see class comment 647 } 648 } 649 650 } // end of ImageIconRenderer class 651 652 /** 653 * {@inheritDoc} 654 */ 655 @Override 656 public void configureTable(JTable table) { 657 super.configureTable(table); 658 if (_graphicState) { 659 // make the rows tall enough for the state icons, once and outside of the 660 // renderer; must come after super.configureTable, which sets the row 661 // height for the buttons 662 table.setRowHeight(Math.max(table.getRowHeight(), ImageIconRenderer.getIconRowHeight())); 663 } 664 XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel(); 665 columnModel.getColumnByModelIndex(FORGETCOL).setHeaderValue(null); 666 columnModel.getColumnByModelIndex(QUERYCOL).setHeaderValue(null); 667 } 668 669 void editButton(Sensor s) { 670 jmri.jmrit.beantable.beanedit.SensorEditAction beanEdit = new jmri.jmrit.beantable.beanedit.SensorEditAction(); 671 beanEdit.setBean(s); 672 beanEdit.actionPerformed(null); 673 } 674 675 /** 676 * Show or hide the Debounce columns. 677 * USEGLOBALDELAY, ACTIVEDELAY, INACTIVEDELAY 678 * @param show true to display, false to hide. 679 * @param table the JTable to set column visibility on. 680 */ 681 public void showDebounce(boolean show, JTable table) { 682 XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel(); 683 TableColumn column = columnModel.getColumnByModelIndex(USEGLOBALDELAY); 684 columnModel.setColumnVisible(column, show); 685 column = columnModel.getColumnByModelIndex(ACTIVEDELAY); 686 columnModel.setColumnVisible(column, show); 687 column = columnModel.getColumnByModelIndex(INACTIVEDELAY); 688 columnModel.setColumnVisible(column, show); 689 } 690 691 /** 692 * Show or hide the Pullup column. 693 * PULLUPCOL 694 * @param show true to display, false to hide. 695 * @param table the JTable to set column visibility on. 696 */ 697 public void showPullUp(boolean show, JTable table) { 698 XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel(); 699 TableColumn column = columnModel.getColumnByModelIndex(PULLUPCOL); 700 columnModel.setColumnVisible(column, show); 701 } 702 703 /** 704 * Show or hide the State - Forget and Query columns.FORGETCOL, QUERYCOL 705 * @param show true to display, false to hide. 706 * @param table the JTable to set column visibility on. 707 */ 708 public void showStateForgetAndQuery(boolean show, JTable table) { 709 XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel(); 710 TableColumn column = columnModel.getColumnByModelIndex(FORGETCOL); 711 columnModel.setColumnVisible(column, show); 712 column = columnModel.getColumnByModelIndex(QUERYCOL); 713 columnModel.setColumnVisible(column, show); 714 } 715 716 protected String getClassName() { 717 return jmri.jmrit.beantable.SensorTableAction.class.getName(); 718 } 719 720 public String getClassDescription() { 721 return Bundle.getMessage("TitleSensorTable"); 722 } 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override 728 protected void setColumnIdentities(JTable table) { 729 super.setColumnIdentities(table); 730 Enumeration<TableColumn> columns; 731 if (table.getColumnModel() instanceof XTableColumnModel) { 732 columns = ((XTableColumnModel) table.getColumnModel()).getColumns(false); 733 } else { 734 columns = table.getColumnModel().getColumns(); 735 } 736 while (columns.hasMoreElements()) { 737 TableColumn column = columns.nextElement(); 738 switch (column.getModelIndex()) { 739 case FORGETCOL: 740 column.setIdentifier("ForgetState"); 741 break; 742 case QUERYCOL: 743 column.setIdentifier("QueryState"); 744 break; 745 default: 746 // use existing value 747 } 748 } 749 } 750 751 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SensorTableDataModel.class); 752 753}