001package jmri.jmrit.beantable; 002 003import jmri.jmrit.beantable.oblock.*; 004import jmri.swing.RowSorterUtil; 005import jmri.util.swing.XTableColumnModel; 006import jmri.util.table.ButtonEditor; 007import jmri.util.table.ButtonRenderer; 008import jmri.util.table.ToggleButtonEditor; 009import jmri.util.table.ToggleButtonRenderer; 010 011import javax.annotation.CheckForNull; 012import javax.annotation.Nonnull; 013 014import javax.swing.*; 015import javax.swing.table.TableModel; 016import javax.swing.table.TableRowSorter; 017import java.awt.*; 018import java.util.Objects; 019 020/** 021 * GUI for tabbed OBlock editing since 2020. Based on AudioTablePanel. 022 * OBlock parts adapted from {@link jmri.jmrit.beantable.oblock.TableFrames} 023 * Which interface will be presented is user settable in Display prefs. 024 * 025 * @author Bob Jacobsen Copyright (C) 2003 026 * @author Matthew Harris copyright (c) 2009 027 * @author Egbert Broerse copyright (c) 2020 028 */ 029public class OBlockTablePanel extends JPanel { 030 031 private OBlockTableModel oblockDataModel; 032 private PortalTableModel portalDataModel; 033 private SignalTableModel signalDataModel; 034 private BlockPortalTableModel blockportalDataModel; 035 036 private JTable oblockTable; 037 private JTable portalTable; 038 private JTable signalTable; 039 private JTable blockportalTable; 040 041 private JScrollPane oblockDataScroll; 042 private JScrollPane portalDataScroll; 043 private JScrollPane signalDataScroll; 044 private JScrollPane blockportalDataScroll; 045 046 private final JTabbedPane oblockTabs; 047 TableFrames _tf; 048 private final JPanel bottomBox; // panel at bottom for extra buttons etc 049 050// @SuppressWarnings("OverridableMethodCallInConstructor") 051 public OBlockTablePanel(OBlockTableModel oblocks, 052 PortalTableModel portals, 053 SignalTableModel signals, 054 BlockPortalTableModel blockportals, 055 TableFrames tf, 056 String helpTarget) { 057 058 super(); // required? nothing set 059 _tf = tf; 060 061 log.debug("Building tables"); 062 063 // OBlock Table 064 oblockDataModel = oblocks; 065 TableRowSorter<OBlockTableModel> sorter = new TableRowSorter<>(oblockDataModel); 066 // use NamedBean's built-in Comparator interface for sorting the system name column 067 RowSorterUtil.setSortOrder(sorter, OBlockTableModel.SYSNAMECOL, SortOrder.ASCENDING); 068 oblockTable = makeJTable(OBlockTableAction.class.getName(), oblockDataModel, sorter); // use our own 069 // style table, check overlap with configureWarrantTable done next 070 oblockTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton())); 071 oblockTable.setDefaultRenderer(JButton.class, new ButtonRenderer()); 072 oblockTable.getColumnModel().getColumn(OBlockTableModel.UNITSCOL).setCellRenderer( 073 new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in"))); 074 oblockTable.getColumnModel().getColumn(OBlockTableModel.UNITSCOL).setCellEditor( 075 new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in"))); 076 oblocks.configCurveColumn(oblockTable); // use real combo 077 oblockTable.getColumnModel().getColumn(OBlockTableModel.REPORT_CURRENTCOL).setCellRenderer( 078 new ToggleButtonRenderer(Bundle.getMessage("Current"), Bundle.getMessage("Last"))); 079 oblockTable.getColumnModel().getColumn(OBlockTableModel.REPORT_CURRENTCOL).setCellEditor( 080 new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Current"), Bundle.getMessage("Last"))); 081 oblocks.configSpeedColumn(oblockTable); // use real combo 082 oblockTable.getColumnModel().getColumn(OBlockTableModel.PERMISSIONCOL).setCellRenderer( 083 new ToggleButtonRenderer(Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute"))); 084 oblockTable.getColumnModel().getColumn(OBlockTableModel.PERMISSIONCOL).setCellEditor( 085 new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute"))); 086 // Use XTableColumnModel so we can control which columns are visible 087 XTableColumnModel tcm = new XTableColumnModel(); 088 oblockTable.setColumnModel(tcm); 089 oblockTable.getTableHeader().setReorderingAllowed(true); // makeJTable not used for oblockTable 090 oblockTable.createDefaultColumnsFromModel(); 091 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.REPORTERCOL), false); // doesn't hide them? 092 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.REPORT_CURRENTCOL), false); 093 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.PERMISSIONCOL), false); 094 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.WARRANTCOL), false); 095 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.ERR_SENSORCOL), false); 096 tcm.setColumnVisible(tcm.getColumnByModelIndex(OBlockTableModel.CURVECOL), false); 097 for (int i = 0; i < tcm.getColumnCount(); i++) { 098 int width = oblockDataModel.getPreferredWidth(i); 099 tcm.getColumn(i).setPreferredWidth(width); 100 } 101 oblockDataModel.addHeaderListener(oblockTable); // HeaderListeners not set up for the other 3 small tables 102 oblockTable.setPreferredScrollableViewportSize(new java.awt.Dimension(550, 300)); // a wide table 103 oblockDataScroll = new JScrollPane(oblockTable); 104 105 // Portal Table 106 portalDataModel = portals; 107 TableRowSorter<PortalTableModel> portalsorter = new TableRowSorter<>(portalDataModel); 108 RowSorterUtil.setSortOrder(portalsorter, portalDataModel.NAME_COLUMN, SortOrder.ASCENDING); 109 portalTable = makeJTable("Portal", portalDataModel, portalsorter); 110 // style table 111 portalTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton())); 112 portalTable.setDefaultRenderer(JButton.class, new ButtonRenderer()); 113 portalTable.doLayout(); 114 //portalTable.setColumnModel(new XTableColumnModel()); 115 portalTable.createDefaultColumnsFromModel(); 116 for (int i = 0; i < portalDataModel.getColumnCount(); i++) { 117 int width = portalDataModel.getPreferredWidth(i); 118 portalTable.getColumnModel().getColumn(i).setPreferredWidth(width); 119 } 120 portalDataScroll = new JScrollPane(portalTable); 121 122 // Signal Table 123 signalDataModel = signals; 124 TableRowSorter<SignalTableModel> sigsorter = new TableRowSorter<>(signalDataModel); 125 RowSorterUtil.setSortOrder(sigsorter, SignalTableModel.NAME_COLUMN, SortOrder.ASCENDING); 126 signalTable = makeJTable("Signals", signalDataModel, sigsorter); 127 // style table 128 signalTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton())); 129 signalTable.setDefaultRenderer(JButton.class, new ButtonRenderer()); 130 signalTable.getColumnModel().getColumn(SignalTableModel.UNITSCOL).setCellRenderer( 131 new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in"))); 132 signalTable.getColumnModel().getColumn(SignalTableModel.UNITSCOL).setCellEditor( 133 new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in"))); 134 signalTable.doLayout(); 135 //signalTable.setColumnModel(new XTableColumnModel()); 136 signalTable.createDefaultColumnsFromModel(); 137 for (int i = 0; i < signalDataModel.getColumnCount(); i++) { 138 int width = SignalTableModel.getPreferredWidth(i); 139 signalTable.getColumnModel().getColumn(i).setPreferredWidth(width); 140 } 141 signalDataScroll = new JScrollPane(signalTable); 142 143 // Block-Portal Xreference table 144 blockportalDataModel = blockportals; // cross-reference (not editable) 145 //sorter = new TableRowSorter<>(blockportalDataModel); 146 RowSorterUtil.setSortOrder(sorter, BlockPortalTableModel.BLOCK_NAME_COLUMN, SortOrder.ASCENDING); 147 blockportalTable = makeJTable("Block-Portal X-ref", blockportalDataModel, sorter); // cannot directly access 148 // style table 149 blockportalTable.setDefaultRenderer(String.class, new jmri.jmrit.symbolicprog.ValueRenderer()); 150 blockportalTable.doLayout(); 151 //blockportalTable.setColumnModel(new XTableColumnModel()); 152 blockportalTable.createDefaultColumnsFromModel(); 153 for (int i = 0; i < blockportalDataModel.getColumnCount(); i++) { 154 int width = blockportalDataModel.getPreferredWidth(i); 155 blockportalTable.getColumnModel().getColumn(i).setPreferredWidth(width); 156 } 157 blockportalDataScroll = new JScrollPane(blockportalTable); 158 159 // configure items for GUI 160 configureWarrantTable(oblockTable); // only class to extend BeanTableDataModel 161 //oblockDataModel.configEditColumn(oblockTable); 162 for (int i = 0; i < oblockTable.getColumnCount(); i++) { 163 // copied from TableFrames#makeOBlockTable() l729 as it needs table so can't copy to oblockDataModel 164 int width = oblockDataModel.getPreferredWidth(i); 165 oblockTable.getColumnModel().getColumn(i).setPreferredWidth(width); 166 } 167 oblockDataModel.persistTable(oblockTable); // only oblockDataModel contains this method 168 169 configureWarrantTable(signalTable); 170 // pathDataModel.configEditColumn(pathTable); 171 //oblockDataModel.persistTable(signalTable); 172 173 configureWarrantTable(portalTable); 174 // portalDataModel.configEditColumn(portalTable); 175 //oblockDataModel.persistTable(portalTable); 176 177 configureWarrantTable(blockportalTable); 178 // portalDataModel.configEditColumn(blockportalTable); 179 //oblockDataModel.persistTable(blockportalTable); 180 181 // add more changeListeners for table (example load, created) to update tables? 182 183 // general GUI config 184 this.setLayout(new BorderLayout()); 185 186 // install the four items in GUI as tabs 187 oblockTabs = new JTabbedPane(); 188 oblockTabs.addTab(Bundle.getMessage("BeanNameOBlocks"), oblockDataScroll); 189 oblockTabs.addTab(Bundle.getMessage("BeanNamePortals"), portalDataScroll); 190 oblockTabs.addTab(Bundle.getMessage("Signals"), signalDataScroll); 191 oblockTabs.addTab(Bundle.getMessage("TitleBlockPortalXRef"), blockportalDataScroll); 192 // turnouts not on a tab: via Edit button in Path Edit pane (or a Tables submenu) 193 194 add(oblockTabs, BorderLayout.CENTER); 195 log.debug("tabs complete"); 196 197 bottomBox = new JPanel(); 198 bottomBox.setLayout(new jmri.util.swing.WrapLayout(jmri.util.swing.WrapLayout.LEFT,20,5)); 199 200 add(bottomBox, BorderLayout.SOUTH); 201 202 log.debug("bottomBox complete"); 203 // set preferred scrolling options 204 oblockDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 205// portalDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 206// signalDataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 207// blockportalDataScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 208 } 209 210 public JMenuItem getPrintItem() { // copied from AudioTablePanel 211 log.debug("OBLOCK TABBED getPrintItem() called"); 212 return _tf.getPrintMenuItems(oblockTable, portalTable, signalTable, blockportalTable); 213 } 214 215 public JMenu getOptionMenu() { 216 log.debug("OBLOCK TABBED getOptionMenu() called"); 217 return _tf.getOptionMenu(); 218 } 219 220 public JMenu getTablesMenu() { 221 log.debug("OBLOCK TABBED getTablesMenu() called"); 222 return _tf.getTablesMenu(); 223 } 224 225 /** 226 * Add a component to the bottom box. 227 * @param comp {@link Component} to add 228 */ 229 protected void addToBottomBox(Component comp) { 230 bottomBox.add(comp); 231 } 232 233 public void dispose() { 234 if (oblockDataModel != null) { 235 oblockDataModel.stopPersistingTable(oblockTable); 236 oblockDataModel.dispose(); 237 } 238 oblockDataModel = null; 239 oblockTable = null; 240 oblockDataScroll = null; 241 242 //if (portalDataModel != null) { 243 // portalDataModel.stopPersistingTable(portalTable); 244 // portalDataModel.dispose(); 245 //} 246 portalDataModel = null; 247 portalTable = null; 248 portalDataScroll = null; 249 250 //if (signalDataModel != null) { 251 // signalDataModel.stopPersistingTable(signalTable); 252 // signalDataModel.dispose(); 253 //} 254 signalDataModel = null; 255 signalTable = null; 256 signalDataScroll = null; 257 258 //if (blockportalDataModel != null) { 259 // blockportalDataModel.stopPersistingTable(blockportalTable); 260 // blockportalDataModel.dispose(); 261 //} 262 blockportalDataModel = null; 263 blockportalTable = null; 264 blockportalDataScroll = null; 265 } 266 267 /** 268 * Create and configure a new table using the given model and row sorter. 269 * 270 * @param name the name of the table 271 * @param model the data model for the table 272 * @param sorter the row sorter for the table; if null, the table will not 273 * be sortable 274 * @return the table 275 * @throws NullPointerException if name or model is null 276 */ 277 public JTable makeJTable(@Nonnull String name, @Nonnull TableModel model, @CheckForNull RowSorter<? extends TableModel> sorter) { 278 Objects.requireNonNull(name, "the table name must be nonnull " + name); 279 Objects.requireNonNull(model, "the table model must be nonnull " + name); 280 JTable table = this.configureJTable(name, new JTable(model), sorter); 281 //model.addHeaderListener(table); 282 return table; 283 } 284 285 /** 286 * Configure a new table using the given model and row sorter. 287 * 288 * @param table the table to configure 289 * @param name the table name 290 * @param sorter the row sorter for the table; if null, the table will not 291 * be sortable 292 * @return the table 293 * @throws NullPointerException if table or the table name is null 294 */ 295 protected JTable configureJTable(@Nonnull String name, @Nonnull JTable table, @CheckForNull RowSorter<? extends TableModel> sorter) { 296 Objects.requireNonNull(table, "the table must be nonnull"); 297 Objects.requireNonNull(name, "the table name must be nonnull"); 298 table.setRowSorter(sorter); 299 table.setName(name); 300 //table.getTableHeader().setReorderingAllowed(true); // already assigned per table above 301 //table.setColumnModel(new XTableColumnModel()); 302 //table.createDefaultColumnsFromModel(); 303 return table; 304 } 305 306 /** 307 * Configure a table to have our standard rows and columns. 308 * This also persists the table user interface state. 309 * Adapted from {@link BeanTableDataModel} for tables 1-4, EBR 2020 310 * 311 * @param table {@link JTable} to configure 312 */ 313 public void configureWarrantTable(JTable table) { 314 // ignore Property columns 315 table.setDefaultRenderer(JButton.class, new ButtonRenderer()); 316 table.setDefaultEditor(JButton.class, new ButtonEditor(new JButton())); 317 table.setDefaultRenderer(JToggleButton.class, new ToggleButtonRenderer(Bundle.getMessage("cm"), Bundle.getMessage("in"))); // overrides 318 table.setDefaultEditor(JToggleButton.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("cm"), Bundle.getMessage("in"))); 319 table.setDefaultRenderer(JRadioButton.class, new ToggleButtonRenderer(Bundle.getMessage("Current"), Bundle.getMessage("Last"))); // overrides 320 table.setDefaultEditor(JRadioButton.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Current"), Bundle.getMessage("Last"))); 321 table.setDefaultRenderer(JCheckBox.class, new ToggleButtonRenderer(Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute"))); 322 table.setDefaultEditor(JCheckBox.class, new ToggleButtonEditor(new JToggleButton(), Bundle.getMessage("Permissive"), Bundle.getMessage("Absolute"))); 323 table.setDefaultEditor(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel()); 324 table.setDefaultRenderer(OBlockTableModel.SpeedComboBoxPanel.class, new OBlockTableModel.SpeedComboBoxPanel()); 325 table.setDefaultEditor(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel()); 326 table.setDefaultRenderer(OBlockTableModel.CurveComboBoxPanel.class, new OBlockTableModel.CurveComboBoxPanel()); 327 // allow reordering of the columns 328 //table.getTableHeader().setReorderingAllowed(true); 329 // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 330 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 331 table.setRowHeight(TableFrames.ROW_HEIGHT); 332 // resize columns per table 333// table.doLayout(); 334 // resize columns as requested (for OBlocks tabbed: throws java.lang.IllegalArgumentException: "Identifier not found") 335// for (int i = 0; i < table.getColumnCount(); i++) { 336// int width = table.getColumn(i).getPreferredWidth(); 337// table.getColumnModel().getColumn(i).setPreferredWidth(width); 338// } 339 } 340 341 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OBlockTablePanel.class); 342 343}